From d6b5e81a57316d3698cef08a6c1b24bd66140ebc Mon Sep 17 00:00:00 2001 From: AmAzing- <115673583+AmAzing129@users.noreply.github.com> Date: Mon, 18 May 2026 11:41:33 +0800 Subject: [PATCH 001/224] =?UTF-8?q?=F0=9F=90=9B=20fix(agent-signal):=20per?= =?UTF-8?q?sist=20memory=20receipt=20routing=20metadata=20(#14912)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AgentSignalReceiptList.test.tsx | 34 ++ .../components/AgentSignalReceiptList.tsx | 29 +- .../actions/__tests__/userMemory.test.ts | 359 +++++++++++++++++- .../analyzeIntent/actions/userMemory.ts | 248 +++++++++++- .../services/__tests__/receiptService.test.ts | 54 ++- .../agentSignal/services/receiptService.ts | 23 +- .../store/__tests__/redisReceiptStore.test.ts | 7 + .../store/adapters/redis/receiptStore.ts | 13 + 8 files changed, 736 insertions(+), 31 deletions(-) diff --git a/src/features/Conversation/ChatList/components/AgentSignalReceiptList.test.tsx b/src/features/Conversation/ChatList/components/AgentSignalReceiptList.test.tsx index bc8f0aac45..fc87752efc 100644 --- a/src/features/Conversation/ChatList/components/AgentSignalReceiptList.test.tsx +++ b/src/features/Conversation/ChatList/components/AgentSignalReceiptList.test.tsx @@ -1,3 +1,4 @@ +import { LayersEnum } from '@lobechat/types'; import { fireEvent, render, screen } from '@testing-library/react'; import { afterEach, describe, expect, it, vi } from 'vitest'; @@ -262,4 +263,37 @@ describe('AgentSignalReceiptList', () => { expect(mocks.navigate).toHaveBeenCalledWith('/memory'); }); + + it('opens memory receipts on their layer detail route when target metadata is available', () => { + render( + , + ); + + fireEvent.click(screen.getByText('Open')); + + expect(mocks.navigate).toHaveBeenCalledWith('/memory/preferences?preferenceId=preference-1'); + }); }); diff --git a/src/features/Conversation/ChatList/components/AgentSignalReceiptList.tsx b/src/features/Conversation/ChatList/components/AgentSignalReceiptList.tsx index 4a83788678..9a2dc75f10 100644 --- a/src/features/Conversation/ChatList/components/AgentSignalReceiptList.tsx +++ b/src/features/Conversation/ChatList/components/AgentSignalReceiptList.tsx @@ -1,5 +1,6 @@ 'use client'; +import { LayersEnum } from '@lobechat/types'; import { Icon } from '@lobehub/ui'; import { createStaticStyles } from 'antd-style'; import type { LucideIcon } from 'lucide-react'; @@ -14,6 +15,13 @@ import { useChatStore } from '@/store/chat'; import type { AgentSignalReceiptView } from '../hooks/useAgentSignalReceipts'; const PAGE_ROUTE_PATTERN = /^\/agent\/([^/]+)\/([^/]+)\/page(?:\/[^/?#]+)?/; +const MEMORY_ROUTE_BY_LAYER = { + [LayersEnum.Activity]: { idParam: 'activityId', path: '/memory/activities' }, + [LayersEnum.Context]: { idParam: 'contextId', path: '/memory/contexts' }, + [LayersEnum.Experience]: { idParam: 'experienceId', path: '/memory/experiences' }, + [LayersEnum.Identity]: { idParam: 'identityId', path: '/memory/identities' }, + [LayersEnum.Preference]: { idParam: 'preferenceId', path: '/memory/preferences' }, +} satisfies Record; const styles = createStaticStyles(({ css, cssVar }) => ({ agentSignalDescription: css` @@ -58,6 +66,17 @@ interface AgentSignalReceiptItemProps { receipt: AgentSignalReceiptView; } +const getMemoryRoute = (target?: AgentSignalReceiptView['target']) => { + if (target?.type !== 'memory') return; + + if (!target.memoryLayer) return '/memory'; + + const route = MEMORY_ROUTE_BY_LAYER[target.memoryLayer]; + if (!route) return '/memory'; + + return target.id ? `${route.path}?${route.idParam}=${encodeURIComponent(target.id)}` : route.path; +}; + const AgentSignalReceiptItem = memo(({ receipt }) => { const { t } = useTranslation(['chat', 'common']); const navigate = useStableNavigate(); @@ -84,10 +103,11 @@ const AgentSignalReceiptItem = memo(({ receipt }) = ); const target = receipt.target; const documentId = target?.type === 'skill' ? (target.documentId ?? target.id) : undefined; - const canOpen = target?.type === 'memory' || Boolean(documentId); + const memoryRoute = getMemoryRoute(target); + const canOpen = Boolean(memoryRoute) || Boolean(documentId); const handleOpen = useCallback(() => { - if (target?.type === 'memory') { - navigate('/memory'); + if (memoryRoute) { + navigate(memoryRoute); return; } @@ -103,7 +123,7 @@ const AgentSignalReceiptItem = memo(({ receipt }) = } openDocument(documentId); - }, [documentId, navigate, openDocument, target]); + }, [documentId, memoryRoute, navigate, openDocument, target]); return ( (({ receipt }) = openLabel={canOpen ? t('common:cmdk.toOpen', 'Open') : undefined} title={title} tooltip={tooltip} - // TODO: Replace memory fallback with category/id-aware routes when Agent Signal receipts expose them. onOpen={canOpen ? handleOpen : undefined} /> ); diff --git a/src/server/services/agentSignal/policies/analyzeIntent/actions/__tests__/userMemory.test.ts b/src/server/services/agentSignal/policies/analyzeIntent/actions/__tests__/userMemory.test.ts index b41a0a18b4..67324c6b86 100644 --- a/src/server/services/agentSignal/policies/analyzeIntent/actions/__tests__/userMemory.test.ts +++ b/src/server/services/agentSignal/policies/analyzeIntent/actions/__tests__/userMemory.test.ts @@ -1,8 +1,9 @@ // @vitest-environment node +import { LayersEnum } from '@lobechat/types'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import type { RuntimeProcessorContext } from '../../../../runtime/context'; -import { defineUserMemoryActionHandler } from '../userMemory'; +import { defineUserMemoryActionHandler, resolveMemoryActionTargetFromState } from '../userMemory'; const memoryActionRunner = vi.fn(); @@ -73,6 +74,64 @@ describe('defineUserMemoryActionHandler', () => { expect(context.runtimeState.touchGuardState).toHaveBeenCalledTimes(1); }); + it('returns the applied memory target from the memory agent runner', async () => { + memoryActionRunner.mockResolvedValue({ + detail: 'Arvin Xu 希望助手在输出时每个段落/模块都写得更长、更展开。', + status: 'applied', + target: { + id: 'mem_td3XirTeX4f7', + memoryId: 'mem_8gISOK6BhxGP', + memoryLayer: LayersEnum.Preference, + summary: 'Arvin Xu 希望助手在输出时每个段落/模块都写得更长、更展开。', + title: '偏好更详细、更长的回答段落', + type: 'memory', + }, + }); + + const handler = defineUserMemoryActionHandler({ + db: {} as never, + memoryActionRunner, + userId: 'user_1', + }); + + const result = await handler.handle( + { + actionId: 'act_memory_target', + actionType: 'action.user-memory.handle', + chain: { chainId: 'chain_1', rootSourceId: 'source_1' }, + payload: { + agentId: 'agent_1', + idempotencyKey: 'source_1:memory:msg_1', + message: + '\n每一块都有点太短了?能否长一点呢', + topicId: 'topic_1', + }, + signal: { + signalId: 'sig_1', + signalType: 'signal.feedback.domain.memory', + }, + source: { sourceId: 'source_1', sourceType: 'agent.user.message' }, + timestamp: 1, + }, + context, + ); + + expect(result).toMatchObject({ + detail: 'Arvin Xu 希望助手在输出时每个段落/模块都写得更长、更展开。', + output: { + target: { + id: 'mem_td3XirTeX4f7', + memoryId: 'mem_8gISOK6BhxGP', + memoryLayer: LayersEnum.Preference, + summary: 'Arvin Xu 希望助手在输出时每个段落/模块都写得更长、更展开。', + title: '偏好更详细、更长的回答段落', + type: 'memory', + }, + }, + status: 'applied', + }); + }); + it('skips memory actions when the feedback message is missing', async () => { const handler = defineUserMemoryActionHandler({ db: {} as never, @@ -241,3 +300,301 @@ describe('defineUserMemoryActionHandler', () => { expect(memoryActionRunner).toHaveBeenCalledTimes(2); }); }); + +describe('resolveMemoryActionTargetFromState', () => { + it('extracts the successful memory title from runtime tool calls', () => { + const target = resolveMemoryActionTargetFromState({ + messages: [ + { + id: 'msg_bad', + role: 'assistant', + tool_calls: [ + { + function: { + arguments: '{,', + name: 'lobe-user-memory____addPreferenceMemory', + }, + id: 'call_bad', + type: 'function', + }, + ], + }, + { + content: 'The tool call arguments string is not valid JSON.', + role: 'tool', + tool_call_id: 'call_bad', + }, + { + id: 'msg_good', + role: 'assistant', + tool_calls: [ + { + function: { + arguments: JSON.stringify({ + details: '用户反馈当前回复的每个模块都太短,希望后续展开得更充分。', + summary: 'Arvin Xu 希望助手在输出时每个段落/模块都写得更长、更展开。', + title: '偏好更详细、更长的回答段落', + withPreference: { + conclusionDirectives: '回答时展开每个段落和模块。', + }, + }), + name: 'lobe-user-memory____addPreferenceMemory', + }, + id: 'call_good', + type: 'function', + }, + ], + }, + { + content: + 'Preference memory "偏好更详细、更长的回答段落" saved with memoryId: "mem_8gISOK6BhxGP" and preferenceId: "mem_td3XirTeX4f7"', + role: 'tool', + tool_call_id: 'call_good', + }, + ], + } as never); + + expect(target).toEqual({ + id: 'mem_td3XirTeX4f7', + memoryId: 'mem_8gISOK6BhxGP', + memoryLayer: LayersEnum.Preference, + summary: 'Arvin Xu 希望助手在输出时每个段落/模块都写得更长、更展开。', + title: '偏好更详细、更长的回答段落', + type: 'memory', + }); + }); + + it('resolves update identity targets from nested set arguments', () => { + const target = resolveMemoryActionTargetFromState({ + messages: [ + { + id: 'msg_update_identity', + role: 'assistant', + tool_calls: [ + { + function: { + arguments: JSON.stringify({ + id: 'identity-existing', + mergeStrategy: 'replace', + set: { + details: 'The user clarified that they maintain LobeHub Agent Signal code.', + summary: 'The user maintains Agent Signal memory receipt behavior.', + title: 'Maintains Agent Signal receipts', + }, + }), + name: 'lobe-user-memory____updateIdentityMemory', + }, + id: 'call_update_identity', + type: 'function', + }, + ], + }, + { + content: 'Identity memory updated: identity-existing', + pluginState: { identityId: 'identity-existing' }, + role: 'tool', + tool_call_id: 'call_update_identity', + }, + ], + } as never); + + expect(target).toEqual({ + id: 'identity-existing', + memoryLayer: LayersEnum.Identity, + summary: 'The user maintains Agent Signal memory receipt behavior.', + title: 'Maintains Agent Signal receipts', + type: 'memory', + }); + }); + + it('resolves receipt targets from persisted tool snapshots', () => { + const target = resolveMemoryActionTargetFromState({ + messages: [ + { + id: 'msg_persisted_tool', + role: 'assistant', + tools: [ + null, + { + apiName: 'addPreferenceMemory', + arguments: { + title: 'Persisted preference title', + withPreference: { + conclusionDirectives: 'Use persisted tool metadata for receipt targets.', + }, + }, + id: 'call_persisted', + identifier: 'lobe-user-memory', + }, + ], + }, + { + content: 'Preference memory saved', + plugin: { id: 'call_persisted' }, + pluginState: { memoryId: 'mem_persisted', preferenceId: 'pref_persisted' }, + role: 'tool', + }, + ], + } as never); + + expect(target).toEqual({ + id: 'pref_persisted', + memoryId: 'mem_persisted', + memoryLayer: LayersEnum.Preference, + summary: 'Use persisted tool metadata for receipt targets.', + title: 'Persisted preference title', + type: 'memory', + }); + }); + + it('skips confirmed memory tool calls with invalid arguments', () => { + const target = resolveMemoryActionTargetFromState({ + messages: [ + { + id: 'msg_confirmed', + role: 'assistant', + tool_calls: [ + { + function: { + arguments: JSON.stringify({ + details: 'Fallback details for a valid confirmed target.', + title: 'Valid confirmed preference', + }), + name: 'lobe-user-memory____addPreferenceMemory', + }, + id: 'call_confirmed', + type: 'function', + }, + ], + }, + { + content: + 'Preference memory "Valid confirmed preference" saved with memoryId: "mem_confirmed" and preferenceId: "pref_confirmed"', + role: 'tool', + tool_call_id: 'call_confirmed', + }, + { + id: 'msg_invalid', + role: 'assistant', + tool_calls: [ + { + function: { + arguments: '{,', + name: 'lobe-user-memory____addPreferenceMemory', + }, + id: 'call_invalid', + type: 'function', + }, + ], + }, + { + content: + 'Preference memory "Invalid latest preference" saved with memoryId: "mem_invalid" and preferenceId: "pref_invalid"', + role: 'tool', + tool_call_id: 'call_invalid', + }, + ], + } as never); + + expect(target).toEqual({ + id: 'pref_confirmed', + memoryId: 'mem_confirmed', + memoryLayer: LayersEnum.Preference, + summary: 'Fallback details for a valid confirmed target.', + title: 'Valid confirmed preference', + type: 'memory', + }); + }); + + it('ignores unconfirmed memory write tool calls when resolving receipt targets', () => { + const target = resolveMemoryActionTargetFromState({ + messages: [ + { + id: 'msg_confirmed', + role: 'assistant', + tool_calls: [ + { + function: { + arguments: JSON.stringify({ + summary: 'The user prefers longer, more developed answers.', + title: 'Confirmed preference title', + }), + name: 'lobe-user-memory____addPreferenceMemory', + }, + id: 'call_confirmed', + type: 'function', + }, + ], + }, + { + content: + 'Preference memory "Confirmed preference title" saved with memoryId: "mem_confirmed" and preferenceId: "pref_confirmed"', + role: 'tool', + tool_call_id: 'call_confirmed', + }, + { + id: 'msg_unconfirmed', + role: 'assistant', + tool_calls: [ + { + function: { + arguments: JSON.stringify({ + summary: 'This write was not confirmed by a successful tool result.', + title: 'Unconfirmed preference title', + }), + name: 'lobe-user-memory____addPreferenceMemory', + }, + id: 'call_unconfirmed', + type: 'function', + }, + ], + }, + { + content: 'addPreferenceMemory with error detail: database timeout', + role: 'tool', + tool_call_id: 'call_unconfirmed', + }, + ], + } as never); + + expect(target).toEqual({ + id: 'pref_confirmed', + memoryId: 'mem_confirmed', + memoryLayer: LayersEnum.Preference, + summary: 'The user prefers longer, more developed answers.', + title: 'Confirmed preference title', + type: 'memory', + }); + }); + + it('does not resolve a target when no memory write has a successful tool result', () => { + const target = resolveMemoryActionTargetFromState({ + messages: [ + { + id: 'msg_unconfirmed', + role: 'assistant', + tool_calls: [ + { + function: { + arguments: JSON.stringify({ + summary: 'This write was not confirmed by a successful tool result.', + title: 'Unconfirmed preference title', + }), + name: 'lobe-user-memory____addPreferenceMemory', + }, + id: 'call_unconfirmed', + type: 'function', + }, + ], + }, + { + content: 'addPreferenceMemory with error detail: database timeout', + role: 'tool', + tool_call_id: 'call_unconfirmed', + }, + ], + } as never); + + expect(target).toBeUndefined(); + }); +}); diff --git a/src/server/services/agentSignal/policies/analyzeIntent/actions/userMemory.ts b/src/server/services/agentSignal/policies/analyzeIntent/actions/userMemory.ts index 597b77d80c..8aa23ca6b1 100644 --- a/src/server/services/agentSignal/policies/analyzeIntent/actions/userMemory.ts +++ b/src/server/services/agentSignal/policies/analyzeIntent/actions/userMemory.ts @@ -11,7 +11,7 @@ import { createAgentSignalMemoryWriterPrompt, createAgentSignalMemoryWriterSystemRole, } from '@lobechat/prompts'; -import { RequestTrigger } from '@lobechat/types'; +import { LayersEnum, RequestTrigger } from '@lobechat/types'; import { nanoid } from '@lobechat/utils'; import { PluginModel } from '@/database/models/plugin'; @@ -44,21 +44,45 @@ import { AGENT_SIGNAL_POLICY_ACTION_TYPES } from '../../types'; const MEMORY_AGENT_MAX_STEPS = 8; +const MEMORY_WRITE_API_NAMES = [ + MemoryApiName.addActivityMemory, + MemoryApiName.addContextMemory, + MemoryApiName.addExperienceMemory, + MemoryApiName.addIdentityMemory, + MemoryApiName.addPreferenceMemory, + MemoryApiName.removeIdentityMemory, + MemoryApiName.updateIdentityMemory, +] as const; + const MEMORY_WRITE_TOOL_NAMES = new Set( - [ - MemoryApiName.addActivityMemory, - MemoryApiName.addContextMemory, - MemoryApiName.addExperienceMemory, - MemoryApiName.addIdentityMemory, - MemoryApiName.addPreferenceMemory, - MemoryApiName.removeIdentityMemory, - MemoryApiName.updateIdentityMemory, - ].map((apiName) => `${MemoryIdentifier}/${apiName}`), + MEMORY_WRITE_API_NAMES.map((apiName) => `${MemoryIdentifier}/${apiName}`), ); +const MEMORY_WRITE_API_NAME_SET = new Set(MEMORY_WRITE_API_NAMES); +const MEMORY_WRITE_TARGET_BY_API_NAME: Record = { + [MemoryApiName.addActivityMemory]: { idKey: 'activityId', layer: LayersEnum.Activity }, + [MemoryApiName.addContextMemory]: { idKey: 'contextId', layer: LayersEnum.Context }, + [MemoryApiName.addExperienceMemory]: { idKey: 'experienceId', layer: LayersEnum.Experience }, + [MemoryApiName.addIdentityMemory]: { idKey: 'identityId', layer: LayersEnum.Identity }, + [MemoryApiName.addPreferenceMemory]: { idKey: 'preferenceId', layer: LayersEnum.Preference }, + [MemoryApiName.removeIdentityMemory]: { idKey: 'identityId', layer: LayersEnum.Identity }, + [MemoryApiName.updateIdentityMemory]: { idKey: 'identityId', layer: LayersEnum.Identity }, +}; +const TOOL_NAME_SEPARATOR = '____'; + +export interface MemoryActionTarget { + id?: string; + memoryId?: string; + memoryLayer?: LayersEnum; + summary?: string; + title: string; + type: 'memory'; +} + export interface MemoryAgentActionResult { detail?: string; status: 'applied' | 'failed' | 'skipped'; + target?: MemoryActionTarget; } export interface UserMemoryActionHandlerOptions { @@ -153,6 +177,196 @@ const hasFailedMemoryWrite = (state: AgentState) => { ); }; +const isRecord = (value: unknown): value is Record => + typeof value === 'object' && value !== null && !Array.isArray(value); + +const getString = (value: unknown) => { + return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined; +}; + +const parseToolArguments = (value: unknown): Record | undefined => { + if (isRecord(value)) return value; + + if (typeof value !== 'string') return; + + try { + const parsed: unknown = JSON.parse(value); + + return isRecord(parsed) ? parsed : undefined; + } catch { + return; + } +}; + +interface MemoryToolCallSnapshot { + apiName?: string; + arguments?: unknown; + id?: string; + identifier?: string; +} + +const getToolCallsFromMessage = (message: unknown): MemoryToolCallSnapshot[] => { + if (!isRecord(message)) return []; + + const toolCalls: MemoryToolCallSnapshot[] = []; + const persistedTools = Array.isArray(message.tools) ? message.tools : []; + + for (const tool of persistedTools) { + if (!isRecord(tool)) continue; + + toolCalls.push({ + apiName: getString(tool.apiName), + arguments: tool.arguments, + id: getString(tool.id), + identifier: getString(tool.identifier), + }); + } + + const rawToolCalls = Array.isArray(message.tool_calls) ? message.tool_calls : []; + + for (const toolCall of rawToolCalls) { + if (!isRecord(toolCall)) continue; + + const fn = isRecord(toolCall.function) ? toolCall.function : undefined; + const name = getString(fn?.name); + if (!name) continue; + + const [identifier, apiName] = name.split(TOOL_NAME_SEPARATOR); + + toolCalls.push({ + apiName: apiName || name, + arguments: fn?.arguments, + id: getString(toolCall.id), + identifier: apiName ? identifier : undefined, + }); + } + + return toolCalls; +}; + +const isMemoryWriteToolCall = ( + toolCall: MemoryToolCallSnapshot, +): toolCall is MemoryToolCallSnapshot & { apiName: string } => { + if (!toolCall.apiName || !MEMORY_WRITE_API_NAME_SET.has(toolCall.apiName)) return false; + + return !toolCall.identifier || toolCall.identifier === MemoryIdentifier; +}; + +const getToolMessageCallId = (message: unknown) => { + if (!isRecord(message)) return; + + const plugin = isRecord(message.plugin) ? message.plugin : undefined; + + return getString(message.tool_call_id) ?? getString(plugin?.id); +}; + +const getMemoryIdsFromToolMessage = (message: unknown) => { + if (!isRecord(message)) return; + + const ids: Record = {}; + const addId = (key: string, value: unknown) => { + if (!key.endsWith('Id')) return; + + const id = getString(value); + if (id) ids[key] = id; + }; + + const pluginState = isRecord(message.pluginState) ? message.pluginState : undefined; + if (pluginState) { + for (const [key, value] of Object.entries(pluginState)) { + addId(key, value); + } + } + + const content = getString(message.content); + if (content) { + for (const match of content.matchAll(/([A-Za-z]\w*Id):\s*"([^"]+)"/g)) { + addId(match[1], match[2]); + } + } + + return Object.keys(ids).length > 0 ? ids : undefined; +}; + +const getMemoryToolResultIds = (state: AgentState) => { + const resultIds = new Map>(); + + for (const message of state.messages ?? []) { + const callId = getToolMessageCallId(message); + const ids = getMemoryIdsFromToolMessage(message); + + if (callId && ids) resultIds.set(callId, ids); + } + + return resultIds; +}; + +const getNestedString = (payload: Record, keys: string[]) => { + let current: unknown = payload; + + for (const key of keys) { + if (!isRecord(current)) return; + + current = current[key]; + } + + return getString(current); +}; + +const getToolArgumentString = (args: Record, key: string) => { + return getString(args[key]) ?? getNestedString(args, ['set', key]); +}; + +const createTargetFromToolArguments = ( + args: Record, + toolCall: MemoryToolCallSnapshot & { apiName: string }, + resultIds?: Record, +): MemoryActionTarget | undefined => { + const title = getToolArgumentString(args, 'title'); + if (!title) return; + + const targetConfig = MEMORY_WRITE_TARGET_BY_API_NAME[toolCall.apiName]; + const id = targetConfig ? resultIds?.[targetConfig.idKey] : undefined; + const memoryId = resultIds?.memoryId; + const summary = + getToolArgumentString(args, 'summary') ?? + getToolArgumentString(args, 'details') ?? + getNestedString(args, ['withPreference', 'conclusionDirectives']); + + return { + ...((id ?? memoryId) ? { id: id ?? memoryId } : {}), + ...(memoryId ? { memoryId } : {}), + ...(targetConfig ? { memoryLayer: targetConfig.layer } : {}), + ...(summary ? { summary } : {}), + title, + type: 'memory', + }; +}; + +export const resolveMemoryActionTargetFromState = ( + state: AgentState, +): MemoryActionTarget | undefined => { + const resultIds = getMemoryToolResultIds(state); + + for (const message of [...(state.messages ?? [])].reverse()) { + const toolCalls = getToolCallsFromMessage(message).reverse(); + + for (const toolCall of toolCalls) { + if (!isMemoryWriteToolCall(toolCall)) continue; + if (!toolCall.id) continue; + + const confirmedResultIds = resultIds.get(toolCall.id); + if (!confirmedResultIds) continue; + + const args = parseToolArguments(toolCall.arguments); + if (!args) continue; + + const target = createTargetFromToolArguments(args, toolCall, confirmedResultIds); + if (target) return target; + } + } +}; + export const runMemoryActionAgent = async ( input: { agentId?: string; @@ -289,7 +503,13 @@ export const runMemoryActionAgent = async ( } if (hasSuccessfulMemoryWrite(finalState)) { - return { status: 'applied' }; + const target = resolveMemoryActionTargetFromState(finalState); + + return { + ...(target?.summary ? { detail: target.summary } : {}), + status: 'applied', + ...(target ? { target } : {}), + }; } if (hasFailedMemoryWrite(finalState)) { @@ -372,13 +592,15 @@ export const handleUserMemoryAction = async ( topicId: typeof action.payload.topicId === 'string' ? action.payload.topicId : undefined, }; const runner = options.memoryActionRunner ?? ((input) => runMemoryActionAgent(input, options)); + let memoryActionResult: MemoryAgentActionResult | undefined; const memoryService = createMemoryService({ writeMemory: async () => { const result = await runner(runnerInput); + memoryActionResult = result; if (result.status === 'applied') { return { - memoryId: idempotencyKey ?? action.actionId, + memoryId: result.target?.id ?? idempotencyKey ?? action.actionId, summary: result.detail, }; } @@ -402,6 +624,7 @@ export const handleUserMemoryAction = async ( .then((writeResult) => ({ detail: writeResult.summary, status: 'applied', + ...(memoryActionResult?.target ? { target: memoryActionResult.target } : {}), })) .catch((error: unknown): MemoryAgentActionResult => { if (error instanceof MemoryActionError) { @@ -421,6 +644,7 @@ export const handleUserMemoryAction = async ( actionId: action.actionId, attempt: finalizeAttempt(startedAt, 'succeeded'), detail: result.detail, + ...(result.target ? { output: { target: result.target } } : {}), status: 'applied', }; } diff --git a/src/server/services/agentSignal/services/__tests__/receiptService.test.ts b/src/server/services/agentSignal/services/__tests__/receiptService.test.ts index fe87856d2e..7dc38fd9ed 100644 --- a/src/server/services/agentSignal/services/__tests__/receiptService.test.ts +++ b/src/server/services/agentSignal/services/__tests__/receiptService.test.ts @@ -1,6 +1,7 @@ // @vitest-environment node import type { BaseAction, ExecutorResult } from '@lobechat/agent-signal'; import { createSource } from '@lobechat/agent-signal'; +import { LayersEnum } from '@lobechat/types'; import { describe, expect, it, vi } from 'vitest'; import { AGENT_SIGNAL_POLICY_ACTION_TYPES } from '../../policies/types'; @@ -52,7 +53,7 @@ const result = (input: { }); describe('projectAgentSignalReceipts', () => { - it('projects applied memory action results', () => { + it('projects applied memory action results without unstructured feedback as target', () => { expect( projectAgentSignalReceipts({ actions: [ @@ -78,10 +79,6 @@ describe('projectAgentSignalReceipts', () => { sourceId: 'source-1', sourceType: 'client.gateway.runtime_end', status: 'applied', - target: { - title: 'Remember that future PR reviews should be decision-first.', - type: 'memory', - }, title: 'Memory saved', topicId: 'topic-1', userId: 'user-1', @@ -89,6 +86,53 @@ describe('projectAgentSignalReceipts', () => { ]); }); + it('prefers the memory target title from action output over the feedback message', () => { + expect( + projectAgentSignalReceipts({ + actions: [ + action({ + actionId: 'action-memory-1', + actionType: AGENT_SIGNAL_POLICY_ACTION_TYPES.userMemoryHandle, + payload: { + message: + '\nEvery section is too short. Can it be longer?', + }, + }), + ], + results: [ + result({ + actionId: 'action-memory-1', + output: { + target: { + id: 'preference_1', + memoryId: 'mem_1', + memoryLayer: LayersEnum.Preference, + summary: 'The user prefers longer, more developed answer sections.', + title: 'Preference for detailed answer sections', + type: 'memory', + }, + }, + status: 'applied', + }), + ], + source, + userId: 'user-1', + }), + ).toMatchObject([ + { + kind: 'memory', + target: { + id: 'preference_1', + memoryId: 'mem_1', + memoryLayer: LayersEnum.Preference, + summary: 'The user prefers longer, more developed answer sections.', + title: 'Preference for detailed answer sections', + type: 'memory', + }, + }, + ]); + }); + it('projects applied skill-management results as updated skill receipts', () => { expect( projectAgentSignalReceipts({ diff --git a/src/server/services/agentSignal/services/receiptService.ts b/src/server/services/agentSignal/services/receiptService.ts index 8297caa59d..5790210efb 100644 --- a/src/server/services/agentSignal/services/receiptService.ts +++ b/src/server/services/agentSignal/services/receiptService.ts @@ -1,4 +1,5 @@ import type { AgentSignalSource, BaseAction, ExecutorResult } from '@lobechat/agent-signal'; +import { LayersEnum } from '@lobechat/types'; import { AGENT_SIGNAL_DEFAULTS } from '../constants'; import { AGENT_SIGNAL_POLICY_ACTION_TYPES } from '../policies/types'; @@ -93,6 +94,10 @@ export interface AgentSignalReceipt { documentId?: string; /** Backing resource id for future navigation when still available. Skill ids use `documents.id`. */ id?: string; + /** User memory base id for audit and fallback lookup. */ + memoryId?: string; + /** User memory layer used to route memory receipts to their detail page. */ + memoryLayer?: LayersEnum; /** Short summary captured at write time. */ summary?: string; /** Human-readable resource title captured at write time. */ @@ -236,6 +241,10 @@ const getPayloadString = (payload: Record, key: string) => { const getClampedString = (value: string, maxLength = 96) => value.length > maxLength ? `${value.slice(0, maxLength - 1)}...` : value; +const isMemoryLayer = (value: unknown): value is LayersEnum => { + return Object.values(LayersEnum).includes(value as LayersEnum); +}; + const getReceiptTarget = ( action: BaseAction, result: ExecutorResult, @@ -262,6 +271,12 @@ const getReceiptTarget = ( ? { documentId: payload.documentId } : {}), ...(typeof payload.id === 'string' && payload.id.length > 0 ? { id: payload.id } : {}), + ...(type === 'memory' && typeof payload.memoryId === 'string' && payload.memoryId.length > 0 + ? { memoryId: payload.memoryId } + : {}), + ...(type === 'memory' && isMemoryLayer(payload.memoryLayer) + ? { memoryLayer: payload.memoryLayer } + : {}), ...(typeof payload.summary === 'string' && payload.summary.length > 0 ? { summary: payload.summary } : {}), @@ -272,14 +287,6 @@ const getReceiptTarget = ( } if (kind !== 'memory') return; - - const message = getPayloadString(action.payload, 'message')?.trim(); - if (!message) return; - - return { - title: getClampedString(message), - type: 'memory', - }; }; const toReceiptKind = ( diff --git a/src/server/services/agentSignal/store/__tests__/redisReceiptStore.test.ts b/src/server/services/agentSignal/store/__tests__/redisReceiptStore.test.ts index ce15194819..0b4eedc251 100644 --- a/src/server/services/agentSignal/store/__tests__/redisReceiptStore.test.ts +++ b/src/server/services/agentSignal/store/__tests__/redisReceiptStore.test.ts @@ -1,4 +1,5 @@ // @vitest-environment node +import { LayersEnum } from '@lobechat/types'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { @@ -27,6 +28,9 @@ const receipt = { sourceType: 'client.gateway.runtime_end', status: 'applied' as const, target: { + id: 'preference-1', + memoryId: 'memory-1', + memoryLayer: LayersEnum.Preference, summary: 'Use short answers in future chats', title: 'Short answer preference', type: 'memory' as const, @@ -61,6 +65,9 @@ describe('redis receipt store', () => { sourceType: 'client.gateway.runtime_end', status: 'applied', target: JSON.stringify({ + id: 'preference-1', + memoryId: 'memory-1', + memoryLayer: LayersEnum.Preference, summary: 'Use short answers in future chats', title: 'Short answer preference', type: 'memory', diff --git a/src/server/services/agentSignal/store/adapters/redis/receiptStore.ts b/src/server/services/agentSignal/store/adapters/redis/receiptStore.ts index 50388f5ec8..61963922f1 100644 --- a/src/server/services/agentSignal/store/adapters/redis/receiptStore.ts +++ b/src/server/services/agentSignal/store/adapters/redis/receiptStore.ts @@ -1,3 +1,5 @@ +import { LayersEnum } from '@lobechat/types'; + import { AGENT_SIGNAL_KEYS } from '../../../constants'; import type { AgentSignalReceipt } from '../../../services/receiptService'; import type { AgentSignalReceiptStore } from '../../types'; @@ -21,6 +23,9 @@ const toReceiptHash = (receipt: AgentSignalReceipt): Record => ( userId: receipt.userId, }); +const isMemoryLayer = (value: unknown): value is LayersEnum => + Object.values(LayersEnum).includes(value as LayersEnum); + const parseReceiptTarget = (value?: string): AgentSignalReceipt['target'] | undefined => { if (!value) return; @@ -38,6 +43,14 @@ const parseReceiptTarget = (value?: string): AgentSignalReceipt['target'] | unde ? { documentId: target.documentId } : {}), ...(typeof target.id === 'string' && target.id.length > 0 ? { id: target.id } : {}), + ...(target.type === 'memory' && + typeof target.memoryId === 'string' && + target.memoryId.length > 0 + ? { memoryId: target.memoryId } + : {}), + ...(target.type === 'memory' && isMemoryLayer(target.memoryLayer) + ? { memoryLayer: target.memoryLayer } + : {}), ...(typeof target.summary === 'string' && target.summary.length > 0 ? { summary: target.summary } : {}), From 6f423863454309dd835f4e4aeb9a7558526b97bd Mon Sep 17 00:00:00 2001 From: Rdmclin2 Date: Mon, 18 May 2026 11:54:10 +0700 Subject: [PATCH 002/224] =?UTF-8?q?=F0=9F=90=9B=20=20fix:=20sidebar=20new?= =?UTF-8?q?=20agent=20(#14920)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: sidebar new agent --- src/routes/(main)/home/_layout/Body/Agent/List/List.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/routes/(main)/home/_layout/Body/Agent/List/List.tsx b/src/routes/(main)/home/_layout/Body/Agent/List/List.tsx index e589082915..6b3d61d54c 100644 --- a/src/routes/(main)/home/_layout/Body/Agent/List/List.tsx +++ b/src/routes/(main)/home/_layout/Body/Agent/List/List.tsx @@ -41,8 +41,10 @@ const List = memo( // Empty custom/default groups always show the Create button so the user can populate them. // Non-empty lists only show it at the bottom of the default group; custom groups rely on - // the group header dropdown for further additions. - const showCreateButton = isEmpty ? groupId !== undefined : isDefaultList; + // the group header dropdown for further additions. When the default list overflows and we + // already render the "More" entry, hide the Create button to keep the footer compact — + // creation is still reachable from the group header dropdown. + const showCreateButton = isEmpty ? groupId !== undefined : isDefaultList && !hasMore; if (isEmpty) { return showCreateButton ? ( From 27f97b2e52341f75c64116abe8dbc35be566641d Mon Sep 17 00:00:00 2001 From: Tsuki <76603360+sudongyuer@users.noreply.github.com> Date: Mon, 18 May 2026 14:30:12 +0800 Subject: [PATCH 003/224] =?UTF-8?q?=F0=9F=90=9B=20fix(agent-tasks):=20prev?= =?UTF-8?q?ent=20schedule=20pill=20from=20wrapping=20in=20Kanban=20card=20?= =?UTF-8?q?(#14923)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The schedule pill (TaskTriggerTag in tag mode) had a fixed 24px height but no single-line constraint on its inner Text, so long descriptions like "每周 日/一/二/六 09:00 运行" wrapped to two lines and broke the row layout in the Kanban card. Force single-line + ellipsis truncation and let the existing tooltip surface the full string + timezone. Also hoist inline style objects to module scope so React.memo on Block/Flexbox/Text isn't defeated as the Kanban re-renders many cards. Fixes LOBE-9149 Co-authored-by: Claude Opus 4.7 (1M context) --- src/features/AgentTasks/features/AgentTaskItem.tsx | 4 +++- src/features/AgentTasks/features/TaskTriggerTag.tsx | 11 +++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/features/AgentTasks/features/AgentTaskItem.tsx b/src/features/AgentTasks/features/AgentTaskItem.tsx index 63e6f046ae..7548adb384 100644 --- a/src/features/AgentTasks/features/AgentTaskItem.tsx +++ b/src/features/AgentTasks/features/AgentTaskItem.tsx @@ -22,6 +22,8 @@ interface TaskItemProps { variant?: 'compact' | 'default'; } +const FLEX_MIN_WIDTH_0 = { minWidth: 0 }; + const TASK_STATUS_SET = new Set([ 'backlog', 'canceled', @@ -162,7 +164,7 @@ const AgentTaskItem = memo(({ task, variant = 'default' }) => { /> - + {scheduleNode} {timeNode} diff --git a/src/features/AgentTasks/features/TaskTriggerTag.tsx b/src/features/AgentTasks/features/TaskTriggerTag.tsx index 96a591fb1d..d4b5bba36b 100644 --- a/src/features/AgentTasks/features/TaskTriggerTag.tsx +++ b/src/features/AgentTasks/features/TaskTriggerTag.tsx @@ -18,6 +18,9 @@ interface TaskTriggerTagProps { scheduleTimezone?: string | null; } +const FLEX_MIN_WIDTH_0 = { minWidth: 0 }; +const PILL_STYLE = { borderRadius: 24, minWidth: 0 }; + const TaskTriggerTag = memo( ({ automationMode, heartbeatInterval, mode = 'tag', schedulePattern, scheduleTimezone }) => { const { t, i18n } = useTranslation('chat'); @@ -63,11 +66,11 @@ const TaskTriggerTag = memo( // plus timezone on hover, so no information is lost. return ( - + @@ -90,11 +93,11 @@ const TaskTriggerTag = memo( gap={4} height={24} paddingInline={'4px 8px'} - style={{ borderRadius: 24 }} + style={PILL_STYLE} variant={'outlined'} > - + {data.primary} From 652005ed21efe14890994fbdf43d7e00927e1bf6 Mon Sep 17 00:00:00 2001 From: Arvin Xu Date: Mon, 18 May 2026 14:47:16 +0800 Subject: [PATCH 004/224] =?UTF-8?q?=F0=9F=90=9B=20fix(agent-signal):=20iso?= =?UTF-8?q?late=20memory-agent=20messages=20into=20a=20child=20thread=20(#?= =?UTF-8?q?14921)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../analyzeIntent/actions/userMemory.ts | 42 ++++++++++++++++++- .../agentSignal/processors/actions.ts | 23 ++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/server/services/agentSignal/policies/analyzeIntent/actions/userMemory.ts b/src/server/services/agentSignal/policies/analyzeIntent/actions/userMemory.ts index 8aa23ca6b1..e7b7995fac 100644 --- a/src/server/services/agentSignal/policies/analyzeIntent/actions/userMemory.ts +++ b/src/server/services/agentSignal/policies/analyzeIntent/actions/userMemory.ts @@ -11,10 +11,11 @@ import { createAgentSignalMemoryWriterPrompt, createAgentSignalMemoryWriterSystemRole, } from '@lobechat/prompts'; -import { LayersEnum, RequestTrigger } from '@lobechat/types'; +import { LayersEnum, RequestTrigger, ThreadType } from '@lobechat/types'; import { nanoid } from '@lobechat/utils'; import { PluginModel } from '@/database/models/plugin'; +import { ThreadModel } from '@/database/models/thread'; import type { LobeChatDatabase } from '@/database/type'; import { InMemoryAgentStateManager, @@ -98,6 +99,7 @@ export interface UserMemoryActionHandlerOptions { reason?: string; serializedContext?: string; sourceHints?: AgentSignalFeedbackSourceHints; + sourceMessageId?: string; topicId?: string; }) => Promise; pluginModel?: Pick; @@ -378,6 +380,13 @@ export const runMemoryActionAgent = async ( reason?: string; serializedContext?: string; sourceHints?: AgentSignalFeedbackSourceHints; + /** + * The assistant message id that triggered this memory action. + * When provided together with topicId, a child thread is created + * under this message so that memory-agent messages are isolated + * from the main topic conversation. + */ + sourceMessageId?: string; topicId?: string; }, options: UserMemoryActionHandlerOptions, @@ -458,11 +467,34 @@ export const runMemoryActionAgent = async ( streamEventManager, }); + // Create a child thread under the triggering assistant message so that + // memory-agent messages are isolated from the main topic conversation + // instead of being flattened into it. + let threadId: string | undefined; + if (input.topicId && input.sourceMessageId) { + try { + const threadModel = new ThreadModel(options.db, options.userId); + const thread = await threadModel.create({ + agentId: input.agentId, + metadata: { operationId }, + sourceMessageId: input.sourceMessageId, + title: 'Agent Signal Memory', + topicId: input.topicId, + type: ThreadType.Isolation, + }); + threadId = thread?.id; + } catch { + // Non-fatal: fall back to writing into the main topic if thread creation fails. + } + } + await runtimeService.createOperation({ agentConfig: memoryRuntimeAgentConfig, appContext: { agentId: input.agentId, scope: 'chat', + sourceMessageId: input.sourceMessageId, + threadId: threadId ?? null, topicId: input.topicId ?? null, trigger: RequestTrigger.AgentSignal, }, @@ -589,6 +621,14 @@ export const handleUserMemoryAction = async ( typeof action.payload.sourceHints === 'object' && action.payload.sourceHints ? action.payload.sourceHints : undefined, + // The assistant message that completed the turn — used to anchor the + // memory-agent child thread under that message instead of the main topic. + // Populated by planUserMemory via extractAssistantMessageIdFromSourceId; + // absent for non-clientRuntimeComplete sources where no assistant boundary exists. + sourceMessageId: + typeof action.payload.assistantMessageId === 'string' + ? action.payload.assistantMessageId + : undefined, topicId: typeof action.payload.topicId === 'string' ? action.payload.topicId : undefined, }; const runner = options.memoryActionRunner ?? ((input) => runMemoryActionAgent(input, options)); diff --git a/src/server/services/agentSignal/processors/actions.ts b/src/server/services/agentSignal/processors/actions.ts index 07b2dbd32b..783e2065da 100644 --- a/src/server/services/agentSignal/processors/actions.ts +++ b/src/server/services/agentSignal/processors/actions.ts @@ -14,6 +14,25 @@ interface SourcePayloadCarrier { payload?: SerializedContextPayload; } +/** + * Extracts the assistant message id embedded in a hydrated `clientRuntimeComplete` source id. + * + * Hydration produces source ids with the format: + * `${assistantMessageId}:completion:${parentMessageId}` + * + * For all other source types the source id does not follow this pattern, so undefined is returned. + */ +const extractAssistantMessageIdFromSourceId = ( + sourceId: string | undefined, +): string | undefined => { + if (!sourceId) return undefined; + const completionMarker = ':completion:'; + const idx = sourceId.indexOf(completionMarker); + if (idx === -1) return undefined; + const candidate = sourceId.slice(0, idx); + return candidate.length > 0 ? candidate : undefined; +}; + /** * Skill-domain feedback signal that is eligible for direct skill-management action planning. */ @@ -64,6 +83,10 @@ export const planUserMemory = (signal: SignalFeedbackDomainMemory): ActionUserMe }, payload: { agentId: payload.agentId, + // Propagate the assistant message id so that the memory-agent thread + // can be anchored under the assistant message that completed the turn, + // rather than under the user message (messageId). + assistantMessageId: extractAssistantMessageIdFromSourceId(signal.source?.sourceId), conflictPolicy: payload.conflictPolicy, evidence: payload.evidence, feedbackHint: payload.satisfactionResult === 'satisfied' ? 'satisfied' : 'not_satisfied', From 519e755aff91fad7f3045738c102de74165bae20 Mon Sep 17 00:00:00 2001 From: CanisMinor Date: Mon, 18 May 2026 15:09:47 +0800 Subject: [PATCH 005/224] =?UTF-8?q?=F0=9F=93=9D=20docs:=20LobeHub=20Your?= =?UTF-8?q?=20Chief=20Agent=20Operator=20(#14924)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * style: update readme * style: update readme * style: update readme --- README.md | 484 ++++-------------------------------------------- README.zh-CN.md | 459 ++++----------------------------------------- 2 files changed, 74 insertions(+), 869 deletions(-) diff --git a/README.md b/README.md index 4a93c6ff69..b488fbd370 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,11 @@ # LobeHub -LobeHub is the ultimate space for work and life:
-to find, build, and collaborate with agent teammates that grow with you.
-We’re building the world’s largest human–agent co-evolving network. +LobeHub organizes your agents into 7×24 operation. + +It hires, schedules, reports on your entire AI team. + +You stay in charge — without staying online. **English** · [简体中文](./README.zh-CN.md) · [Official Site][official-site] · [Changelog][changelog] · [Documents][docs] · [Blog][blog] · [Feedback][github-issues-link] @@ -37,7 +39,7 @@ We’re building the world’s largest human–agent co-evolving network. [![][share-mastodon-shield]][share-mastodon-link] [![][share-linkedin-shield]][share-linkedin-link] -Agent teammates that grow with you +Your Chief Agent Operator [![][github-trending-shield]][github-trending-url] @@ -52,30 +54,10 @@ We’re building the world’s largest human–agent co-evolving network. - [👋🏻 Getting Started & Join Our Community](#-getting-started--join-our-community) - [✨ Features](#-features) + - [Operator: Agents as the Unit of Work](#operator-agents-as-the-unit-of-work) - [Create: Agents as the Unit of Work](#create-agents-as-the-unit-of-work) - [Collaborate: Scale New Forms of Collaboration Networks](#collaborate-scale-new-forms-of-collaboration-networks) - [Evolve: Co-evolution of Humans and Agents](#evolve-co-evolution-of-humans-and-agents) - - [MCP Plugin One-Click Installation](#mcp-plugin-one-click-installation) - - [MCP Marketplace](#mcp-marketplace) - - [Desktop App](#desktop-app) - - [Smart Internet Search](#smart-internet-search) - - [Chain of Thought](#chain-of-thought) - - [Branching Conversations](#branching-conversations) - - [Artifacts Support](#artifacts-support) - - [File Upload /Knowledge Base](#file-upload-knowledge-base) - - [Multi-Model Service Provider Support](#multi-model-service-provider-support) - - [Local Large Language Model (LLM) Support](#local-large-language-model-llm-support) - - [Model Visual Recognition](#model-visual-recognition) - - [TTS & STT Voice Conversation](#tts--stt-voice-conversation) - - [Text to Image Generation](#text-to-image-generation) - - [Plugin System (Function Calling)](#plugin-system-function-calling) - - [Agent Market (GPTs)](#agent-market-gpts) - - [Support Local / Remote Database](#support-local--remote-database) - - [Support Multi-User Management](#support-multi-user-management) - - [Progressive Web App (PWA)](#progressive-web-app-pwa) - - [Mobile Device Adaptation](#mobile-device-adaptation) - - [Custom Themes](#custom-themes) - - [`*` What's more](#-whats-more) - [🛳 Self Hosting](#-self-hosting) - [`A` Deploying with Vercel, Zeabur , Sealos or Alibaba Cloud](#a-deploying-with-vercel-zeabur--sealos-or-alibaba-cloud) - [`B` Deploying with Docker](#b-deploying-with-docker) @@ -95,7 +77,7 @@ We’re building the world’s largest human–agent co-evolving network.
- + ## 👋🏻 Getting Started & Join Our Community @@ -104,9 +86,9 @@ By adopting the Bootstrapping approach, we aim to provide developers and users w Whether for users or professional developers, LobeHub will be your AI Agent playground. Please be aware that LobeHub is currently under active development, and feedback is welcome for any [issues][issues-link] encountered. -| [![](https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1065874&theme=light&t=1769347414733)](https://www.producthunt.com/products/lobehub?embed=true&utm_source=badge-featured&utm_medium=badge&utm_campaign=badge-lobehub) | We are live on Product Hunt! We are thrilled to bring LobeHub to the world. If you believe in a future where humans and agents co-evolve, please support our journey. | -| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [![][discord-shield-badge]][discord-link] | Join our Discord community! This is where you can connect with developers and other enthusiastic users of LobeHub. | +| [![](https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1065874&theme=light&t=1769347414733)](https://www.producthunt.com/products/lobehub?launch=lobehub-2&embed=true&utm_source=badge-featured&utm_medium=badge&utm_campaign=badge-lobehub) | We are live on Product Hunt! We are thrilled to bring LobeHub to the world. If you believe in a future where humans and agents co-evolve, please support our journey. | +| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [![][discord-shield-badge]][discord-link] | Join our Discord community! This is where you can connect with developers and other enthusiastic users of LobeHub. | > \[!IMPORTANT] > @@ -130,7 +112,26 @@ Today’s agents are one-off, task-driven tools. They lack context, live in isol LobeHub is a work-and-lifestyle space to find, build, and collaborate with agent teammates that grow with you. In LobeHub, we treat **Agents as the unit of work**, providing an infrastructure where humans and agents co-evolve. -![](https://hub-apac-1.lobeobjects.space/blog/assets/2204cde2228fb3f583f3f2c090bc49fb.webp) +![](https://github.com/user-attachments/assets/89d1c402-a62b-4794-82ea-17e5ee1a6165) + +### Operator: Agents as the Unit of Work + +Hires, schedules, and reports on your entire AI team. + +- **More productivity. Fewer tools**: Bring all your agents under one roof. +- **IM Gateway**: Agents where you already chat. + +![](https://github.com/user-attachments/assets/7b08d6d9-9dff-4b06-a919-324630554509) + +[![][back-to-top]](#readme-top) + +
+ +[![][back-to-top]](#readme-top) + +
+ +![](https://github.com/user-attachments/assets/81e89324-fc66-4024-99a3-aa8e16ec8184) ### Create: Agents as the Unit of Work @@ -139,6 +140,8 @@ Building a personalized AI team starts with the **Agent Builder**. You can descr - **Unified Intelligence**: Seamlessly access any model and any modality—all under your control. - **10,000+ Skills**: Connect your agents to the skills you use every day with a library of over 10,000 tools and MCP-compatible plugins. +![](https://github.com/user-attachments/assets/949b8166-486d-4750-ad7a-cfe7bfcb84e3) + [![][back-to-top]](#readme-top)
@@ -158,6 +161,8 @@ LobeHub introduces **Agent Groups**, allowing you to work with agents like real - **Project**: Organize work by project to keep everything structured and easy to track. - **Workspace**: A shared space for teams to collaborate with agents, ensuring clear ownership and visibility across the organization. +![](https://github.com/user-attachments/assets/e51526c6-e09c-4a5a-9cec-dcd3fd68a3a8) + [![][back-to-top]](#readme-top)
@@ -175,113 +180,7 @@ The best AI is one that understands you deeply. LobeHub features **Personal Memo - **Continual Learning**: Your agents learn from how you work, adapting their behavior to act at the right moment. - **White-Box Memory**: We believe in transparency. Your agents use structured, editable memory, giving you full control over what they remember. -
- -[![][back-to-top]](#readme-top) - -
- -
-More Features - -![][image-feat-mcp] - -### MCP Plugin One-Click Installation - -**Seamlessly Connect Your AI to the World** - -Unlock the full potential of your AI by enabling smooth, secure, and dynamic interactions with external tools, data sources, and services. LobeHub's MCP (Model Context Protocol) plugin system breaks down the barriers between your AI and the digital ecosystem, allowing for unprecedented connectivity and functionality. - -Transform your conversations into powerful workflows by connecting to databases, APIs, file systems, and more. Experience the freedom of AI that truly understands and interacts with your world. - -[![][back-to-top]](#readme-top) - -![][image-feat-mcp-market] - -### MCP Marketplace - -**Discover, Connect, Extend** - -Browse a growing library of MCP plugins to expand your AI's capabilities and streamline your workflows effortlessly. Visit [lobehub.com/mcp](https://lobehub.com/mcp) to explore the MCP Marketplace, which offers a curated collection of integrations that enhance your AI's ability to work with various tools and services. - -From productivity tools to development environments, discover new ways to extend your AI's reach and effectiveness. Connect with the community and find the perfect plugins for your specific needs. - -[![][back-to-top]](#readme-top) - -![][image-feat-desktop] - -### Desktop App - -**Peak Performance, Zero Distractions** - -Get the full LobeHub experience without browser limitations—comprehensive, focused, and always ready to go. Our desktop application provides a dedicated environment for your AI interactions, ensuring optimal performance and minimal distractions. - -Experience faster response times, better resource management, and a more stable connection to your AI assistant. The desktop app is designed for users who demand the best performance from their AI tools. - -[![][back-to-top]](#readme-top) - -![][image-feat-web-search] - -### Smart Internet Search - -**Online Knowledge On Demand** - -With real-time internet access, your AI keeps up with the world—news, data, trends, and more. Stay informed and get the most current information available, enabling your AI to provide accurate and up-to-date responses. - -Access live information, verify facts, and explore current events without leaving your conversation. Your AI becomes a gateway to the world's knowledge, always current and comprehensive. - -[![][back-to-top]](#readme-top) - -[![][image-feat-cot]][docs-feat-cot] - -### [Chain of Thought][docs-feat-cot] - -Experience AI reasoning like never before. Watch as complex problems unfold step by step through our innovative Chain of Thought (CoT) visualization. This breakthrough feature provides unprecedented transparency into AI's decision-making process, allowing you to observe how conclusions are reached in real-time. - -By breaking down complex reasoning into clear, logical steps, you can better understand and validate the AI's problem-solving approach. Whether you're debugging, learning, or simply curious about AI reasoning, CoT visualization transforms abstract thinking into an engaging, interactive experience. - -[![][back-to-top]](#readme-top) - -[![][image-feat-branch]][docs-feat-branch] - -### [Branching Conversations][docs-feat-branch] - -Introducing a more natural and flexible way to chat with AI. With Branch Conversations, your discussions can flow in multiple directions, just like human conversations do. Create new conversation branches from any message, giving you the freedom to explore different paths while preserving the original context. - -Choose between two powerful modes: - -- **Continuation Mode:** Seamlessly extend your current discussion while maintaining valuable context -- **Standalone Mode:** Start fresh with a new topic based on any previous message - -This groundbreaking feature transforms linear conversations into dynamic, tree-like structures, enabling deeper exploration of ideas and more productive interactions. - -[![][back-to-top]](#readme-top) - -[![][image-feat-artifacts]][docs-feat-artifacts] - -### [Artifacts Support][docs-feat-artifacts] - -Experience the power of Claude Artifacts, now integrated into LobeHub. This revolutionary feature expands the boundaries of AI-human interaction, enabling real-time creation and visualization of diverse content formats. - -Create and visualize with unprecedented flexibility: - -- Generate and display dynamic SVG graphics -- Build and render interactive HTML pages in real-time -- Produce professional documents in multiple formats - -[![][back-to-top]](#readme-top) - -[![][image-feat-knowledgebase]][docs-feat-knowledgebase] - -### [File Upload /Knowledge Base][docs-feat-knowledgebase] - -LobeHub supports file upload and knowledge base functionality. You can upload various types of files including documents, images, audio, and video, as well as create knowledge bases, making it convenient for users to manage and search for files. Additionally, you can utilize files and knowledge base features during conversations, enabling a richer dialogue experience. - - - -> \[!TIP] -> -> Learn more on [📘 LobeHub Knowledge Base Launch — From Now On, Every Step Counts](https://lobehub.com/blog/knowledge-base) +![](https://github.com/user-attachments/assets/5c6e16f0-7f47-4baf-9aeb-3a00deb8ff5b)
@@ -289,277 +188,6 @@ LobeHub supports file upload and knowledge base functionality. You can upload va
-[![][image-feat-privoder]][docs-feat-provider] - -### [Multi-Model Service Provider Support][docs-feat-provider] - -In the continuous development of LobeHub, we deeply understand the importance of diversity in model service providers for meeting the needs of the community when providing AI conversation services. Therefore, we have expanded our support to multiple model service providers, rather than being limited to a single one, in order to offer users a more diverse and rich selection of conversations. - -In this way, LobeHub can more flexibly adapt to the needs of different users, while also providing developers with a wider range of choices. - -#### Supported Model Service Providers - -We have implemented support for the following model service providers: - - - -
See more providers (+-10) - -
- -> 📊 Total providers: [**0**](https://lobechat.com/discover/providers) - - - -At the same time, we are also planning to support more model service providers. If you would like LobeHub to support your favorite service provider, feel free to join our [💬 community discussion](https://github.com/lobehub/lobehub/discussions/1284). - -
- -[![][back-to-top]](#readme-top) - -
- -[![][image-feat-local]][docs-feat-local] - -### [Local Large Language Model (LLM) Support][docs-feat-local] - -To meet the specific needs of users, LobeHub also supports the use of local models based on [Ollama](https://ollama.ai), allowing users to flexibly use their own or third-party models. - -> \[!TIP] -> -> Learn more about [📘 Using Ollama in LobeHub][docs-usage-ollama] by checking it out. - -
- -[![][back-to-top]](#readme-top) - -
- -[![][image-feat-vision]][docs-feat-vision] - -### [Model Visual Recognition][docs-feat-vision] - -LobeHub now supports OpenAI's latest [`gpt-4-vision`](https://platform.openai.com/docs/guides/vision) model with visual recognition capabilities, -a multimodal intelligence that can perceive visuals. Users can easily upload or drag and drop images into the dialogue box, -and the agent will be able to recognize the content of the images and engage in intelligent conversation based on this, -creating smarter and more diversified chat scenarios. - -This feature opens up new interactive methods, allowing communication to transcend text and include a wealth of visual elements. -Whether it's sharing images in daily use or interpreting images within specific industries, the agent provides an outstanding conversational experience. - -
- -[![][back-to-top]](#readme-top) - -
- -[![][image-feat-tts]][docs-feat-tts] - -### [TTS & STT Voice Conversation][docs-feat-tts] - -LobeHub supports Text-to-Speech (TTS) and Speech-to-Text (STT) technologies, enabling our application to convert text messages into clear voice outputs, -allowing users to interact with our conversational agent as if they were talking to a real person. Users can choose from a variety of voices to pair with the agent. - -Moreover, TTS offers an excellent solution for those who prefer auditory learning or desire to receive information while busy. -In LobeHub, we have meticulously selected a range of high-quality voice options (OpenAI Audio, Microsoft Edge Speech) to meet the needs of users from different regions and cultural backgrounds. -Users can choose the voice that suits their personal preferences or specific scenarios, resulting in a personalized communication experience. - -
- -[![][back-to-top]](#readme-top) - -
- -[![][image-feat-t2i]][docs-feat-t2i] - -### [Text to Image Generation][docs-feat-t2i] - -With support for the latest text-to-image generation technology, LobeHub now allows users to invoke image creation tools directly within conversations with the agent. By leveraging the capabilities of AI tools such as [`DALL-E 3`](https://openai.com/dall-e-3), [`MidJourney`](https://www.midjourney.com/), and [`Pollinations`](https://pollinations.ai/), the agents are now equipped to transform your ideas into images. - -This enables a more private and immersive creative process, allowing for the seamless integration of visual storytelling into your personal dialogue with the agent. - -
- -[![][back-to-top]](#readme-top) - -
- -[![][image-feat-plugin]][docs-feat-plugin] - -### [Plugin System (Function Calling)][docs-feat-plugin] - -The plugin ecosystem of LobeHub is an important extension of its core functionality, greatly enhancing the practicality and flexibility of the LobeHub assistant. - - - -By utilizing plugins, LobeHub assistants can obtain and process real-time information, such as searching for web information and providing users with instant and relevant news. - -In addition, these plugins are not limited to news aggregation, but can also extend to other practical functions, such as quickly searching documents, generating images, obtaining data from various platforms like Bilibili, Steam, and interacting with various third-party services. - -> \[!TIP] -> -> Learn more about [📘 Plugin Usage][docs-usage-plugin] by checking it out. - - - -| Recent Submits | Description | -| -------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | -| [Shopping tools](https://lobechat.com/discover/plugin/ShoppingTools)
By **shoppingtools** on **2026-01-12** | Search for products on eBay & AliExpress, find eBay events & coupons. Get prompt examples.
`shopping` `e-bay` `ali-express` `coupons` | -| [SEO Assistant](https://lobechat.com/discover/plugin/seo_assistant)
By **webfx** on **2026-01-12** | The SEO Assistant can generate search engine keyword information in order to aid the creation of content.
`seo` `keyword` | -| [Video Captions](https://lobechat.com/discover/plugin/VideoCaptions)
By **maila** on **2025-12-13** | Convert Youtube links into transcribed text, enable asking questions, create chapters, and summarize its content.
`video-to-text` `youtube` | -| [WeatherGPT](https://lobechat.com/discover/plugin/WeatherGPT)
By **steven-tey** on **2025-12-13** | Get current weather information for a specific location.
`weather` | - -> 📊 Total plugins: [**40**](https://lobechat.com/discover/plugins) - - - -
- -[![][back-to-top]](#readme-top) - -
- -[![][image-feat-agent]][docs-feat-agent] - -### [Agent Market (GPTs)][docs-feat-agent] - -In LobeHub Agent Marketplace, creators can discover a vibrant and innovative community that brings together a multitude of well-designed agents, -which not only play an important role in work scenarios but also offer great convenience in learning processes. -Our marketplace is not just a showcase platform but also a collaborative space. Here, everyone can contribute their wisdom and share the agents they have developed. - -> \[!TIP] -> -> By [🤖/🏪 Submit Agents][submit-agents-link], you can easily submit your agent creations to our platform. -> Importantly, LobeHub has established a sophisticated automated internationalization (i18n) workflow, -> capable of seamlessly translating your agent into multiple language versions. -> This means that no matter what language your users speak, they can experience your agent without barriers. - -> \[!IMPORTANT] -> -> We welcome all users to join this growing ecosystem and participate in the iteration and optimization of agents. -> Together, we can create more interesting, practical, and innovative agents, further enriching the diversity and practicality of the agent offerings. - - - -| Recent Submits | Description | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [Turtle Soup Host](https://lobechat.com/discover/assistant/lateral-thinking-puzzle)
By **[CSY2022](https://github.com/CSY2022)** on **2025-06-19** | A turtle soup host needs to provide the scenario, the complete story (truth of the event), and the key point (the condition for guessing correctly).
`turtle-soup` `reasoning` `interaction` `puzzle` `role-playing` | -| [Academic Writing Assistant](https://lobechat.com/discover/assistant/academic-writing-assistant)
By **[swarfte](https://github.com/swarfte)** on **2025-06-17** | Expert in academic research paper writing and formal documentation
`academic-writing` `research` `formal-style` | -| [Gourmet Reviewer🍟](https://lobechat.com/discover/assistant/food-reviewer)
By **[renhai-lab](https://github.com/renhai-lab)** on **2025-06-17** | Food critique expert
`gourmet` `review` `writing` | -| [Minecraft Senior Developer](https://lobechat.com/discover/assistant/java-development)
By **[iamyuuk](https://github.com/iamyuuk)** on **2025-06-17** | Expert in advanced Java development and Minecraft mod and server plugin development
`development` `programming` `minecraft` `java` | - -> 📊 Total agents: [**505** ](https://lobechat.com/discover/assistants) - - - -
- -[![][back-to-top]](#readme-top) - -
- -[![][image-feat-database]][docs-feat-database] - -### [Support Local / Remote Database][docs-feat-database] - -LobeHub supports the use of both server-side and local databases. Depending on your needs, you can choose the appropriate deployment solution: - -- **Local database**: suitable for users who want more control over their data and privacy protection. LobeHub uses CRDT (Conflict-Free Replicated Data Type) technology to achieve multi-device synchronization. This is an experimental feature aimed at providing a seamless data synchronization experience. -- **Server-side database**: suitable for users who want a more convenient user experience. LobeHub supports PostgreSQL as a server-side database. For detailed documentation on how to configure the server-side database, please visit [Configure Server-side Database](https://lobehub.com/docs/self-hosting/advanced/server-database). - -Regardless of which database you choose, LobeHub can provide you with an excellent user experience. - -
- -[![][back-to-top]](#readme-top) - -
- -[![][image-feat-auth]][docs-feat-auth] - -### [Support Multi-User Management][docs-feat-auth] - -LobeHub supports multi-user management and provides flexible user authentication solutions: - -- **Better Auth**: LobeHub integrates `Better Auth`, a modern and flexible authentication library that supports multiple authentication methods, including OAuth, email login, credential login, magic links, and more. With `Better Auth`, you can easily implement user registration, login, session management, social login, multi-factor authentication (MFA), and other functions to ensure the security and privacy of user data. - -
- -[![][back-to-top]](#readme-top) - -
- -[![][image-feat-pwa]][docs-feat-pwa] - -### [Progressive Web App (PWA)][docs-feat-pwa] - -We deeply understand the importance of providing a seamless experience for users in today's multi-device environment. -Therefore, we have adopted Progressive Web Application ([PWA](https://support.google.com/chrome/answer/9658361)) technology, -a modern web technology that elevates web applications to an experience close to that of native apps. - -Through PWA, LobeHub can offer a highly optimized user experience on both desktop and mobile devices while maintaining high-performance characteristics. -Visually and in terms of feel, we have also meticulously designed the interface to ensure it is indistinguishable from native apps, -providing smooth animations, responsive layouts, and adapting to different device screen resolutions. - -> \[!NOTE] -> -> If you are unfamiliar with the installation process of PWA, you can add LobeHub as your desktop application (also applicable to mobile devices) by following these steps: -> -> - Launch the Chrome or Edge browser on your computer. -> - Visit the LobeHub webpage. -> - In the upper right corner of the address bar, click on the Install icon. -> - Follow the instructions on the screen to complete the PWA Installation. - -
- -[![][back-to-top]](#readme-top) - -
- -[![][image-feat-mobile]][docs-feat-mobile] - -### [Mobile Device Adaptation][docs-feat-mobile] - -We have carried out a series of optimization designs for mobile devices to enhance the user's mobile experience. Currently, we are iterating on the mobile user experience to achieve smoother and more intuitive interactions. If you have any suggestions or ideas, we welcome you to provide feedback through GitHub Issues or Pull Requests. - -
- -[![][back-to-top]](#readme-top) - -
- -[![][image-feat-theme]][docs-feat-theme] - -### [Custom Themes][docs-feat-theme] - -As a design-engineering-oriented application, LobeHub places great emphasis on users' personalized experiences, -hence introducing flexible and diverse theme modes, including a light mode for daytime and a dark mode for nighttime. -Beyond switching theme modes, a range of color customization options allow users to adjust the application's theme colors according to their preferences. -Whether it's a desire for a sober dark blue, a lively peach pink, or a professional gray-white, users can find their style of color choices in LobeHub. - -> \[!TIP] -> -> The default configuration can intelligently recognize the user's system color mode and automatically switch themes to ensure a consistent visual experience with the operating system. -> For users who like to manually control details, LobeHub also offers intuitive setting options and a choice between chat bubble mode and document mode for conversation scenarios. - -
- -[![][back-to-top]](#readme-top) - -
- -### `*` What's more - -Beside these features, LobeHub also have much better basic technique underground: - -- [x] 💨 **Quick Deployment**: Using the Vercel platform or docker image, you can deploy with just one click and complete the process within 1 minute without any complex configuration. -- [x] 🌐 **Custom Domain**: If users have their own domain, they can bind it to the platform for quick access to the dialogue agent from anywhere. -- [x] 🔒 **Privacy Protection**: All data is stored locally in the user's browser, ensuring user privacy. -- [x] 💎 **Exquisite UI Design**: With a carefully designed interface, it offers an elegant appearance and smooth interaction. It supports light and dark themes and is mobile-friendly. PWA support provides a more native-like experience. -- [x] 🗣️ **Smooth Conversation Experience**: Fluid responses ensure a smooth conversation experience. It fully supports Markdown rendering, including code highlighting, LaTex formulas, Mermaid flowcharts, and more. - -
- > ✨ more features will be added when LobeHub evolve.
@@ -855,28 +483,10 @@ This project is [LobeHub Community License](./LICENSE) licensed. [docs-dev-guide]: https://lobehub.com/docs/development/start [docs-docker]: https://lobehub.com/docs/self-hosting/server-database/docker-compose [docs-env-var]: https://lobehub.com/docs/self-hosting/environment-variables -[docs-feat-agent]: https://lobehub.com/docs/usage/features/agent-market -[docs-feat-artifacts]: https://lobehub.com/docs/usage/features/artifacts -[docs-feat-auth]: https://lobehub.com/docs/usage/features/auth -[docs-feat-branch]: https://lobehub.com/docs/usage/features/branching-conversations -[docs-feat-cot]: https://lobehub.com/docs/usage/features/cot -[docs-feat-database]: https://lobehub.com/docs/usage/features/database -[docs-feat-knowledgebase]: https://lobehub.com/blog/knowledge-base -[docs-feat-local]: https://lobehub.com/docs/usage/features/local-llm -[docs-feat-mobile]: https://lobehub.com/docs/usage/features/mobile -[docs-feat-plugin]: https://lobehub.com/docs/usage/features/plugin-system -[docs-feat-provider]: https://lobehub.com/docs/usage/features/multi-ai-providers -[docs-feat-pwa]: https://lobehub.com/docs/usage/features/pwa -[docs-feat-t2i]: https://lobehub.com/docs/usage/features/text-to-image -[docs-feat-theme]: https://lobehub.com/docs/usage/features/theme -[docs-feat-tts]: https://lobehub.com/docs/usage/features/tts -[docs-feat-vision]: https://lobehub.com/docs/usage/features/vision [docs-function-call]: https://lobehub.com/blog/openai-function-call [docs-plugin-dev]: https://lobehub.com/docs/usage/plugins/development [docs-self-hosting]: https://lobehub.com/docs/self-hosting/start [docs-upstream-sync]: https://lobehub.com/docs/self-hosting/advanced/upstream-sync -[docs-usage-ollama]: https://lobehub.com/docs/usage/providers/ollama -[docs-usage-plugin]: https://lobehub.com/docs/usage/plugins/basic [fossa-license-link]: https://app.fossa.com/projects/git%2Bgithub.com%2Flobehub%2Flobehub [fossa-license-shield]: https://app.fossa.com/api/projects/git%2Bgithub.com%2Flobehub%2Flobehub.svg?type=large [github-action-release-link]: https://github.com/actions/workflows/lobehub/lobehub/release.yml @@ -900,27 +510,7 @@ This project is [LobeHub Community License](./LICENSE) licensed. [github-stars-shield]: https://img.shields.io/github/stars/lobehub/lobehub?color=ffcb47&labelColor=black&style=flat-square [github-trending-shield]: https://trendshift.io/api/badge/repositories/2256 [github-trending-url]: https://trendshift.io/repositories/2256 -[image-banner]: https://github.com/user-attachments/assets/0fe626a3-0ddc-4f67-b595-3c5b3f1701e0 -[image-feat-agent]: https://github.com/user-attachments/assets/b3ab6e35-4fbc-468d-af10-e3e0c687350f -[image-feat-artifacts]: https://github.com/user-attachments/assets/7f95fad6-b210-4e6e-84a0-7f39e96f3a00 -[image-feat-auth]: https://github.com/user-attachments/assets/80bb232e-19d1-4f97-98d6-e291f3585e6d -[image-feat-branch]: https://github.com/user-attachments/assets/92f72082-02bd-4835-9c54-b089aad7fd41 -[image-feat-cot]: https://github.com/user-attachments/assets/f74f1139-d115-4e9c-8c43-040a53797a5e -[image-feat-database]: https://github.com/user-attachments/assets/f1697c8b-d1fb-4dac-ba05-153c6295d91d -[image-feat-desktop]: https://github.com/user-attachments/assets/a7bac8d3-ea96-4000-bb39-fadc9b610f96 -[image-feat-knowledgebase]: https://github.com/user-attachments/assets/7da7a3b2-92fd-4630-9f4e-8560c74955ae -[image-feat-local]: https://github.com/user-attachments/assets/1239da50-d832-4632-a7ef-bd754c0f3850 -[image-feat-mcp]: https://github.com/user-attachments/assets/1be85d36-3975-4413-931f-27e05e440995 -[image-feat-mcp-market]: https://github.com/user-attachments/assets/bb114f9f-24c5-4000-a984-c10d187da5a0 -[image-feat-mobile]: https://github.com/user-attachments/assets/32cf43c4-96bd-4a4c-bfb6-59acde6fe380 -[image-feat-plugin]: https://github.com/user-attachments/assets/66a891ac-01b6-4e3f-b978-2eb07b489b1b -[image-feat-privoder]: https://github.com/user-attachments/assets/e553e407-42de-4919-977d-7dbfcf44a821 -[image-feat-pwa]: https://github.com/user-attachments/assets/9647f70f-b71b-43b6-9564-7cdd12d1c24d -[image-feat-t2i]: https://github.com/user-attachments/assets/708274a7-2458-494b-a6ec-b73dfa1fa7c2 -[image-feat-theme]: https://github.com/user-attachments/assets/b47c39f1-806f-492b-8fcb-b0fa973937c1 -[image-feat-tts]: https://github.com/user-attachments/assets/50189597-2cc3-4002-b4c8-756a52ad5c0a -[image-feat-vision]: https://github.com/user-attachments/assets/18574a1f-46c2-4cbc-af2c-35a86e128a07 -[image-feat-web-search]: https://github.com/user-attachments/assets/cfdc48ac-b5f8-4a00-acee-db8f2eba09ad +[image-banner]: https://github.com/user-attachments/assets/5f78ae58-ed4f-4d38-8037-96109fbba58c [image-star]: https://github.com/user-attachments/assets/3216e25b-186f-4a54-9cb4-2f124aec0471 [issues-link]: https://img.shields.io/github/issues/lobehub/lobehub.svg?style=flat [lobe-chat-plugins]: https://github.com/lobehub/lobe-chat-plugins diff --git a/README.zh-CN.md b/README.zh-CN.md index 68614ffdae..cd98e044c4 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -4,8 +4,11 @@ # LobeHub -LobeHub 是一个工作与生活空间,用于发现、构建并与会随着您一起成长的 Agent 队友协作。
-在 LobeHub 中,我们将 **Agent 视为工作单元**,提供一个让人类与 Agent 共同进化的基础设施。 +LobeHub 帮你把专属 Agent 组织成 7×24 不打烊的高效队伍: + +自动为你招募适配的 AI 队友、调度任务排班、汇总生成工作报告, + +你始终掌控全局,从此不用再时刻在线盯守,真正解放自己的时间。 [English](./README.md) · **简体中文** · [官网][official-site] · [更新日志][changelog] · [文档][docs] · [博客][blog] · [反馈问题][github-issues-link] @@ -35,7 +38,7 @@ LobeHub 是一个工作与生活空间,用于发现、构建并与会随着您 [![][share-weibo-shield]][share-weibo-link] [![][share-mastodon-shield]][share-mastodon-link] -Agent teammates that grow with you +你的首席 Agent 运营官 [![][github-trending-shield]][github-trending-url] [![][github-hello-shield]][github-hello-url] @@ -49,30 +52,10 @@ LobeHub 是一个工作与生活空间,用于发现、构建并与会随着您 - [👋🏻 开始使用 & 交流](#-开始使用--交流) - [✨ 特性一览](#-特性一览) + - [运营:你制定策略,我们负责运行 Agent。](#运营你制定策略我们负责运行-agent) - [创建:以 Agent 为工作单元](#创建以-agent-为工作单元) - [协作:扩展新型协作网络](#协作扩展新型协作网络) - [进化:人类与 Agent 的共生进化](#进化人类与-agent-的共生进化) - - [MCP](#mcp) - - [发现、连接、扩展](#发现连接扩展) - - [巅峰性能,零干扰](#巅峰性能零干扰) - - [在线知识,按需获取](#在线知识按需获取) - - [思维链 (CoT)](#思维链-cot) - - [分支对话](#分支对话) - - [支持白板 (Artifacts)](#支持白板-artifacts) - - [文件上传 / 知识库](#文件上传--知识库) - - [多模型服务商支持](#多模型服务商支持) - - [支持本地大语言模型 (LLM)](#支持本地大语言模型-llm) - - [模型视觉识别 (Model Visual)](#模型视觉识别-model-visual) - - [TTS & STT 语音会话](#tts--stt-语音会话) - - [Text to Image 文生图](#text-to-image-文生图) - - [插件系统 (Tools Calling)](#插件系统-tools-calling) - - [助手市场 (GPTs)](#助手市场-gpts) - - [支持本地 / 远程数据库](#支持本地--远程数据库) - - [支持多用户管理](#支持多用户管理) - - [渐进式 Web 应用 (PWA)](#渐进式-web-应用-pwa) - - [移动设备适配](#移动设备适配) - - [自定义主题](#自定义主题) - - [`*` 更多特性](#-更多特性) - [🛳 开箱即用](#-开箱即用) - [`A` 使用 Vercel、Zeabur 、Sealos 或 阿里云计算巢 部署](#a-使用-vercelzeabur-sealos-或-阿里云计算巢-部署) - [`B` 使用 Docker 部署](#b-使用-docker-部署) @@ -93,7 +76,7 @@ LobeHub 是一个工作与生活空间,用于发现、构建并与会随着您
- + ## 👋🏻 开始使用 & 交流 @@ -102,9 +85,9 @@ LobeHub 是一个工作与生活空间,用于发现、构建并与会随着您 不论普通用户与专业开发者,LobeHub 旨在成为所有人的 AI Agent 实验场。LobeHub 目前正在积极开发中,有任何需求或者问题,欢迎提交 [issues][issues-link] -| [![](https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1065874&theme=light&t=1769347414733)](https://www.producthunt.com/products/lobehub?embed=true&utm_source=badge-featured&utm_medium=badge&utm_campaign=badge-lobehub) | 我们已在 Product Hunt 上线!我们很高兴将 LobeHub 推向世界。如果您相信人类与 Agent 共同进化的未来,请支持我们的旅程。 | -| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------- | -| [![][discord-shield-badge]][discord-link] | 加入我们的 Discord 社区!这是你可以与开发者和其他 LobeHub 热衷用户交流的地方 | +| [![](https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1065874&theme=light&t=1769347414733)](https://www.producthunt.com/products/lobehub?launch=lobehub-2&embed=true&utm_source=badge-featured&utm_medium=badge&utm_campaign=badge-lobehub) | 我们已在 Product Hunt 上线!我们很高兴将 LobeHub 推向世界。如果您相信人类与 Agent 共同进化的未来,请支持我们的旅程。 | +| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------- | +| [![][discord-shield-badge]][discord-link] | 加入我们的 Discord 社区!这是你可以与开发者和其他 LobeHub 热衷用户交流的地方 | > \[!IMPORTANT] > @@ -127,7 +110,26 @@ LobeHub 是一个工作与生活空间,用于发现、构建并与会随着您 LobeHub 是一个工作与生活空间,用于发现、构建并与会随着您一起成长的 Agent 队友协作。在 LobeHub 中,我们将 **Agent 视为工作单元**,提供一个让人类与 Agent 共同进化的基础设施。 -![](https://hub-apac-1.lobeobjects.space/blog/assets/2204cde2228fb3f583f3f2c090bc49fb.webp) +![](https://github.com/user-attachments/assets/89d1c402-a62b-4794-82ea-17e5ee1a6165) + +### 运营:你制定策略,我们负责运行 Agent。 + +雇用、排程并汇报你整个 AI 团队的工作 + +- **更高生产力,更少工具**:将你所有的 Agent 集中在一个平台。 +- **IM 网关**: Agent 连接到您每天使用的技能。 + +![](https://github.com/user-attachments/assets/7b08d6d9-9dff-4b06-a919-324630554509) + +[![][back-to-top]](#readme-top) + +
+ +[![][back-to-top]](#readme-top) + +
+ +![](https://github.com/user-attachments/assets/81e89324-fc66-4024-99a3-aa8e16ec8184) ### 创建:以 Agent 为工作单元 @@ -136,6 +138,8 @@ LobeHub 是一个工作与生活空间,用于发现、构建并与会随着您 - **统一智能**:无缝访问任何模型与任何模态 —— 全部由您掌控。 - **1 万 + 技能**:通过超过 10,000 个工具和与 MCP 兼容的插件,将 Agent 连接到您每天使用的技能。 +![](https://github.com/user-attachments/assets/949b8166-486d-4750-ad7a-cfe7bfcb84e3) + [![][back-to-top]](#readme-top)
@@ -155,6 +159,8 @@ LobeHub 引入了 **Agent Groups**,让您可以像对待真实队友一样与 - **项目(Project)**:按项目组织工作,保持一切结构化且易于跟踪。 - **工作区(Workspace)**:供团队与 Agent 协作的共享空间,确保明确的所有权和组织内的可见性。 +![](https://github.com/user-attachments/assets/e51526c6-e09c-4a5a-9cec-dcd3fd68a3a8) + [![][back-to-top]](#readme-top)
@@ -172,105 +178,7 @@ LobeHub 引入了 **Agent Groups**,让您可以像对待真实队友一样与 - **持续学习**:您的 Agent 会从您的工作方式中学习,调整其行为以在恰当时刻采取行动。 - **白盒记忆**:我们相信透明性。您的 Agent 使用结构化、可编辑的记忆,让您完全掌控它们记住的内容。 -
- -[![][back-to-top]](#readme-top) - -
- -
-更多特性 - -[![](https://github.com/user-attachments/assets/1be85d36-3975-4413-931f-27e05e440995)](https://lobehub.com/mcp) - -### MCP - -通过启用与外部工具、数据源和服务的平滑、安全和动态交互,释放你的 AI 的全部潜力。基于 MCP(模型上下文协议)的插件系统打破了 AI 与数字生态系统之间的壁垒,实现了前所未有的连接性和功能性。 - -将对话转化为强大的工作流程,连接数据库、API、文件系统等。体验真正理解并与你的世界互动的 AI Agent。 - -[![][back-to-top]](#readme-top) - -![][image-feat-mcp-market] - -### 发现、连接、扩展 - -浏览不断增长的 MCP 插件库,轻松扩展你的 AI 能力并简化工作流程。访问 [lobehub.com/mcp](https://lobehub.com/mcp) 探索 MCP 市场,提供精选的集成集合,增强你的 AI 与各种工具和服务协作的能力。 - -从生产力工具到开发环境,发现扩展 AI 覆盖范围和效率的新方式。与社区连接,找到满足特定需求的完美插件。 - -[![][back-to-top]](#readme-top) - -![][image-feat-desktop] - -### 巅峰性能,零干扰 - -获得完整的 LobeHub 体验,摆脱浏览器限制 —— 轻量级、专注且随时就绪。我们的桌面应用程序为你的 AI 交互提供专用环境,确保最佳性能和最小干扰。 - -体验更快的响应时间、更好的资源管理和与 AI 助手的更稳定连接。桌面应用专为要求 AI 工具最佳性能的用户设计。 - -[![][back-to-top]](#readme-top) - -![][image-feat-web-search] - -### 在线知识,按需获取 - -通过实时联网访问,你的 AI 与世界保持同步 —— 新闻、数据、趋势等。保持信息更新,获取最新可用信息,使你的 AI 能够提供准确和最新的回复。 - -访问实时信息,验证事实,探索当前事件,无需离开对话。你的 AI 成为通向世界知识的门户,始终保持最新和全面。 - -[![][back-to-top]](#readme-top) - -[![][image-feat-cot]][docs-feat-cot] - -### [思维链 (CoT)][docs-feat-cot] - -体验前所未有的 AI 推理过程。通过创新的思维链(CoT)可视化功能,您可以实时观察复杂问题是如何一步步被解析的。这项突破性的功能为 AI 的决策过程提供了前所未有的透明度,让您能够清晰地了解结论是如何得出的。 - -通过将复杂的推理过程分解为清晰的逻辑步骤,您可以更好地理解和验证 AI 的解题思路。无论您是在调试问题、学习知识,还是单纯对 AI 推理感兴趣,思维链可视化都能将抽象思维转化为一种引人入胜的互动体验。 - -[![][back-to-top]](#readme-top) - -[![][image-feat-branch]][docs-feat-branch] - -### [分支对话][docs-feat-branch] - -为您带来更自然、更灵活的 AI 对话方式。通过分支对话功能,您的讨论可以像人类对话一样自然延伸。在任意消息处创建新的对话分支,让您在保留原有上下文的同时,自由探索不同的对话方向。 - -两种强大模式任您选择: - -- **延续模式**:无缝延展当前讨论,保持宝贵的对话上下文 -- **独立模式**:基于任意历史消息,开启全新话题探讨 - -这项突破性功能将线性对话转变为动态的树状结构,让您能够更深入地探索想法,实现更高效的互动体验。 - -[![][back-to-top]](#readme-top) - -[![][image-feat-artifacts]][docs-feat-artifacts] - -### [支持白板 (Artifacts)][docs-feat-artifacts] - -体验集成于 LobeHub 的 Claude Artifacts 能力。这项革命性功能突破了 AI 人机交互的边界,让您能够实时创建和可视化各种格式的内容。 - -以前所未有的灵活度进行创作与可视化: - -- 生成并展示动态 SVG 图形 -- 实时构建与渲染交互式 HTML 页面 -- 输出多种格式的专业文档 - -[![][back-to-top]](#readme-top) - -[![][image-feat-knowledgebase]][docs-feat-knowledgebase] - -### [文件上传 / 知识库][docs-feat-knowledgebase] - -LobeHub 支持文件上传与知识库功能,你可以上传文件、图片、音频、视频等多种类型的文件,以及创建知识库,方便用户管理和查找文件。同时在对话中使用文件和知识库功能,实现更加丰富的对话体验。 - - - -> \[!TIP] -> -> 查阅 [📘 LobeHub 知识库上线 —— 此刻起,跬步千里](https://lobehub.com/zh/blog/knowledge-base) 了解详情。 +![](https://github.com/user-attachments/assets/5c6e16f0-7f47-4baf-9aeb-3a00deb8ff5b)
@@ -278,262 +186,6 @@ LobeHub 支持文件上传与知识库功能,你可以上传文件、图片、
-[![][image-feat-privoder]][docs-feat-provider] - -### [多模型服务商支持][docs-feat-provider] - -在 LobeHub 的不断发展过程中,我们深刻理解到在提供 AI 会话服务时模型服务商的多样性对于满足社区需求的重要性。因此,我们不再局限于单一的模型服务商,而是拓展了对多种模型服务商的支持,以便为用户提供更为丰富和多样化的会话选择。 - -通过这种方式,LobeHub 能够更灵活地适应不同用户的需求,同时也为开发者提供了更为广泛的选择空间。 - -#### 已支持的模型服务商 - -我们已经实现了对以下模型服务商的支持: - - - -
See more providers (+-10) - -
- -> 📊 Total providers: [**0**](https://lobechat.com/discover/providers) - - - -同时,我们也在计划支持更多的模型服务商,以进一步丰富我们的服务商库。如果你希望让 LobeHub 支持你喜爱的服务商,欢迎加入我们的 [💬 社区讨论](https://github.com/lobehub/lobehub/discussions/6157)。 - -
- -[![][back-to-top]](#readme-top) - -
- -[![][image-feat-local]][docs-feat-local] - -### [支持本地大语言模型 (LLM)][docs-feat-local] - -为了满足特定用户的需求,LobeHub 还基于 [Ollama](https://ollama.ai) 支持了本地模型的使用,让用户能够更灵活地使用自己的或第三方的模型。 - -> \[!TIP] -> -> 查阅 [📘 在 LobeHub 中使用 Ollama][docs-usage-ollama] 获得更多信息 - -
- -[![][back-to-top]](#readme-top) - -
- -[![][image-feat-vision]][docs-feat-vision] - -### [模型视觉识别 (Model Visual)][docs-feat-vision] - -LobeHub 已经支持 OpenAI 最新的 [`gpt-4-vision`](https://platform.openai.com/docs/guides/vision) 支持视觉识别的模型,这是一个具备视觉识别能力的多模态应用。 -用户可以轻松上传图片或者拖拽图片到对话框中,助手将能够识别图片内容,并在此基础上进行智能对话,构建更智能、更多元化的聊天场景。 - -这一特性打开了新的互动方式,使得交流不再局限于文字,而是可以涵盖丰富的视觉元素。无论是日常使用中的图片分享,还是在特定行业内的图像解读,助手都能提供出色的对话体验。 - -
- -[![][back-to-top]](#readme-top) - -
- -[![][image-feat-tts]][docs-feat-tts] - -### [TTS & STT 语音会话][docs-feat-tts] - -LobeHub 支持文字转语音(Text-to-Speech,TTS)和语音转文字(Speech-to-Text,STT)技术,这使得我们的应用能够将文本信息转化为清晰的语音输出,用户可以像与真人交谈一样与我们的对话助手进行交流。 -用户可以从多种声音中选择,给助手搭配合适的音源。 同时,对于那些倾向于听觉学习或者想要在忙碌中获取信息的用户来说,TTS 提供了一个极佳的解决方案。 - -在 LobeHub 中,我们精心挑选了一系列高品质的声音选项 (OpenAI Audio, Microsoft Edge Speech),以满足不同地域和文化背景用户的需求。用户可以根据个人喜好或者特定场景来选择合适的语音,从而获得个性化的交流体验。 - -
- -[![][back-to-top]](#readme-top) - -
- -[![][image-feat-t2i]][docs-feat-t2i] - -### [Text to Image 文生图][docs-feat-t2i] - -支持最新的文本到图片生成技术,LobeHub 现在能够让用户在与助手对话中直接调用文生图工具进行创作。 -通过利用 [`DALL-E 3`](https://openai.com/dall-e-3)、[`MidJourney`](https://www.midjourney.com/) 和 [`Pollinations`](https://pollinations.ai/) 等 AI 工具的能力, 助手们现在可以将你的想法转化为图像。 -同时可以更私密和沉浸式地完成你的创作过程。 - -
- -[![][back-to-top]](#readme-top) - -
- -[![][image-feat-plugin]][docs-feat-plugin] - -### [插件系统 (Tools Calling)][docs-feat-plugin] - -LobeHub 的插件生态系统是其核心功能的重要扩展,它极大地增强了 ChatGPT 的实用性和灵活性。 - - - -通过利用插件,ChatGPT 能够实现实时信息的获取和处理,例如自动获取最新新闻头条,为用户提供即时且相关的资讯。 - -此外,这些插件不仅局限于新闻聚合,还可以扩展到其他实用的功能,如快速检索文档、生成图象、获取电商平台数据,以及其他各式各样的第三方服务。 - -> 通过文档了解更多 [📘 插件使用][docs-usage-plugin] - - - -| 最近新增 | 描述 | -| -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | -| [购物工具](https://lobechat.com/discover/plugin/ShoppingTools)
By **shoppingtools** on **2026-01-12** | 在 eBay 和 AliExpress 上搜索产品,查找 eBay 活动和优惠券。获取快速示例。
`购物` `e-bay` `ali-express` `优惠券` | -| [SEO 助手](https://lobechat.com/discover/plugin/seo_assistant)
By **webfx** on **2026-01-12** | SEO 助手可以生成搜索引擎关键词信息,以帮助创建内容。
`seo` `关键词` | -| [视频字幕](https://lobechat.com/discover/plugin/VideoCaptions)
By **maila** on **2025-12-13** | 将 Youtube 链接转换为转录文本,使其能够提问,创建章节,并总结其内容。
`视频转文字` `you-tube` | -| [天气 GPT](https://lobechat.com/discover/plugin/WeatherGPT)
By **steven-tey** on **2025-12-13** | 获取特定位置的当前天气信息。
`天气` | - -> 📊 Total plugins: [**40**](https://lobechat.com/discover/plugins) - - - -
- -[![][back-to-top]](#readme-top) - -
- -[![][image-feat-agent]][docs-feat-agent] - -### [助手市场 (GPTs)][docs-feat-agent] - -在 LobeHub 的助手市场中,创作者们可以发现一个充满活力和创新的社区,它汇聚了众多精心设计的助手,这些助手不仅在工作场景中发挥着重要作用,也在学习过程中提供了极大的便利。 -我们的市场不仅是一个展示平台,更是一个协作的空间。在这里,每个人都可以贡献自己的智慧,分享个人开发的助手。 - -> \[!TIP] -> -> 通过 [🤖/🏪 提交助手][submit-agents-link] ,你可以轻松地将你的助手作品提交到我们的平台。我们特别强调的是,LobeHub 建立了一套精密的自动化国际化(i18n)工作流程, 它的强大之处在于能够无缝地将你的助手转化为多种语言版本。 -> 这意味着,不论你的用户使用何种语言,他们都能无障碍地体验到你的助手。 - -> \[!IMPORTANT] -> -> 我欢迎所有用户加入这个不断成长的生态系统,共同参与到助手的迭代与优化中来。共同创造出更多有趣、实用且具有创新性的助手,进一步丰富助手的多样性和实用性。 - - - -| 最近新增 | 描述 | -| ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | -| [海龟汤主持人](https://lobechat.com/discover/assistant/lateral-thinking-puzzle)
By **[CSY2022](https://github.com/CSY2022)** on **2025-06-19** | 一个海龟汤主持人,需要自己提供汤面,汤底与关键点(猜中的判定条件)。
`海龟汤` `推理` `互动` `谜题` `角色扮演` | -| [学术写作助手](https://lobechat.com/discover/assistant/academic-writing-assistant)
By **[swarfte](https://github.com/swarfte)** on **2025-06-17** | 专业的学术研究论文写作和正式文档编写专家
`学术写作` `研究` `正式风格` | -| [美食评论员🍟](https://lobechat.com/discover/assistant/food-reviewer)
By **[renhai-lab](https://github.com/renhai-lab)** on **2025-06-17** | 美食评价专家
`美食` `评价` `写作` | -| [Minecraft 资深开发者](https://lobechat.com/discover/assistant/java-development)
By **[iamyuuk](https://github.com/iamyuuk)** on **2025-06-17** | 擅长高级 Java 开发及 Minecraft 开发
`开发` `编程` `minecraft` `java` | - -> 📊 Total agents: [**505** ](https://lobechat.com/discover/assistants) - - - -
- -[![][back-to-top]](#readme-top) - -
- -[![][image-feat-database]][docs-feat-database] - -### [支持本地 / 远程数据库][docs-feat-database] - -LobeHub 支持同时使用服务端数据库和本地数据库。根据您的需求,您可以选择合适的部署方案: - -- 本地数据库:适合希望对数据有更多掌控感和隐私保护的用户。LobeHub 采用了 CRDT (Conflict-Free Replicated Data Type) 技术,实现了多端同步功能。这是一项实验性功能,旨在提供无缝的数据同步体验。 -- 服务端数据库:适合希望更便捷使用体验的用户。LobeHub 支持 PostgreSQL 作为服务端数据库。关于如何配置服务端数据库的详细文档,请前往 [配置服务端数据库](https://lobehub.com/zh/docs/self-hosting/advanced/server-database)。 - -无论您选择哪种数据库,LobeHub 都能为您提供卓越的用户体验。 - -
- -[![][back-to-top]](#readme-top) - -
- -[![][image-feat-auth]][docs-feat-auth] - -### [支持多用户管理][docs-feat-auth] - -LobeHub 支持多用户管理,提供了灵活的用户认证方案: - -- **Better Auth**:LobeHub 集成了 `Better Auth`,一个现代化且灵活的身份验证库,支持多种身份验证方式,包括 OAuth、邮件登录、凭证登录、魔法链接等。通过 `Better Auth`,您可以轻松实现用户的注册、登录、会话管理、社交登录、多因素认证 (MFA) 等功能,确保用户数据的安全性和隐私性。 - -
- -[![][back-to-top]](#readme-top) - -
- -[![][image-feat-pwa]][docs-feat-pwa] - -### [渐进式 Web 应用 (PWA)][docs-feat-pwa] - -我们深知在当今多设备环境下为用户提供无缝体验的重要性。为此,我们采用了渐进式 Web 应用 [PWA](https://support.google.com/chrome/answer/9658361) 技术, -这是一种能够将网页应用提升至接近原生应用体验的现代 Web 技术。通过 PWA,LobeHub 能够在桌面和移动设备上提供高度优化的用户体验,同时保持轻量级和高性能的特点。 -在视觉和感觉上,我们也经过精心设计,以确保它的界面与原生应用无差别,提供流畅的动画、响应式布局和适配不同设备的屏幕分辨率。 - -> \[!NOTE] -> -> 若您未熟悉 PWA 的安装过程,您可以按照以下步骤将 LobeHub 添加为您的桌面应用(也适用于移动设备): -> -> - 在电脑上运行 Chrome 或 Edge 浏览器 . -> - 访问 LobeHub 网页 . -> - 在地址栏的右上角,单击 安装 图标 . - -
- -[![][back-to-top]](#readme-top) - -
- -[![][image-feat-mobile]][docs-feat-mobile] - -### [移动设备适配][docs-feat-mobile] - -针对移动设备进行了一系列的优化设计,以提升用户的移动体验。目前,我们正在对移动端的用户体验进行版本迭代,以实现更加流畅和直观的交互。如果您有任何建议或想法,我们非常欢迎您通过 GitHub Issues 或者 Pull Requests 提供反馈。 - -
- -[![][back-to-top]](#readme-top) - -
- -[![][image-feat-theme]][docs-feat-theme] - -### [自定义主题][docs-feat-theme] - -作为设计工程师出身,LobeHub 在界面设计上充分考虑用户的个性化体验,因此引入了灵活多变的主题模式,其中包括日间的亮色模式和夜间的深色模式。 -除了主题模式的切换,还提供了一系列的颜色定制选项,允许用户根据自己的喜好来调整应用的主题色彩。无论是想要沉稳的深蓝,还是希望活泼的桃粉,或者是专业的灰白,用户都能够在 LobeHub 中找到匹配自己风格的颜色选择。 - -> \[!TIP] -> -> 默认配置能够智能地识别用户系统的颜色模式,自动进行主题切换,以确保应用界面与操作系统保持一致的视觉体验。对于喜欢手动调控细节的用户,LobeHub 同样提供了直观的设置选项,针对聊天场景也提供了对话气泡模式和文档模式的选择。 - -
- -
- -[![][back-to-top]](#readme-top) - -
- -
- -### `*` 更多特性 - -除了上述功能特性以外,LobeHub 所具有的设计和技术能力将为你带来更多使用保障: - -- [x] 💎 **精致 UI 设计**:经过精心设计的界面,具有优雅的外观和流畅的交互效果,支持亮暗色主题,适配移动端。支持 PWA,提供更加接近原生应用的体验。 -- [x] 🗣️ **流畅的对话体验**:流式响应带来流畅的对话体验,并且支持完整的 Markdown 渲染,包括代码高亮、LaTex 公式、Mermaid 流程图等。 -- [x] 💨 **快速部署**:使用 Vercel 平台或者我们的 Docker 镜像,只需点击一键部署按钮,即可在 1 分钟内完成部署,无需复杂的配置过程。 -- [x] 🔒 **隐私安全**:所有数据保存在用户浏览器本地,保证用户的隐私安全。 -- [x] 🌐 **自定义域名**:如果用户拥有自己的域名,可以将其绑定到平台上,方便在任何地方快速访问对话助手。 - -
- > ✨ 随着产品迭代持续更新,我们将会带来更多更多令人激动的功能!
@@ -867,28 +519,10 @@ This project is [LobeHub Community License](./LICENSE) licensed. [docs-dev-guide]: https://lobehub.com/docs/development/start [docs-docker]: https://lobehub.com/zh/docs/self-hosting/server-database/docker-compose [docs-env-var]: https://lobehub.com/docs/self-hosting/environment-variables -[docs-feat-agent]: https://lobehub.com/docs/usage/features/agent-market -[docs-feat-artifacts]: https://lobehub.com/docs/usage/features/artifacts -[docs-feat-auth]: https://lobehub.com/docs/usage/features/auth -[docs-feat-branch]: https://lobehub.com/docs/usage/features/branching-conversations -[docs-feat-cot]: https://lobehub.com/docs/usage/features/cot -[docs-feat-database]: https://lobehub.com/docs/usage/features/database -[docs-feat-knowledgebase]: https://lobehub.com/blog/knowledge-base -[docs-feat-local]: https://lobehub.com/docs/usage/features/local-llm -[docs-feat-mobile]: https://lobehub.com/docs/usage/features/mobile -[docs-feat-plugin]: https://lobehub.com/docs/usage/features/plugin-system -[docs-feat-provider]: https://lobehub.com/docs/usage/features/multi-ai-providers -[docs-feat-pwa]: https://lobehub.com/docs/usage/features/pwa -[docs-feat-t2i]: https://lobehub.com/docs/usage/features/text-to-image -[docs-feat-theme]: https://lobehub.com/docs/usage/features/theme -[docs-feat-tts]: https://lobehub.com/docs/usage/features/tts -[docs-feat-vision]: https://lobehub.com/docs/usage/features/vision [docs-function-call]: https://lobehub.com/zh/blog/openai-function-call [docs-plugin-dev]: https://lobehub.com/docs/usage/plugins/development [docs-self-hosting]: https://lobehub.com/docs/self-hosting/start [docs-upstream-sync]: https://lobehub.com/docs/self-hosting/advanced/upstream-sync -[docs-usage-ollama]: https://lobehub.com/docs/usage/providers/ollama -[docs-usage-plugin]: https://lobehub.com/docs/usage/plugins/basic [fossa-license-link]: https://app.fossa.com/projects/git%2Bgithub.com%2Flobehub%2Flobehub [fossa-license-shield]: https://app.fossa.com/api/projects/git%2Bgithub.com%2Flobehub%2Flobehub.svg?type=large [github-action-release-link]: https://github.com/lobehub/lobehub/actions/workflows/release.yml @@ -914,26 +548,7 @@ This project is [LobeHub Community License](./LICENSE) licensed. [github-stars-shield]: https://github.com/user-attachments/assets/3216e25b-186f-4a54-9cb4-2f124aec0471 [github-trending-shield]: https://trendshift.io/api/badge/repositories/2256 [github-trending-url]: https://trendshift.io/repositories/2256 -[image-banner]: https://github.com/user-attachments/assets/0fe626a3-0ddc-4f67-b595-3c5b3f1701e0 -[image-feat-agent]: https://github.com/user-attachments/assets/b3ab6e35-4fbc-468d-af10-e3e0c687350f -[image-feat-artifacts]: https://github.com/user-attachments/assets/7f95fad6-b210-4e6e-84a0-7f39e96f3a00 -[image-feat-auth]: https://github.com/user-attachments/assets/80bb232e-19d1-4f97-98d6-e291f3585e6d -[image-feat-branch]: https://github.com/user-attachments/assets/92f72082-02bd-4835-9c54-b089aad7fd41 -[image-feat-cot]: https://github.com/user-attachments/assets/f74f1139-d115-4e9c-8c43-040a53797a5e -[image-feat-database]: https://github.com/user-attachments/assets/f1697c8b-d1fb-4dac-ba05-153c6295d91d -[image-feat-desktop]: https://github.com/user-attachments/assets/a7bac8d3-ea96-4000-bb39-fadc9b610f96 -[image-feat-knowledgebase]: https://github.com/user-attachments/assets/7da7a3b2-92fd-4630-9f4e-8560c74955ae -[image-feat-local]: https://github.com/user-attachments/assets/1239da50-d832-4632-a7ef-bd754c0f3850 -[image-feat-mcp-market]: https://github.com/user-attachments/assets/bb114f9f-24c5-4000-a984-c10d187da5a0 -[image-feat-mobile]: https://github.com/user-attachments/assets/32cf43c4-96bd-4a4c-bfb6-59acde6fe380 -[image-feat-plugin]: https://github.com/user-attachments/assets/66a891ac-01b6-4e3f-b978-2eb07b489b1b -[image-feat-privoder]: https://github.com/user-attachments/assets/e553e407-42de-4919-977d-7dbfcf44a821 -[image-feat-pwa]: https://github.com/user-attachments/assets/9647f70f-b71b-43b6-9564-7cdd12d1c24d -[image-feat-t2i]: https://github.com/user-attachments/assets/708274a7-2458-494b-a6ec-b73dfa1fa7c2 -[image-feat-theme]: https://github.com/user-attachments/assets/b47c39f1-806f-492b-8fcb-b0fa973937c1 -[image-feat-tts]: https://github.com/user-attachments/assets/50189597-2cc3-4002-b4c8-756a52ad5c0a -[image-feat-vision]: https://github.com/user-attachments/assets/18574a1f-46c2-4cbc-af2c-35a86e128a07 -[image-feat-web-search]: https://github.com/user-attachments/assets/cfdc48ac-b5f8-4a00-acee-db8f2eba09ad +[image-banner]: https://github.com/user-attachments/assets/5f78ae58-ed4f-4d38-8037-96109fbba58c [image-star]: https://github.com/user-attachments/assets/c3b482e7-cef5-4e94-bef9-226900ecfaab [issues-link]: https://img.shields.io/github/issues/lobehub/lobehub.svg?style=flat [lobe-chat-plugins]: https://github.com/lobehub/lobe-chat-plugins From d359a83ade1d36c69d4478f532bb9f946a095002 Mon Sep 17 00:00:00 2001 From: LiJian Date: Mon, 18 May 2026 17:45:37 +0800 Subject: [PATCH 006/224] =?UTF-8?q?=F0=9F=90=9B=20fix:=20wire=20server-sid?= =?UTF-8?q?e=20exec=5Ftask/exec=5Ftasks=20for=20callAgent=20async=20mode?= =?UTF-8?q?=20(#14913)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🐛 fix: wire server-side exec_task/exec_tasks for callAgent async mode When a parent agent runs as a server-side QStash task and calls `lobe-agent-management.callAgent(agentId, { runAsTask: true })`, the sub-agent was silently never spawned. Root cause (three missing links): 1. `RuntimeExecutors.ts` `call_tool` did not set `stop: true` in the `tool_result` payload when the tool returned an `execTask`/`execTasks` state, so `GeneralChatAgent` fell through to the normal LLM-call path instead of emitting an `exec_task` instruction. 2. No `exec_task` / `exec_tasks` executor existed in `RuntimeExecutors.ts`, so even if the instruction had been emitted the runtime would have thrown `No executor found for instruction type: exec_task`. 3. `AiAgentService` did not inject an `execSubAgentTask` callback into `AgentRuntimeService`, so the executors had no way to spawn the child operation. Fix: - Detect `execTask` / `execTasks` state type in `call_tool` and forward `stop: true` so `GeneralChatAgent` routes correctly. - Add server-side `exec_task` and `exec_tasks` executors that create a task message and fire `execSubAgentTask` via an injected callback, then return a `task_result` / `tasks_batch_result` context so the parent agent can do a final LLM summary call. - Extend `AgentRuntimeServiceOptions` with `execSubAgentTask` callback and propagate it through the executor context. - Wire `this.execSubAgentTask` into `AgentRuntimeService` from `AiAgentService` constructor. Co-Authored-By: Claude Sonnet 4.6 * ♻️ refactor: simplify execSubAgentTask injection + sync canary renames - Remove bespoke ExecSubAgentTaskCallbackParams interface; reuse ExecSubAgentTaskParams from @lobechat/types directly (structurally identical, avoids duplication) - Use this.execSubAgentTask.bind(this) instead of lambda wrapper in AiAgentService constructor - Sync instruction/state type renames from canary: exec_task → exec_sub_agent exec_tasks → exec_sub_agents execTask state → execSubAgent execTasks state → execSubAgents task_result phase → sub_agent_result tasks_batch_result phase → sub_agents_batch_result AgentInstructionExecTask → AgentInstructionExecSubAgent AgentInstructionExecTasks → AgentInstructionExecSubAgents Co-Authored-By: Claude Sonnet 4.6 * ✅ test: add unit tests for server-side exec_sub_agent executor Three cases covering the callAgent async fix: 1. call_tool sets stop:true when tool returns execSubAgent state 2. exec_sub_agent creates task message + calls execSubAgentTask callback 3. exec_sub_agent gracefully skips dispatch when callback not injected Co-Authored-By: Claude Sonnet 4.6 * 🐛 fix(exec-sub-agent): report actual dispatch outcome instead of callback existence Co-Authored-By: Claude Sonnet 4.6 * 🐛 fix(test): add as const to toolCalling.type to satisfy ToolManifestType Co-Authored-By: Claude Sonnet 4.6 --------- Co-authored-by: Claude Sonnet 4.6 --- .../modules/AgentRuntime/RuntimeExecutors.ts | 230 +++++++++++++++++- .../__tests__/RuntimeExecutors.test.ts | 143 +++++++++++ .../agentRuntime/AgentRuntimeService.ts | 22 +- src/server/services/aiAgent/index.ts | 5 +- 4 files changed, 395 insertions(+), 5 deletions(-) diff --git a/src/server/modules/AgentRuntime/RuntimeExecutors.ts b/src/server/modules/AgentRuntime/RuntimeExecutors.ts index 23839649ef..b27ef7e1df 100644 --- a/src/server/modules/AgentRuntime/RuntimeExecutors.ts +++ b/src/server/modules/AgentRuntime/RuntimeExecutors.ts @@ -2,6 +2,9 @@ import { type AgentEvent, type AgentInstruction, type AgentInstructionCompressContext, + type AgentInstructionExecSubAgent, + type AgentInstructionExecSubAgents, + type AgentRuntimeContext, type AgentState, type CallLLMPayload, type GeneralAgentCallLLMResultPayload, @@ -30,7 +33,12 @@ import { import { parse } from '@lobechat/conversation-flow'; import { consumeStreamUntilDone } from '@lobechat/model-runtime'; import { chainCompressContext } from '@lobechat/prompts'; -import { type ChatToolPayload, type MessageToolCall, type UIChatMessage } from '@lobechat/types'; +import { + type ChatToolPayload, + type ExecSubAgentTaskParams, + type MessageToolCall, + type UIChatMessage, +} from '@lobechat/types'; import { sanitizeToolCallArguments, serializePartsForStorage } from '@lobechat/utils'; import debug from 'debug'; @@ -210,6 +218,12 @@ export interface RuntimeExecutorContext { botPlatformContext?: BotPlatformContext; discordContext?: any; evalContext?: EvalContext; + /** + * Callback to spawn a sub-agent task server-side. + * Injected by AiAgentService so exec_sub_agent / exec_sub_agents executors + * can dispatch callAgent-triggered tasks without a circular import. + */ + execSubAgentTask?: (params: ExecSubAgentTaskParams) => Promise; hookDispatcher?: HookDispatcher; loadAgentState?: (operationId: string) => Promise; messageModel: MessageModel; @@ -1853,6 +1867,15 @@ export const createRuntimeExecutors = ( log('[%s:%d] Tool execution completed', operationId, stepIndex); + // When the tool result carries an execSubAgent / execSubAgents state the + // GeneralChatAgent needs `stop: true` in the payload to detect it and + // emit the matching exec_sub_agent / exec_sub_agents instruction. Without + // this flag the agent falls through to the normal LLM-call path and the + // sub-agent is never spawned. + const execTaskStateType = executionResult.state?.type as string | undefined; + const isExecTaskState = + execTaskStateType === 'execSubAgent' || execTaskStateType === 'execSubAgents'; + return { events, newState, @@ -1863,6 +1886,7 @@ export const createRuntimeExecutors = ( isSuccess, // Pass tool message ID as parentMessageId for the next LLM call parentMessageId: toolMessageId, + ...(isExecTaskState && { stop: true }), toolCall: chatToolPayload, toolCallId: chatToolPayload.id, }, @@ -2426,6 +2450,210 @@ export const createRuntimeExecutors = ( }; }, + /** + * Server-side exec_sub_agent executor + * + * Mirrors the client-side exec_sub_agent executor in createAgentExecutors.ts + * but runs entirely server-side (no polling required). Flow: + * 1. Create a task message (role: 'task') as a placeholder visible in the UI. + * 2. Fire execSubAgentTask via the injected callback so the sub-agent runs as + * an independent QStash operation. + * 3. Return a sub_agent_result context so GeneralChatAgent calls the LLM once + * more and the parent agent can acknowledge the delegation. + */ + exec_sub_agent: async (instruction, state) => { + const { payload } = instruction as AgentInstructionExecSubAgent; + const { parentMessageId, task } = payload; + const events: AgentEvent[] = []; + const { operationId } = ctx; + const taskLogId = `${operationId}:exec_sub_agent`; + + const topicId = ctx.topicId ?? state.metadata?.topicId; + const agentId = state.metadata?.agentId; + // targetAgentId is a cloud extension injected by agentManagement.callAgent + const targetAgentId = (task as any).targetAgentId ?? agentId; + + let taskMessageId: string | undefined; + try { + const taskMessage = await ctx.messageModel.create({ + agentId: agentId!, + content: '', + metadata: { + instruction: task.instruction, + taskTitle: task.description, + ...(targetAgentId && targetAgentId !== agentId && { targetAgentId }), + }, + parentId: parentMessageId, + role: 'task', + threadId: state.metadata?.threadId ?? undefined, + topicId: topicId!, + }); + taskMessageId = taskMessage.id; + log('[%s] Created task message: %s', taskLogId, taskMessageId); + } catch (error) { + log('[%s] Failed to create task message: %O', taskLogId, error); + } + + const effectiveTaskMessageId = taskMessageId ?? parentMessageId; + + let dispatched = false; + if (ctx.execSubAgentTask && topicId && agentId) { + try { + await ctx.execSubAgentTask({ + agentId: targetAgentId, + groupId: state.metadata?.groupId ?? undefined, + instruction: task.instruction, + parentMessageId: effectiveTaskMessageId, + parentOperationId: operationId, + timeout: task.timeout, + title: task.description, + topicId, + }); + dispatched = true; + log('[%s] Spawned sub-agent task for agent %s', taskLogId, targetAgentId); + } catch (error) { + log('[%s] Failed to spawn sub-agent task: %O', taskLogId, error); + if (taskMessageId) { + try { + await ctx.messageModel.update(taskMessageId, { + content: `Task failed to start: ${(error as Error).message}`, + }); + } catch { + // best-effort + } + } + } + } else { + log('[%s] execSubAgentTask not available, skipping sub-agent dispatch', taskLogId); + } + + return { + events, + newState: state, + nextContext: { + payload: { + parentMessageId: effectiveTaskMessageId, + result: { + success: dispatched, + taskMessageId: effectiveTaskMessageId, + threadId: '', + }, + }, + phase: 'sub_agent_result', + session: { + messageCount: state.messages.length, + sessionId: operationId, + status: 'running', + stepCount: state.stepCount + 1, + }, + } as unknown as AgentRuntimeContext, + }; + }, + + /** + * Server-side exec_sub_agents executor + * + * Same as exec_sub_agent but for a batch. Each sub-agent is fired + * independently via execSubAgentTask and a task message is created for each. + */ + exec_sub_agents: async (instruction, state) => { + const { payload } = instruction as AgentInstructionExecSubAgents; + const { parentMessageId, tasks } = payload; + const events: AgentEvent[] = []; + const { operationId } = ctx; + const taskLogId = `${operationId}:exec_sub_agents`; + + const topicId = ctx.topicId ?? state.metadata?.topicId; + const agentId = state.metadata?.agentId; + + log('[%s] Starting batch of %d tasks', taskLogId, tasks.length); + + let lastTaskMessageId: string | undefined; + const taskResults: Array<{ success: boolean; taskMessageId: string; threadId: string }> = []; + + for (const task of tasks) { + const targetAgentId = (task as any).targetAgentId ?? agentId; + let taskMessageId: string | undefined; + + try { + const taskMessage = await ctx.messageModel.create({ + agentId: agentId!, + content: '', + metadata: { + instruction: task.instruction, + taskTitle: task.description, + ...(targetAgentId && targetAgentId !== agentId && { targetAgentId }), + }, + parentId: parentMessageId, + role: 'task', + threadId: state.metadata?.threadId ?? undefined, + topicId: topicId!, + }); + taskMessageId = taskMessage.id; + lastTaskMessageId = taskMessageId; + } catch (error) { + log('[%s] Failed to create task message for "%s": %O', taskLogId, task.description, error); + } + + let taskDispatched = false; + if (ctx.execSubAgentTask && topicId && agentId) { + try { + await ctx.execSubAgentTask({ + agentId: targetAgentId, + groupId: state.metadata?.groupId ?? undefined, + instruction: task.instruction, + parentMessageId: taskMessageId ?? parentMessageId, + parentOperationId: operationId, + timeout: task.timeout, + title: task.description, + topicId, + }); + taskDispatched = true; + log( + '[%s] Spawned sub-agent task "%s" for agent %s', + taskLogId, + task.description, + targetAgentId, + ); + } catch (error) { + log('[%s] Failed to spawn task "%s": %O', taskLogId, task.description, error); + if (taskMessageId) { + try { + await ctx.messageModel.update(taskMessageId, { + content: `Task failed to start: ${(error as Error).message}`, + }); + } catch { + // best-effort + } + } + } + } + taskResults.push({ + success: taskDispatched, + taskMessageId: taskMessageId ?? parentMessageId, + threadId: '', + }); + } + + return { + events, + newState: state, + nextContext: { + payload: { + parentMessageId: lastTaskMessageId ?? parentMessageId, + results: taskResults, + }, + phase: 'sub_agents_batch_result', + session: { + messageCount: state.messages.length, + sessionId: operationId, + status: 'running', + stepCount: state.stepCount + 1, + }, + } as unknown as AgentRuntimeContext, + }; + }, + /** * Complete runtime execution */ diff --git a/src/server/modules/AgentRuntime/__tests__/RuntimeExecutors.test.ts b/src/server/modules/AgentRuntime/__tests__/RuntimeExecutors.test.ts index 96ad15ba31..53aafb739a 100644 --- a/src/server/modules/AgentRuntime/__tests__/RuntimeExecutors.test.ts +++ b/src/server/modules/AgentRuntime/__tests__/RuntimeExecutors.test.ts @@ -3826,4 +3826,147 @@ describe('RuntimeExecutors', () => { }); }); }); + + // ─── callAgent server-side exec_sub_agent fix ────────────────────────────── + describe('call_tool → exec_sub_agent (callAgent async mode)', () => { + const createMockState = (overrides?: Partial): AgentState => ({ + cost: createMockCost(), + createdAt: new Date().toISOString(), + lastModified: new Date().toISOString(), + maxSteps: 100, + messages: [], + metadata: { + agentId: 'parent-agent-id', + topicId: 'topic-123', + }, + modelRuntimeConfig: { model: 'gpt-4', provider: 'openai' }, + operationId: 'op-123', + status: 'running', + stepCount: 0, + toolManifestMap: {}, + usage: createMockUsage(), + ...overrides, + }); + + it('call_tool sets stop:true in tool_result payload when tool returns execSubAgent state', async () => { + // Simulate agentManagement.callAgent returning execSubAgent state + mockToolExecutionService.executeTool.mockResolvedValue({ + content: '🚀 Triggered async task to call agent "target-agent"', + executionTime: 10, + state: { + parentMessageId: 'tool-msg-id', + task: { + description: 'Call agent target-agent', + instruction: 'Do something', + targetAgentId: 'target-agent-id', + timeout: 1_800_000, + }, + type: 'execSubAgent', + }, + success: true, + }); + + const executors = createRuntimeExecutors(ctx); + const state = createMockState(); + const instruction = { + payload: { + parentMessageId: 'assistant-msg-id', + toolCalling: { + apiName: 'callAgent', + arguments: JSON.stringify({ + agentId: 'target-agent-id', + instruction: 'Do something', + runAsTask: true, + }), + id: 'tool-call-1', + identifier: 'lobe-agent-management', + type: 'default' as const, + }, + }, + type: 'call_tool' as const, + }; + + const result = await executors.call_tool!(instruction, state); + + expect(result.nextContext?.phase).toBe('tool_result'); + expect((result.nextContext?.payload as any).stop).toBe(true); + }); + + it('exec_sub_agent executor creates task message and calls execSubAgentTask callback', async () => { + const mockExecSubAgentTask = vi + .fn() + .mockResolvedValue({ success: true, operationId: 'child-op', threadId: 'thread-child' }); + const ctxWithCallback = { + ...ctx, + execSubAgentTask: mockExecSubAgentTask, + topicId: 'topic-123', + }; + + const executors = createRuntimeExecutors(ctxWithCallback); + const state = createMockState(); + + const instruction = { + payload: { + parentMessageId: 'tool-msg-id', + task: { + description: 'Call agent target-agent', + instruction: 'Do something useful', + targetAgentId: 'target-agent-id', + timeout: 1_800_000, + }, + }, + type: 'exec_sub_agent' as const, + }; + + const result = await executors.exec_sub_agent!(instruction as any, state); + + // Task message created with role:'task' + expect(mockMessageModel.create).toHaveBeenCalledWith( + expect.objectContaining({ + agentId: 'parent-agent-id', + role: 'task', + parentId: 'tool-msg-id', + topicId: 'topic-123', + }), + ); + + // execSubAgentTask callback fired with targetAgentId + expect(mockExecSubAgentTask).toHaveBeenCalledWith( + expect.objectContaining({ + agentId: 'target-agent-id', + instruction: 'Do something useful', + topicId: 'topic-123', + parentOperationId: 'op-123', + }), + ); + + // Returns sub_agent_result so GeneralChatAgent continues with LLM call + expect(result.nextContext?.phase).toBe('sub_agent_result'); + }); + + it('exec_sub_agent gracefully skips dispatch when execSubAgentTask not injected', async () => { + // No callback injected (e.g. in tests that don't set it up) + const executors = createRuntimeExecutors(ctx); + const state = createMockState(); + + const instruction = { + payload: { + parentMessageId: 'tool-msg-id', + task: { + description: 'Call agent target-agent', + instruction: 'Do something', + targetAgentId: 'target-agent-id', + }, + }, + type: 'exec_sub_agent' as const, + }; + + const result = await executors.exec_sub_agent!(instruction as any, state); + + // Should still return sub_agent_result (not crash) + expect(result.nextContext?.phase).toBe('sub_agent_result'); + // Task message still created for UI + expect(mockMessageModel.create).toHaveBeenCalled(); + }); + }); }); diff --git a/src/server/services/agentRuntime/AgentRuntimeService.ts b/src/server/services/agentRuntime/AgentRuntimeService.ts index 888741d2c7..7b906fd812 100644 --- a/src/server/services/agentRuntime/AgentRuntimeService.ts +++ b/src/server/services/agentRuntime/AgentRuntimeService.ts @@ -8,7 +8,12 @@ import { AgentRuntime, findInMessages, GeneralChatAgent } from '@lobechat/agent- import type { ISnapshotStore } from '@lobechat/agent-tracing'; import { dynamicInterventionAudits } from '@lobechat/builtin-tools/dynamicInterventionAudits'; import { getModelPropertyWithFallback } from '@lobechat/model-runtime'; -import { AgentRuntimeErrorType, ChatErrorType, type ChatMessageError } from '@lobechat/types'; +import { + AgentRuntimeErrorType, + ChatErrorType, + type ChatMessageError, + type ExecSubAgentTaskParams, +} from '@lobechat/types'; import debug from 'debug'; import urlJoin from 'url-join'; @@ -17,8 +22,10 @@ import { type LobeChatDatabase } from '@/database/type'; import { appEnv } from '@/envs/app'; import { type AgentRuntimeCoordinatorOptions } from '@/server/modules/AgentRuntime'; import { AgentRuntimeCoordinator, createStreamEventManager } from '@/server/modules/AgentRuntime'; -import { type RuntimeExecutorContext } from '@/server/modules/AgentRuntime/RuntimeExecutors'; -import { createRuntimeExecutors } from '@/server/modules/AgentRuntime/RuntimeExecutors'; +import { + createRuntimeExecutors, + type RuntimeExecutorContext, +} from '@/server/modules/AgentRuntime/RuntimeExecutors'; import { type IStreamEventManager } from '@/server/modules/AgentRuntime/types'; import { emitAgentSignalSourceEvent } from '@/server/services/agentSignal'; import { toAgentSignalTraceEvents } from '@/server/services/agentSignal/observability/traceEvents'; @@ -116,6 +123,12 @@ export interface AgentRuntimeServiceOptions { * Allows injection of custom stateManager and streamEventManager */ coordinatorOptions?: AgentRuntimeCoordinatorOptions; + /** + * Callback to spawn a sub-agent task from within a running server-side agent. + * Injected by AiAgentService to wire up the exec_task / exec_tasks executors + * without creating a circular import between RuntimeExecutors and AiAgentService. + */ + execSubAgentTask?: (params: ExecSubAgentTaskParams) => Promise; /** * Custom QueueService * Set to null to disable queue scheduling (for synchronous execution tests) @@ -155,6 +168,7 @@ export class AgentRuntimeService { private agentFactory?: (config: GeneralAgentConfig) => Agent; private completionLifecycle: CompletionLifecycle; private coordinator: AgentRuntimeCoordinator; + private execSubAgentTaskCallback?: (params: ExecSubAgentTaskParams) => Promise; private humanIntervention: HumanInterventionHandler; private streamManager: IStreamEventManager; private queueService: QueueService | null; @@ -185,6 +199,7 @@ export class AgentRuntimeService { options?.snapshotStore ?? this.createDefaultSnapshotStore(), ); this.agentFactory = options?.agentFactory; + this.execSubAgentTaskCallback = options?.execSubAgentTask; this.serverDB = db; this.userId = userId; this.messageModel = new MessageModel(db, this.userId); @@ -1360,6 +1375,7 @@ export class AgentRuntimeService { discordContext: metadata?.discordContext, userTimezone: metadata?.userTimezone, evalContext: metadata?.evalContext, + execSubAgentTask: this.execSubAgentTaskCallback, hookDispatcher, loadAgentState: this.coordinator.loadAgentState.bind(this.coordinator), messageModel: this.messageModel, diff --git a/src/server/services/aiAgent/index.ts b/src/server/services/aiAgent/index.ts index 5464761e45..1131d0fb35 100644 --- a/src/server/services/aiAgent/index.ts +++ b/src/server/services/aiAgent/index.ts @@ -263,7 +263,10 @@ export class AiAgentService { this.taskModel = new TaskModel(db, userId); this.threadModel = new ThreadModel(db, userId); this.topicModel = new TopicModel(db, userId); - this.agentRuntimeService = new AgentRuntimeService(db, userId, options?.runtimeOptions); + this.agentRuntimeService = new AgentRuntimeService(db, userId, { + ...options?.runtimeOptions, + execSubAgentTask: this.execSubAgentTask.bind(this), + }); this.marketService = new MarketService({ userInfo: { userId } }); this.klavisService = new KlavisService({ db, userId }); } From 8a2d05d64ec0d59328240d28f69509c5f7666754 Mon Sep 17 00:00:00 2001 From: LiJian Date: Mon, 18 May 2026 17:48:46 +0800 Subject: [PATCH 007/224] =?UTF-8?q?=F0=9F=90=9B=20fix(market):=20map=20get?= =?UTF-8?q?UserByUsername=20404=20to=20NOT=5FFOUND=20instead=20of=20500=20?= =?UTF-8?q?(#14929)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🐛 fix(market): map 404 from market API to NOT_FOUND instead of 500 When a user hasn't set up a market username yet, getUserByUsername returns 404 — an expected first-login scenario. The backend was wrapping this as INTERNAL_SERVER_ERROR (500), causing SWR to retry 3× per component and flooding server logs with false-alarm 500s. - server: catch MarketAPIError status 404 and re-throw as TRPCError NOT_FOUND - client: add shouldRetryOnError to useMarketUserProfile so SWR does not retry on NOT_FOUND, eliminating log noise from UserAvatar / MarketAuthProvider Co-authored-by: LobeHub Bot Co-authored-by: Claude Sonnet 4.6 --- .../AuthProvider/MarketAuth/useMarketUserProfile.ts | 2 ++ src/server/routers/lambda/market/user.ts | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/layout/AuthProvider/MarketAuth/useMarketUserProfile.ts b/src/layout/AuthProvider/MarketAuth/useMarketUserProfile.ts index 7da6ec82a9..7c7bb978c8 100644 --- a/src/layout/AuthProvider/MarketAuth/useMarketUserProfile.ts +++ b/src/layout/AuthProvider/MarketAuth/useMarketUserProfile.ts @@ -26,6 +26,8 @@ export const useMarketUserProfile = (username: string | null | undefined) => { dedupingInterval: 60_000, // 1 minute deduplication revalidateOnFocus: false, revalidateOnReconnect: false, + // NOT_FOUND means the user hasn't created a market username yet — no point retrying + shouldRetryOnError: (error) => error?.data?.code !== 'NOT_FOUND', }, ); }; diff --git a/src/server/routers/lambda/market/user.ts b/src/server/routers/lambda/market/user.ts index 4e87dbb53b..80b8b8f792 100644 --- a/src/server/routers/lambda/market/user.ts +++ b/src/server/routers/lambda/market/user.ts @@ -70,6 +70,19 @@ export const userRouter = router({ } catch (error) { if (error instanceof TRPCError) throw error; + // 404 is expected when a user hasn't set up a market username yet — not an internal error + if ( + error instanceof Error && + 'status' in error && + (error as Error & { status: unknown }).status === 404 + ) { + throw new TRPCError({ + cause: error, + code: 'NOT_FOUND', + message: `User not found: ${input.username}`, + }); + } + log('Error getting user profile: %O', error); throw new TRPCError({ cause: error, From ae4145ba1289f2eab83732b38e97afd6db924fcb Mon Sep 17 00:00:00 2001 From: Innei Date: Mon, 18 May 2026 19:50:12 +0800 Subject: [PATCH 008/224] =?UTF-8?q?=F0=9F=90=9B=20fix(desktop):=20restore?= =?UTF-8?q?=20route=20after=20update=20restart=20(#14922)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🐛 fix(desktop): restore route after update restart When the desktop app installs an update and restarts via quitAndInstall, the main window always reloaded path '/', dropping whatever route the user was on. Capture the active route in installNow() and restore it on the next launch (consume-once). * 🐛 fix(desktop): consume update restore route once --- apps/desktop/src/main/const/store.ts | 1 + .../src/main/core/browser/BrowserManager.ts | 26 +++++++++- .../browser/__tests__/BrowserManager.test.ts | 44 +++++++++++++++++ .../core/infrastructure/UpdaterManager.ts | 18 +++++++ .../__tests__/UpdaterManager.test.ts | 45 +++++++++++++++++ .../modules/updater/__tests__/utils.test.ts | 49 +++++++++++++++++++ .../desktop/src/main/modules/updater/utils.ts | 33 +++++++++++++ apps/desktop/src/main/types/store.ts | 1 + 8 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 apps/desktop/src/main/modules/updater/__tests__/utils.test.ts diff --git a/apps/desktop/src/main/const/store.ts b/apps/desktop/src/main/const/store.ts index 292e265e43..bc42268de6 100644 --- a/apps/desktop/src/main/const/store.ts +++ b/apps/desktop/src/main/const/store.ts @@ -37,6 +37,7 @@ export const STORE_DEFAULTS: ElectronMainStore = { locale: 'auto', localFileWorkspaceRoots: [], networkProxy: defaultProxySettings, + pendingRestoreRoute: '', shortcuts: DEFAULT_ELECTRON_DESKTOP_SHORTCUTS, storagePath: appStorageDir, themeMode: 'system', diff --git a/apps/desktop/src/main/core/browser/BrowserManager.ts b/apps/desktop/src/main/core/browser/BrowserManager.ts index 37aaf5537a..a04a8b6532 100644 --- a/apps/desktop/src/main/core/browser/BrowserManager.ts +++ b/apps/desktop/src/main/core/browser/BrowserManager.ts @@ -256,6 +256,26 @@ export class BrowserManager { }); } + /** + * Consume a route captured before an update restart. The captured route is + * cleared before any navigation decision so a subsequent normal launch never + * restores a stale route. + */ + private consumePendingRestoreRoute(): string { + const pendingRestoreRoute = this.app.storeManager.get('pendingRestoreRoute', ''); + if (pendingRestoreRoute) this.app.storeManager.set('pendingRestoreRoute', ''); + return pendingRestoreRoute; + } + + private resolveMainWindowInitialPath( + isOnboardingCompleted: boolean, + pendingRestoreRoute: string, + ): string { + if (!isOnboardingCompleted) return '/desktop-onboarding'; + if (pendingRestoreRoute) return pendingRestoreRoute; + return '/'; + } + /** * Initialize all browsers when app starts up */ @@ -271,7 +291,11 @@ export class BrowserManager { // Dynamically determine initial path for main window if (browser.identifier === BrowsersIdentifiers.app) { - const initialPath = isOnboardingCompleted ? '/' : '/desktop-onboarding'; + const pendingRestoreRoute = this.consumePendingRestoreRoute(); + const initialPath = this.resolveMainWindowInitialPath( + isOnboardingCompleted, + pendingRestoreRoute, + ); browser = { ...browser, keepAlive: isLinux ? false : browser.keepAlive, diff --git a/apps/desktop/src/main/core/browser/__tests__/BrowserManager.test.ts b/apps/desktop/src/main/core/browser/__tests__/BrowserManager.test.ts index d476afaf5b..18d709e41a 100644 --- a/apps/desktop/src/main/core/browser/__tests__/BrowserManager.test.ts +++ b/apps/desktop/src/main/core/browser/__tests__/BrowserManager.test.ts @@ -110,6 +110,13 @@ describe('BrowserManager', () => { getController: vi.fn().mockReturnValue({ isRemoteServerConfigured: vi.fn().mockResolvedValue(true), }), + storeManager: { + get: vi.fn((key: string) => { + if (key === 'pendingRestoreRoute') return ''; + return ''; + }), + set: vi.fn(), + }, } as unknown as AppCore; manager = new BrowserManager(mockApp); @@ -266,6 +273,43 @@ describe('BrowserManager', () => { expect(manager.browsers.has('app')).toBe(true); expect(manager.browsers.has('settings')).toBe(false); }); + + it('restores a captured route as the main window initial path', async () => { + (mockApp.storeManager.get as any).mockImplementation((key: string) => { + if (key === 'pendingRestoreRoute') return '/agent/abc'; + return ''; + }); + + await manager.initializeBrowsers(); + + expect(manager.browsers.get('app')?.options.path).toBe('/agent/abc'); + }); + + it('clears the captured route after consuming it', async () => { + (mockApp.storeManager.get as any).mockImplementation((key: string) => { + if (key === 'pendingRestoreRoute') return '/agent/abc'; + return ''; + }); + + await manager.initializeBrowsers(); + + expect(mockApp.storeManager.set).toHaveBeenCalledWith('pendingRestoreRoute', ''); + }); + + it('ignores the captured route when onboarding is not completed', async () => { + (mockApp.storeManager.get as any).mockImplementation((key: string) => { + if (key === 'pendingRestoreRoute') return '/agent/abc'; + return ''; + }); + (mockApp.getController as any).mockReturnValue({ + isRemoteServerConfigured: vi.fn().mockResolvedValue(false), + }); + + await manager.initializeBrowsers(); + + expect(manager.browsers.get('app')?.options.path).toBe('/desktop-onboarding'); + expect(mockApp.storeManager.set).toHaveBeenCalledWith('pendingRestoreRoute', ''); + }); }); describe('broadcastToAllWindows', () => { diff --git a/apps/desktop/src/main/core/infrastructure/UpdaterManager.ts b/apps/desktop/src/main/core/infrastructure/UpdaterManager.ts index d9ed61df2a..053b89e420 100644 --- a/apps/desktop/src/main/core/infrastructure/UpdaterManager.ts +++ b/apps/desktop/src/main/core/infrastructure/UpdaterManager.ts @@ -12,6 +12,7 @@ import { autoUpdater } from 'electron-updater'; import { isDev, isWindows } from '@/const/env'; import { getDesktopEnv } from '@/env'; import { UPDATE_CHANNEL, UPDATE_SERVER_URL, updaterConfig } from '@/modules/updater/configs'; +import { extractRestoreRoute } from '@/modules/updater/utils'; import { createLogger } from '@/utils/logger'; import type { App as AppCore } from '../App'; @@ -239,12 +240,29 @@ export class UpdaterManager { } }; + private captureRestoreRoute = () => { + try { + const url = this.mainWindow.webContents?.getURL(); + if (!url) return; + + const route = extractRestoreRoute(url); + if (!route) return; + + this.app.storeManager.set('pendingRestoreRoute', route); + logger.info(`Captured route for restore after update restart: ${route}`); + } catch (error) { + logger.warn('Failed to capture route for restore after update restart:', error); + } + }; + /** * Install update immediately */ public installNow = () => { logger.info('Installing update now...'); + this.captureRestoreRoute(); + this.app.isQuiting = true; logger.info('Closing all windows before update installation...'); diff --git a/apps/desktop/src/main/core/infrastructure/__tests__/UpdaterManager.test.ts b/apps/desktop/src/main/core/infrastructure/__tests__/UpdaterManager.test.ts index 59d7cd91ef..8ee116db48 100644 --- a/apps/desktop/src/main/core/infrastructure/__tests__/UpdaterManager.test.ts +++ b/apps/desktop/src/main/core/infrastructure/__tests__/UpdaterManager.test.ts @@ -361,6 +361,51 @@ describe('UpdaterManager', () => { }); }); + describe('captureRestoreRoute', () => { + const callCapture = () => (updaterManager as any).captureRestoreRoute(); + + it('stores the derived route from the main window URL', () => { + (mockApp.browserManager.getMainWindow as any).mockReturnValue({ + webContents: { getURL: () => 'app://renderer/agent/abc' }, + }); + + callCapture(); + + expect(mockApp.storeManager.set).toHaveBeenCalledWith('pendingRestoreRoute', '/agent/abc'); + }); + + it('stores nothing when the URL is not a restorable route', () => { + (mockApp.browserManager.getMainWindow as any).mockReturnValue({ + webContents: { getURL: () => 'app://renderer/' }, + }); + + callCapture(); + + expect(mockApp.storeManager.set).not.toHaveBeenCalled(); + }); + + it('stores nothing when there is no webContents', () => { + (mockApp.browserManager.getMainWindow as any).mockReturnValue({ webContents: null }); + + callCapture(); + + expect(mockApp.storeManager.set).not.toHaveBeenCalled(); + }); + + it('does not throw when reading the URL fails', () => { + (mockApp.browserManager.getMainWindow as any).mockReturnValue({ + webContents: { + getURL: () => { + throw new Error('boom'); + }, + }, + }); + + expect(() => callCapture()).not.toThrow(); + expect(mockApp.storeManager.set).not.toHaveBeenCalled(); + }); + }); + describe('installLater', () => { it('should set autoInstallOnAppQuit to true', () => { updaterManager.installLater(); diff --git a/apps/desktop/src/main/modules/updater/__tests__/utils.test.ts b/apps/desktop/src/main/modules/updater/__tests__/utils.test.ts new file mode 100644 index 0000000000..d17c90b94d --- /dev/null +++ b/apps/desktop/src/main/modules/updater/__tests__/utils.test.ts @@ -0,0 +1,49 @@ +import { describe, expect, it } from 'vitest'; + +import { extractRestoreRoute } from '../utils'; + +describe('extractRestoreRoute', () => { + it('extracts the route from a production renderer URL', () => { + expect(extractRestoreRoute('app://renderer/agent/abc')).toBe('/agent/abc'); + }); + + it('extracts the route from a dev renderer URL', () => { + expect(extractRestoreRoute('http://localhost:5173/settings/provider')).toBe( + '/settings/provider', + ); + }); + + it('strips the lng query param but keeps the rest', () => { + expect(extractRestoreRoute('app://renderer/agent?lng=zh-CN&x=1')).toBe('/agent?x=1'); + }); + + it('strips a lone lng query param', () => { + expect(extractRestoreRoute('app://renderer/chat?lng=en-US')).toBe('/chat'); + }); + + it('returns null for the root route', () => { + expect(extractRestoreRoute('app://renderer/')).toBeNull(); + }); + + it('returns null for file: protocol (splash/error pages)', () => { + expect(extractRestoreRoute('file:///Users/x/resources/splash.html')).toBeNull(); + }); + + it('returns null for static asset paths', () => { + expect(extractRestoreRoute('app://renderer/assets/index.js')).toBeNull(); + }); + + it('returns null for root static files', () => { + expect(extractRestoreRoute('app://renderer/favicon.ico')).toBeNull(); + }); + + it('preserves dotted SPA route slugs', () => { + expect(extractRestoreRoute('app://renderer/community/skill/github.owner.repo')).toBe( + '/community/skill/github.owner.repo', + ); + }); + + it('returns null for an unparseable URL', () => { + expect(extractRestoreRoute('not a url')).toBeNull(); + }); +}); diff --git a/apps/desktop/src/main/modules/updater/utils.ts b/apps/desktop/src/main/modules/updater/utils.ts index d848524fa3..ae09b90a9c 100644 --- a/apps/desktop/src/main/modules/updater/utils.ts +++ b/apps/desktop/src/main/modules/updater/utils.ts @@ -1,5 +1,12 @@ import semver from 'semver'; +const STATIC_ASSET_PATH_PREFIXES = ['/assets/', '/_next/', '/static/']; +const ROOT_STATIC_FILE_RE = /^\/[^/]+\.[^/]+$/; + +const isStaticAssetPath = (pathname: string) => + ROOT_STATIC_FILE_RE.test(pathname) || + STATIC_ASSET_PATH_PREFIXES.some((prefix) => pathname.startsWith(prefix)); + /** * Determine if application update is needed rather than just renderer update * @param currentVersion Current version @@ -31,3 +38,29 @@ export const shouldUpdateApp = (currentVersion: string, nextVersion: string): bo return true; } }; + +/** + * Extract a restorable SPA route (`pathname + search`) from a renderer window URL. + * Returns `null` when the URL is not a restorable route — splash/error pages + * (`file:` protocol), known static asset paths, or the root route (identical + * to the default, nothing worth restoring). + */ +export const extractRestoreRoute = (rawUrl: string): string | null => { + let url: URL; + try { + url = new URL(rawUrl); + } catch { + return null; + } + + if (url.protocol === 'file:') return null; + if (isStaticAssetPath(url.pathname)) return null; + + // `lng` is re-appended by Browser.buildUrlWithLocale on the next load + url.searchParams.delete('lng'); + + const route = `${url.pathname}${url.search}`; + if (route === '/' || route === '') return null; + + return route; +}; diff --git a/apps/desktop/src/main/types/store.ts b/apps/desktop/src/main/types/store.ts index 266573db99..799d48662d 100644 --- a/apps/desktop/src/main/types/store.ts +++ b/apps/desktop/src/main/types/store.ts @@ -21,6 +21,7 @@ export interface ElectronMainStore { locale: string; localFileWorkspaceRoots: string[]; networkProxy: NetworkProxySettings; + pendingRestoreRoute: string; shortcuts: Record; storagePath: string; themeMode: 'dark' | 'light' | 'system'; From c6d36333378cc5a56592857c27bd73510afc02e4 Mon Sep 17 00:00:00 2001 From: Innei Date: Mon, 18 May 2026 20:17:19 +0800 Subject: [PATCH 009/224] =?UTF-8?q?=F0=9F=90=9B=20fix(desktop):=20prevent?= =?UTF-8?q?=20frequent=20logout=20from=20token=20refresh=20retry=20(#14928?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🐛 fix(desktop): prevent frequent logout from token refresh retry The OIDC server rotates refresh tokens and revokes the whole grant when a consumed refresh token is reused. The desktop refresh wrapper retried the token request up to 4 times reusing the same stored refresh token, so any failure after the server had already consumed it (lost response, timeout, parse error) guaranteed an invalid_grant on the next attempt and logged the user out. - RemoteServerConfigCtr: drop the in-line retry — refresh is now a single attempt; transient failures recover on the next refresh cycle - AuthCtr: refresh proactively only when the access token is near expiry instead of on every launch/activation, cutting refresh-token rotations from dozens a day to roughly one a week - remove the now-unused async-retry dependency * 🐛 fix(desktop): use a small buffer for proactive token refresh checks isTokenExpiringSoon() defaults to a 24h buffer. An OIDC server issuing access tokens with a lifetime <= 24h would be treated as "expiring soon" right after login, refreshing on every launch/activation and recreating the refresh-token rotation churn this branch removes. Pass an explicit 10-minute buffer at all three call sites (auto-refresh timer, startup init, app activation) so the behaviour no longer depends on the server's access-token lifetime. --- apps/desktop/package.json | 2 - apps/desktop/src/main/controllers/AuthCtr.ts | 72 +++------- .../main/controllers/RemoteServerConfigCtr.ts | 67 ++------- .../controllers/__tests__/AuthCtr.test.ts | 128 +++++------------- .../__tests__/RemoteServerConfigCtr.test.ts | 8 +- 5 files changed, 67 insertions(+), 210 deletions(-) diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 84db6df938..bc8ce85345 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -64,14 +64,12 @@ "@lobehub/i18n-cli": "^1.25.1", "@modelcontextprotocol/sdk": "^1.24.3", "@t3-oss/env-core": "^0.13.8", - "@types/async-retry": "^1.4.9", "@types/resolve": "^1.20.6", "@types/semver": "^7.7.1", "@types/set-cookie-parser": "^2.4.10", "@typescript/native-preview": "7.0.0-dev.20251210.1", "@vanilla-extract/css": "^1.17.4", "@vanilla-extract/vite-plugin": "^5.1.0", - "async-retry": "^1.3.3", "consola": "^3.4.2", "cookie": "^1.1.1", "cross-env": "^10.1.0", diff --git a/apps/desktop/src/main/controllers/AuthCtr.ts b/apps/desktop/src/main/controllers/AuthCtr.ts index 6e1a26764f..d67967de99 100644 --- a/apps/desktop/src/main/controllers/AuthCtr.ts +++ b/apps/desktop/src/main/controllers/AuthCtr.ts @@ -21,7 +21,12 @@ const logger = createLogger('controllers:AuthCtr'); const MAX_POLL_TIME = 2 * 60 * 1000; // 2 minutes (reduced from 5 minutes for better UX) const POLL_INTERVAL = 3000; // 3 seconds -const TOKEN_REFRESH_DEBOUNCE = 5 * 60 * 1000; // 5 minutes - debounce interval to prevent excessive refreshes on rapid app restarts + +// Refresh the access token only once it is within this window of its expiry. Kept +// small (minutes) on purpose: a buffer that is large relative to the server's +// access-token lifetime makes the token look "expiring soon" right after login, +// refreshing on every launch/activation and churning refresh-token rotations. +const TOKEN_REFRESH_BUFFER = 10 * 60 * 1000; // 10 minutes /** * Authentication Controller @@ -292,8 +297,7 @@ export default class AuthCtr extends ControllerModule { this.autoRefreshTimer = setInterval(async () => { try { - // Check if token is expiring soon (refresh 5 minutes in advance) - if (!this.remoteServerConfigCtr.isTokenExpiringSoon()) { + if (!this.remoteServerConfigCtr.isTokenExpiringSoon(TOKEN_REFRESH_BUFFER)) { return; } const expiresAt = this.remoteServerConfigCtr.getTokenExpiresAt(); @@ -683,7 +687,7 @@ export default class AuthCtr extends ControllerModule { /** * Initialize auto-refresh functionality * Checks for valid token at app startup and starts auto-refresh timer if token exists - * Proactively refreshes token on every startup (with 5-minute debounce to prevent rapid restart issues) + * Proactively refreshes the token only when it is expired or near expiry */ private async initializeAutoRefresh() { try { @@ -711,26 +715,18 @@ export default class AuthCtr extends ControllerModule { return; } - const currentTime = Date.now(); - - // Check if token has already expired - if (currentTime >= expiresAt) { - logger.info('Token has expired, attempting to refresh it'); - await this.performProactiveRefresh(); - return; - } - - // Proactively refresh token if it hasn't been refreshed in the last 6 hours - // This ensures token validity even if the server has revoked it - if (this.shouldProactivelyRefresh()) { - logger.info('Token refresh interval exceeded, proactively refreshing token on startup'); + // Refresh proactively only when the token is actually near expiry. The access + // token is long-lived; refreshing on every launch just multiplies refresh-token + // rotations — and the chance of a lost-response logout — for no benefit. + if (this.remoteServerConfigCtr.isTokenExpiringSoon(TOKEN_REFRESH_BUFFER)) { + logger.info('Token is expired or expiring soon, refreshing on startup'); await this.performProactiveRefresh(); return; } // Start auto-refresh timer logger.info( - `Token is valid and recently refreshed, starting auto-refresh timer. Token expires at: ${new Date(expiresAt).toISOString()}`, + `Token is valid, starting auto-refresh timer. Token expires at: ${new Date(expiresAt).toISOString()}`, ); this.startAutoRefresh(); } catch (error) { @@ -738,36 +734,6 @@ export default class AuthCtr extends ControllerModule { } } - /** - * Check if token should be proactively refreshed - * Returns true if the token hasn't been refreshed recently (within debounce interval) - * This ensures we refresh on every app launch while preventing excessive refreshes on rapid restarts - */ - private shouldProactivelyRefresh(): boolean { - const lastRefreshAt = this.remoteServerConfigCtr.getLastTokenRefreshAt(); - - // If never refreshed, should refresh - if (!lastRefreshAt) { - logger.debug('No last refresh time found, should proactively refresh'); - return true; - } - - const timeSinceLastRefresh = Date.now() - lastRefreshAt; - const shouldRefresh = timeSinceLastRefresh >= TOKEN_REFRESH_DEBOUNCE; - - if (shouldRefresh) { - logger.debug( - `Time since last refresh: ${Math.round(timeSinceLastRefresh / 1000 / 60)} minutes, exceeds ${TOKEN_REFRESH_DEBOUNCE / 1000 / 60} minutes debounce threshold`, - ); - } else { - logger.debug( - `Time since last refresh: ${Math.round(timeSinceLastRefresh / 1000 / 60)} minutes, within ${TOKEN_REFRESH_DEBOUNCE / 1000 / 60} minutes debounce threshold, skipping refresh`, - ); - } - - return shouldRefresh; - } - /** * Perform proactive token refresh (used on startup and app activation) */ @@ -796,7 +762,7 @@ export default class AuthCtr extends ControllerModule { /** * Handle app activation event (e.g., Mac dock click, window focus) - * Proactively refresh token if needed (respects 6-hour interval) + * Proactively refresh token if it is expired or near expiry */ async onAppActivate(): Promise { logger.debug('App activated, checking if token refresh is needed'); @@ -817,12 +783,12 @@ export default class AuthCtr extends ControllerModule { return; } - // Only refresh if interval has passed - if (this.shouldProactivelyRefresh()) { - logger.info('Token refresh interval exceeded on app activation, refreshing token'); + // Refresh only when the token is actually near expiry (see initializeAutoRefresh). + if (this.remoteServerConfigCtr.isTokenExpiringSoon(TOKEN_REFRESH_BUFFER)) { + logger.info('Token is expiring soon on app activation, refreshing token'); await this.performProactiveRefresh(); } else { - logger.debug('Token was recently refreshed, skipping activation refresh'); + logger.debug('Token is still valid, skipping activation refresh'); } } catch (error) { logger.error('Error during app activation refresh check:', error); diff --git a/apps/desktop/src/main/controllers/RemoteServerConfigCtr.ts b/apps/desktop/src/main/controllers/RemoteServerConfigCtr.ts index 2c7fa225cb..6f1c154a9d 100644 --- a/apps/desktop/src/main/controllers/RemoteServerConfigCtr.ts +++ b/apps/desktop/src/main/controllers/RemoteServerConfigCtr.ts @@ -2,7 +2,6 @@ import querystring from 'node:querystring'; import { URL } from 'node:url'; import type { DataSyncConfig } from '@lobechat/electron-client-ipc'; -import retry from 'async-retry'; import { safeStorage, session as electronSession } from 'electron'; import { OFFICIAL_CLOUD_SERVER } from '@/const/env'; @@ -378,10 +377,8 @@ export default class RemoteServerConfigCtr extends ControllerModule { } /** - * Refresh access token with retry mechanism - * Use stored refresh token to obtain a new access token - * Handles concurrent requests by returning the existing refresh promise if one is in progress. - * Retries up to 3 times with exponential backoff for transient errors. + * Refresh the access token using the stored refresh token (single attempt). + * Concurrent callers share the in-progress refresh promise. */ async refreshAccessToken(): Promise<{ error?: string; success: boolean }> { // If a refresh is already in progress, return the existing promise @@ -390,60 +387,18 @@ export default class RemoteServerConfigCtr extends ControllerModule { return this.refreshPromise; } - // Start a new refresh operation with retry - logger.info('Initiating new token refresh operation with retry.'); - this.refreshPromise = this.performTokenRefreshWithRetry(); + logger.info('Initiating new token refresh operation.'); - // Return the promise so callers can wait - return this.refreshPromise; - } - - /** - * Performs token refresh with retry mechanism - * Uses exponential backoff: 1s, 2s, 4s - */ - private async performTokenRefreshWithRetry(): Promise<{ error?: string; success: boolean }> { - try { - return await retry( - async (bail, attemptNumber) => { - logger.debug(`Token refresh attempt ${attemptNumber}/3`); - - const result = await this.performTokenRefresh(); - - if (result.success) { - return result; - } - - // Check if error is non-retryable - if (this.isNonRetryableError(result.error)) { - logger.warn(`Non-retryable error encountered: ${result.error}`); - // Use bail to stop retrying immediately - bail(new Error(result.error)); - return result; // This won't be reached, but TypeScript needs it - } - - // Throw error to trigger retry for transient errors - throw new Error(result.error); - }, - { - factor: 2, // Exponential backoff factor - maxTimeout: 4000, // Max wait time between retries: 4s - minTimeout: 1000, // Min wait time between retries: 1s - onRetry: (err: Error, attempt: number) => { - logger.info(`Token refresh retry ${attempt}/3: ${err.message}`); - }, - retries: 3, // Total retry attempts - }, - ); - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - logger.error('Token refresh failed after all retries:', errorMessage); - return { error: errorMessage, success: false }; - } finally { - // Ensure the promise reference is cleared once the operation completes + // No retry: with refresh token rotation the server consumes the old token as soon + // as the request lands. Resending it (e.g. after a lost response) triggers reuse + // detection — invalid_grant + revocation of the whole grant — which logs the user + // out. Transient failures are recovered by the next refresh cycle instead. + this.refreshPromise = this.performTokenRefresh().finally(() => { logger.debug('Clearing the refresh promise reference.'); this.refreshPromise = null; - } + }); + + return this.refreshPromise; } /** diff --git a/apps/desktop/src/main/controllers/__tests__/AuthCtr.test.ts b/apps/desktop/src/main/controllers/__tests__/AuthCtr.test.ts index 29fe01fb18..691bcd0bdc 100644 --- a/apps/desktop/src/main/controllers/__tests__/AuthCtr.test.ts +++ b/apps/desktop/src/main/controllers/__tests__/AuthCtr.test.ts @@ -721,10 +721,7 @@ describe('AuthCtr', () => { }); describe('Proactive Token Refresh', () => { - const FIVE_MINUTES = 5 * 60 * 1000; // Debounce interval - beforeEach(() => { - // Reset mocks for proactive refresh tests vi.mocked(mockRemoteServerConfigCtr.getRemoteServerConfig).mockResolvedValue({ active: true, remoteServerUrl: 'https://lobehub-cloud.com', @@ -733,22 +730,16 @@ describe('AuthCtr', () => { vi.mocked(mockRemoteServerConfigCtr.isRemoteServerConfigured).mockResolvedValue(true); vi.mocked(mockRemoteServerConfigCtr.getAccessToken).mockResolvedValue('mock-access-token'); vi.mocked(mockRemoteServerConfigCtr.getTokenExpiresAt).mockReturnValue( - Date.now() + 3600000, // Token valid for 1 hour + Date.now() + 7 * 24 * 60 * 60 * 1000, // Token valid for 7 days ); - // Reset getLastTokenRefreshAt to a recent value by default - // Individual tests will override this as needed - vi.mocked(mockRemoteServerConfigCtr.getLastTokenRefreshAt).mockReturnValue(Date.now()); + vi.mocked(mockRemoteServerConfigCtr.isTokenExpiringSoon).mockReturnValue(false); + vi.mocked(mockRemoteServerConfigCtr.refreshAccessToken).mockResolvedValue({ success: true }); + vi.mocked(mockRemoteServerConfigCtr.isNonRetryableError).mockReturnValue(false); }); describe('onAppActivate', () => { - it('should refresh token when last refresh was more than 5 minutes ago', async () => { - // Last refresh was 10 minutes ago (exceeds 5-minute debounce) - vi.mocked(mockRemoteServerConfigCtr.getLastTokenRefreshAt).mockReturnValue( - Date.now() - 10 * 60 * 1000, - ); - vi.mocked(mockRemoteServerConfigCtr.refreshAccessToken).mockResolvedValue({ - success: true, - }); + it('should refresh token when it is expiring soon', async () => { + vi.mocked(mockRemoteServerConfigCtr.isTokenExpiringSoon).mockReturnValue(true); await authCtr.onAppActivate(); @@ -756,33 +747,27 @@ describe('AuthCtr', () => { expect(mockWindow.webContents.send).toHaveBeenCalledWith('tokenRefreshed'); }); - it('should NOT refresh token when last refresh was within 5 minutes', async () => { - // Last refresh was 2 minutes ago (within 5-minute debounce) - vi.mocked(mockRemoteServerConfigCtr.getLastTokenRefreshAt).mockReturnValue( - Date.now() - 2 * 60 * 1000, - ); + it('should NOT refresh token when it is not expiring soon', async () => { + vi.mocked(mockRemoteServerConfigCtr.isTokenExpiringSoon).mockReturnValue(false); await authCtr.onAppActivate(); expect(mockRemoteServerConfigCtr.refreshAccessToken).not.toHaveBeenCalled(); }); - it('should refresh token when lastRefreshAt is undefined (never refreshed)', async () => { - vi.mocked(mockRemoteServerConfigCtr.getLastTokenRefreshAt).mockReturnValue(undefined); - vi.mocked(mockRemoteServerConfigCtr.refreshAccessToken).mockResolvedValue({ - success: true, - }); + it('should check expiry with a small buffer, not the 24h default', async () => { + vi.mocked(mockRemoteServerConfigCtr.isTokenExpiringSoon).mockReturnValue(false); await authCtr.onAppActivate(); - expect(mockRemoteServerConfigCtr.refreshAccessToken).toHaveBeenCalled(); + const [buffer] = vi.mocked(mockRemoteServerConfigCtr.isTokenExpiringSoon).mock.calls[0]; + expect(buffer).toBeGreaterThan(0); + expect(buffer).toBeLessThanOrEqual(60 * 60 * 1000); }); it('should skip refresh when remote server is not active', async () => { vi.mocked(mockRemoteServerConfigCtr.isRemoteServerConfigured).mockResolvedValue(false); - vi.mocked(mockRemoteServerConfigCtr.getLastTokenRefreshAt).mockReturnValue( - Date.now() - 10 * 60 * 1000, - ); + vi.mocked(mockRemoteServerConfigCtr.isTokenExpiringSoon).mockReturnValue(true); await authCtr.onAppActivate(); @@ -791,19 +776,15 @@ describe('AuthCtr', () => { it('should skip refresh when no access token exists', async () => { vi.mocked(mockRemoteServerConfigCtr.getAccessToken).mockResolvedValue(null); - vi.mocked(mockRemoteServerConfigCtr.getLastTokenRefreshAt).mockReturnValue( - Date.now() - 10 * 60 * 1000, - ); + vi.mocked(mockRemoteServerConfigCtr.isTokenExpiringSoon).mockReturnValue(true); await authCtr.onAppActivate(); expect(mockRemoteServerConfigCtr.refreshAccessToken).not.toHaveBeenCalled(); }); - it('should handle refresh failure with non-retryable error', async () => { - vi.mocked(mockRemoteServerConfigCtr.getLastTokenRefreshAt).mockReturnValue( - Date.now() - 10 * 60 * 1000, - ); + it('should clear tokens and require re-auth on non-retryable error', async () => { + vi.mocked(mockRemoteServerConfigCtr.isTokenExpiringSoon).mockReturnValue(true); vi.mocked(mockRemoteServerConfigCtr.refreshAccessToken).mockResolvedValue({ error: 'invalid_grant', success: false, @@ -819,10 +800,8 @@ describe('AuthCtr', () => { expect(mockWindow.webContents.send).toHaveBeenCalledWith('authorizationRequired'); }); - it('should handle refresh failure with transient error (start auto-refresh)', async () => { - vi.mocked(mockRemoteServerConfigCtr.getLastTokenRefreshAt).mockReturnValue( - Date.now() - 10 * 60 * 1000, - ); + it('should preserve tokens on transient error', async () => { + vi.mocked(mockRemoteServerConfigCtr.isTokenExpiringSoon).mockReturnValue(true); vi.mocked(mockRemoteServerConfigCtr.refreshAccessToken).mockResolvedValue({ error: 'network_error', success: false, @@ -831,90 +810,49 @@ describe('AuthCtr', () => { await authCtr.onAppActivate(); - // Should not clear tokens for transient errors expect(mockRemoteServerConfigCtr.clearTokens).not.toHaveBeenCalled(); }); }); describe('afterAppReady (initializeAutoRefresh)', () => { - it('should proactively refresh token on startup when debounce interval exceeded', async () => { - // Last refresh was 10 minutes ago (exceeds 5-minute debounce) - vi.mocked(mockRemoteServerConfigCtr.getLastTokenRefreshAt).mockReturnValue( - Date.now() - 10 * 60 * 1000, - ); - vi.mocked(mockRemoteServerConfigCtr.refreshAccessToken).mockResolvedValue({ - success: true, - }); + it('should proactively refresh on startup when token is expiring soon', async () => { + vi.mocked(mockRemoteServerConfigCtr.isTokenExpiringSoon).mockReturnValue(true); authCtr.afterAppReady(); - - // Wait for async initialization await new Promise((resolve) => setTimeout(resolve, 100)); expect(mockRemoteServerConfigCtr.refreshAccessToken).toHaveBeenCalled(); }); - it('should NOT refresh on startup when token was recently refreshed (within debounce)', async () => { - // Last refresh was 2 minutes ago (within 5-minute debounce) - vi.mocked(mockRemoteServerConfigCtr.getLastTokenRefreshAt).mockReturnValue( - Date.now() - 2 * 60 * 1000, - ); + it('should NOT refresh on startup when token is not expiring soon', async () => { + vi.mocked(mockRemoteServerConfigCtr.isTokenExpiringSoon).mockReturnValue(false); authCtr.afterAppReady(); - - // Wait for async initialization await new Promise((resolve) => setTimeout(resolve, 100)); expect(mockRemoteServerConfigCtr.refreshAccessToken).not.toHaveBeenCalled(); }); - it('should refresh on startup when token is expired regardless of last refresh time', async () => { - // Token expired 1 hour ago - vi.mocked(mockRemoteServerConfigCtr.getTokenExpiresAt).mockReturnValue( - Date.now() - 60 * 60 * 1000, - ); - // Last refresh was 2 minutes ago (within debounce, but token is expired) - vi.mocked(mockRemoteServerConfigCtr.getLastTokenRefreshAt).mockReturnValue( - Date.now() - 2 * 60 * 1000, - ); - vi.mocked(mockRemoteServerConfigCtr.refreshAccessToken).mockResolvedValue({ - success: true, - }); + it('should check expiry with a small buffer, not the 24h default', async () => { + vi.mocked(mockRemoteServerConfigCtr.isTokenExpiringSoon).mockReturnValue(false); authCtr.afterAppReady(); - - // Wait for async initialization await new Promise((resolve) => setTimeout(resolve, 100)); - expect(mockRemoteServerConfigCtr.refreshAccessToken).toHaveBeenCalled(); + const [buffer] = vi.mocked(mockRemoteServerConfigCtr.isTokenExpiringSoon).mock.calls[0]; + expect(buffer).toBeGreaterThan(0); + expect(buffer).toBeLessThanOrEqual(60 * 60 * 1000); }); - }); - describe('refresh debounce boundary tests', () => { - it('should NOT refresh at exactly 5 minutes minus 1 second', async () => { - // Last refresh was 4 minutes 59 seconds ago - vi.mocked(mockRemoteServerConfigCtr.getLastTokenRefreshAt).mockReturnValue( - Date.now() - (FIVE_MINUTES - 1000), - ); + it('should skip initialization when no access token exists', async () => { + vi.mocked(mockRemoteServerConfigCtr.getAccessToken).mockResolvedValue(null); + vi.mocked(mockRemoteServerConfigCtr.isTokenExpiringSoon).mockReturnValue(true); - await authCtr.onAppActivate(); + authCtr.afterAppReady(); + await new Promise((resolve) => setTimeout(resolve, 100)); expect(mockRemoteServerConfigCtr.refreshAccessToken).not.toHaveBeenCalled(); }); - - it('should refresh at exactly 5 minutes', async () => { - // Last refresh was exactly 5 minutes ago - vi.mocked(mockRemoteServerConfigCtr.getLastTokenRefreshAt).mockReturnValue( - Date.now() - FIVE_MINUTES, - ); - vi.mocked(mockRemoteServerConfigCtr.refreshAccessToken).mockResolvedValue({ - success: true, - }); - - await authCtr.onAppActivate(); - - expect(mockRemoteServerConfigCtr.refreshAccessToken).toHaveBeenCalled(); - }); }); }); }); diff --git a/apps/desktop/src/main/controllers/__tests__/RemoteServerConfigCtr.test.ts b/apps/desktop/src/main/controllers/__tests__/RemoteServerConfigCtr.test.ts index f44bceb2e2..3a5362a240 100644 --- a/apps/desktop/src/main/controllers/__tests__/RemoteServerConfigCtr.test.ts +++ b/apps/desktop/src/main/controllers/__tests__/RemoteServerConfigCtr.test.ts @@ -535,6 +535,7 @@ describe('RemoteServerConfigCtr', () => { expect(result.success).toBe(false); expect(result.error).toContain('Token refresh failed'); + expect(mockFetch).toHaveBeenCalledTimes(1); }); it('should handle missing tokens in response', async () => { @@ -618,7 +619,7 @@ describe('RemoteServerConfigCtr', () => { expect(mockFetch).toHaveBeenCalledTimes(1); }); - it('should handle network errors with retry', async () => { + it('should not retry after a network error', async () => { const { safeStorage } = await import('electron'); vi.mocked(safeStorage.isEncryptionAvailable).mockReturnValue(true); vi.mocked(safeStorage.decryptString).mockImplementation((buffer: Buffer) => @@ -644,9 +645,8 @@ describe('RemoteServerConfigCtr', () => { expect(result.success).toBe(false); expect(result.error).toContain('Network error'); - // With retry mechanism, fetch should be called 4 times (1 initial + 3 retries) - expect(mockFetch).toHaveBeenCalledTimes(4); - }, 15000); + expect(mockFetch).toHaveBeenCalledTimes(1); + }); }); describe('afterAppReady', () => { From c9505f7ea2d46630bedb36689d21f281f9f07f93 Mon Sep 17 00:00:00 2001 From: Innei Date: Mon, 18 May 2026 21:36:38 +0800 Subject: [PATCH 010/224] =?UTF-8?q?=E2=9C=A8=20feat(follow-up):=20allow=20?= =?UTF-8?q?scene-specific=20model=20config=20for=20follow-up=20action=20ex?= =?UTF-8?q?traction=20(#14797)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ feat(follow-up): allow scene-specific model config for follow-up action extraction Add optional modelConfig to FollowUpExtractInput so callers (e.g. the onboarding agent) can specify which model/provider to use for chip generation instead of always falling back to the generic topic system agent. Priority chain: caller-provided config > env overrides > default system agent config. * ✨ Use scene model config for follow-up actions --- packages/types/src/followUpAction.ts | 12 +++++ src/features/Onboarding/Agent/index.tsx | 14 +++++- .../Agent/useOnboardingFollowUp.test.ts | 28 +++++++---- .../Onboarding/Agent/useOnboardingFollowUp.ts | 10 +++- .../services/followUpAction/index.test.ts | 45 +++++++++++++++--- src/server/services/followUpAction/index.ts | 24 +++------- src/store/followUpAction/action.ts | 11 +++-- src/store/followUpAction/index.test.ts | 46 ++++++++++++++----- 8 files changed, 140 insertions(+), 50 deletions(-) diff --git a/packages/types/src/followUpAction.ts b/packages/types/src/followUpAction.ts index d1699661f2..228df81c0e 100644 --- a/packages/types/src/followUpAction.ts +++ b/packages/types/src/followUpAction.ts @@ -11,8 +11,14 @@ export interface FollowUpChip { export type FollowUpHint = { kind: 'onboarding'; phase: OnboardingPhase } | { kind: 'chat' }; +export interface FollowUpModelConfig { + model: string; + provider: string; +} + export interface FollowUpExtractInput { hint?: FollowUpHint; + modelConfig: FollowUpModelConfig; topicId: string; } @@ -32,7 +38,13 @@ export const FollowUpHintSchema = z.union([ }), ]); +export const FollowUpModelConfigSchema = z.object({ + model: z.string().min(1), + provider: z.string().min(1), +}); + export const FollowUpExtractInputSchema = z.object({ hint: FollowUpHintSchema.optional(), + modelConfig: FollowUpModelConfigSchema, topicId: z.string().min(1), }); diff --git a/src/features/Onboarding/Agent/index.tsx b/src/features/Onboarding/Agent/index.tsx index 53fb61bda2..4b4f6b078a 100644 --- a/src/features/Onboarding/Agent/index.tsx +++ b/src/features/Onboarding/Agent/index.tsx @@ -13,6 +13,7 @@ import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import Loading from '@/components/Loading/BrandTextLoading'; +import { ONBOARDING_PRODUCTION_DEFAULT_MODEL } from '@/const/onboarding'; import ModeSwitch from '@/features/Onboarding/components/ModeSwitch'; import { useClientDataSWR, useOnlyFetchOnceSWR } from '@/libs/swr'; import OnboardingContainer from '@/routes/onboarding/_layout'; @@ -21,7 +22,7 @@ import { messageService } from '@/services/message'; import { topicService } from '@/services/topic'; import { userService } from '@/services/user'; import { useAgentStore } from '@/store/agent'; -import { builtinAgentSelectors } from '@/store/agent/selectors'; +import { agentByIdSelectors, builtinAgentSelectors } from '@/store/agent/selectors'; import { useChatStore } from '@/store/chat'; import { messageMapKey } from '@/store/chat/utils/messageMapKey'; import { useUserStore } from '@/store/user'; @@ -55,6 +56,9 @@ const AgentOnboardingPage = memo(() => { const onboardingAgentId = useAgentStore( builtinAgentSelectors.getBuiltinAgentId(BUILTIN_AGENT_SLUGS.webOnboarding), ); + const onboardingAgentConfig = useAgentStore((s) => + onboardingAgentId ? agentByIdSelectors.getAgentConfigById(onboardingAgentId)(s) : undefined, + ); const inboxAgentId = useAgentStore( builtinAgentSelectors.getBuiltinAgentId(BUILTIN_AGENT_SLUGS.inbox), ); @@ -123,10 +127,18 @@ const AgentOnboardingPage = memo(() => { () => !messagesForOnboarding || messagesForOnboarding.length === 0, [messagesForOnboarding], ); + const onboardingFollowUpModelConfig = useMemo( + () => ({ + model: onboardingAgentConfig?.model ?? ONBOARDING_PRODUCTION_DEFAULT_MODEL.model, + provider: onboardingAgentConfig?.provider ?? ONBOARDING_PRODUCTION_DEFAULT_MODEL.provider, + }), + [onboardingAgentConfig?.model, onboardingAgentConfig?.provider], + ); const onboardingFollowUp = useOnboardingFollowUp({ enabled: !onboardingFinished && !viewingHistoricalTopic, isGreeting, + modelConfig: onboardingFollowUpModelConfig, }); const { onBeforeSendMessage, triggerExtract } = onboardingFollowUp; diff --git a/src/features/Onboarding/Agent/useOnboardingFollowUp.test.ts b/src/features/Onboarding/Agent/useOnboardingFollowUp.test.ts index 43b3883761..6fa5c2a199 100644 --- a/src/features/Onboarding/Agent/useOnboardingFollowUp.test.ts +++ b/src/features/Onboarding/Agent/useOnboardingFollowUp.test.ts @@ -5,6 +5,11 @@ import { useFollowUpActionStore } from '@/store/followUpAction'; import { useOnboardingFollowUp } from './useOnboardingFollowUp'; +const MODEL_CONFIG = { + model: 'scene-model', + provider: 'scene-provider', +}; + describe('useOnboardingFollowUp', () => { let fetchFor: ReturnType; let clear: ReturnType; @@ -24,7 +29,7 @@ describe('useOnboardingFollowUp', () => { it('triggerExtract skips when disabled', async () => { const { result } = renderHook(() => - useOnboardingFollowUp({ enabled: false, isGreeting: false }), + useOnboardingFollowUp({ enabled: false, isGreeting: false, modelConfig: MODEL_CONFIG }), ); await result.current.triggerExtract('topic-1', 'discovery'); expect(fetchFor).not.toHaveBeenCalled(); @@ -32,7 +37,7 @@ describe('useOnboardingFollowUp', () => { it('triggerExtract skips when phase is undefined', async () => { const { result } = renderHook(() => - useOnboardingFollowUp({ enabled: true, isGreeting: false }), + useOnboardingFollowUp({ enabled: true, isGreeting: false, modelConfig: MODEL_CONFIG }), ); await result.current.triggerExtract('topic-1', undefined); expect(fetchFor).not.toHaveBeenCalled(); @@ -40,32 +45,37 @@ describe('useOnboardingFollowUp', () => { it('triggerExtract skips when phase is summary', async () => { const { result } = renderHook(() => - useOnboardingFollowUp({ enabled: true, isGreeting: false }), + useOnboardingFollowUp({ enabled: true, isGreeting: false, modelConfig: MODEL_CONFIG }), ); await result.current.triggerExtract('topic-1', 'summary'); expect(fetchFor).not.toHaveBeenCalled(); }); it('triggerExtract skips when isGreeting is true', async () => { - const { result } = renderHook(() => useOnboardingFollowUp({ enabled: true, isGreeting: true })); + const { result } = renderHook(() => + useOnboardingFollowUp({ enabled: true, isGreeting: true, modelConfig: MODEL_CONFIG }), + ); await result.current.triggerExtract('topic-1', 'agent_identity'); expect(fetchFor).not.toHaveBeenCalled(); }); it('triggerExtract fires fetchFor with onboarding hint on a normal turn', async () => { const { result } = renderHook(() => - useOnboardingFollowUp({ enabled: true, isGreeting: false }), + useOnboardingFollowUp({ enabled: true, isGreeting: false, modelConfig: MODEL_CONFIG }), ); await result.current.triggerExtract('topic-1', 'discovery'); expect(fetchFor).toHaveBeenCalledWith('topic-1', { - kind: 'onboarding', - phase: 'discovery', + hint: { + kind: 'onboarding', + phase: 'discovery', + }, + modelConfig: MODEL_CONFIG, }); }); it('onBeforeSendMessage clears when enabled', async () => { const { result } = renderHook(() => - useOnboardingFollowUp({ enabled: true, isGreeting: false }), + useOnboardingFollowUp({ enabled: true, isGreeting: false, modelConfig: MODEL_CONFIG }), ); await result.current.onBeforeSendMessage(); expect(clear).toHaveBeenCalledTimes(1); @@ -73,7 +83,7 @@ describe('useOnboardingFollowUp', () => { it('onBeforeSendMessage does nothing when disabled', async () => { const { result } = renderHook(() => - useOnboardingFollowUp({ enabled: false, isGreeting: false }), + useOnboardingFollowUp({ enabled: false, isGreeting: false, modelConfig: MODEL_CONFIG }), ); await result.current.onBeforeSendMessage(); expect(clear).not.toHaveBeenCalled(); diff --git a/src/features/Onboarding/Agent/useOnboardingFollowUp.ts b/src/features/Onboarding/Agent/useOnboardingFollowUp.ts index f816df44c4..0337bca1d1 100644 --- a/src/features/Onboarding/Agent/useOnboardingFollowUp.ts +++ b/src/features/Onboarding/Agent/useOnboardingFollowUp.ts @@ -1,3 +1,4 @@ +import type { FollowUpModelConfig } from '@lobechat/types'; import { useCallback } from 'react'; import { useFollowUpActionStore } from '@/store/followUpAction'; @@ -6,6 +7,7 @@ import type { OnboardingPhase } from '@/types/user'; interface UseOnboardingFollowUpParams { enabled: boolean; isGreeting: boolean; + modelConfig: FollowUpModelConfig; } interface OnboardingFollowUpHandlers { @@ -16,6 +18,7 @@ interface OnboardingFollowUpHandlers { export const useOnboardingFollowUp = ({ enabled, isGreeting, + modelConfig, }: UseOnboardingFollowUpParams): OnboardingFollowUpHandlers => { const triggerExtract = useCallback( async (topicId: string, phase: OnboardingPhase | undefined) => { @@ -24,9 +27,12 @@ export const useOnboardingFollowUp = ({ if (phase === 'summary') return; if (isGreeting) return; - await useFollowUpActionStore.getState().fetchFor(topicId, { kind: 'onboarding', phase }); + await useFollowUpActionStore.getState().fetchFor(topicId, { + hint: { kind: 'onboarding', phase }, + modelConfig, + }); }, - [enabled, isGreeting], + [enabled, isGreeting, modelConfig], ); const onBeforeSendMessage = useCallback(async () => { diff --git a/src/server/services/followUpAction/index.test.ts b/src/server/services/followUpAction/index.test.ts index 9407eea57c..a843ed6583 100644 --- a/src/server/services/followUpAction/index.test.ts +++ b/src/server/services/followUpAction/index.test.ts @@ -8,6 +8,10 @@ import { FollowUpActionService } from './index'; const TEST_USER = 'user-1'; const TEST_TOPIC = 'topic-1'; const FOUND_MSG = 'msg-real'; +const MODEL_CONFIG = { + model: 'scene-model', + provider: 'scene-provider', +}; describe('FollowUpActionService.extract', () => { let svc: FollowUpActionService; @@ -37,7 +41,7 @@ describe('FollowUpActionService.extract', () => { it('returns empty (with empty messageId) when no eligible assistant message found', async () => { queryFindFirstSpy.mockResolvedValue(undefined); - const result = await svc.extract({ topicId: TEST_TOPIC }); + const result = await svc.extract({ modelConfig: MODEL_CONFIG, topicId: TEST_TOPIC }); expect(result).toEqual({ chips: [], messageId: '' }); expect(runtimeMock.generateObject).not.toHaveBeenCalled(); }); @@ -57,18 +61,46 @@ describe('FollowUpActionService.extract', () => { const result = await svc.extract({ topicId: TEST_TOPIC, hint: { kind: 'onboarding', phase: 'agent_identity' }, + modelConfig: MODEL_CONFIG, }); expect(result.messageId).toBe(FOUND_MSG); expect(result.chips).toHaveLength(3); expect(result.chips[0].label).toBe('Lumi'); }); + it('uses the caller-provided scene model config for extraction', async () => { + queryFindFirstSpy.mockResolvedValue({ + id: FOUND_MSG, + content: 'What would you like to call me?', + }); + runtimeMock.generateObject.mockResolvedValue({ chips: [] }); + + await svc.extract({ + topicId: TEST_TOPIC, + modelConfig: { + model: 'custom-scene-model', + provider: 'custom-provider', + }, + }); + + expect(ModelRuntimeModule.initModelRuntimeFromDB).toHaveBeenCalledWith( + dbMock, + TEST_USER, + 'custom-provider', + ); + expect(runtimeMock.generateObject).toHaveBeenCalledWith( + expect.objectContaining({ + model: 'custom-scene-model', + }), + ); + }); + it('truncates more than 4 chips', async () => { queryFindFirstSpy.mockResolvedValue({ id: FOUND_MSG, content: 'choose' }); runtimeMock.generateObject.mockResolvedValue({ chips: Array.from({ length: 6 }, (_, i) => ({ label: `c${i}`, message: `c${i}` })), }); - const result = await svc.extract({ topicId: TEST_TOPIC }); + const result = await svc.extract({ modelConfig: MODEL_CONFIG, topicId: TEST_TOPIC }); expect(result.chips).toHaveLength(4); }); @@ -80,7 +112,7 @@ describe('FollowUpActionService.extract', () => { { label: 'ok', message: 'ok' }, ], }); - const result = await svc.extract({ topicId: TEST_TOPIC }); + const result = await svc.extract({ modelConfig: MODEL_CONFIG, topicId: TEST_TOPIC }); expect(result.chips).toEqual([{ label: 'ok', message: 'ok' }]); }); @@ -93,21 +125,21 @@ describe('FollowUpActionService.extract', () => { { label: 'bad', message: '' }, ], }); - const result = await svc.extract({ topicId: TEST_TOPIC }); + const result = await svc.extract({ modelConfig: MODEL_CONFIG, topicId: TEST_TOPIC }); expect(result.chips).toEqual([{ label: 'ok', message: 'ok' }]); }); it('returns empty (with messageId) when LLM throws', async () => { queryFindFirstSpy.mockResolvedValue({ id: FOUND_MSG, content: 'q?' }); runtimeMock.generateObject.mockRejectedValue(new Error('boom')); - const result = await svc.extract({ topicId: TEST_TOPIC }); + const result = await svc.extract({ modelConfig: MODEL_CONFIG, topicId: TEST_TOPIC }); expect(result).toEqual({ chips: [], messageId: FOUND_MSG }); }); it('returns empty (with messageId) when LLM response fails schema validation', async () => { queryFindFirstSpy.mockResolvedValue({ id: FOUND_MSG, content: 'q?' }); runtimeMock.generateObject.mockResolvedValue({ chips: 'not-an-array' }); - const result = await svc.extract({ topicId: TEST_TOPIC }); + const result = await svc.extract({ modelConfig: MODEL_CONFIG, topicId: TEST_TOPIC }); expect(result).toEqual({ chips: [], messageId: FOUND_MSG }); }); @@ -117,6 +149,7 @@ describe('FollowUpActionService.extract', () => { await svc.extract({ topicId: TEST_TOPIC, hint: { kind: 'onboarding', phase: 'discovery' }, + modelConfig: MODEL_CONFIG, }); const passedMessages = runtimeMock.generateObject.mock.calls[0][0].messages; const sysContent = passedMessages.find((m: any) => m.role === 'system').content; diff --git a/src/server/services/followUpAction/index.ts b/src/server/services/followUpAction/index.ts index 9cac815a38..b4636b38d3 100644 --- a/src/server/services/followUpAction/index.ts +++ b/src/server/services/followUpAction/index.ts @@ -1,8 +1,7 @@ -import { DEFAULT_SYSTEM_AGENT_CONFIG } from '@lobechat/const'; import type { FollowUpChip, FollowUpExtractInput, FollowUpExtractResult } from '@lobechat/types'; import debug from 'debug'; -import { type LobeChatDatabase } from '@/database/type'; +import type { LobeChatDatabase } from '@/database/type'; import { initModelRuntimeFromDB } from '@/server/modules/ModelRuntime'; import { buildSuggestionPrompt } from './prompts'; @@ -21,7 +20,11 @@ export class FollowUpActionService { this.userId = userId; } - async extract({ topicId, hint }: FollowUpExtractInput): Promise { + async extract({ + topicId, + hint, + modelConfig, + }: FollowUpExtractInput): Promise { // Resolve the latest assistant message that actually has user-facing text. // Tool-call-only messages have empty content and must be skipped. const row = await this.db.query.messages.findFirst({ @@ -43,7 +46,7 @@ export class FollowUpActionService { if (!text) return EMPTY_RESULT(row.id); const { system, user } = buildSuggestionPrompt({ assistantText: text, hint }); - const { model, provider } = this.getModelConfig(); + const { model, provider } = modelConfig; let raw: unknown; try { @@ -79,17 +82,4 @@ export class FollowUpActionService { return { chips, messageId: row.id }; } - - private getModelConfig(): { model: string; provider: string } { - const overrideModel = process.env.FOLLOW_UP_ACTION_MODEL; - const overrideProvider = process.env.FOLLOW_UP_ACTION_PROVIDER; - if (overrideModel && overrideProvider) { - return { model: overrideModel, provider: overrideProvider }; - } - const fallback = DEFAULT_SYSTEM_AGENT_CONFIG.topic; - return { - model: overrideModel ?? fallback.model, - provider: overrideProvider ?? fallback.provider, - }; - } } diff --git a/src/store/followUpAction/action.ts b/src/store/followUpAction/action.ts index 35cb0fc4cd..3a3e5cb8a5 100644 --- a/src/store/followUpAction/action.ts +++ b/src/store/followUpAction/action.ts @@ -1,4 +1,4 @@ -import type { FollowUpChip, FollowUpHint } from '@lobechat/types'; +import type { FollowUpChip, FollowUpHint, FollowUpModelConfig } from '@lobechat/types'; import { followUpActionService } from '@/services/followUpAction'; import { type StoreSetter } from '@/store/types'; @@ -11,6 +11,11 @@ const TIMEOUT_MS = 20_000; type Setter = StoreSetter; +interface FetchForParams { + hint?: FollowUpHint; + modelConfig: FollowUpModelConfig; +} + export const createFollowUpActionSlice = ( set: Setter, get: () => FollowUpActionStore, @@ -27,7 +32,7 @@ export class FollowUpActionImpl { this.#get = get; } - fetchFor = async (topicId: string, hint?: FollowUpHint): Promise => { + fetchFor = async (topicId: string, params: FetchForParams): Promise => { const cur = this.#get(); // Dedupe: skip if already loading/ready for the same topic if (cur.pendingTopicId === topicId && cur.status !== 'idle') return; @@ -50,7 +55,7 @@ export class FollowUpActionImpl { 'fetchFor:start', ); - const result = await followUpActionService.extract({ hint, topicId }, controller.signal); + const result = await followUpActionService.extract({ ...params, topicId }, controller.signal); clearTimeout(timeoutId); // Discard stale results: if the active controller in state is no longer diff --git a/src/store/followUpAction/index.test.ts b/src/store/followUpAction/index.test.ts index e7cf144397..db805cc47d 100644 --- a/src/store/followUpAction/index.test.ts +++ b/src/store/followUpAction/index.test.ts @@ -7,6 +7,8 @@ import { useFollowUpActionStore } from './store'; const TOPIC = 'topic-1'; const NEW_TOPIC = 'topic-2'; const MSG = 'msg-real'; +const MODEL_CONFIG = { model: 'scene-model', provider: 'scene-provider' }; +const FETCH_PARAMS = { modelConfig: MODEL_CONFIG }; describe('useFollowUpActionStore', () => { beforeEach(() => { @@ -24,7 +26,7 @@ describe('useFollowUpActionStore', () => { chips: [{ label: 'a', message: 'a' }], }); - const promise = useFollowUpActionStore.getState().fetchFor(TOPIC); + const promise = useFollowUpActionStore.getState().fetchFor(TOPIC, FETCH_PARAMS); expect(useFollowUpActionStore.getState().status).toBe('loading'); await promise; expect(spy).toHaveBeenCalledOnce(); @@ -34,9 +36,29 @@ describe('useFollowUpActionStore', () => { expect(useFollowUpActionStore.getState().topicId).toBe(TOPIC); }); + it('fetchFor forwards modelConfig to the service', async () => { + const spy = vi.spyOn(followUpActionService, 'extract').mockResolvedValue({ + messageId: MSG, + chips: [{ label: 'a', message: 'a' }], + }); + await useFollowUpActionStore.getState().fetchFor(TOPIC, { + hint: { kind: 'onboarding', phase: 'discovery' }, + modelConfig: MODEL_CONFIG, + }); + + expect(spy).toHaveBeenCalledWith( + { + hint: { kind: 'onboarding', phase: 'discovery' }, + modelConfig: MODEL_CONFIG, + topicId: TOPIC, + }, + expect.any(AbortSignal), + ); + }); + it('fetchFor returns idle when service returns null', async () => { vi.spyOn(followUpActionService, 'extract').mockResolvedValue(null); - await useFollowUpActionStore.getState().fetchFor(TOPIC); + await useFollowUpActionStore.getState().fetchFor(TOPIC, FETCH_PARAMS); expect(useFollowUpActionStore.getState().status).toBe('idle'); expect(useFollowUpActionStore.getState().chips).toHaveLength(0); expect(useFollowUpActionStore.getState().messageId).toBeUndefined(); @@ -44,7 +66,7 @@ describe('useFollowUpActionStore', () => { it('fetchFor returns idle when service returns empty messageId', async () => { vi.spyOn(followUpActionService, 'extract').mockResolvedValue({ chips: [], messageId: '' }); - await useFollowUpActionStore.getState().fetchFor(TOPIC); + await useFollowUpActionStore.getState().fetchFor(TOPIC, FETCH_PARAMS); expect(useFollowUpActionStore.getState().status).toBe('idle'); expect(useFollowUpActionStore.getState().messageId).toBeUndefined(); }); @@ -53,8 +75,8 @@ describe('useFollowUpActionStore', () => { const spy = vi .spyOn(followUpActionService, 'extract') .mockImplementation(() => new Promise(() => {})); - const p1 = useFollowUpActionStore.getState().fetchFor(TOPIC); - const p2 = useFollowUpActionStore.getState().fetchFor(TOPIC); + const p1 = useFollowUpActionStore.getState().fetchFor(TOPIC, FETCH_PARAMS); + const p2 = useFollowUpActionStore.getState().fetchFor(TOPIC, FETCH_PARAMS); void p1; void p2; expect(spy).toHaveBeenCalledTimes(1); @@ -66,17 +88,17 @@ describe('useFollowUpActionStore', () => { if (!firstSignal) firstSignal = signal; return new Promise(() => {}); }); - const p1 = useFollowUpActionStore.getState().fetchFor(TOPIC); + const p1 = useFollowUpActionStore.getState().fetchFor(TOPIC, FETCH_PARAMS); void p1; await Promise.resolve(); await Promise.resolve(); - void useFollowUpActionStore.getState().fetchFor(NEW_TOPIC); + void useFollowUpActionStore.getState().fetchFor(NEW_TOPIC, FETCH_PARAMS); expect(firstSignal?.aborted).toBe(true); }); it('clear() aborts and resets state', async () => { vi.spyOn(followUpActionService, 'extract').mockImplementation(() => new Promise(() => {})); - const p = useFollowUpActionStore.getState().fetchFor(TOPIC); + const p = useFollowUpActionStore.getState().fetchFor(TOPIC, FETCH_PARAMS); void p; useFollowUpActionStore.getState().clear(); expect(useFollowUpActionStore.getState().status).toBe('idle'); @@ -90,7 +112,7 @@ describe('useFollowUpActionStore', () => { signal = s; return new Promise(() => {}); }); - const p = useFollowUpActionStore.getState().fetchFor(TOPIC); + const p = useFollowUpActionStore.getState().fetchFor(TOPIC, FETCH_PARAMS); void p; await Promise.resolve(); vi.advanceTimersByTime(20_000); @@ -124,7 +146,7 @@ describe('useFollowUpActionStore', () => { }); // First fetchFor is in flight (does not yet resolve). - const p1 = useFollowUpActionStore.getState().fetchFor(TOPIC); + const p1 = useFollowUpActionStore.getState().fetchFor(TOPIC, FETCH_PARAMS); void p1; await Promise.resolve(); @@ -133,7 +155,7 @@ describe('useFollowUpActionStore', () => { expect(useFollowUpActionStore.getState().status).toBe('idle'); // Next turn starts another fetchFor for the SAME topic. - const p2 = useFollowUpActionStore.getState().fetchFor(TOPIC); + const p2 = useFollowUpActionStore.getState().fetchFor(TOPIC, FETCH_PARAMS); // The first call now resolves with a stale result. It must be discarded // because its controller is no longer the active one — even though the @@ -156,7 +178,7 @@ describe('useFollowUpActionStore', () => { signal = s; return new Promise(() => {}); }); - const p = useFollowUpActionStore.getState().fetchFor(TOPIC); + const p = useFollowUpActionStore.getState().fetchFor(TOPIC, FETCH_PARAMS); void p; await Promise.resolve(); useFollowUpActionStore.getState().reset(); From b3a31ec2eedf7bddfdeecfbb9eca913f509d0d29 Mon Sep 17 00:00:00 2001 From: Innei Date: Mon, 18 May 2026 22:04:39 +0800 Subject: [PATCH 011/224] =?UTF-8?q?=F0=9F=92=84=20refactor(ToolTag):=20alw?= =?UTF-8?q?ays=20use=20filled=20variant=20regardless=20of=20dark=20mode=20?= =?UTF-8?q?(#14937)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/ToolTag/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/ToolTag/index.tsx b/src/features/ToolTag/index.tsx index 7bf900494d..cb48c1d4cb 100644 --- a/src/features/ToolTag/index.tsx +++ b/src/features/ToolTag/index.tsx @@ -156,7 +156,7 @@ const ToolTag = memo(({ identifier, variant = 'default' }) => { {displayTitle} From c21076eec441e5fc9925d27da20f2c547508de9f Mon Sep 17 00:00:00 2001 From: AmAzing- <115673583+AmAzing129@users.noreply.github.com> Date: Mon, 18 May 2026 22:54:25 +0800 Subject: [PATCH 012/224] =?UTF-8?q?=F0=9F=90=9B=20fix(tasks):=20preserve?= =?UTF-8?q?=20agent=20context=20in=20task=20routes=20(#14926)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/types/src/task/index.ts | 2 +- .../TaskDetailHeaderActions.tsx | 7 +- .../AgentTaskDetail/TaskDetailPage.tsx | 15 +- .../AgentTaskDetail/TaskParentBar.test.tsx | 171 ++++++++++++++++ .../AgentTaskDetail/TaskParentBar.tsx | 26 ++- .../AgentTaskDetail/TaskSubtasks.test.tsx | 193 ++++++++++++++++++ .../AgentTaskDetail/TaskSubtasks.tsx | 16 +- .../AgentTaskList/AgentTasksPage.tsx | 3 +- .../AgentTasks/AgentTaskList/KanbanBoard.tsx | 3 +- .../features/AgentTaskItem.test.tsx | 190 +++++++++++++++++ .../AgentTasks/features/AgentTaskItem.tsx | 9 +- .../features/TaskSubtaskProgressTag.test.tsx | 77 +++++++ .../features/TaskSubtaskProgressTag.tsx | 5 +- .../features/useTaskItemContextMenu.tsx | 13 +- .../AgentTasks/shared/Breadcrumb.test.tsx | 108 ++++++++++ src/features/AgentTasks/shared/Breadcrumb.tsx | 21 +- .../AgentTasks/shared/taskDetailPath.test.ts | 50 +++++ .../AgentTasks/shared/taskDetailPath.ts | 26 +++ src/features/DailyBrief/BriefCard.tsx | 7 +- .../Electron/navigation/routeMetadata.ts | 5 +- .../useTaskTemplateCreate.ts | 5 +- .../_layout/Sidebar/Task/StatusGroup.tsx | 6 +- .../_layout/Sidebar/Task/TaskItem.test.tsx | 76 +++++++ .../agent/_layout/Sidebar/Task/TaskItem.tsx | 8 +- .../(main)/agent/task/[taskId]/index.tsx | 16 ++ .../features/Recents/AllRecentsDrawer.tsx | 3 +- .../(main)/home/features/Recents/List.tsx | 3 +- src/server/routers/lambda/recent.ts | 4 +- src/server/services/task/index.test.ts | 13 +- src/server/services/task/index.ts | 8 +- .../router/desktopRouter.config.desktop.tsx | 5 + src/spa/router/desktopRouter.config.tsx | 7 + src/spa/router/desktopRouter.sync.test.tsx | 8 +- src/spa/router/mobileRouter.config.tsx | 13 ++ src/spa/router/mobileRouter.test.tsx | 6 +- .../task/selectors/detailSelectors.test.ts | 3 +- 36 files changed, 1069 insertions(+), 62 deletions(-) create mode 100644 src/features/AgentTasks/AgentTaskDetail/TaskParentBar.test.tsx create mode 100644 src/features/AgentTasks/AgentTaskDetail/TaskSubtasks.test.tsx create mode 100644 src/features/AgentTasks/features/AgentTaskItem.test.tsx create mode 100644 src/features/AgentTasks/features/TaskSubtaskProgressTag.test.tsx create mode 100644 src/features/AgentTasks/shared/Breadcrumb.test.tsx create mode 100644 src/features/AgentTasks/shared/taskDetailPath.test.ts create mode 100644 src/features/AgentTasks/shared/taskDetailPath.ts create mode 100644 src/routes/(main)/agent/_layout/Sidebar/Task/TaskItem.test.tsx create mode 100644 src/routes/(main)/agent/task/[taskId]/index.tsx diff --git a/packages/types/src/task/index.ts b/packages/types/src/task/index.ts index dbee11296a..53c8ea6241 100644 --- a/packages/types/src/task/index.ts +++ b/packages/types/src/task/index.ts @@ -306,7 +306,7 @@ export interface TaskDetailData { identifier: string; instruction: string; name?: string | null; - parent?: { identifier: string; name: string | null } | null; + parent?: { agentId?: string | null; identifier: string; name: string | null } | null; priority?: number | null; review?: Record | null; schedule?: { diff --git a/src/features/AgentTasks/AgentTaskDetail/TaskDetailHeaderActions.tsx b/src/features/AgentTasks/AgentTaskDetail/TaskDetailHeaderActions.tsx index 0074d36fbb..4c83d8e61f 100644 --- a/src/features/AgentTasks/AgentTaskDetail/TaskDetailHeaderActions.tsx +++ b/src/features/AgentTasks/AgentTaskDetail/TaskDetailHeaderActions.tsx @@ -9,12 +9,15 @@ import { useAppOrigin } from '@/hooks/useAppOrigin'; import { useTaskStore } from '@/store/task'; import { taskDetailSelectors } from '@/store/task/selectors'; +import { taskDetailPath } from '../shared/taskDetailPath'; + const TaskDetailHeaderActions = memo(() => { const { t } = useTranslation(['chat', 'common']); const { modal, message } = App.useApp(); const navigate = useNavigate(); const appOrigin = useAppOrigin(); const taskId = useTaskStore(taskDetailSelectors.activeTaskId); + const taskAgentId = useTaskStore(taskDetailSelectors.activeTaskAgentId); const deleteTask = useTaskStore((s) => s.deleteTask); const triggerDelete = useCallback(() => { @@ -36,7 +39,7 @@ const TaskDetailHeaderActions = memo(() => { const menuItems = useMemo(() => { if (!taskId) return []; - const taskUrl = `${appOrigin}/task/${taskId}`; + const taskUrl = `${appOrigin}${taskDetailPath(taskId, taskAgentId ?? undefined)}`; return [ { @@ -66,7 +69,7 @@ const TaskDetailHeaderActions = memo(() => { onClick: triggerDelete, }, ]; - }, [taskId, appOrigin, t, message, triggerDelete]); + }, [taskId, taskAgentId, appOrigin, t, message, triggerDelete]); if (!taskId) return null; diff --git a/src/features/AgentTasks/AgentTaskDetail/TaskDetailPage.tsx b/src/features/AgentTasks/AgentTaskDetail/TaskDetailPage.tsx index 310d0433cc..d9cb5b0632 100644 --- a/src/features/AgentTasks/AgentTaskDetail/TaskDetailPage.tsx +++ b/src/features/AgentTasks/AgentTaskDetail/TaskDetailPage.tsx @@ -27,10 +27,11 @@ import TaskSubtasks from './TaskSubtasks'; import TopicChatDrawer from './TopicChatDrawer'; interface TaskDetailPageProps { + showTaskAgentPanelToggle?: boolean; taskId: string; } -const TaskDetailPage = memo(({ taskId }) => { +const TaskDetailPage = memo(({ taskId, showTaskAgentPanelToggle = true }) => { const setActiveTaskId = useTaskStore((s) => s.setActiveTaskId); const useFetchTaskDetail = useTaskStore((s) => s.useFetchTaskDetail); const isLoading = useTaskStore(taskDetailSelectors.isTaskDetailLoading); @@ -58,11 +59,13 @@ const TaskDetailPage = memo(({ taskId }) => { } right={ - toggleTaskAgentPanel()} - /> + showTaskAgentPanelToggle ? ( + toggleTaskAgentPanel()} + /> + ) : undefined } styles={{ left: { diff --git a/src/features/AgentTasks/AgentTaskDetail/TaskParentBar.test.tsx b/src/features/AgentTasks/AgentTaskDetail/TaskParentBar.test.tsx new file mode 100644 index 0000000000..ae11b04210 --- /dev/null +++ b/src/features/AgentTasks/AgentTaskDetail/TaskParentBar.test.tsx @@ -0,0 +1,171 @@ +/** + * @vitest-environment happy-dom + */ +import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'; +import type { ReactNode } from 'react'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import TaskParentBar from './TaskParentBar'; + +const mocks = vi.hoisted(() => ({ + getDetail: vi.fn(), + navigate: vi.fn(), + taskState: {} as any, +})); + +const createState = (parent: any) => ({ + activeTaskId: 'T-child', + taskDetailMap: { + 'T-child': { + identifier: 'T-child', + instruction: 'Child instruction', + parent, + status: 'running', + }, + }, +}); + +vi.mock('@lobehub/ui', () => ({ + Button: ({ + children, + icon, + onClick, + }: { + children: ReactNode; + icon?: ReactNode; + onClick?: () => void; + }) => ( + + ), + Flexbox: ({ children }: { children: ReactNode }) =>
{children}
, + Text: ({ children }: { children: ReactNode }) => {children}, +})); + +vi.mock('react-i18next', () => ({ + useTranslation: () => ({ t: (key: string) => key }), +})); + +vi.mock('react-router-dom', () => ({ + useNavigate: () => mocks.navigate, +})); + +vi.mock('@/services/task', () => ({ + taskService: { + getDetail: mocks.getDetail, + }, +})); + +vi.mock('@/store/task', () => ({ + useTaskStore: (selector: any) => selector(mocks.taskState), +})); + +vi.mock('../features/TaskStatusIcon', () => ({ + default: () => status, +})); + +vi.mock('../features/TaskSubtaskProgressTag', () => ({ + default: ({ + onSubtaskClick, + subtasks, + }: { + onSubtaskClick?: (identifier: string, assigneeAgentId?: string) => void; + subtasks?: any[]; + }) => { + const subtask = subtasks?.[0]; + if (!subtask) return null; + + return ( + + ); + }, +})); + +describe('TaskParentBar', () => { + beforeEach(() => { + mocks.navigate.mockClear(); + mocks.getDetail.mockReset(); + mocks.taskState = createState({ + agentId: 'agt_parent', + identifier: 'T-parent', + name: 'Parent task', + }); + mocks.getDetail.mockResolvedValue({ + data: { + agentId: 'agt_parent', + identifier: 'T-parent', + instruction: 'Parent instruction', + status: 'running', + subtasks: [], + }, + }); + }); + + afterEach(() => { + cleanup(); + }); + + it("opens the parent task inside the parent task's owning agent route", async () => { + render(); + + fireEvent.click(screen.getByText('Parent task').closest('button')!); + + expect(mocks.navigate).toHaveBeenCalledWith('/agent/agt_parent/task/T-parent'); + await waitFor(() => expect(mocks.getDetail).toHaveBeenCalledWith('T-parent')); + }); + + it('falls back to the global route when the parent task owner is unknown', async () => { + mocks.taskState = createState({ + identifier: 'T-parent', + name: 'Parent task', + }); + mocks.getDetail.mockResolvedValue({ + data: { + agentId: null, + identifier: 'T-parent', + instruction: 'Parent instruction', + status: 'running', + subtasks: [], + }, + }); + + render(); + + await waitFor(() => expect(mocks.getDetail).toHaveBeenCalledWith('T-parent')); + fireEvent.click(screen.getByText('Parent task').closest('button')!); + + expect(mocks.navigate).toHaveBeenCalledWith('/task/T-parent'); + }); + + it("opens parent subtasks inside the clicked subtask's owning agent route", async () => { + mocks.getDetail.mockResolvedValue({ + data: { + agentId: 'agt_parent', + identifier: 'T-parent', + instruction: 'Parent instruction', + status: 'running', + subtasks: [ + { + assignee: { id: 'agt_sibling' }, + identifier: 'T-sibling', + status: 'backlog', + }, + ], + }, + }); + + render(); + + fireEvent.click(await screen.findByTestId('parent-subtask')); + + expect(mocks.navigate).toHaveBeenCalledWith('/agent/agt_sibling/task/T-sibling'); + }); +}); diff --git a/src/features/AgentTasks/AgentTaskDetail/TaskParentBar.tsx b/src/features/AgentTasks/AgentTaskDetail/TaskParentBar.tsx index 60f27e0cbf..f34790524f 100644 --- a/src/features/AgentTasks/AgentTaskDetail/TaskParentBar.tsx +++ b/src/features/AgentTasks/AgentTaskDetail/TaskParentBar.tsx @@ -10,6 +10,7 @@ import { taskDetailSelectors } from '@/store/task/selectors'; import TaskStatusIcon from '../features/TaskStatusIcon'; import TaskSubtaskProgressTag from '../features/TaskSubtaskProgressTag'; +import { taskDetailPath } from '../shared/taskDetailPath'; const TASK_STATUS_SET = new Set([ 'backlog', @@ -31,10 +32,15 @@ const TaskParentBar = memo(() => { const parent = useTaskStore(taskDetailSelectors.activeTaskParent); const currentIdentifier = useTaskStore(taskDetailSelectors.activeTaskDetail)?.identifier; + const [fetchedParentAgent, setFetchedParentAgent] = useState< + { agentId?: string | null; identifier: string } | undefined + >(); const [parentSubtasks, setParentSubtasks] = useState([]); const [parentStatus, setParentStatus] = useState('backlog'); useEffect(() => { + let isActive = true; + setFetchedParentAgent(undefined); setParentSubtasks([]); setParentStatus('backlog'); if (!parent?.identifier) return; @@ -42,17 +48,31 @@ const TaskParentBar = memo(() => { taskService .getDetail(parent.identifier) .then((res) => { + if (!isActive) return; const detail = res.data as TaskDetailData; + setFetchedParentAgent({ agentId: detail.agentId, identifier: parent.identifier }); setParentStatus(toTaskStatus(detail.status)); setParentSubtasks(detail.subtasks ?? []); }) .catch((err) => { + if (!isActive) return; console.error('[TaskParentBar] Failed to load parent subtasks', err); }); + + return () => { + isActive = false; + }; }, [parent?.identifier]); if (!parent) return null; + const parentAgentId = + parent.agentId === undefined + ? fetchedParentAgent?.identifier === parent.identifier + ? fetchedParentAgent.agentId + : undefined + : parent.agentId; + return ( @@ -62,7 +82,7 @@ const TaskParentBar = memo(() => { icon={} size={'small'} type={'text'} - onClick={() => navigate(`/task/${parent.identifier}`)} + onClick={() => navigate(taskDetailPath(parent.identifier, parentAgentId ?? undefined))} > {parent.name} @@ -70,7 +90,9 @@ const TaskParentBar = memo(() => { navigate(`/task/${identifier}`)} + onSubtaskClick={(identifier, assigneeAgentId) => + navigate(taskDetailPath(identifier, assigneeAgentId)) + } /> )} diff --git a/src/features/AgentTasks/AgentTaskDetail/TaskSubtasks.test.tsx b/src/features/AgentTasks/AgentTaskDetail/TaskSubtasks.test.tsx new file mode 100644 index 0000000000..85694b0a42 --- /dev/null +++ b/src/features/AgentTasks/AgentTaskDetail/TaskSubtasks.test.tsx @@ -0,0 +1,193 @@ +/** + * @vitest-environment happy-dom + */ +import { cleanup, fireEvent, render, screen } from '@testing-library/react'; +import type { ReactNode } from 'react'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import TaskSubtasks from './TaskSubtasks'; + +const mocks = vi.hoisted(() => ({ + navigate: vi.fn(), + runReadySubtasks: vi.fn(), + taskState: { + activeTaskId: 'T-parent', + taskDetailMap: { + 'T-parent': { + agentId: 'agt_parent', + identifier: 'T-parent', + instruction: 'Parent instruction', + status: 'running', + subtasks: [ + { + assignee: { avatar: null, backgroundColor: null, id: 'agt_child', title: 'Child' }, + identifier: 'T-child', + name: 'Child task', + status: 'backlog', + }, + ], + }, + }, + } as any, +})); + +vi.mock('@lobehub/ui', () => ({ + ActionIcon: ({ onClick }: { onClick?: () => void }) => ( + + ), + Block: ({ + children, + clickable, + onClick, + }: { + children: ReactNode; + clickable?: boolean; + onClick?: () => void; + }) => + clickable ? ( + + ) : ( +
{children}
+ ), + Flexbox: ({ children }: { children: ReactNode }) =>
{children}
, + Icon: () => icon, + Text: ({ children }: { children: ReactNode }) => {children}, + showContextMenu: vi.fn(), +})); + +vi.mock('antd', () => ({ + App: { + useApp: () => ({ + message: { error: vi.fn(), info: vi.fn(), success: vi.fn(), warning: vi.fn() }, + modal: { confirm: vi.fn() }, + }), + }, + ConfigProvider: ({ children }: { children: ReactNode }) => <>{children}, + Tree: ({ onSelect }: { onSelect?: (keys: string[]) => void }) => ( + + ), +})); + +vi.mock('antd-style', () => ({ + cssVar: { + colorTextDescription: '#999', + colorTextSecondary: '#666', + }, +})); + +vi.mock('react-i18next', () => ({ + useTranslation: () => ({ t: (key: string) => key }), +})); + +vi.mock('react-router-dom', () => ({ + useNavigate: () => mocks.navigate, +})); + +vi.mock('@/services/task', () => ({ + taskService: { + previewSubtaskLayers: vi.fn(), + }, +})); + +vi.mock('@/store/task', () => ({ + useTaskStore: (selector: any) => + selector({ + ...mocks.taskState, + runReadySubtasks: mocks.runReadySubtasks, + }), +})); + +vi.mock('../AgentTaskList/CreateTaskInlineEntry', () => ({ + default: () =>
create task
, +})); + +vi.mock('../features/AssigneeAgentSelector', () => ({ + default: ({ children }: { children: ReactNode }) => <>{children}, +})); + +vi.mock('../features/AssigneeAvatar', () => ({ + default: () => assignee, +})); + +vi.mock('../features/TaskPriorityTag', () => ({ + default: () => priority, +})); + +vi.mock('../features/TaskStatusTag', () => ({ + default: () => status, +})); + +vi.mock('../features/TaskSubtaskProgressTag', () => ({ + default: () => progress, +})); + +vi.mock('../features/TaskTriggerTag', () => ({ + default: () => trigger, +})); + +vi.mock('../features/useTaskItemContextMenu', () => ({ + useTaskContextMenuActions: () => ({ + buildItems: vi.fn(() => []), + installKeyboardHandlers: vi.fn(), + }), +})); + +vi.mock('../shared/AccordionArrowIcon', () => ({ + default: () => arrow, +})); + +vi.mock('../shared/style', () => ({ + styles: { subtaskTree: 'subtask-tree' }, +})); + +vi.mock('./RunSubtasksPreview', () => ({ + default: () =>
preview
, +})); + +describe('TaskSubtasks', () => { + beforeEach(() => { + mocks.navigate.mockClear(); + mocks.taskState.taskDetailMap['T-parent'].subtasks = [ + { + assignee: { avatar: null, backgroundColor: null, id: 'agt_child', title: 'Child' }, + identifier: 'T-child', + name: 'Child task', + status: 'backlog', + }, + ]; + }); + + afterEach(() => { + cleanup(); + }); + + it("opens a selected subtask using the subtask's assignee agent", () => { + render(); + + fireEvent.click(screen.getByTestId('subtask-tree-node')); + + expect(mocks.navigate).toHaveBeenCalledWith('/agent/agt_child/task/T-child'); + }); + + it('falls back to the global task route when the selected subtask has no assignee', () => { + mocks.taskState.taskDetailMap['T-parent'].subtasks = [ + { + identifier: 'T-child', + name: 'Child task', + status: 'backlog', + }, + ]; + + render(); + + fireEvent.click(screen.getByTestId('subtask-tree-node')); + + expect(mocks.navigate).toHaveBeenCalledWith('/task/T-child'); + }); +}); diff --git a/src/features/AgentTasks/AgentTaskDetail/TaskSubtasks.tsx b/src/features/AgentTasks/AgentTaskDetail/TaskSubtasks.tsx index 28d143c56d..41e5682a09 100644 --- a/src/features/AgentTasks/AgentTaskDetail/TaskSubtasks.tsx +++ b/src/features/AgentTasks/AgentTaskDetail/TaskSubtasks.tsx @@ -23,6 +23,7 @@ import TaskTriggerTag from '../features/TaskTriggerTag'; import { useTaskContextMenuActions } from '../features/useTaskItemContextMenu'; import AccordionArrowIcon from '../shared/AccordionArrowIcon'; import { styles } from '../shared/style'; +import { taskDetailPath } from '../shared/taskDetailPath'; import RunSubtasksPreview from './RunSubtasksPreview'; type TaskStatus = 'backlog' | 'canceled' | 'completed' | 'failed' | 'paused' | 'running'; @@ -139,13 +140,6 @@ const TaskSubtasks = memo(() => { const [isExpanded, setIsExpanded] = useState(true); const [isPlanning, setIsPlanning] = useState(false); - const handleNavigate = useCallback( - (identifier: string) => { - navigate(`/task/${identifier}`); - }, - [navigate], - ); - const subtaskMap = useMemo(() => { const map = new Map(); const walk = (items: TaskDetailSubtask[]) => { @@ -158,6 +152,14 @@ const TaskSubtasks = memo(() => { return map; }, [subtasks]); + const handleNavigate = useCallback( + (identifier: string) => { + const subtask = subtaskMap.get(identifier); + navigate(taskDetailPath(identifier, subtask?.assignee?.id ?? undefined)); + }, + [navigate, subtaskMap], + ); + const treeData = useMemo(() => { if (subtasks.length === 0) return []; return toTreeData(buildTree(subtasks)); diff --git a/src/features/AgentTasks/AgentTaskList/AgentTasksPage.tsx b/src/features/AgentTasks/AgentTaskList/AgentTasksPage.tsx index 7cd29707b2..1eb6d1ca7f 100644 --- a/src/features/AgentTasks/AgentTaskList/AgentTasksPage.tsx +++ b/src/features/AgentTasks/AgentTaskList/AgentTasksPage.tsx @@ -15,6 +15,7 @@ import { taskListSelectors } from '@/store/task/selectors'; import { createTaskModal } from '../CreateTaskModal'; import Breadcrumb from '../shared/Breadcrumb'; +import { taskDetailPath } from '../shared/taskDetailPath'; import CreateTaskInlineEntry from './CreateTaskInlineEntry'; import EmptyState from './EmptyState'; import KanbanBoard from './KanbanBoard'; @@ -50,7 +51,7 @@ const AgentTasksPage = memo(() => { const handleCreateTask = useCallback(() => { createTaskModal({ onCreated: (task) => { - navigate(`/task/${task.identifier}`); + navigate(taskDetailPath(task.identifier, task.agentId)); }, }); }, [navigate]); diff --git a/src/features/AgentTasks/AgentTaskList/KanbanBoard.tsx b/src/features/AgentTasks/AgentTaskList/KanbanBoard.tsx index 3f0c0fcdc2..d0226cbad6 100644 --- a/src/features/AgentTasks/AgentTaskList/KanbanBoard.tsx +++ b/src/features/AgentTasks/AgentTaskList/KanbanBoard.tsx @@ -24,6 +24,7 @@ import type { TaskGroupItem, TaskListItem } from '@/store/task/slices/list/initi import { createTaskModal } from '../CreateTaskModal'; import AgentTaskItem from '../features/AgentTaskItem'; +import { taskDetailPath } from '../shared/taskDetailPath'; import HiddenColumnsPanel from './HiddenColumnsPanel'; import KanbanColumn, { COLUMN_I18N_KEYS, COLUMN_STATUS_ICON, COLUMN_WIDTH } from './KanbanColumn'; @@ -135,7 +136,7 @@ const KanbanBoard = memo(() => { const handleCreateTask = useCallback(() => { createTaskModal({ onCreated: (task) => { - navigate(`/task/${task.identifier}`); + navigate(taskDetailPath(task.identifier, task.agentId)); }, showInlineToggle: false, }); diff --git a/src/features/AgentTasks/features/AgentTaskItem.test.tsx b/src/features/AgentTasks/features/AgentTaskItem.test.tsx new file mode 100644 index 0000000000..631a22369a --- /dev/null +++ b/src/features/AgentTasks/features/AgentTaskItem.test.tsx @@ -0,0 +1,190 @@ +/** + * @vitest-environment happy-dom + */ +import { cleanup, fireEvent, render, screen } from '@testing-library/react'; +import type { ReactNode } from 'react'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import AgentTaskItem from './AgentTaskItem'; + +const mocks = vi.hoisted(() => ({ + navigate: vi.fn(), + taskDetailMap: {} as Record, + useFetchTaskDetail: vi.fn(), +})); + +vi.mock('@lobehub/ui', () => ({ + Block: ({ + children, + clickable, + onClick, + }: { + children: ReactNode; + clickable?: boolean; + onClick?: () => void; + }) => + clickable ? ( + + ) : ( + {children} + ), + ContextMenuTrigger: ({ children }: { children: ReactNode }) => <>{children}, + Flexbox: ({ children }: { children: ReactNode }) =>
{children}
, + Text: ({ children }: { children: ReactNode }) => {children}, +})); + +vi.mock('react-i18next', () => ({ + useTranslation: () => ({ + i18n: { language: 'en-US' }, + t: (key: string, options?: { defaultValue?: string }) => options?.defaultValue ?? key, + }), +})); + +vi.mock('react-router-dom', () => ({ + useNavigate: () => mocks.navigate, +})); + +vi.mock('@/store/task', () => ({ + useTaskStore: (selector: any) => + selector({ + taskDetailMap: mocks.taskDetailMap, + useFetchTaskDetail: mocks.useFetchTaskDetail, + }), +})); + +vi.mock('./AssigneeAgentSelector', () => ({ + default: ({ children }: { children: ReactNode }) => <>{children}, +})); + +vi.mock('./AssigneeAvatar', () => ({ + default: () => assignee, +})); + +vi.mock('./formatTaskItemDate', () => ({ + formatTaskItemDate: () => 'today', +})); + +vi.mock('./TaskLatestActivity', () => ({ + default: () => null, +})); + +vi.mock('./TaskPriorityTag', () => ({ + default: () => priority, +})); + +vi.mock('./TaskStatusTag', () => ({ + default: () => status, +})); + +vi.mock('./TaskSubtaskProgressTag', () => ({ + default: ({ + onSubtaskClick, + subtasks, + }: { + onSubtaskClick?: (identifier: string, assigneeAgentId?: string) => void; + subtasks?: any[]; + }) => { + const subtask = subtasks?.[0]; + if (!subtask) return null; + + return ( + { + event.stopPropagation(); + onSubtaskClick?.(subtask.identifier, subtask.assignee?.id ?? undefined); + }} + > + subtask + + ); + }, +})); + +vi.mock('./TaskTriggerTag', () => ({ + default: () => trigger, +})); + +vi.mock('./useTaskItemContextMenu', () => ({ + useTaskItemContextMenu: () => ({ items: [], onContextMenu: vi.fn() }), +})); + +const createTask = (assigneeAgentId?: string | null) => + ({ + assigneeAgentId, + createdAt: new Date('2026-05-18T00:00:00.000Z'), + identifier: 'T-22', + name: 'Hourly trend update', + priority: 2, + status: 'scheduled', + updatedAt: new Date('2026-05-18T00:00:00.000Z'), + }) as any; + +describe('AgentTaskItem', () => { + beforeEach(() => { + mocks.navigate.mockClear(); + mocks.taskDetailMap = {}; + mocks.useFetchTaskDetail.mockClear(); + }); + + afterEach(() => { + cleanup(); + }); + + it('opens an assigned task inside its owning agent route', () => { + render(); + + fireEvent.click(screen.getByTestId('task-card')); + + expect(mocks.navigate).toHaveBeenCalledWith('/agent/agt_owner/task/T-22'); + }); + + it('falls back to the global task detail route when the task has no assignee', () => { + render(); + + fireEvent.click(screen.getByTestId('task-card')); + + expect(mocks.navigate).toHaveBeenCalledWith('/task/T-22'); + }); + + it("opens a subtask inside the clicked subtask's assignee route", () => { + mocks.taskDetailMap = { + 'T-22': { + subtasks: [ + { + assignee: { id: 'agt_child' }, + identifier: 'T-23', + status: 'backlog', + }, + ], + }, + }; + + render(); + + fireEvent.click(screen.getAllByTestId('subtask-progress')[0]); + + expect(mocks.navigate).toHaveBeenCalledWith('/agent/agt_child/task/T-23'); + }); + + it('falls back to the global route when the clicked subtask has no assignee', () => { + mocks.taskDetailMap = { + 'T-22': { + subtasks: [ + { + identifier: 'T-23', + status: 'backlog', + }, + ], + }, + }; + + render(); + + fireEvent.click(screen.getAllByTestId('subtask-progress')[0]); + + expect(mocks.navigate).toHaveBeenCalledWith('/task/T-23'); + }); +}); diff --git a/src/features/AgentTasks/features/AgentTaskItem.tsx b/src/features/AgentTasks/features/AgentTaskItem.tsx index 7548adb384..2a934f2c6a 100644 --- a/src/features/AgentTasks/features/AgentTaskItem.tsx +++ b/src/features/AgentTasks/features/AgentTaskItem.tsx @@ -7,6 +7,7 @@ import { useNavigate } from 'react-router-dom'; import { useTaskStore } from '@/store/task'; import type { TaskListItem } from '@/store/task/slices/list/initialState'; +import { taskDetailPath } from '../shared/taskDetailPath'; import AssigneeAgentSelector from './AssigneeAgentSelector'; import AssigneeAvatar from './AssigneeAvatar'; import { formatTaskItemDate } from './formatTaskItemDate'; @@ -57,12 +58,12 @@ const AgentTaskItem = memo(({ task, variant = 'default' }) => { const hasName = Boolean(task.name?.trim()); const handleClick = useCallback(() => { - navigate(`/task/${task.identifier}`); - }, [navigate, task.identifier]); + navigate(taskDetailPath(task.identifier, task.assigneeAgentId ?? undefined)); + }, [navigate, task.assigneeAgentId, task.identifier]); const handleSubtaskClick = useCallback( - (identifier: string) => { - navigate(`/task/${identifier}`); + (identifier: string, assigneeAgentId?: string) => { + navigate(taskDetailPath(identifier, assigneeAgentId)); }, [navigate], ); diff --git a/src/features/AgentTasks/features/TaskSubtaskProgressTag.test.tsx b/src/features/AgentTasks/features/TaskSubtaskProgressTag.test.tsx new file mode 100644 index 0000000000..23ca7179d9 --- /dev/null +++ b/src/features/AgentTasks/features/TaskSubtaskProgressTag.test.tsx @@ -0,0 +1,77 @@ +/** + * @vitest-environment happy-dom + */ +import { cleanup, fireEvent, render, screen } from '@testing-library/react'; +import type { ReactNode } from 'react'; +import { afterEach, describe, expect, it, vi } from 'vitest'; + +import TaskSubtaskProgressTag from './TaskSubtaskProgressTag'; + +vi.mock('@lobehub/ui', () => ({ + Block: ({ children, onClick }: { children: ReactNode; onClick?: () => void }) => ( +
{children}
+ ), + DropdownMenu: ({ + children, + items, + }: { + children: ReactNode; + items?: Array<{ key: string; onClick?: () => void }>; + }) => ( +
+ {children} + {items?.map((item) => ( + + ))} +
+ ), + Flexbox: ({ children }: { children: ReactNode }) =>
{children}
, + Text: ({ children }: { children: ReactNode }) => {children}, +})); + +vi.mock('antd', () => ({ + Progress: () => progress, +})); + +vi.mock('antd-style', () => ({ + cssVar: { colorSuccess: 'green' }, +})); + +vi.mock('./TaskStatusIcon', () => ({ + default: () => status, +})); + +describe('TaskSubtaskProgressTag', () => { + afterEach(() => { + cleanup(); + }); + + it("passes the clicked subtask's assignee to the navigation callback", () => { + const onSubtaskClick = vi.fn(); + + render( + , + ); + + fireEvent.click(screen.getByTestId('subtask-T-2')); + + expect(onSubtaskClick).toHaveBeenCalledWith('T-2', 'agt_child'); + }); +}); diff --git a/src/features/AgentTasks/features/TaskSubtaskProgressTag.tsx b/src/features/AgentTasks/features/TaskSubtaskProgressTag.tsx index 148b1b9e8e..bdf51c3c12 100644 --- a/src/features/AgentTasks/features/TaskSubtaskProgressTag.tsx +++ b/src/features/AgentTasks/features/TaskSubtaskProgressTag.tsx @@ -75,7 +75,7 @@ const flattenSubtasks = (nodes: TaskDetailSubtask[]) => { interface TaskSubtaskProgressTagProps { currentIdentifier?: string; - onSubtaskClick?: (identifier: string) => void; + onSubtaskClick?: (identifier: string, assigneeAgentId?: string) => void; subtasks?: TaskDetailSubtask[]; } @@ -116,7 +116,8 @@ const TaskSubtaskProgressTag = memo( ), - onClick: () => onSubtaskClick?.(subtask.task.identifier), + onClick: () => + onSubtaskClick?.(subtask.task.identifier, subtask.task.assignee?.id ?? undefined), }; }) as DropdownMenuProps['items']; diff --git a/src/features/AgentTasks/features/useTaskItemContextMenu.tsx b/src/features/AgentTasks/features/useTaskItemContextMenu.tsx index ed4aa4794e..89c248948e 100644 --- a/src/features/AgentTasks/features/useTaskItemContextMenu.tsx +++ b/src/features/AgentTasks/features/useTaskItemContextMenu.tsx @@ -25,6 +25,7 @@ import { useAgentStore } from '@/store/agent'; import { builtinAgentSelectors } from '@/store/agent/selectors'; import { useTaskStore } from '@/store/task'; +import { taskDetailPath } from '../shared/taskDetailPath'; import { renderMenuExtra } from './menuExtra'; import { PRIORITY_META } from './TaskPriorityTag'; import { STATUS_META, USER_SELECTABLE_STATUSES } from './TaskStatusTag'; @@ -125,7 +126,10 @@ export const useTaskContextMenuActions = (): TaskContextMenuActions => { } as ContextMenuItem; }); - const taskUrl = `${appOrigin}/task/${task.identifier}`; + const taskUrl = `${appOrigin}${taskDetailPath( + task.identifier, + task.assigneeAgentId ?? undefined, + )}`; const canRunNow = RUN_NOW_STATUSES.has(currentStatus); return [ @@ -288,13 +292,10 @@ export const useTaskContextMenuActions = (): TaskContextMenuActions => { export const useTaskItemContextMenu = (task: TaskContextMenuTarget): TaskItemContextMenu => { const { buildItems, installKeyboardHandlers } = useTaskContextMenuActions(); - const items = useMemo( - () => buildItems(task), - [buildItems, task.identifier, task.status, task.priority, task.assigneeAgentId], - ); + const items = useMemo(() => buildItems(task), [buildItems, task]); const onContextMenu = useCallback( () => installKeyboardHandlers(task), - [installKeyboardHandlers, task.identifier, task.status, task.priority], + [installKeyboardHandlers, task], ); return { items, onContextMenu }; }; diff --git a/src/features/AgentTasks/shared/Breadcrumb.test.tsx b/src/features/AgentTasks/shared/Breadcrumb.test.tsx new file mode 100644 index 0000000000..44db026ffe --- /dev/null +++ b/src/features/AgentTasks/shared/Breadcrumb.test.tsx @@ -0,0 +1,108 @@ +/** + * @vitest-environment happy-dom + */ +import { cleanup, render, screen } from '@testing-library/react'; +import type { ReactNode } from 'react'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import Breadcrumb from './Breadcrumb'; + +const mocks = vi.hoisted(() => ({ + taskState: {} as any, +})); + +const createState = (taskDetailMap: Record) => ({ + taskDetailMap, +}); + +vi.mock('@lobehub/ui', () => ({ + Icon: () => icon, + Text: ({ children }: { children: ReactNode }) => {children}, +})); + +vi.mock('antd', () => ({ + Breadcrumb: ({ items }: { items: Array<{ key?: string; title: ReactNode }> }) => ( + + ), +})); + +vi.mock('react-i18next', () => ({ + useTranslation: () => ({ t: (key: string) => key }), +})); + +vi.mock('react-router-dom', () => ({ + Link: ({ children, to }: { children: ReactNode; to: string }) => {children}, +})); + +vi.mock('zustand/react/shallow', () => ({ + useShallow: (selector: any) => selector, +})); + +vi.mock('@/store/task', () => ({ + useTaskStore: (selector: any) => selector(mocks.taskState), +})); + +vi.mock('./style', () => ({ + styles: { breadcrumb: 'breadcrumb' }, +})); + +describe('Breadcrumb', () => { + beforeEach(() => { + mocks.taskState = createState({ + 'T-child': { + identifier: 'T-child', + instruction: 'Child instruction', + name: 'Child task', + parent: { agentId: 'agt_parent', identifier: 'T-parent', name: 'Parent task' }, + status: 'running', + }, + 'T-parent': { + agentId: 'agt_parent', + identifier: 'T-parent', + instruction: 'Parent instruction', + name: 'Parent task', + parent: { agentId: null, identifier: 'T-root', name: 'Root task' }, + status: 'running', + }, + }); + }); + + afterEach(() => { + cleanup(); + }); + + it('uses the ancestor owner agent when building breadcrumb links', () => { + render(); + + expect(screen.getByRole('link', { name: 'T-parent' })).toHaveAttribute( + 'href', + '/agent/agt_parent/task/T-parent', + ); + expect(screen.getByRole('link', { name: 'T-root' })).toHaveAttribute('href', '/task/T-root'); + }); + + it('falls back to the global route when an ancestor owner is unknown', () => { + mocks.taskState = createState({ + 'T-child': { + identifier: 'T-child', + instruction: 'Child instruction', + name: 'Child task', + parent: { identifier: 'T-parent', name: 'Parent task' }, + status: 'running', + }, + }); + + render(); + + expect(screen.getByRole('link', { name: 'T-parent' })).toHaveAttribute( + 'href', + '/task/T-parent', + ); + }); +}); diff --git a/src/features/AgentTasks/shared/Breadcrumb.tsx b/src/features/AgentTasks/shared/Breadcrumb.tsx index d0911926de..4837825aaf 100644 --- a/src/features/AgentTasks/shared/Breadcrumb.tsx +++ b/src/features/AgentTasks/shared/Breadcrumb.tsx @@ -9,6 +9,7 @@ import { useShallow } from 'zustand/react/shallow'; import { useTaskStore } from '@/store/task'; import { styles } from './style'; +import { taskDetailPath } from './taskDetailPath'; interface BreadcrumbProps { taskId?: string; @@ -23,13 +24,17 @@ const Breadcrumb = memo(({ taskId }) => { const ancestors = useTaskStore( useShallow((s) => { if (!taskId) return []; - const chain: string[] = []; + const chain: Array<{ agentId?: string | null; identifier: string }> = []; const visited = new Set([taskId]); - let cursor = s.taskDetailMap[taskId]?.parent?.identifier; - while (cursor && !visited.has(cursor)) { - visited.add(cursor); - chain.push(cursor); - cursor = s.taskDetailMap[cursor]?.parent?.identifier; + let cursor = s.taskDetailMap[taskId]?.parent; + while (cursor?.identifier && !visited.has(cursor.identifier)) { + const detail = s.taskDetailMap[cursor.identifier]; + visited.add(cursor.identifier); + chain.push({ + agentId: cursor.agentId === undefined ? detail?.agentId : cursor.agentId, + identifier: cursor.identifier, + }); + cursor = detail?.parent; } return chain.reverse(); }), @@ -41,10 +46,10 @@ const Breadcrumb = memo(({ taskId }) => { ); - const ancestorCrumbs = ancestors.map((identifier) => ({ + const ancestorCrumbs = ancestors.map(({ identifier, agentId }) => ({ key: identifier, title: ( - + {identifier} diff --git a/src/features/AgentTasks/shared/taskDetailPath.test.ts b/src/features/AgentTasks/shared/taskDetailPath.test.ts new file mode 100644 index 0000000000..7262429a07 --- /dev/null +++ b/src/features/AgentTasks/shared/taskDetailPath.test.ts @@ -0,0 +1,50 @@ +/** + * @vitest-environment happy-dom + */ +import { renderHook } from '@testing-library/react'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { taskDetailPath, useNavigateToTaskDetail, useTaskDetailPath } from './taskDetailPath'; + +const mocks = vi.hoisted(() => ({ + navigate: vi.fn(), + params: {} as { aid?: string }, +})); + +vi.mock('react-router-dom', () => ({ + useNavigate: () => mocks.navigate, + useParams: () => mocks.params, +})); + +describe('taskDetailPath', () => { + beforeEach(() => { + mocks.navigate.mockClear(); + mocks.params = {}; + }); + + it('builds an agent-scoped path when an agent id is provided', () => { + expect(taskDetailPath('T-1', 'agt_owner')).toBe('/agent/agt_owner/task/T-1'); + }); + + it('falls back to the global task path without an agent id', () => { + expect(taskDetailPath('T-1')).toBe('/task/T-1'); + }); + + it('uses the route agent by default and allows explicit assignee override', () => { + mocks.params = { aid: 'agt_current' }; + + const { result } = renderHook(() => useTaskDetailPath()); + + expect(result.current('T-1')).toBe('/agent/agt_current/task/T-1'); + expect(result.current('T-2', 'agt_child')).toBe('/agent/agt_child/task/T-2'); + }); + + it('navigates to an explicit assignee route when provided', () => { + mocks.params = { aid: 'agt_current' }; + + const { result } = renderHook(() => useNavigateToTaskDetail()); + result.current('T-2', 'agt_child'); + + expect(mocks.navigate).toHaveBeenCalledWith('/agent/agt_child/task/T-2'); + }); +}); diff --git a/src/features/AgentTasks/shared/taskDetailPath.ts b/src/features/AgentTasks/shared/taskDetailPath.ts new file mode 100644 index 0000000000..00013551a0 --- /dev/null +++ b/src/features/AgentTasks/shared/taskDetailPath.ts @@ -0,0 +1,26 @@ +import { useCallback } from 'react'; +import { useNavigate, useParams } from 'react-router-dom'; + +export const taskDetailPath = (taskId: string, agentId?: string) => + agentId ? `/agent/${agentId}/task/${taskId}` : `/task/${taskId}`; + +export const useTaskDetailPath = () => { + const { aid } = useParams<{ aid?: string }>(); + + return useCallback( + (taskId: string, agentId?: string) => taskDetailPath(taskId, agentId ?? aid), + [aid], + ); +}; + +export const useNavigateToTaskDetail = () => { + const navigate = useNavigate(); + const getTaskDetailPath = useTaskDetailPath(); + + return useCallback( + (taskId: string, agentId?: string) => { + navigate(getTaskDetailPath(taskId, agentId)); + }, + [getTaskDetailPath, navigate], + ); +}; diff --git a/src/features/DailyBrief/BriefCard.tsx b/src/features/DailyBrief/BriefCard.tsx index c0bb0df9f6..5c3d872e18 100644 --- a/src/features/DailyBrief/BriefCard.tsx +++ b/src/features/DailyBrief/BriefCard.tsx @@ -8,6 +8,7 @@ import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { DEFAULT_INBOX_AVATAR } from '@/const/meta'; +import { taskDetailPath } from '@/features/AgentTasks/shared/taskDetailPath'; import Time from '@/routes/(main)/home/features/components/Time'; import BriefCardActions from './BriefCardActions'; @@ -54,6 +55,10 @@ const BriefCard = memo( const showFull = !isResolved || expanded; const canNavigate = enableNavigation && Boolean(brief.taskId); + const handleNavigate = () => { + if (!brief.taskId) return; + navigate(taskDetailPath(brief.taskId, brief.agentId ?? undefined)); + }; return ( ( className={canNavigate ? styles.clickableHeader : undefined} gap={16} justify={'space-between'} - onClick={canNavigate ? () => navigate(`/task/${brief.taskId}`) : undefined} + onClick={canNavigate ? handleNavigate : undefined} > diff --git a/src/features/Electron/navigation/routeMetadata.ts b/src/features/Electron/navigation/routeMetadata.ts index fe5b646a3b..e1d0129690 100644 --- a/src/features/Electron/navigation/routeMetadata.ts +++ b/src/features/Electron/navigation/routeMetadata.ts @@ -58,7 +58,7 @@ const routePatterns: RoutePattern[] = [ }, { icon: MessageSquare, - test: (p) => p.startsWith('/agent/'), + test: (p) => p.startsWith('/agent/') && !/^\/agent\/[^/]+\/task\//.test(p), titleKey: 'navigation.chat', useDynamicTitle: true, }, @@ -123,7 +123,8 @@ const routePatterns: RoutePattern[] = [ // Tasks routes (cross-agent global view + singular task detail) { icon: tasksIcon, - test: (p) => p.startsWith('/tasks') || p.startsWith('/task/'), + test: (p) => + p.startsWith('/tasks') || p.startsWith('/task/') || /^\/agent\/[^/]+\/task\//.test(p), titleKey: 'navigation.tasks', }, diff --git a/src/features/RecommendTaskTemplates/useTaskTemplateCreate.ts b/src/features/RecommendTaskTemplates/useTaskTemplateCreate.ts index 681aed3222..2342200ff1 100644 --- a/src/features/RecommendTaskTemplates/useTaskTemplateCreate.ts +++ b/src/features/RecommendTaskTemplates/useTaskTemplateCreate.ts @@ -4,6 +4,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; +import { taskDetailPath } from '@/features/AgentTasks/shared/taskDetailPath'; import { taskTemplateService } from '@/services/taskTemplate'; import { useAgentStore } from '@/store/agent'; import { builtinAgentSelectors } from '@/store/agent/selectors'; @@ -63,7 +64,9 @@ export const useTaskTemplateCreate = ({ setCreated(true); onCreated(template.id); if (createdTask?.identifier) { - navigate(`/task/${createdTask.identifier}`); + navigate( + taskDetailPath(createdTask.identifier, createdTask.assigneeAgentId ?? inboxAgentId), + ); } } catch (error) { console.error('[taskTemplate:create]', error); diff --git a/src/routes/(main)/agent/_layout/Sidebar/Task/StatusGroup.tsx b/src/routes/(main)/agent/_layout/Sidebar/Task/StatusGroup.tsx index d21925fbd6..7173b60f80 100644 --- a/src/routes/(main)/agent/_layout/Sidebar/Task/StatusGroup.tsx +++ b/src/routes/(main)/agent/_layout/Sidebar/Task/StatusGroup.tsx @@ -60,7 +60,11 @@ const StatusGroup = memo(({ group }) => { > {group.tasks.map((task) => ( - + ))} diff --git a/src/routes/(main)/agent/_layout/Sidebar/Task/TaskItem.test.tsx b/src/routes/(main)/agent/_layout/Sidebar/Task/TaskItem.test.tsx new file mode 100644 index 0000000000..fc5743a9db --- /dev/null +++ b/src/routes/(main)/agent/_layout/Sidebar/Task/TaskItem.test.tsx @@ -0,0 +1,76 @@ +/** + * @vitest-environment happy-dom + */ +import { cleanup, fireEvent, render, screen } from '@testing-library/react'; +import type { ReactNode } from 'react'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import TaskItem from './TaskItem'; + +const mocks = vi.hoisted(() => ({ + navigate: vi.fn(), + params: {} as { aid?: string }, +})); + +vi.mock('@lobehub/ui', () => ({ + Text: ({ children }: { children: ReactNode }) => {children}, +})); + +vi.mock('@/features/NavPanel/components/NavItem', () => ({ + default: ({ + active, + onClick, + slots, + title, + }: { + active?: boolean; + onClick?: () => void; + slots?: { titlePrefix?: ReactNode }; + title: ReactNode; + }) => ( + + ), +})); + +vi.mock('react-router-dom', () => ({ + useNavigate: () => mocks.navigate, + useParams: () => mocks.params, +})); + +const task = { + id: 'task_1', + identifier: 'T-22', + name: 'Hourly trend update', +}; + +describe('Task sidebar item', () => { + beforeEach(() => { + mocks.navigate.mockClear(); + mocks.params = {}; + }); + + afterEach(() => { + cleanup(); + }); + + it('opens task detail inside the current agent route when an agent route param exists', () => { + mocks.params = { aid: 'agt_current' }; + + render(); + + fireEvent.click(screen.getByRole('button', { name: 'T-22 Hourly trend update' })); + + expect(mocks.navigate).toHaveBeenCalledWith('/agent/agt_current/task/T-22'); + }); + + it('falls back to the global task detail route outside agent context', () => { + render(); + + fireEvent.click(screen.getByRole('button', { name: 'T-22 Hourly trend update' })); + + expect(mocks.navigate).toHaveBeenCalledWith('/task/T-22'); + }); +}); diff --git a/src/routes/(main)/agent/_layout/Sidebar/Task/TaskItem.tsx b/src/routes/(main)/agent/_layout/Sidebar/Task/TaskItem.tsx index b5d3eaf4c2..f859f45b48 100644 --- a/src/routes/(main)/agent/_layout/Sidebar/Task/TaskItem.tsx +++ b/src/routes/(main)/agent/_layout/Sidebar/Task/TaskItem.tsx @@ -3,8 +3,8 @@ import { Text } from '@lobehub/ui'; import { cssVar } from 'antd-style'; import { memo, useCallback } from 'react'; -import { useNavigate } from 'react-router-dom'; +import { useNavigateToTaskDetail } from '@/features/AgentTasks/shared/taskDetailPath'; import NavItem from '@/features/NavPanel/components/NavItem'; import type { TaskGroupItem } from '@/store/task/slices/list/initialState'; @@ -16,11 +16,11 @@ interface TaskItemProps { } const TaskItem = memo(({ task, active }) => { - const navigate = useNavigate(); + const navigateToTaskDetail = useNavigateToTaskDetail(); const handleClick = useCallback(() => { - navigate(`/task/${task.identifier}`); - }, [navigate, task.identifier]); + navigateToTaskDetail(task.identifier); + }, [navigateToTaskDetail, task.identifier]); const hasName = Boolean(task.name?.trim()); const displayTitle = hasName ? task.name : task.identifier; diff --git a/src/routes/(main)/agent/task/[taskId]/index.tsx b/src/routes/(main)/agent/task/[taskId]/index.tsx new file mode 100644 index 0000000000..2933d5d7b1 --- /dev/null +++ b/src/routes/(main)/agent/task/[taskId]/index.tsx @@ -0,0 +1,16 @@ +'use client'; + +import { memo } from 'react'; +import { useParams } from 'react-router-dom'; + +import { TaskDetailPage } from '@/features/AgentTasks'; + +const AgentTaskDetailRoute = memo(() => { + const { taskId } = useParams<{ taskId?: string }>(); + + if (!taskId) return null; + + return ; +}); + +export default AgentTaskDetailRoute; diff --git a/src/routes/(main)/home/features/Recents/AllRecentsDrawer.tsx b/src/routes/(main)/home/features/Recents/AllRecentsDrawer.tsx index 1e76926902..76a6dc8bb1 100644 --- a/src/routes/(main)/home/features/Recents/AllRecentsDrawer.tsx +++ b/src/routes/(main)/home/features/Recents/AllRecentsDrawer.tsx @@ -6,6 +6,7 @@ import { memo, useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; +import { taskDetailPath } from '@/features/AgentTasks/shared/taskDetailPath'; import SkeletonList from '@/features/NavPanel/components/SkeletonList'; import SideBarDrawer from '@/features/NavPanel/SideBarDrawer'; import { useClientDataSWR } from '@/libs/swr'; @@ -40,7 +41,7 @@ const AllRecentsDrawer = memo(({ open, onClose }) => { const taskId = item.id; if (!taskId) return item.routePath; - return `/task/${taskId}`; + return taskDetailPath(taskId, item.agentId ?? undefined); }, []); return ( diff --git a/src/routes/(main)/home/features/Recents/List.tsx b/src/routes/(main)/home/features/Recents/List.tsx index 2d73df683f..51ce8248d5 100644 --- a/src/routes/(main)/home/features/Recents/List.tsx +++ b/src/routes/(main)/home/features/Recents/List.tsx @@ -4,6 +4,7 @@ import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; +import { taskDetailPath } from '@/features/AgentTasks/shared/taskDetailPath'; import NavItem from '@/features/NavPanel/components/NavItem'; import SkeletonList from '@/features/NavPanel/components/SkeletonList'; import { useGlobalStore } from '@/store/global'; @@ -33,7 +34,7 @@ const RecentsList = memo(() => { const taskId = item.id; if (!taskId) return item.routePath; - return `/task/${taskId}`; + return taskDetailPath(taskId, item.agentId ?? undefined); }, []); if (!isInit) { diff --git a/src/server/routers/lambda/recent.ts b/src/server/routers/lambda/recent.ts index ddb6fe69cf..5e666a9ae5 100644 --- a/src/server/routers/lambda/recent.ts +++ b/src/server/routers/lambda/recent.ts @@ -56,7 +56,9 @@ export const recentRouter = router({ break; } case 'task': { - routePath = item.routeId ? `/agent/${item.routeId}` : '/'; + routePath = item.routeId + ? `/agent/${item.routeId}/task/${item.id}` + : `/task/${item.id}`; break; } } diff --git a/src/server/services/task/index.test.ts b/src/server/services/task/index.test.ts index 7a5dd970d0..59f03fc8ff 100644 --- a/src/server/services/task/index.test.ts +++ b/src/server/services/task/index.test.ts @@ -164,7 +164,12 @@ describe('TaskService', () => { totalTopics: 0, }; - const parentTask = { id: 'task_001', identifier: 'TASK-1', name: 'Parent Task' }; + const parentTask = { + assigneeAgentId: 'agt_parent', + id: 'task_001', + identifier: 'TASK-1', + name: 'Parent Task', + }; mockTaskModel.resolve.mockResolvedValue(task); mockTaskModel.findAllDescendants.mockResolvedValue([]); @@ -181,7 +186,11 @@ describe('TaskService', () => { const service = new TaskService(db, userId); const result = await service.getTaskDetail('TASK-2'); - expect(result?.parent).toEqual({ identifier: 'TASK-1', name: 'Parent Task' }); + expect(result?.parent).toEqual({ + agentId: 'agt_parent', + identifier: 'TASK-1', + name: 'Parent Task', + }); expect(mockTaskModel.findById).toHaveBeenCalledWith('task_001'); }); diff --git a/src/server/services/task/index.ts b/src/server/services/task/index.ts index 325be52f60..ebeb25f089 100644 --- a/src/server/services/task/index.ts +++ b/src/server/services/task/index.ts @@ -487,11 +487,15 @@ export class TaskService { ); // Resolve parent - let parent: { identifier: string; name: string | null } | null = null; + let parent: { agentId: string | null; identifier: string; name: string | null } | null = null; if (task.parentTaskId) { const parentTask = await this.taskModel.findById(task.parentTaskId); if (parentTask) { - parent = { identifier: parentTask.identifier, name: parentTask.name }; + parent = { + agentId: parentTask.assigneeAgentId, + identifier: parentTask.identifier, + name: parentTask.name, + }; } } diff --git a/src/spa/router/desktopRouter.config.desktop.tsx b/src/spa/router/desktopRouter.config.desktop.tsx index 44997e5ca2..6a241575cf 100644 --- a/src/spa/router/desktopRouter.config.desktop.tsx +++ b/src/spa/router/desktopRouter.config.desktop.tsx @@ -23,6 +23,7 @@ import AgentTopicNotebookDocPage from '@/routes/(main)/agent/[topicId]/page/[doc import AgentChannelPage from '@/routes/(main)/agent/channel'; import AgentPageRedirectPage from '@/routes/(main)/agent/page'; import AgentProfilePage from '@/routes/(main)/agent/profile'; +import AgentTaskDetailRoute from '@/routes/(main)/agent/task/[taskId]'; import CommunityLayout from '@/routes/(main)/community/_layout'; import CommunityDetailLayout from '@/routes/(main)/community/(detail)/_layout'; import CommunityDetailAgentPage from '@/routes/(main)/community/(detail)/agent'; @@ -138,6 +139,10 @@ export const desktopRoutes: RouteObject[] = [ element: , path: 'channel', }, + { + element: , + path: 'task/:taskId', + }, ], element: , errorElement: , diff --git a/src/spa/router/desktopRouter.config.tsx b/src/spa/router/desktopRouter.config.tsx index 23d1a686c6..0be1c002d1 100644 --- a/src/spa/router/desktopRouter.config.tsx +++ b/src/spa/router/desktopRouter.config.tsx @@ -84,6 +84,13 @@ export const desktopRoutes: RouteObject[] = [ ), path: 'channel', }, + { + element: dynamicElement( + () => import('@/routes/(main)/agent/task/[taskId]'), + 'Desktop > Chat > Task Detail', + ), + path: 'task/:taskId', + }, ], element: dynamicLayout( () => import('@/routes/(main)/agent/_layout'), diff --git a/src/spa/router/desktopRouter.sync.test.tsx b/src/spa/router/desktopRouter.sync.test.tsx index c5b766da68..6e33fd840a 100644 --- a/src/spa/router/desktopRouter.sync.test.tsx +++ b/src/spa/router/desktopRouter.sync.test.tsx @@ -1,5 +1,5 @@ import { readFile } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import { describe, expect, it } from 'vitest'; @@ -25,8 +25,8 @@ function normalizePaths(paths: string[]) { async function readDesktopRouterSources() { return Promise.all([ - readFile(join(process.cwd(), 'src/spa/router/desktopRouter.config.tsx'), 'utf8'), - readFile(join(process.cwd(), 'src/spa/router/desktopRouter.config.desktop.tsx'), 'utf8'), + readFile(path.join(process.cwd(), 'src/spa/router/desktopRouter.config.tsx'), 'utf8'), + readFile(path.join(process.cwd(), 'src/spa/router/desktopRouter.config.desktop.tsx'), 'utf8'), ]); } @@ -54,6 +54,8 @@ describe('desktopRouter config sync', () => { expect(asyncSource).toContain("import('@/routes/(main)/(task-workspace)/_layout')"); expect(syncSource).toContain("from '@/routes/(main)/(task-workspace)/_layout'"); + expect(asyncSource).toContain("import('@/routes/(main)/agent/task/[taskId]')"); + expect(syncSource).toContain("from '@/routes/(main)/agent/task/[taskId]'"); expect(asyncSource).not.toContain("import('@/routes/(main)/task-workspace/_layout')"); expect(syncSource).not.toContain("from '@/routes/(main)/task-workspace/_layout'"); expect(asyncSource).not.toContain("import('@/routes/(main)/tasks/_layout')"); diff --git a/src/spa/router/mobileRouter.config.tsx b/src/spa/router/mobileRouter.config.tsx index 3d1a20b00f..3a8e33ce5b 100644 --- a/src/spa/router/mobileRouter.config.tsx +++ b/src/spa/router/mobileRouter.config.tsx @@ -251,6 +251,19 @@ export const mobileRoutes: RouteObject[] = [ errorElement: , path: 'task', }, + { + children: [ + { + element: dynamicElement( + () => import('@/routes/(main)/agent/task/[taskId]'), + 'Mobile > Agent Task Detail', + ), + path: ':aid/task/:taskId', + }, + ], + errorElement: , + path: 'agent', + }, ], element: dynamicLayout( () => import('@/routes/(main)/(task-workspace)/_layout'), diff --git a/src/spa/router/mobileRouter.test.tsx b/src/spa/router/mobileRouter.test.tsx index 71655b652d..813c0f003c 100644 --- a/src/spa/router/mobileRouter.test.tsx +++ b/src/spa/router/mobileRouter.test.tsx @@ -1,21 +1,23 @@ import { readFile } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import { describe, expect, it } from 'vitest'; describe('mobileRouter task routes', () => { it('registers task list and detail routes under the shared workspace layout', async () => { const source = await readFile( - join(process.cwd(), 'src/spa/router/mobileRouter.config.tsx'), + path.join(process.cwd(), 'src/spa/router/mobileRouter.config.tsx'), 'utf8', ); expect(source).toContain("import('@/routes/(main)/(task-workspace)/_layout')"); expect(source).toContain("import('@/routes/(main)/tasks')"); expect(source).toContain("import('@/routes/(main)/task/[taskId]')"); + expect(source).toContain("import('@/routes/(main)/agent/task/[taskId]')"); expect(source).toContain("path: 'tasks'"); expect(source).toContain("path: 'task'"); expect(source).toContain("path: ':taskId'"); + expect(source).toContain("path: ':aid/task/:taskId'"); expect(source).not.toContain("import('@/routes/(main)/tasks/_layout')"); }); }); diff --git a/src/store/task/selectors/detailSelectors.test.ts b/src/store/task/selectors/detailSelectors.test.ts index 44c3d864ed..a493a29f10 100644 --- a/src/store/task/selectors/detailSelectors.test.ts +++ b/src/store/task/selectors/detailSelectors.test.ts @@ -16,7 +16,7 @@ const mockDetail: TaskDetailData = { identifier: 'T-1', instruction: 'Do something', name: 'Test Task', - parent: { identifier: 'T-0', name: 'Parent' }, + parent: { agentId: 'agt_parent', identifier: 'T-0', name: 'Parent' }, priority: 2, review: { enabled: false }, status: 'running', @@ -115,6 +115,7 @@ describe('taskDetailSelectors', () => { it('should return activeTaskParent', () => { expect(taskDetailSelectors.activeTaskParent(state)?.identifier).toBe('T-0'); + expect(taskDetailSelectors.activeTaskParent(state)?.agentId).toBe('agt_parent'); }); it('should return activeTaskTopicCount', () => { From 2dc812ac9777a309a2ae3867dc8522b4e5111f32 Mon Sep 17 00:00:00 2001 From: Innei Date: Tue, 19 May 2026 01:27:42 +0800 Subject: [PATCH 013/224] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(onboardin?= =?UTF-8?q?g):=20group=20chat=20input=20feature=20switches=20(#14943)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ♻️ refactor(onboarding): group chat input feature switches * ✅ test(onboarding): satisfy chat input prop ordering lint --- src/features/ChatInput/ChatInputProvider.tsx | 10 ++++---- src/features/ChatInput/InputEditor/index.tsx | 24 ++++++++++--------- src/features/ChatInput/StoreUpdater.tsx | 7 +++--- src/features/ChatInput/index.ts | 2 +- src/features/ChatInput/store/initialState.ts | 22 ++++++++++------- src/features/Conversation/ChatInput/index.tsx | 20 ++++++---------- .../Onboarding/Agent/Conversation.test.tsx | 9 ++++--- .../Onboarding/Agent/Conversation.tsx | 10 +++++--- 8 files changed, 55 insertions(+), 49 deletions(-) diff --git a/src/features/ChatInput/ChatInputProvider.tsx b/src/features/ChatInput/ChatInputProvider.tsx index 9ad0ea400c..1f275215f0 100644 --- a/src/features/ChatInput/ChatInputProvider.tsx +++ b/src/features/ChatInput/ChatInputProvider.tsx @@ -3,6 +3,7 @@ import { type ReactNode } from 'react'; import { memo, useRef } from 'react'; import { createStore, Provider } from './store'; +import { DEFAULT_CHAT_INPUT_FEATURE } from './store/initialState'; import { type StoreUpdaterProps } from './StoreUpdater'; import StoreUpdater from './StoreUpdater'; @@ -15,8 +16,7 @@ export const ChatInputProvider = memo( agentId, children, contextWindowMessages, - disableMention, - disableSlash, + feature = DEFAULT_CHAT_INPUT_FEATURE, leftActions, rightActions, mobile, @@ -39,9 +39,8 @@ export const ChatInputProvider = memo( createStore({ allowExpand, contextWindowMessages, - disableMention, - disableSlash, editor, + feature, leftActions, mentionItems, mobile, @@ -58,8 +57,7 @@ export const ChatInputProvider = memo( allowExpand={allowExpand} chatInputEditorRef={chatInputEditorRef} contextWindowMessages={contextWindowMessages} - disableMention={disableMention} - disableSlash={disableSlash} + feature={feature} getMessages={getMessages} leftActions={leftActions} mentionItems={mentionItems} diff --git a/src/features/ChatInput/InputEditor/index.tsx b/src/features/ChatInput/InputEditor/index.tsx index 4a3543dc75..b696410f51 100644 --- a/src/features/ChatInput/InputEditor/index.tsx +++ b/src/features/ChatInput/InputEditor/index.tsx @@ -62,8 +62,9 @@ const InputEditor = memo<{ updateMarkdownContent, expand, slashPlacement, - disableMention, - disableSlash, + isInputCompletionEnabled, + isMentionEnabled, + isSlashEnabled, ] = useChatInputStore((s) => [ s.editor, s.slashMenuRef, @@ -71,8 +72,9 @@ const InputEditor = memo<{ s.updateMarkdownContent, s.expand, s.slashPlacement ?? 'top', - s.disableMention, - s.disableSlash, + s.feature?.inputCompletion ?? true, + s.feature?.mention ?? true, + s.feature?.slash ?? true, ]); const storeApi = useStoreApi(); @@ -132,13 +134,13 @@ const InputEditor = memo<{ const MentionMenuComp = useMemo(() => createMentionMenu(stateRef, categoriesRef), []); - const enableMention = !disableMention && (allMentionItems.length > 0 || enableLocalFileMention); + const enableMention = isMentionEnabled && (allMentionItems.length > 0 || enableLocalFileMention); const heterogeneousName = heterogeneousType ? (HETEROGENEOUS_TYPE_LABELS[heterogeneousType] ?? heterogeneousType) : undefined; // Heterogeneous agents (e.g. Claude Code) don't yet support @-assigning to other agents const showAgentAssignmentHint = - !disableMention && !heterogeneousName && categories.some((category) => category.id === 'agent'); + isMentionEnabled && !heterogeneousName && categories.some((category) => category.id === 'agent'); const { handleUploadFiles } = useUploadFiles({ model, provider }); // Listen to editor's paste event for file uploads @@ -175,7 +177,7 @@ const InputEditor = memo<{ // --- Auto-completion --- const inputCompletionConfig = useUserStore(systemAgentSelectors.inputCompletion); - const isAutoCompleteEnabled = inputCompletionConfig.enabled; + const isAutoCompleteEnabled = isInputCompletionEnabled && inputCompletionConfig.enabled; const getMessagesRef = useRef(storeApi.getState().getMessages); useEffect(() => { @@ -298,10 +300,10 @@ const InputEditor = memo<{ [enableMention, mentionItemsFn, mentionMarkdownWriter, mentionOnSelect, MentionMenuComp], ); - const slashOption = useMemo( - () => (disableSlash ? undefined : { items: slashItems }), - [disableSlash, slashItems], - ); + const slashOption = useMemo(() => (isSlashEnabled ? { items: slashItems } : undefined), [ + isSlashEnabled, + slashItems, + ]); const richRenderProps = useMemo(() => { const basePlugins = !enableRichRender diff --git a/src/features/ChatInput/StoreUpdater.tsx b/src/features/ChatInput/StoreUpdater.tsx index 02e12cd90c..b847a19f94 100644 --- a/src/features/ChatInput/StoreUpdater.tsx +++ b/src/features/ChatInput/StoreUpdater.tsx @@ -8,6 +8,7 @@ import { type ChatInputEditor } from './hooks/useChatInputEditor'; import { useChatInputEditor } from './hooks/useChatInputEditor'; import { type PublicState } from './store'; import { useStoreApi } from './store'; +import { DEFAULT_CHAT_INPUT_FEATURE } from './store/initialState'; export interface StoreUpdaterProps extends Partial { chatInputEditorRef?: ForwardedRef; @@ -18,8 +19,7 @@ const StoreUpdater = memo( agentId, chatInputEditorRef, contextWindowMessages, - disableMention, - disableSlash, + feature = DEFAULT_CHAT_INPUT_FEATURE, mobile, sendButtonProps, leftActions, @@ -43,8 +43,7 @@ const StoreUpdater = memo( useStoreUpdater('leftActions', leftActions!); useStoreUpdater('rightActions', rightActions!); useStoreUpdater('allowExpand', allowExpand); - useStoreUpdater('disableMention', disableMention); - useStoreUpdater('disableSlash', disableSlash); + useStoreUpdater('feature', feature); useStoreUpdater('slashPlacement', slashPlacement); useStoreUpdater('getMessages', getMessages); diff --git a/src/features/ChatInput/index.ts b/src/features/ChatInput/index.ts index 9b257f8870..7d8c58e2a4 100644 --- a/src/features/ChatInput/index.ts +++ b/src/features/ChatInput/index.ts @@ -4,4 +4,4 @@ export { default as DesktopChatInput } from './Desktop'; export type { ChatInputEditor } from './hooks/useChatInputEditor'; export { useChatInputEditor } from './hooks/useChatInputEditor'; export { default as MobileChatInput } from './Mobile'; -export type { SendButtonHandler } from './store/initialState'; +export type { ChatInputFeature, SendButtonHandler } from './store/initialState'; diff --git a/src/features/ChatInput/store/initialState.ts b/src/features/ChatInput/store/initialState.ts index 56108fc3b0..de9a1879c1 100644 --- a/src/features/ChatInput/store/initialState.ts +++ b/src/features/ChatInput/store/initialState.ts @@ -32,19 +32,24 @@ export interface ContextWindowMessage { content: string; } +export interface ChatInputFeature { + inputCompletion?: boolean; + mention?: boolean; + slash?: boolean; +} + +export const DEFAULT_CHAT_INPUT_FEATURE = { + inputCompletion: true, + mention: true, + slash: true, +} as const satisfies Required; + export interface PublicState { agentId?: string; allowExpand?: boolean; contextWindowMessages?: ContextWindowMessage[]; - /** - * Disable @ mention trigger (no menu, no agent-assignment hint in placeholder) - */ - disableMention?: boolean; - /** - * Disable / slash command trigger - */ - disableSlash?: boolean; expand?: boolean; + feature?: ChatInputFeature; getMessages?: () => OpenAIChatMessage[]; leftActions: ActionKeys[]; mentionItems?: SlashOptions['items']; @@ -72,6 +77,7 @@ export interface State extends PublicState { export const initialState: State = { allowExpand: true, expand: false, + feature: DEFAULT_CHAT_INPUT_FEATURE, isContentEmpty: false, leftActions: [], markdownContent: '', diff --git a/src/features/Conversation/ChatInput/index.tsx b/src/features/Conversation/ChatInput/index.tsx index 0d487da3f7..642fb11b1e 100644 --- a/src/features/Conversation/ChatInput/index.tsx +++ b/src/features/Conversation/ChatInput/index.tsx @@ -8,7 +8,7 @@ import { type ReactNode } from 'react'; import { memo, useCallback, useEffect, useMemo, useRef } from 'react'; import { useTranslation } from 'react-i18next'; -import { type ActionKeys } from '@/features/ChatInput'; +import type { ActionKeys, ChatInputFeature } from '@/features/ChatInput'; import { ChatInputProvider, DesktopChatInput } from '@/features/ChatInput'; import { type SendButtonHandler, @@ -55,23 +55,19 @@ export interface ChatInputProps { * follow-up design). When true, placeholder stays in default variant. */ disableFollowUpVariant?: boolean; - /** - * Disable the @ mention trigger and its placeholder hint - */ - disableMention?: boolean; /** * Disable enqueuing follow-up messages while the agent is streaming. * Hides the QueueTray and gates handleSend so Enter does not enqueue. */ disableQueue?: boolean; - /** - * Disable the / slash command trigger - */ - disableSlash?: boolean; /** * Extra action items to append to the ActionBar */ extraActionItems?: ChatInputActionsProps['items']; + /** + * Chat input capability switches. Omitted capabilities keep the default enabled state. + */ + feature?: ChatInputFeature; /** * Swap the action bar and send area for skeleton placeholders while * the underlying agent/session config is still hydrating. The editor @@ -136,9 +132,8 @@ const ChatInput = memo( actionBarStyle, allowExpand, disableFollowUpVariant, - disableMention, disableQueue, - disableSlash, + feature, leftActions = [], leftContent, rightActions = [], @@ -340,8 +335,7 @@ const ChatInput = memo( agentId={agentId} allowExpand={allowExpand} contextWindowMessages={contextWindowMessages} - disableMention={disableMention} - disableSlash={disableSlash} + feature={feature} getMessages={getMessages} leftActions={leftActions} mentionItems={mentionItems} diff --git a/src/features/Onboarding/Agent/Conversation.test.tsx b/src/features/Onboarding/Agent/Conversation.test.tsx index 1a34e18927..b657996361 100644 --- a/src/features/Onboarding/Agent/Conversation.test.tsx +++ b/src/features/Onboarding/Agent/Conversation.test.tsx @@ -122,7 +122,7 @@ describe('AgentOnboardingConversation', () => { ); }); - it('disables / @ triggers, follow-up placeholder, and message queueing', () => { + it('disables input completion, / @ triggers, follow-up placeholder, and message queueing', () => { mockState.displayMessages = [{ id: 'assistant-1', role: 'assistant' }]; render(); @@ -130,9 +130,12 @@ describe('AgentOnboardingConversation', () => { expect(chatInputSpy).toHaveBeenCalledWith( expect.objectContaining({ disableFollowUpVariant: true, - disableMention: true, disableQueue: true, - disableSlash: true, + feature: expect.objectContaining({ + inputCompletion: false, + mention: false, + slash: false, + }), }), ); }); diff --git a/src/features/Onboarding/Agent/Conversation.tsx b/src/features/Onboarding/Agent/Conversation.tsx index b25f6b526b..146478f3a3 100644 --- a/src/features/Onboarding/Agent/Conversation.tsx +++ b/src/features/Onboarding/Agent/Conversation.tsx @@ -8,7 +8,7 @@ import { Flexbox } from '@lobehub/ui'; import { memo, useEffect, useMemo, useRef, useState } from 'react'; import { flushSync } from 'react-dom'; -import type { ActionKeys } from '@/features/ChatInput'; +import type { ActionKeys, ChatInputFeature } from '@/features/ChatInput'; import { ChatInput, ChatList, @@ -43,6 +43,11 @@ interface AgentOnboardingConversationProps { const chatInputLeftActions: ActionKeys[] = isDev ? ['model'] : []; const chatInputRightActions: ActionKeys[] = []; +const chatInputFeature = { + inputCompletion: false, + mention: false, + slash: false, +} satisfies ChatInputFeature; const AgentOnboardingConversation = memo( ({ @@ -212,10 +217,9 @@ const AgentOnboardingConversation = memo( )} Date: Tue, 19 May 2026 01:46:53 +0800 Subject: [PATCH 014/224] =?UTF-8?q?=F0=9F=90=9B=20fix(sidebar):=20restore?= =?UTF-8?q?=20home=20nav=20for=20task=20workspace=20(#14945)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/en-US/topic.json | 1 + locales/zh-CN/topic.json | 1 + .../AgentTasks/TaskWorkspaceLayout.test.tsx | 52 +++++++++++++++++++ .../AgentTasks/TaskWorkspaceLayout.tsx | 7 ++- src/features/NavPanel/index.tsx | 6 +++ src/locales/default/topic.ts | 1 + .../agent/_layout/Sidebar/Topic/index.tsx | 7 ++- 7 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 src/features/AgentTasks/TaskWorkspaceLayout.test.tsx diff --git a/locales/en-US/topic.json b/locales/en-US/topic.json index 2fca67a365..58ae4586ed 100644 --- a/locales/en-US/topic.json +++ b/locales/en-US/topic.json @@ -56,6 +56,7 @@ "renameModal.title": "Rename Topic", "searchPlaceholder": "Search Topics...", "searchResultEmpty": "No search results found.", + "sidebar.title": "Topics", "taskManager.agent": "Task Agent", "taskManager.welcome": "Ask me about your tasks", "temp": "Temporary", diff --git a/locales/zh-CN/topic.json b/locales/zh-CN/topic.json index ee6cd16884..8afb5c7c68 100644 --- a/locales/zh-CN/topic.json +++ b/locales/zh-CN/topic.json @@ -56,6 +56,7 @@ "renameModal.title": "重命名话题", "searchPlaceholder": "搜索话题…", "searchResultEmpty": "暂无搜索结果", + "sidebar.title": "话题", "taskManager.agent": "任务助手", "taskManager.welcome": "问我关于你的任务", "temp": "临时", diff --git a/src/features/AgentTasks/TaskWorkspaceLayout.test.tsx b/src/features/AgentTasks/TaskWorkspaceLayout.test.tsx new file mode 100644 index 0000000000..7d7ce4fbfd --- /dev/null +++ b/src/features/AgentTasks/TaskWorkspaceLayout.test.tsx @@ -0,0 +1,52 @@ +/** + * @vitest-environment happy-dom + */ +import { render, screen } from '@testing-library/react'; +import type { ReactNode } from 'react'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { resetNavPanel } from '@/features/NavPanel'; + +import TaskWorkspaceLayout from './TaskWorkspaceLayout'; + +vi.mock('@lobehub/ui', () => ({ + Flexbox: ({ children, ...props }: { children?: ReactNode; [key: string]: unknown }) => ( +
{children}
+ ), +})); + +vi.mock('react-router-dom', async () => { + // eslint-disable-next-line @typescript-eslint/consistent-type-imports + const actual = (await vi.importActual('react-router-dom')) as typeof import('react-router-dom'); + + return { + ...actual, + Outlet: () =>
outlet
, + }; +}); + +vi.mock('@/features/AgentTaskManager', () => ({ + default: () =>
, +})); + +vi.mock('@/features/NavPanel', () => ({ + resetNavPanel: vi.fn(), +})); + +vi.mock('@/hooks/useIsMobile', () => ({ + useIsMobile: () => false, +})); + +describe('TaskWorkspaceLayout', () => { + beforeEach(() => { + vi.mocked(resetNavPanel).mockClear(); + }); + + it('resets the nav panel to the home sidebar fallback', () => { + render(); + + expect(resetNavPanel).toHaveBeenCalledTimes(1); + expect(screen.getByTestId('task-workspace-outlet')).toBeInTheDocument(); + expect(screen.getByTestId('task-agent-manager')).toBeInTheDocument(); + }); +}); diff --git a/src/features/AgentTasks/TaskWorkspaceLayout.tsx b/src/features/AgentTasks/TaskWorkspaceLayout.tsx index b53b2b83b6..d37dfa3945 100644 --- a/src/features/AgentTasks/TaskWorkspaceLayout.tsx +++ b/src/features/AgentTasks/TaskWorkspaceLayout.tsx @@ -1,15 +1,20 @@ 'use client'; import { Flexbox } from '@lobehub/ui'; -import { memo } from 'react'; +import { memo, useLayoutEffect } from 'react'; import { Outlet } from 'react-router-dom'; import AgentTaskManager from '@/features/AgentTaskManager'; +import { resetNavPanel } from '@/features/NavPanel'; import { useIsMobile } from '@/hooks/useIsMobile'; const TaskWorkspaceLayout = memo(() => { const isMobile = useIsMobile(); + useLayoutEffect(() => { + resetNavPanel(); + }, []); + return ( diff --git a/src/features/NavPanel/index.tsx b/src/features/NavPanel/index.tsx index dad6ccaf1c..03db988a96 100644 --- a/src/features/NavPanel/index.tsx +++ b/src/features/NavPanel/index.tsx @@ -28,6 +28,12 @@ const setNavPanelSnapshot = (snapshot: NavPanelSnapshot) => { listeners.forEach((listener) => listener()); }; +export const resetNavPanel = () => { + if (!currentSnapshot) return; + + setNavPanelSnapshot(null); +}; + const FALLBACK_NAV_KEY = 'home'; const getActiveNavKey = () => currentSnapshot?.key ?? FALLBACK_NAV_KEY; diff --git a/src/locales/default/topic.ts b/src/locales/default/topic.ts index e52849d07e..7878fdc1d2 100644 --- a/src/locales/default/topic.ts +++ b/src/locales/default/topic.ts @@ -59,6 +59,7 @@ export default { 'renameModal.title': 'Rename Topic', 'searchPlaceholder': 'Search Topics...', 'searchResultEmpty': 'No search results found.', + 'sidebar.title': 'Topics', 'taskManager.agent': 'Task Agent', 'taskManager.welcome': 'Ask me about your tasks', 'temp': 'Temporary', diff --git a/src/routes/(main)/agent/_layout/Sidebar/Topic/index.tsx b/src/routes/(main)/agent/_layout/Sidebar/Topic/index.tsx index 97575af15a..269a32b5a8 100644 --- a/src/routes/(main)/agent/_layout/Sidebar/Topic/index.tsx +++ b/src/routes/(main)/agent/_layout/Sidebar/Topic/index.tsx @@ -42,8 +42,13 @@ const Topic = memo(({ itemKey }) => { title={ - {`${t('title')} ${topicCount > 0 ? topicCount : ''}`} + {t('sidebar.title')} + {topicCount > 0 && ( + + {topicCount} + + )} {isRevalidating && } } From 62187d55c5c222f1904af5db71bc0225eede4fa2 Mon Sep 17 00:00:00 2001 From: Arvin Xu Date: Tue, 19 May 2026 10:11:51 +0800 Subject: [PATCH 015/224] =?UTF-8?q?=F0=9F=90=9B=20fix(portal):=20make=20ma?= =?UTF-8?q?rkdown=20preview=20scrollable=20in=20LocalFile=20portal=20(#149?= =?UTF-8?q?19)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/Portal/LocalFile/Body.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/Portal/LocalFile/Body.tsx b/src/features/Portal/LocalFile/Body.tsx index dd569c9ae4..5eacec4936 100644 --- a/src/features/Portal/LocalFile/Body.tsx +++ b/src/features/Portal/LocalFile/Body.tsx @@ -235,7 +235,7 @@ const TextPreviewPane = memo( {truncatedLabel} )} - +
{isMarkdown && mode === 'render' ? ( <> @@ -249,7 +249,7 @@ const TextPreviewPane = memo( {content} )} - +
); }, From 8ddd8e2cff875b7f93183f36a02a68a660f71a39 Mon Sep 17 00:00:00 2001 From: LobeHub Bot Date: Tue, 19 May 2026 10:13:35 +0800 Subject: [PATCH 016/224] =?UTF-8?q?=F0=9F=8C=90=20chore:=20translate=20non?= =?UTF-8?q?-English=20comments=20to=20English=20in=20tests-utils=20and=20h?= =?UTF-8?q?eterogeneous-agents=20(#14914)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/adapters/claudeCode.ts | 2 +- tests/setup.ts | 2 +- tests/utils.tsx | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/heterogeneous-agents/src/adapters/claudeCode.ts b/packages/heterogeneous-agents/src/adapters/claudeCode.ts index 33a4867d8a..4056f05ca7 100644 --- a/packages/heterogeneous-agents/src/adapters/claudeCode.ts +++ b/packages/heterogeneous-agents/src/adapters/claudeCode.ts @@ -95,7 +95,7 @@ const TASK_CREATE_RESULT_PATTERN = /^Task #(\d+) created successfully/; const TASK_UPDATE_RESULT_PATTERN = /^Updated task #\d+/; /** - * One line of `TaskList`'s plain-text output: `#1 [in_progress] 读 hosts`. + * One line of `TaskList`'s plain-text output: `#1 [in_progress] read hosts`. * Used as the resume reconciliation path — when this adapter joins a CC * session mid-stream and missed earlier Create / Update events, parsing * TaskList rebuilds id / subject / status. `activeForm` and `description` diff --git a/tests/setup.ts b/tests/setup.ts index 194ab20fa0..731bb99108 100644 --- a/tests/setup.ts +++ b/tests/setup.ts @@ -72,5 +72,5 @@ await i18n.init({ }, }); -// 将 React 设置为全局变量,这样就不需要在每个测试文件中导入它了 +// Set React as a global variable so it doesn't need to be imported in each test file (globalThis as any).React = React; diff --git a/tests/utils.tsx b/tests/utils.tsx index 72d8823881..d3b9ad2eaf 100644 --- a/tests/utils.tsx +++ b/tests/utils.tsx @@ -1,7 +1,7 @@ import { type PropsWithChildren } from 'react'; import { SWRConfig } from 'swr'; -// 全局的 SWR 配置 +// Global SWR configuration const swrConfig = { provider: () => new Map(), }; @@ -11,11 +11,11 @@ export const withSWR = ({ children }: PropsWithChildren) => ( ); interface TestServiceOptions { - /** 是否检查 async */ + /** Whether to check async */ checkAsync?: boolean; - /** 自定义的额外检查 */ + /** Custom additional checks */ extraChecks?: (method: string, func: () => any) => void; - /** 是否跳过某些方法 */ + /** Whether to skip certain methods */ skipMethods?: string[]; } @@ -34,20 +34,20 @@ export const testService = (ServiceClass: new () => any, options: TestServiceOpt methods.forEach((method) => { const func = service[method]; - // 检查是否为函数 + // Check if it's a function expect(typeof func).toBe('function'); const funcString = func.toString(); - // 验证是否是箭头函数 + // Verify if it's an arrow function expect(funcString).toContain('=>'); - // 可选的 async 检查 + // Optional async check if (checkAsync) { expect(funcString).toMatch(/^async.*=>/); } - // 运行额外的自定义检查 + // Run additional custom checks if (extraChecks) { extraChecks(method, func); } From 500a02bd88ba9488cebbdf6bca2d454fae8a8dca Mon Sep 17 00:00:00 2001 From: Arvin Xu Date: Tue, 19 May 2026 11:42:01 +0800 Subject: [PATCH 017/224] =?UTF-8?q?=F0=9F=94=92=20chore:=20remove=20compro?= =?UTF-8?q?mised=20actions-cool/issues-helper@v3=20(#14956)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: remove compromised actions-cool/issues-helper@v3 * fix: remove actions-cool/issues-helper * fix: pin actions-cool/issues-helper to safe commit SHA in sync.yml --- .github/workflows/issue-auto-comments.yml | 15 ++---- .github/workflows/issue-close-require.yml | 63 ----------------------- .github/workflows/sync.yml | 4 +- 3 files changed, 6 insertions(+), 76 deletions(-) delete mode 100644 .github/workflows/issue-close-require.yml diff --git a/.github/workflows/issue-auto-comments.yml b/.github/workflows/issue-auto-comments.yml index c6cac85dad..a1bed8f204 100644 --- a/.github/workflows/issue-auto-comments.yml +++ b/.github/workflows/issue-auto-comments.yml @@ -16,14 +16,14 @@ permissions: jobs: run: permissions: - issues: write # for actions-cool/issues-helper to update issues - pull-requests: write # for actions-cool/issues-helper to update PRs + issues: write + pull-requests: write runs-on: ubuntu-latest steps: - name: Auto Comment on Issues Closed uses: wow-actions/auto-comment@v1 with: - GITHUB_TOKEN: ${{ secrets.GH_TOKEN}} + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} issuesClosed: | ✅ @{{ author }} @@ -51,11 +51,4 @@ jobs: The growth of project is inseparable from user feedback and contribution, thanks for your contribution! If you are interesting with the lobehub developer community, please join our [discord](https://discord.com/invite/AYFPHvv2jT) and then dm @arvinxx or @canisminor1990. They will invite you to our private developer channel. We are talking about the lobe-chat development or sharing ai newsletter around the world. emoji: 'hooray' pr-emoji: '+1, heart' - - name: Remove inactive - if: github.event.issue.state == 'open' && github.actor == github.event.issue.user.login - uses: actions-cool/issues-helper@v3 - with: - actions: 'remove-labels' - token: ${{ secrets.GH_TOKEN }} - issue-number: ${{ github.event.issue.number }} - labels: 'Inactive' + diff --git a/.github/workflows/issue-close-require.yml b/.github/workflows/issue-close-require.yml deleted file mode 100644 index a3bbce8f16..0000000000 --- a/.github/workflows/issue-close-require.yml +++ /dev/null @@ -1,63 +0,0 @@ -name: Issue Close Require - -on: - schedule: - - cron: '0 0 * * *' - -permissions: - contents: read - -jobs: - issue-check-inactive: - permissions: - issues: write # for actions-cool/issues-helper to update issues - pull-requests: write # for actions-cool/issues-helper to update PRs - runs-on: ubuntu-latest - steps: - - name: check-inactive - uses: actions-cool/issues-helper@v3 - with: - actions: 'check-inactive' - token: ${{ secrets.GH_TOKEN }} - inactive-label: 'Inactive' - inactive-day: 60 - - issue-close-require: - permissions: - issues: write # for actions-cool/issues-helper to update issues - pull-requests: write # for actions-cool/issues-helper to update PRs - runs-on: ubuntu-latest - steps: - - name: need reproduce - uses: actions-cool/issues-helper@v3 - with: - actions: 'close-issues' - token: ${{ secrets.GH_TOKEN }} - labels: '✅ Fixed' - inactive-day: 3 - body: | - 👋 @{{ author }} -
- Since the issue was labeled with `✅ Fixed`, but no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply. - - name: need reproduce - uses: actions-cool/issues-helper@v3 - with: - actions: 'close-issues' - token: ${{ secrets.GH_TOKEN }} - labels: '🤔 Need Reproduce' - inactive-day: 3 - body: | - 👋 @{{ author }} -
- Since the issue was labeled with `🤔 Need Reproduce`, but no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply. - - name: need reproduce - uses: actions-cool/issues-helper@v3 - with: - actions: 'close-issues' - token: ${{ secrets.GH_TOKEN }} - labels: "🙅🏻‍♀️ WON'T DO" - inactive-day: 3 - body: | - 👋 @{{ github.event.issue.user.login }} -
- Since the issue was labeled with `🙅🏻‍♀️ WON'T DO`, and no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply. diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index ae81e8affc..f4ef6930b8 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v6 - name: Clean issue notice - uses: actions-cool/issues-helper@v3 + uses: actions-cool/issues-helper@e361abf610221f09495ad510cb1e69328d839e1c # v3.7.6 with: actions: 'close-issues' labels: '🚨 Sync Fail' @@ -37,7 +37,7 @@ jobs: - name: Sync check if: failure() - uses: actions-cool/issues-helper@v3 + uses: actions-cool/issues-helper@e361abf610221f09495ad510cb1e69328d839e1c # v3.7.6 with: actions: 'create-issue' title: '🚨 同步失败 | Sync Fail' From fd0d208152fd3f8ce1a30cb49353a1d348d9e79c Mon Sep 17 00:00:00 2001 From: YuTengjing Date: Tue, 19 May 2026 11:44:27 +0800 Subject: [PATCH 018/224] =?UTF-8?q?=F0=9F=92=84=20style(subscription):=20u?= =?UTF-8?q?pdate=20budget=20recovery=20copy=20(#14875)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/ar/chat.json | 8 +++++- locales/ar/models.json | 28 +++++++++++++-------- locales/ar/plugin.json | 8 ++++++ locales/ar/providers.json | 1 + locales/ar/subscription.json | 19 ++++++++++++++ locales/bg-BG/chat.json | 8 +++++- locales/bg-BG/models.json | 32 +++++++++++++++--------- locales/bg-BG/plugin.json | 8 ++++++ locales/bg-BG/providers.json | 1 + locales/bg-BG/subscription.json | 19 ++++++++++++++ locales/de-DE/chat.json | 8 +++++- locales/de-DE/models.json | 26 ++++++++++++------- locales/de-DE/plugin.json | 8 ++++++ locales/de-DE/providers.json | 1 + locales/de-DE/subscription.json | 19 ++++++++++++++ locales/en-US/chat.json | 5 +++- locales/en-US/models.json | 32 +++++++++++++++--------- locales/en-US/providers.json | 1 + locales/en-US/subscription.json | 20 +++++++++++++-- locales/es-ES/chat.json | 8 +++++- locales/es-ES/models.json | 28 +++++++++++++-------- locales/es-ES/plugin.json | 8 ++++++ locales/es-ES/providers.json | 1 + locales/es-ES/subscription.json | 19 ++++++++++++++ locales/fa-IR/chat.json | 8 +++++- locales/fa-IR/models.json | 28 +++++++++++++-------- locales/fa-IR/plugin.json | 8 ++++++ locales/fa-IR/providers.json | 1 + locales/fa-IR/subscription.json | 19 ++++++++++++++ locales/fr-FR/chat.json | 8 +++++- locales/fr-FR/models.json | 24 ++++++++++++------ locales/fr-FR/plugin.json | 8 ++++++ locales/fr-FR/providers.json | 1 + locales/fr-FR/subscription.json | 19 ++++++++++++++ locales/it-IT/chat.json | 8 +++++- locales/it-IT/models.json | 28 +++++++++++++-------- locales/it-IT/plugin.json | 8 ++++++ locales/it-IT/providers.json | 1 + locales/it-IT/subscription.json | 19 ++++++++++++++ locales/ja-JP/chat.json | 8 +++++- locales/ja-JP/models.json | 32 +++++++++++++++--------- locales/ja-JP/plugin.json | 8 ++++++ locales/ja-JP/providers.json | 1 + locales/ja-JP/subscription.json | 19 ++++++++++++++ locales/ko-KR/chat.json | 8 +++++- locales/ko-KR/models.json | 30 ++++++++++++++-------- locales/ko-KR/plugin.json | 8 ++++++ locales/ko-KR/providers.json | 1 + locales/ko-KR/subscription.json | 19 ++++++++++++++ locales/nl-NL/chat.json | 8 +++++- locales/nl-NL/models.json | 26 ++++++++++++------- locales/nl-NL/plugin.json | 8 ++++++ locales/nl-NL/providers.json | 1 + locales/nl-NL/subscription.json | 19 ++++++++++++++ locales/pl-PL/chat.json | 8 +++++- locales/pl-PL/models.json | 32 +++++++++++++++--------- locales/pl-PL/plugin.json | 8 ++++++ locales/pl-PL/providers.json | 1 + locales/pl-PL/subscription.json | 19 ++++++++++++++ locales/pt-BR/chat.json | 8 +++++- locales/pt-BR/models.json | 24 ++++++++++++------ locales/pt-BR/plugin.json | 8 ++++++ locales/pt-BR/providers.json | 1 + locales/pt-BR/subscription.json | 19 ++++++++++++++ locales/ru-RU/chat.json | 8 +++++- locales/ru-RU/models.json | 30 ++++++++++++++-------- locales/ru-RU/plugin.json | 8 ++++++ locales/ru-RU/providers.json | 1 + locales/ru-RU/subscription.json | 19 ++++++++++++++ locales/tr-TR/chat.json | 8 +++++- locales/tr-TR/models.json | 30 ++++++++++++++-------- locales/tr-TR/plugin.json | 8 ++++++ locales/tr-TR/providers.json | 1 + locales/tr-TR/subscription.json | 19 ++++++++++++++ locales/vi-VN/chat.json | 8 +++++- locales/vi-VN/models.json | 28 +++++++++++++-------- locales/vi-VN/plugin.json | 8 ++++++ locales/vi-VN/providers.json | 1 + locales/vi-VN/subscription.json | 19 ++++++++++++++ locales/zh-CN/chat.json | 5 +++- locales/zh-CN/models.json | 30 ++++++++++++++-------- locales/zh-CN/providers.json | 1 + locales/zh-CN/subscription.json | 20 +++++++++++++-- locales/zh-TW/chat.json | 8 +++++- locales/zh-TW/models.json | 30 ++++++++++++++-------- locales/zh-TW/plugin.json | 8 ++++++ locales/zh-TW/providers.json | 1 + locales/zh-TW/subscription.json | 19 ++++++++++++++ packages/model-runtime/src/types/type.ts | 3 ++- src/locales/default/subscription.ts | 23 +++++++++++++++-- 90 files changed, 960 insertions(+), 212 deletions(-) diff --git a/locales/ar/chat.json b/locales/ar/chat.json index 449208cff9..eb7d063717 100644 --- a/locales/ar/chat.json +++ b/locales/ar/chat.json @@ -196,6 +196,9 @@ "groupWizard.searchTemplates": "البحث في القوالب...", "groupWizard.title": "إنشاء مجموعة", "groupWizard.useTemplate": "استخدام قالب", + "heteroAgent.cloudNotConfigured.action": "تكوين", + "heteroAgent.cloudNotConfigured.desc": "قم بتكوين رمز Claude Code في ملف تعريف الوكيل الخاص بك لبدء إرسال الرسائل.", + "heteroAgent.cloudNotConfigured.title": "مطلوب بيانات اعتماد السحابة", "heteroAgent.cloudRepo.multiSelected": "{{count}} مستودعات محددة", "heteroAgent.cloudRepo.noRepos": "لم يتم تكوين أي مستودعات. أضفها في إعدادات الوكيل.", "heteroAgent.cloudRepo.notSet": "لم يتم تحديد أي مستودع", @@ -467,6 +470,10 @@ "sharePage.error.unauthorized.subtitle": "يرجى تسجيل الدخول لعرض هذا الموضوع المشترك.", "sharePage.error.unauthorized.title": "مطلوب تسجيل الدخول", "sharePageDisclaimer": "تمت مشاركة هذا المحتوى من قبل مستخدم ولا يعبر عن آراء LobeHub. لا تتحمل LobeHub أي مسؤولية عن أي نتائج ناتجة عن هذا المحتوى المشترك.", + "signalCallbacks.collapse": "إخفاء التفاصيل", + "signalCallbacks.empty": "لا توجد رسائل استرجاع", + "signalCallbacks.expand": "عرض التفاصيل", + "signalCallbacks.title": "{{tool}} · {{count}} تحديثات الاسترجاع", "stt.action": "إدخال صوتي", "stt.loading": "جارٍ التعرف...", "stt.prettifying": "جارٍ التجميل...", @@ -689,7 +696,6 @@ "thread.closeSubagentThread": "طي محادثة الوكيل الفرعي", "thread.divider": "موضوع فرعي", "thread.openSubagentThread": "عرض محادثة الوكيل الفرعي كاملة", - "thread.subagentBadge": "وكيل فرعي", "thread.subagentReadOnlyHint": "المحادثات مع الوكيل الفرعي للقراءة فقط — يتم التنفيذ بواسطة الوكيل الرئيسي.", "thread.threadMessageCount": "{{messageCount}} رسالة", "thread.title": "موضوع فرعي", diff --git a/locales/ar/models.json b/locales/ar/models.json index 7078aae3f6..6069c3eff4 100644 --- a/locales/ar/models.json +++ b/locales/ar/models.json @@ -106,6 +106,7 @@ "MiniMax-Hailuo-2.3.description": "نموذج جديد لإنشاء الفيديو مع تحسينات شاملة في حركة الجسم، والواقعية الفيزيائية، واتباع التعليمات.", "MiniMax-M1.description": "نموذج استدلال داخلي جديد بسلسلة تفكير تصل إلى 80K ومدخلات حتى 1M، يقدم أداءً مماثلاً لأفضل النماذج العالمية.", "MiniMax-M2-Stable.description": "مصمم لتدفقات العمل البرمجية والوكلاء بكفاءة عالية، مع قدرة تزامن أعلى للاستخدام التجاري.", + "MiniMax-M2.1-Lightning.description": "قدرات برمجة متعددة اللغات قوية مع استنتاج أسرع وأكثر كفاءة.", "MiniMax-M2.1-highspeed.description": "قدرات برمجة متعددة اللغات قوية، تجربة برمجة مطورة بشكل شامل. أسرع وأكثر كفاءة.", "MiniMax-M2.1.description": "MiniMax-M2.1 هو نموذج مفتوح المصدر رائد من MiniMax، يركز على حل المهام الواقعية المعقدة. يتميز بقدرات برمجة متعددة اللغات والقدرة على أداء المهام المعقدة كوكلاء ذكي.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: نفس أداء M2.5 مع استدلال أسرع.", @@ -314,13 +315,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku هو أسرع وأصغر نموذج من Anthropic، مصمم لتقديم استجابات شبه فورية بأداء سريع ودقيق.", "claude-3-opus-20240229.description": "Claude 3 Opus هو أقوى نموذج من Anthropic للمهام المعقدة، يتميز بالأداء العالي، الذكاء، الطلاقة، والفهم.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet يوازن بين الذكاء والسرعة لتلبية احتياجات المؤسسات، ويوفر فائدة عالية بتكلفة أقل ونشر موثوق على نطاق واسع.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 هو النموذج الأسرع والأذكى من Anthropic، يتميز بسرعة البرق وقدرات استدلال موسعة.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 هو أسرع وأذكى نموذج هايكو من Anthropic، يتميز بسرعة البرق وتفكير ممتد.", "claude-haiku-4-5.description": "Claude Haiku 4.5 من Anthropic — نموذج Haiku من الجيل التالي مع تحسينات في التفكير والرؤية.", "claude-haiku-4.5.description": "Claude Haiku 4.5 هو نموذج Haiku الأسرع والأذكى من Anthropic، يتميز بسرعة البرق وقدرات استدلال موسعة.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking هو إصدار متقدم يمكنه عرض عملية تفكيره.", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1 هو أحدث وأقوى نموذج من Anthropic للمهام المعقدة للغاية، يتميز بالأداء العالي، الذكاء، الطلاقة، والفهم.", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1 هو أحدث وأقوى نموذج من Anthropic للمهام المعقدة للغاية، يتميز بالأداء والذكاء والطلاقة والفهم.", "claude-opus-4-1.description": "Claude Opus 4.1 من Anthropic — نموذج تفكير متميز مع قدرات تحليل عميقة.", - "claude-opus-4-20250514.description": "Claude Opus 4 هو النموذج الأكثر قوة من Anthropic للمهام المعقدة للغاية، يتميز بالأداء العالي، الذكاء، الطلاقة، والاستيعاب.", + "claude-opus-4-20250514.description": "Claude Opus 4 هو أقوى نموذج من Anthropic للمهام المعقدة للغاية، يتميز بالأداء والذكاء والطلاقة والفهم.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 هو النموذج الرائد من Anthropic، يجمع بين الذكاء الاستثنائي والأداء القابل للتوسع، مثالي للمهام المعقدة التي تتطلب استجابات عالية الجودة وتفكير متقدم.", "claude-opus-4-5.description": "Claude Opus 4.5 من Anthropic — نموذج رئيسي مع تفكير وبرمجة من الدرجة الأولى.", "claude-opus-4-6.description": "Claude Opus 4.6 من Anthropic — نافذة سياق 1M نموذج رئيسي مع تفكير متقدم.", @@ -329,7 +330,7 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 هو النموذج الأكثر ذكاءً من Anthropic لبناء الوكلاء والبرمجة.", "claude-opus-4.6.description": "Claude Opus 4.6 هو النموذج الأكثر ذكاءً من Anthropic لبناء الوكلاء والبرمجة.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking يمكنه تقديم استجابات شبه فورية أو تفكير متسلسل مرئي.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 يمكنه تقديم استجابات شبه فورية أو تفكير ممتد خطوة بخطوة مع عرض العملية.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 هو النموذج الأكثر ذكاءً من Anthropic حتى الآن، يقدم استجابات شبه فورية أو تفكير ممتد خطوة بخطوة مع تحكم دقيق لمستخدمي API.", "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 هو النموذج الأكثر ذكاءً من Anthropic حتى الآن.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 من Anthropic — نموذج Sonnet محسّن مع أداء برمجي معزز.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 من Anthropic — أحدث نموذج Sonnet مع برمجة واستخدام أدوات متفوقة.", @@ -403,7 +404,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) هو نموذج مبتكر يوفر فهمًا عميقًا للغة وتفاعلًا ذكيًا.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 هو نموذج تفكير من الجيل التالي يتمتع بقدرات أقوى في التفكير المعقد وسلسلة التفكير لمهام التحليل العميق.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 هو نموذج استدلال من الجيل التالي يتميز بقدرات استدلال معقدة وسلسلة التفكير.", - "deepseek-chat.description": "نموذج مفتوح المصدر جديد يجمع بين القدرات العامة والبرمجية. يحافظ على حوار النموذج العام وقوة البرمجة للنموذج البرمجي، مع تحسين توافق التفضيلات. كما يحسن DeepSeek-V2.5 الكتابة واتباع التعليمات.", + "deepseek-chat.description": "اسم مستعار متوافق لوضع عدم التفكير في DeepSeek V4 Flash. مقرر إيقافه — استخدم DeepSeek V4 Flash بدلاً منه.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B هو نموذج لغة برمجية تم تدريبه على 2 تريليون رمز (87٪ كود، 13٪ نص صيني/إنجليزي). يقدم نافذة سياق 16K ومهام الإكمال في المنتصف، ويوفر إكمال كود على مستوى المشاريع وملء مقاطع الكود.", "deepseek-coder-v2.description": "DeepSeek Coder V2 هو نموذج كود MoE مفتوح المصدر يتميز بأداء قوي في مهام البرمجة، ويضاهي GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 هو نموذج كود MoE مفتوح المصدر يتميز بأداء قوي في مهام البرمجة، ويضاهي GPT-4 Turbo.", @@ -425,7 +426,7 @@ "deepseek-r1-fast-online.description": "الإصدار الكامل السريع من DeepSeek R1 مع بحث ويب في الوقت الحقيقي، يجمع بين قدرات بحجم 671B واستجابة أسرع.", "deepseek-r1-online.description": "الإصدار الكامل من DeepSeek R1 مع 671 مليار معلمة وبحث ويب في الوقت الحقيقي، يوفر فهمًا وتوليدًا أقوى.", "deepseek-r1.description": "يستخدم DeepSeek-R1 بيانات البداية الباردة قبل التعلم المعزز ويؤدي أداءً مماثلًا لـ OpenAI-o1 في الرياضيات، والبرمجة، والتفكير.", - "deepseek-reasoner.description": "نموذج DeepSeek مخصص لمهام الاستدلال المنطقي المعقدة.", + "deepseek-reasoner.description": "اسم مستعار متوافق لوضع التفكير في DeepSeek V4 Flash. مقرر إيقافه — استخدم DeepSeek V4 Flash بدلاً منه.", "deepseek-v2.description": "DeepSeek V2 هو نموذج MoE فعال لمعالجة منخفضة التكلفة.", "deepseek-v2:236b.description": "DeepSeek V2 236B هو نموذج DeepSeek الموجه للبرمجة مع قدرات قوية في توليد الكود.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 هو نموذج MoE يحتوي على 671 مليار معلمة يتميز بقوة في البرمجة، والقدرات التقنية، وفهم السياق، والتعامل مع النصوص الطويلة.", @@ -490,6 +491,8 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 هو نموذج توليد صور من ByteDance Seed، يدعم إدخال النصوص والصور مع توليد صور عالية الجودة وقابلة للتحكم بدرجة كبيرة. يُولّد الصور من التعليمات النصية.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 هو أحدث نموذج متعدد الوسائط من ByteDance، يدمج قدرات تحويل النص إلى صورة، والصورة إلى صورة، وتوليد الصور بالجملة، مع دمج الفهم العام وقدرات الاستدلال. مقارنة بالإصدار السابق 4.0، يقدم جودة توليد محسّنة بشكل كبير، مع تحسين تناسق التحرير ودمج الصور المتعددة. يوفر تحكمًا أكثر دقة في التفاصيل البصرية، مما يجعل النصوص الصغيرة والوجوه الصغيرة أكثر طبيعية، ويحقق تخطيطًا وألوانًا أكثر انسجامًا، مما يعزز الجماليات العامة.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite هو أحدث نموذج لتوليد الصور من ByteDance. لأول مرة، يدمج قدرات الاسترجاع عبر الإنترنت، مما يسمح له بتضمين معلومات الويب في الوقت الفعلي وتعزيز حداثة الصور المولدة. كما تم ترقية ذكاء النموذج، مما يمكنه من تفسير التعليمات المعقدة والمحتوى البصري بدقة. بالإضافة إلى ذلك، يقدم تغطية محسّنة للمعرفة العالمية، وتناسقًا مرجعيًا، وجودة توليد في السيناريوهات المهنية، مما يلبي بشكل أفضل احتياجات الإبداع البصري على مستوى المؤسسات.", + "dreamina-seedance-2-0-260128.description": "Seedance 2.0 من ByteDance هو أقوى نموذج لتوليد الفيديو، يدعم إنشاء الفيديو المرجعي متعدد الوسائط، تحرير الفيديو، تمديد الفيديو، تحويل النص إلى فيديو، وتحويل الصورة إلى فيديو مع صوت متزامن.", + "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast من ByteDance يقدم نفس القدرات مثل Seedance 2.0 مع سرعات توليد أسرع وسعر أكثر تنافسية.", "emohaa.description": "Emohaa هو نموذج للصحة النفسية يتمتع بقدرات استشارية احترافية لمساعدة المستخدمين على فهم المشكلات العاطفية.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B هو نموذج مفتوح المصدر وخفيف الوزن، مصمم للنشر المحلي والمخصص.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview هو نموذج معاينة بسياق 8K لتقييم أداء ERNIE 4.5.", @@ -514,7 +517,8 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K هو نموذج تفكير سريع بسياق 32K للاستدلال المعقد والدردشة متعددة الأدوار.", "ernie-x1.1-preview.description": "معاينة ERNIE X1.1 هو نموذج تفكير مخصص للتقييم والاختبار.", "ernie-x1.1.description": "ERNIE X1.1 هو نموذج تفكير تجريبي للتقييم والاختبار.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 هو نموذج توليد الصور من ByteDance Seed، يدعم إدخال النصوص والصور مع توليد صور عالية الجودة وقابلة للتحكم بشكل كبير. يقوم بتوليد الصور من التعليمات النصية.", + "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5، تم تطويره بواسطة فريق ByteDance Seed، يدعم تحرير الصور المتعددة والتكوين. يتميز بتناسق الموضوع المحسن، اتباع التعليمات بدقة، فهم المنطق المكاني، التعبير الجمالي، تصميم الملصقات وتصميم الشعارات مع توليد النصوص والصور بدقة عالية.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0، تم تطويره بواسطة ByteDance Seed، يدعم إدخال النصوص والصور لتوليد صور عالية الجودة وقابلة للتحكم بناءً على التعليمات.", "fal-ai/flux-kontext/dev.description": "نموذج FLUX.1 يركز على تحرير الصور، ويدعم إدخال النصوص والصور.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] يقبل النصوص وصور مرجعية كمدخلات، مما يتيح تعديلات محلية مستهدفة وتحولات معقدة في المشهد العام.", "fal-ai/flux/krea.description": "Flux Krea [dev] هو نموذج لتوليد الصور يتميز بميول جمالية نحو صور أكثر واقعية وطبيعية.", @@ -522,8 +526,8 @@ "fal-ai/hunyuan-image/v3.description": "نموذج قوي لتوليد الصور متعدد الوسائط أصلي.", "fal-ai/imagen4/preview.description": "نموذج عالي الجودة لتوليد الصور من Google.", "fal-ai/nano-banana.description": "Nano Banana هو أحدث وأسرع وأكثر نماذج Google كفاءةً لتوليد وتحرير الصور من خلال المحادثة.", - "fal-ai/qwen-image-edit.description": "نموذج تحرير الصور الاحترافي من فريق Qwen يدعم التعديلات الدلالية والمظهرية، ويحرر النصوص الصينية والإنجليزية بدقة، ويمكّن من تعديلات عالية الجودة مثل نقل الأنماط وتدوير الكائنات.", - "fal-ai/qwen-image.description": "نموذج توليد الصور القوي من فريق Qwen يتميز بعرض نصوص صينية مذهلة وأنماط بصرية متنوعة.", + "fal-ai/qwen-image-edit.description": "نموذج تحرير الصور الاحترافي من فريق Qwen، يدعم التعديلات الدلالية والمظهرية، تحرير النصوص الدقيقة باللغتين الصينية والإنجليزية، نقل الأنماط، التدوير، والمزيد.", + "fal-ai/qwen-image.description": "نموذج قوي لتوليد الصور من فريق Qwen يتميز بتقديم نصوص صينية قوية وأنماط بصرية متنوعة.", "flux-1-schnell.description": "نموذج تحويل النص إلى صورة يحتوي على 12 مليار معلمة من Black Forest Labs يستخدم تقنيات تقطير الانتشار العدائي الكامن لتوليد صور عالية الجودة في 1-4 خطوات. ينافس البدائل المغلقة ومتاح بموجب ترخيص Apache-2.0 للاستخدام الشخصي والبحثي والتجاري.", "flux-dev.description": "نموذج مفتوح المصدر مخصص لتوليد الصور لأغراض البحث والابتكار غير التجاري، مع تحسينات فعالة.", "flux-kontext-max.description": "توليد وتحرير صور سياقية متقدمة، تجمع بين النصوص والصور لتحقيق نتائج دقيقة ومتسقة.", @@ -565,7 +569,7 @@ "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) هو نموذج توليد الصور من Google ويدعم أيضًا الدردشة متعددة الوسائط.", "gemini-3-pro-preview.description": "Gemini 3 Pro هو أقوى نموذج من Google للوكيل الذكي والبرمجة الإبداعية، يقدم تفاعلاً أعمق وصورًا أغنى مع استدلال متقدم.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) يقدم جودة صور احترافية بسرعة فائقة مع دعم الدردشة متعددة الوسائط.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) هو أسرع نموذج توليد صور أصلي من Google مع دعم التفكير، وتوليد الصور الحواري وتحريرها.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) يقدم جودة صور بمستوى احترافي بسرعة Flash مع دعم الدردشة متعددة الوسائط.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview هو النموذج الأكثر كفاءة من حيث التكلفة من Google، مُحسّن للمهام الوكيلة ذات الحجم الكبير، الترجمة، ومعالجة البيانات.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite هو النموذج متعدد الوسائط الأكثر كفاءة من Google، مُحسّن للمهام الوكيلية ذات الحجم الكبير، الترجمة، ومعالجة البيانات.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview يحسن من Gemini 3 Pro مع قدرات استدلال محسّنة ويضيف دعم مستوى التفكير المتوسط.", @@ -730,6 +734,8 @@ "grok-4-fast-reasoning.description": "يسعدنا إطلاق Grok 4 Fast، أحدث تقدم في نماذج الاستدلال منخفضة التكلفة.", "grok-4.20-0309-non-reasoning.description": "نموذج غير تفكير للاستخدامات البسيطة.", "grok-4.20-0309-reasoning.description": "نموذج ذكي وسريع للغاية يفكر قبل الرد.", + "grok-4.20-beta-0309-non-reasoning.description": "نسخة غير تفكيرية للاستخدامات البسيطة.", + "grok-4.20-beta-0309-reasoning.description": "نموذج ذكي وسريع للغاية يفكر قبل الرد.", "grok-4.20-multi-agent-0309.description": "فريق من 4 أو 16 وكيلًا، يتفوق في حالات الاستخدام البحثية، لا يدعم حاليًا الأدوات على جانب العميل. يدعم فقط أدوات xAI على جانب الخادم (مثل X Search، أدوات البحث على الويب) وأدوات MCP البعيدة.", "grok-4.3.description": "أكثر نموذج لغة كبير يسعى للحقيقة في العالم.", "grok-4.description": "أحدث نموذج Grok الرائد بأداء لا مثيل له في اللغة، الرياضيات، والاستدلال — نموذج شامل حقيقي. يشير حاليًا إلى grok-4-0709؛ نظرًا للموارد المحدودة، فإن سعره مؤقتًا أعلى بنسبة 10% من السعر الرسمي ومن المتوقع أن يعود إلى السعر الرسمي لاحقًا.", @@ -1214,6 +1220,8 @@ "qwq.description": "QwQ هو نموذج استدلال من عائلة Qwen. مقارنة بالنماذج المضبوطة على التعليمات، يقدم قدرات تفكير واستدلال تعزز الأداء بشكل كبير، خاصة في المشكلات الصعبة. QwQ-32B هو نموذج متوسط الحجم ينافس أفضل نماذج الاستدلال مثل DeepSeek-R1 و o1-mini.", "qwq_32b.description": "نموذج استدلال متوسط الحجم من عائلة Qwen. مقارنة بالنماذج المضبوطة على التعليمات، تعزز قدرات التفكير والاستدلال في QwQ الأداء بشكل كبير، خاصة في المشكلات الصعبة.", "r1-1776.description": "R1-1776 هو إصدار ما بعد التدريب من DeepSeek R1 مصمم لتقديم معلومات واقعية غير خاضعة للرقابة أو التحيز.", + "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro من ByteDance يدعم تحويل النص إلى فيديو، تحويل الصورة إلى فيديو (الإطار الأول، الإطار الأول + الأخير)، وتوليد الصوت المتزامن مع المرئيات.", + "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite من BytePlus يتميز بتوليد معزز بالاسترجاع عبر الويب للحصول على معلومات في الوقت الحقيقي، تفسير محسّن للتعليمات المعقدة، وتحسين تناسق المرجع لإنشاء بصري احترافي.", "solar-mini-ja.description": "Solar Mini (Ja) يوسع Solar Mini مع تركيز على اللغة اليابانية مع الحفاظ على الأداء القوي والكفاءة في الإنجليزية والكورية.", "solar-mini.description": "Solar Mini هو نموذج لغة مدمج يتفوق على GPT-3.5، يتميز بقدرات متعددة اللغات قوية تدعم الإنجليزية والكورية، ويقدم حلاً فعالاً بصمة صغيرة.", "solar-pro.description": "Solar Pro هو نموذج لغة عالي الذكاء من Upstage، يركز على اتباع التعليمات باستخدام وحدة معالجة رسومات واحدة، مع درجات IFEval تتجاوز 80. حالياً يدعم اللغة الإنجليزية؛ وكان من المقرر إصدار النسخة الكاملة في نوفمبر 2024 مع دعم لغات موسع وسياق أطول.", diff --git a/locales/ar/plugin.json b/locales/ar/plugin.json index 0d86b1ecaf..7962c61b62 100644 --- a/locales/ar/plugin.json +++ b/locales/ar/plugin.json @@ -237,6 +237,14 @@ "builtins.lobe-page-agent.apiName.updateNode": "تحديث العقدة", "builtins.lobe-page-agent.apiName.wrapNodes": "تغليف العقد", "builtins.lobe-page-agent.title": "الصفحة", + "builtins.lobe-self-feedback-intent.apiName.declareSelfFeedbackIntent": "تسجيل فكرة تحسين", + "builtins.lobe-self-feedback-intent.inspector.gap.proposal": "اقتراح تحسين", + "builtins.lobe-self-feedback-intent.inspector.memory.write": "تسجيل التفضيل", + "builtins.lobe-self-feedback-intent.inspector.rejected": "لم يتم التسجيل", + "builtins.lobe-self-feedback-intent.inspector.skill.consolidate": "تنظيم الأساليب", + "builtins.lobe-self-feedback-intent.inspector.skill.create": "تم العثور على أسلوب جديد", + "builtins.lobe-self-feedback-intent.inspector.skill.refine": "تحسين الأسلوب", + "builtins.lobe-self-feedback-intent.title": "أفكار للتحسين", "builtins.lobe-skill-store.apiName.importFromMarket": "استيراد من السوق", "builtins.lobe-skill-store.apiName.importSkill": "استيراد المهارة", "builtins.lobe-skill-store.apiName.searchSkill": "البحث عن المهارات", diff --git a/locales/ar/providers.json b/locales/ar/providers.json index 313b2aecb3..e7614ea9fd 100644 --- a/locales/ar/providers.json +++ b/locales/ar/providers.json @@ -33,6 +33,7 @@ "jina.description": "تأسست Jina AI في عام 2020، وهي شركة رائدة في مجال البحث الذكي. تشمل تقنياتها نماذج المتجهات، ومعيدو الترتيب، ونماذج لغوية صغيرة لبناء تطبيقات بحث توليدية ومتعددة الوسائط عالية الجودة.", "kimicodingplan.description": "كود Kimi من Moonshot AI يوفر الوصول إلى نماذج Kimi بما في ذلك K2.5 لأداء مهام الترميز.", "lmstudio.description": "LM Studio هو تطبيق سطح مكتب لتطوير وتجربة النماذج اللغوية الكبيرة على جهازك.", + "lobehub.description": "يستخدم LobeHub Cloud واجهات برمجة التطبيقات الرسمية للوصول إلى نماذج الذكاء الاصطناعي ويقيس الاستخدام باستخدام أرصدة مرتبطة برموز النماذج.", "longcat.description": "LongCat هو سلسلة من نماذج الذكاء الاصطناعي التوليدية الكبيرة التي تم تطويرها بشكل مستقل بواسطة Meituan. تم تصميمه لتعزيز إنتاجية المؤسسة الداخلية وتمكين التطبيقات المبتكرة من خلال بنية حسابية فعالة وقدرات متعددة الوسائط قوية.", "minimax.description": "تأسست MiniMax في عام 2021، وتبني نماذج ذكاء اصطناعي متعددة الوسائط للأغراض العامة، بما في ذلك نماذج نصية بمليارات المعلمات، ونماذج صوتية وبصرية، بالإضافة إلى تطبيقات مثل Hailuo AI.", "minimaxcodingplan.description": "خطة الرموز MiniMax توفر الوصول إلى نماذج MiniMax بما في ذلك M2.7 لأداء مهام الترميز عبر اشتراك ثابت الرسوم.", diff --git a/locales/ar/subscription.json b/locales/ar/subscription.json index a3ac60cc5d..c46b2cc6fd 100644 --- a/locales/ar/subscription.json +++ b/locales/ar/subscription.json @@ -41,7 +41,9 @@ "credits.autoTopUp.monthlyLimitDesc": "الحد الأقصى الذي يمكن شحنه تلقائيًا شهريًا. اتركه فارغًا لعدم وجود حد", "credits.autoTopUp.monthlyLimitPlaceholder": "لا يوجد حد", "credits.autoTopUp.monthlyTopUpAmount": "مبلغ الشحن الشهري", + "credits.autoTopUp.noCustomerHint": "قم بشراء أرصدة مرة واحدة لحفظ طريقة الدفع قبل تمكين التعبئة التلقائية.", "credits.autoTopUp.noPaymentMethodHint": "لا توجد وسيلة دفع مسجلة. يتطلب الشحن التلقائي وجود بطاقة محفوظة للشحن تلقائيًا.", + "credits.autoTopUp.purchaseCredits": "شراء الأرصدة", "credits.autoTopUp.saveError": "فشل في حفظ إعدادات الشحن التلقائي", "credits.autoTopUp.saveSuccess": "تم حفظ إعدادات الشحن التلقائي", "credits.autoTopUp.setupPaymentMethod": "إضافة وسيلة دفع", @@ -83,6 +85,7 @@ "credits.packages.title": "حزم الأرصدة الخاصة بي", "credits.topUp.cancel": "إلغاء", "credits.topUp.custom": "مخصص", + "credits.topUp.freeFeeHint": "تشمل تعبئة الخطة المجانية رسوم خدمة بقيمة {{fee}} لكل مليون رصيد.", "credits.topUp.maxAmountError": "لا يمكن أن يتجاوز مبلغ الشراء الواحد ${{max}}", "credits.topUp.purchaseError": "فشل الشراء، يرجى المحاولة لاحقًا", "credits.topUp.purchaseNow": "اشترِ الآن", @@ -120,6 +123,9 @@ "keyMissMatch.button": "استعادة الاستخدام ومتابعة المحادثة", "keyMissMatch.description": "بسبب خلل مؤقت في النظام، تم تعطيل استخدام اشتراكك مؤقتًا. يرجى النقر على الزر أدناه لاستعادة الاستخدام ومتابعة المحادثة. إذا تكرر ذلك، يرجى التواصل معنا عبر البريد الإلكتروني (support@lobehub.com)", "keyMissMatch.title": "استعادة استخدام الاشتراك الآن", + "limitation.chat.budgetReady.action": "متابعة الدردشة", + "limitation.chat.budgetReady.desc": "الأرصدة المتوفرة لديك تغطي هذا الطلب الآن.", + "limitation.chat.budgetReady.title": "الأرصدة جاهزة", "limitation.chat.success.action": "متابعة المحادثة", "limitation.chat.success.desc": "تمت ترقية اشتراكك في {{plan}} بنجاح. استمتع بالدردشة مع الذكاء الاصطناعي. تتضمن خطتك الحالية:", "limitation.chat.success.title": "تمت الترقية بنجاح", @@ -134,14 +140,23 @@ "limitation.hobby.docs": "عرض مستندات التكوين", "limitation.hobby.tip": "تأكد من التبديل إلى نموذج يستخدم مفتاح API مخصص", "limitation.hobby.title": "يرجى تكوين واجهة برمجة التطبيقات لخدمة النموذج", + "limitation.image.budgetReady.action": "متابعة التوليد", + "limitation.image.budgetReady.desc": "الأرصدة المتوفرة لديك تغطي هذا التوليد الآن.", + "limitation.image.budgetReady.title": "الأرصدة جاهزة", "limitation.image.success.action": "متابعة التوليد", "limitation.image.success.desc": "تمت ترقية اشتراكك في {{plan}} بنجاح. استمتع بتوليد الصور بالذكاء الاصطناعي. تتضمن خطتك الحالية:", "limitation.image.success.title": "تمت الترقية بنجاح", "limitation.image.topupSuccess.action": "متابعة التوليد", "limitation.image.topupSuccess.desc": "أرصدة الشحن الخاصة بك نشطة الآن. استمتع بتوليد الصور بالذكاء الاصطناعي. تتضمن خطتك الحالية:", "limitation.image.topupSuccess.title": "تم الشحن بنجاح", + "limitation.insufficientBudget.approximateDesc": "قد يتطلب هذا الطلب المزيد من الأرصدة. قم بتعبئة الأرصدة أو ترقية خطتك.", + "limitation.insufficientBudget.available": "الأرصدة المتوفرة", "limitation.insufficientBudget.desc": "الأرصدة المتبقية لديك غير كافية لتغطية التكلفة المقدرة لهذا النموذج. يرجى إعادة شحن الأرصدة أو التبديل إلى نموذج أقل تكلفة.", + "limitation.insufficientBudget.estimatedDesc": "من المتوقع أن يتطلب هذا الطلب المزيد من الأرصدة. قم بتعبئة الأرصدة أو ترقية خطتك.", + "limitation.insufficientBudget.exactDesc": "يتطلب هذا الطلب المزيد من الأرصدة. قم بتعبئة الأرصدة أو ترقية خطتك.", + "limitation.insufficientBudget.required": "الأرصدة المطلوبة", "limitation.insufficientBudget.retry": "إعادة المحاولة", + "limitation.insufficientBudget.shortfall": "نقص الأرصدة", "limitation.insufficientBudget.title": "الأرصدة غير كافية لهذا النموذج", "limitation.limited.action": "الترقية الآن", "limitation.limited.advanceFeature": "قم بالترقية للاستمتاع بالميزات المميزة:", @@ -151,6 +166,7 @@ "limitation.limited.title": "تم استهلاك أرصدة الحوسبة", "limitation.limited.topup": "شحن الأرصدة", "limitation.limited.upgrade": "الترقية إلى خطة أعلى", + "limitation.limited.upgradeToPlan": "قم بالترقية إلى {{plan}}", "limitation.providers.lock.addNew": "اشترك الآن لإنشاء مزودي ذكاء اصطناعي مخصصين", "limitation.providers.lock.enableProvider": "اشترك الآن لتفعيل هذا المزود", "limitation.providers.lock.menuItem": "اشترك الآن لتكوين خدمة API مخصصة", @@ -161,6 +177,9 @@ "limitation.providers.prompter.subTitle": "خدمة API المخصصة متاحة فقط للخطط المدفوعة. قم بالترقية الآن للاستفادة من خدمات النماذج العالمية", "limitation.providers.prompter.title": "اشترك الآن لاستخدام خدمة API مخصصة", "limitation.providers.tooltip": "خدمة API المخصصة متاحة فقط للخطط المدفوعة", + "limitation.video.budgetReady.action": "متابعة التوليد", + "limitation.video.budgetReady.desc": "الأرصدة المتوفرة لديك تغطي هذا التوليد الآن.", + "limitation.video.budgetReady.title": "الأرصدة جاهزة", "limitation.video.success.action": "تابع التوليد", "limitation.video.success.desc": "تمت ترقية اشتراكك في خطة {{plan}} بنجاح. استمتع بتوليد الفيديو باستخدام الذكاء الاصطناعي. خطتك الحالية تتضمن:", "limitation.video.success.title": "تمت الترقية بنجاح", diff --git a/locales/bg-BG/chat.json b/locales/bg-BG/chat.json index 9911c612db..3cfc15d008 100644 --- a/locales/bg-BG/chat.json +++ b/locales/bg-BG/chat.json @@ -196,6 +196,9 @@ "groupWizard.searchTemplates": "Търсене на шаблони...", "groupWizard.title": "Създай група", "groupWizard.useTemplate": "Използвай шаблон", + "heteroAgent.cloudNotConfigured.action": "Конфигурирай", + "heteroAgent.cloudNotConfigured.desc": "Конфигурирайте вашия Claude Code токен в профила на агента, за да започнете да изпращате съобщения.", + "heteroAgent.cloudNotConfigured.title": "Необходими са облачни идентификационни данни", "heteroAgent.cloudRepo.multiSelected": "{{count}} хранилища избрани", "heteroAgent.cloudRepo.noRepos": "Няма конфигурирани хранилища. Добавете ги в настройките на агента.", "heteroAgent.cloudRepo.notSet": "Няма избрано хранилище", @@ -467,6 +470,10 @@ "sharePage.error.unauthorized.subtitle": "Моля, влезте в профила си, за да видите споделената тема.", "sharePage.error.unauthorized.title": "Необходим е вход", "sharePageDisclaimer": "Това съдържание е споделено от потребител и не отразява възгледите на LobeHub. LobeHub не носи отговорност за последствията от това споделено съдържание.", + "signalCallbacks.collapse": "Скрий подробности", + "signalCallbacks.empty": "Няма съобщения за обратна връзка", + "signalCallbacks.expand": "Покажи подробности", + "signalCallbacks.title": "{{tool}} · {{count}} актуализации за обратна връзка", "stt.action": "Гласов вход", "stt.loading": "Разпознаване...", "stt.prettifying": "Редактиране...", @@ -689,7 +696,6 @@ "thread.closeSubagentThread": "Свиване на разговора със субагента", "thread.divider": "Подтема", "thread.openSubagentThread": "Преглед на пълния разговор със субагента", - "thread.subagentBadge": "Субагент", "thread.subagentReadOnlyHint": "Разговорите със SubAgent са само за четене — изпълнението се управлява от основния агент.", "thread.threadMessageCount": "{{messageCount}} съобщения", "thread.title": "Подтема", diff --git a/locales/bg-BG/models.json b/locales/bg-BG/models.json index 8d83ce4bf9..6289b8babe 100644 --- a/locales/bg-BG/models.json +++ b/locales/bg-BG/models.json @@ -106,6 +106,7 @@ "MiniMax-Hailuo-2.3.description": "Чисто нов модел за видео генериране с цялостни подобрения в движенията на тялото, физическата реалистичност и следването на инструкции.", "MiniMax-M1.description": "Нов вътрешен модел за разсъждение с 80K верига на мисълта и 1M вход, предлагащ производителност, сравнима с водещите глобални модели.", "MiniMax-M2-Stable.description": "Създаден за ефективно програмиране и агентски работни потоци, с по-висока едновременност за търговска употреба.", + "MiniMax-M2.1-Lightning.description": "Мощни многоезични програмни възможности с по-бързо и ефективно извеждане.", "MiniMax-M2.1-highspeed.description": "Мощни многоезични програмни възможности, цялостно подобрено програмиране. По-бързо и по-ефективно.", "MiniMax-M2.1.description": "MiniMax-M2.1 е водеща отворена голяма езикова система от MiniMax, фокусирана върху решаването на сложни реални задачи. Основните ѝ предимства са възможностите за програмиране на множество езици и способността да действа като агент за решаване на сложни задачи.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: Същата производителност като M2.5, но с по-бързо извеждане.", @@ -314,13 +315,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku е най-бързият и най-компактен модел на Anthropic, проектиран за почти мигновени отговори с бърза и точна производителност.", "claude-3-opus-20240229.description": "Claude 3 Opus е най-мощният модел на Anthropic за силно сложни задачи, отличаващ се с производителност, интелигентност, плавност и разбиране.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet балансира интелигентност и скорост за корпоративни натоварвания, осигурявайки висока полезност на по-ниска цена и надеждно мащабно внедряване.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 е най-бързият и интелигентен Haiku модел на Anthropic, с мълниеносна скорост и разширено разсъждение.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 е най-бързият и интелигентен модел Haiku на Anthropic, с мълниеносна скорост и разширено мислене.", "claude-haiku-4-5.description": "Claude Haiku 4.5 от Anthropic — ново поколение Haiku с подобрено разсъждение и визия.", "claude-haiku-4.5.description": "Claude Haiku 4.5 е най-бързият и най-умен Haiku модел на Anthropic, с мълниеносна скорост и разширено разсъждение.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking е усъвършенстван вариант, който може да разкрие процеса си на разсъждение.", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1 е най-новият и най-способен модел на Anthropic за изключително сложни задачи, отличаващ се с производителност, интелигентност, плавност и разбиране.", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1 е най-новият и най-способен модел на Anthropic за силно сложни задачи, превъзхождащ в производителност, интелигентност, плавност и разбиране.", "claude-opus-4-1.description": "Claude Opus 4.1 от Anthropic — премиум модел за дълбок анализ и разсъждение.", - "claude-opus-4-20250514.description": "Claude Opus 4 е най-мощният модел на Anthropic за изключително сложни задачи, отличаващ се с производителност, интелигентност, плавност и разбиране.", + "claude-opus-4-20250514.description": "Claude Opus 4 е най-мощният модел на Anthropic за силно сложни задачи, превъзхождащ в производителност, интелигентност, плавност и разбиране.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 е флагманският модел на Anthropic, комбиниращ изключителна интелигентност с мащабируема производителност, идеален за сложни задачи, изискващи най-висококачествени отговори и разсъждение.", "claude-opus-4-5.description": "Claude Opus 4.5 от Anthropic — флагмански модел с върхово разсъждение и кодови умения.", "claude-opus-4-6.description": "Claude Opus 4.6 от Anthropic — флагман с 1M контекст и усъвършенствано разсъждение.", @@ -329,8 +330,8 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 е най-интелигентният модел на Anthropic за създаване на агенти и програмиране.", "claude-opus-4.6.description": "Claude Opus 4.6 е най-интелигентният модел на Anthropic за създаване на агенти и програмиране.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking може да генерира почти мигновени отговори или разширено стъпково мислене с видим процес.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 може да генерира почти мигновени отговори или разширено стъпка по стъпка мислене с видим процес.", - "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 е най-интелигентният модел на Anthropic до момента.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 е най-интелигентният модел на Anthropic досега, предлагащ почти мигновени отговори или разширено стъпка по стъпка мислене с фино управление за API потребители.", + "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 е най-интелигентният модел на Anthropic досега.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 от Anthropic — подобрен Sonnet с по‑силни кодови способности.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 от Anthropic — последният Sonnet с превъзходно кодиране и работа с инструменти.", "claude-sonnet-4.5.description": "Claude Sonnet 4.5 е най-интелигентният модел на Anthropic до момента.", @@ -403,7 +404,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) е иновативен модел, предлагащ дълбоко езиково разбиране и интеракция.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 е модел за разсъждение от ново поколение с по-силни способности за сложни разсъждения и верига от мисли за задълбочени аналитични задачи.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 е модел за разсъждение от следващо поколение с по-силни способности за сложни разсъждения и верига на мисълта.", - "deepseek-chat.description": "Нов модел с отворен код, който комбинира общи и кодови способности. Той запазва общия диалогов модел и силното кодиране на кодовия модел, с по-добро съответствие на предпочитанията. DeepSeek-V2.5 също така подобрява писането и следването на инструкции.", + "deepseek-chat.description": "Съвместимостен псевдоним за DeepSeek V4 Flash в режим без мислене. Предстои да бъде прекратен — използвайте DeepSeek V4 Flash вместо това.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B е езиков модел за програмиране, обучен върху 2 трилиона токени (87% код, 13% китайски/английски текст). Въвежда 16K контекстен прозорец и задачи за попълване в средата, осигурявайки допълване на код на ниво проект и попълване на фрагменти.", "deepseek-coder-v2.description": "DeepSeek Coder V2 е отворен MoE модел за програмиране, който се представя на ниво GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 е отворен MoE модел за програмиране, който се представя на ниво GPT-4 Turbo.", @@ -425,7 +426,7 @@ "deepseek-r1-fast-online.description": "Пълна бърза версия на DeepSeek R1 с търсене в реално време в уеб, комбинираща възможности от мащаб 671B и по-бърз отговор.", "deepseek-r1-online.description": "Пълна версия на DeepSeek R1 с 671 милиарда параметъра и търсене в реално време в уеб, предлагаща по-силно разбиране и генериране.", "deepseek-r1.description": "DeepSeek-R1 използва данни от студен старт преди подсиленото обучение и се представя наравно с OpenAI-o1 в математика, програмиране и разсъждение.", - "deepseek-reasoner.description": "Модел за разсъждение DeepSeek, фокусиран върху сложни логически задачи.", + "deepseek-reasoner.description": "Съвместимостен псевдоним за DeepSeek V4 Flash в режим на мислене. Предстои да бъде прекратен — използвайте DeepSeek V4 Flash вместо това.", "deepseek-v2.description": "DeepSeek V2 е ефективен MoE модел за икономична обработка.", "deepseek-v2:236b.description": "DeepSeek V2 236B е модел на DeepSeek, фокусиран върху програмиране, с висока производителност при генериране на код.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 е MoE модел с 671 милиарда параметъра, с изключителни способности в програмиране, технически задачи, разбиране на контекст и обработка на дълги текстове.", @@ -490,6 +491,8 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 е модел за генериране на изображения от ByteDance Seed, поддържащ вход от текст и изображения с високо контролируемо, висококачествено генериране на изображения. Генерира изображения от текстови подсказки.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 е най-новият мултимодален модел за изображения на ByteDance, интегриращ текст-към-изображение, изображение-към-изображение и групово генериране на изображения, като включва обща логика и способности за разсъждение. В сравнение с предишната версия 4.0, той предлага значително подобрено качество на генериране, с по-добра консистентност при редактиране и мулти-изображение сливане. Осигурява по-прецизен контрол върху визуалните детайли, като произвежда малък текст и малки лица по-естествено, и постига по-хармонично оформление и цветове, подобрявайки цялостната естетика.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite е най-новият модел за генериране на изображения на ByteDance. За първи път интегрира възможности за онлайн извличане, позволявайки му да включва информация в реално време от уеб и да подобрява актуалността на генерираните изображения. Интелигентността на модела също е подобрена, позволявайки прецизно интерпретиране на сложни инструкции и визуално съдържание. Освен това предлага подобрено глобално покритие на знания, консистентност на референциите и качество на генериране в професионални сценарии, по-добре отговаряйки на нуждите за визуално създаване на корпоративно ниво.", + "dreamina-seedance-2-0-260128.description": "Seedance 2.0 от ByteDance е най-мощният модел за генериране на видео, поддържащ многомодално генериране на референтни видеа, редактиране на видео, разширение на видео, текст към видео и изображение към видео със синхронизиран звук.", + "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast от ByteDance предлага същите възможности като Seedance 2.0 с по-бързи скорости на генериране на по-конкурентна цена.", "emohaa.description": "Emohaa е модел за психично здраве с професионални консултантски способности, който помага на потребителите да разберат емоционални проблеми.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B е лек модел с отворен код за локално и персонализирано внедряване.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview е модел за предварителен преглед с 8K контекст за оценка на ERNIE 4.5.", @@ -514,7 +517,8 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K е бърз мислещ модел с 32K контекст за сложни разсъждения и многозавойни разговори.", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview е предварителен модел за мислене, предназначен за оценка и тестване.", "ernie-x1.1.description": "ERNIE X1.1 е мисловен модел за предварителен преглед за оценка и тестване.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 е модел за генериране на изображения от ByteDance Seed, който поддържа текстови и визуални входове с високо контролируемо и висококачествено генериране на изображения. Той генерира изображения от текстови подсказки.", + "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5, създаден от екипа Seed на ByteDance, поддържа редактиране и композиция на множество изображения. Характеризира се с подобрена консистентност на обектите, прецизно следване на инструкции, разбиране на пространствена логика, естетично изразяване, оформление на плакати и дизайн на лого с високопрецизно рендиране на текст и изображения.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, създаден от ByteDance Seed, поддържа текстови и визуални входове за силно контролируемо, висококачествено генериране на изображения от подсказки.", "fal-ai/flux-kontext/dev.description": "FLUX.1 модел, фокусиран върху редактиране на изображения, поддържащ вход от текст и изображения.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] приема текст и референтни изображения като вход, позволявайки целенасочени локални редакции и сложни глобални трансформации на сцени.", "fal-ai/flux/krea.description": "Flux Krea [dev] е модел за генериране на изображения с естетично предпочитание към по-реалистични и естествени изображения.", @@ -522,8 +526,8 @@ "fal-ai/hunyuan-image/v3.description": "Мощен роден мултимодален модел за генериране на изображения.", "fal-ai/imagen4/preview.description": "Модел за висококачествено генериране на изображения от Google.", "fal-ai/nano-banana.description": "Nano Banana е най-новият, най-бърз и най-ефективен роден мултимодален модел на Google, позволяващ генериране и редактиране на изображения чрез разговор.", - "fal-ai/qwen-image-edit.description": "Професионален модел за редактиране на изображения от екипа на Qwen, който поддържа семантични и визуални редакции, прецизно редактира китайски и английски текст и позволява висококачествени редакции като прехвърляне на стил и завъртане на обекти.", - "fal-ai/qwen-image.description": "Мощен модел за генериране на изображения от екипа на Qwen с впечатляващо рендиране на китайски текст и разнообразни визуални стилове.", + "fal-ai/qwen-image-edit.description": "Професионален модел за редактиране на изображения от екипа Qwen, поддържащ семантични и визуални редакции, прецизно редактиране на текст на китайски/английски, трансфер на стил, ротация и други.", + "fal-ai/qwen-image.description": "Мощен модел за генериране на изображения от екипа Qwen с силно рендиране на китайски текст и разнообразни визуални стилове.", "flux-1-schnell.description": "Модел за преобразуване на текст в изображение с 12 милиарда параметъра от Black Forest Labs, използващ латентна дифузионна дестилация за генериране на висококачествени изображения в 1–4 стъпки. Съперничи на затворени алтернативи и е пуснат под лиценз Apache-2.0 за лична, изследователска и търговска употреба.", "flux-dev.description": "Модел за генериране на изображения с отворен код, оптимизиран за неконкурентни изследвания и иновации.", "flux-kontext-max.description": "Съвременно генериране и редактиране на изображения с контекст, комбиниращо текст и изображения за прецизни и последователни резултати.", @@ -562,10 +566,10 @@ "gemini-3-flash-preview.description": "Gemini 3 Flash е най-интелигентният модел, създаден за скорост, съчетаващ авангардна интелигентност с отлично търсене и обоснованост.", "gemini-3-flash.description": "Gemini 3 Flash от Google — ултрабърз модел с мултимодална поддръжка.", "gemini-3-pro-image-preview.description": "Gemini 3 Pro Image (Nano Banana Pro) е модел за генериране на изображения на Google, който също поддържа мултимодален диалог.", - "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) е моделът на Google за генериране на изображения и също така поддържа мултимодален чат.", + "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) е моделът на Google за генериране на изображения и също така поддържа многомодален чат.", "gemini-3-pro-preview.description": "Gemini 3 Pro е най-мощният агентен и „vibe-coding“ модел на Google, който предлага по-богати визуализации и по-дълбоко взаимодействие, базирано на съвременно логическо мислене.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) е най-бързият модел на Google за генериране на изображения с поддръжка на мислене, разговорно генериране и редактиране на изображения.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) е най-бързият собствен модел на Google за генериране на изображения с поддръжка на мислене, разговорно генериране и редактиране на изображения.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) предоставя качество на изображения от професионално ниво с Flash скорост и поддръжка на многомодален чат.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview е най-икономичният мултимодален модел на Google, оптимизиран за задачи с голям обем, превод и обработка на данни.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite е най-икономичният мултимодален модел на Google, оптимизиран за задачи с голям обем, превод и обработка на данни.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview подобрява Gemini 3 Pro с усъвършенствани способности за разсъждение и добавя поддръжка за средно ниво на мислене.", @@ -730,6 +734,8 @@ "grok-4-fast-reasoning.description": "С гордост представяме Grok 4 Fast – нашият най-нов напредък в икономичните логически модели.", "grok-4.20-0309-non-reasoning.description": "Неразсъждаващ вариант за прости случаи.", "grok-4.20-0309-reasoning.description": "Интелигентен, изключително бърз модел с разсъждение.", + "grok-4.20-beta-0309-non-reasoning.description": "Вариант без мислене за прости случаи на употреба.", + "grok-4.20-beta-0309-reasoning.description": "Интелигентен, изключително бърз модел, който разсъждава преди да отговори.", "grok-4.20-multi-agent-0309.description": "Екип от 4 или 16 агента — отличен за проучвания; поддържа само xAI сървърни инструменти.", "grok-4.3.description": "Най-истинно търсещият голям езиков модел в света", "grok-4.description": "Най-новият флагман Grok с ненадмината производителност в езика, математиката и разсъжденията — истински универсален модел. В момента сочи към grok-4-0709; поради ограничени ресурси временно е с 10% по-висока цена от официалната и се очаква да се върне към официалната цена по-късно.", @@ -1214,6 +1220,8 @@ "qwq.description": "QwQ е модел за аргументация от семейството на Qwen. В сравнение със стандартните модели, обучени с инструкции, предлага мисловни и логически способности, които значително подобряват ефективността при трудни задачи. QwQ-32B е среден по размер модел, който се конкурира с водещи модели като DeepSeek-R1 и o1-mini.", "qwq_32b.description": "Среден по размер модел за аргументация от семейството на Qwen. В сравнение със стандартните модели, обучени с инструкции, мисловните и логическите способности на QwQ значително подобряват ефективността при трудни задачи.", "r1-1776.description": "R1-1776 е дообучен вариант на DeepSeek R1, създаден да предоставя неконфронтирана, обективна и фактическа информация.", + "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro от ByteDance поддържа текст към видео, изображение към видео (първи кадър, първи+последен кадър) и генериране на звук, синхронизиран с визуализации.", + "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite от BytePlus предлага генериране, обогатено с уеб извличане за информация в реално време, подобрена интерпретация на сложни подсказки и подобрена консистентност на референциите за професионално визуално създаване.", "solar-mini-ja.description": "Solar Mini (Ja) разширява Solar Mini с фокус върху японски език, като запазва ефективността и силната производителност на английски и корейски.", "solar-mini.description": "Solar Mini е компактен LLM, който превъзхожда GPT-3.5, с мощни многоезични възможности, поддържащ английски и корейски, и предлага ефективно решение с малък отпечатък.", "solar-pro.description": "Solar Pro е интелигентен LLM от Upstage, фокусиран върху следване на инструкции на един GPU, с IFEval резултати над 80. Понастоящем поддържа английски; пълното издание е планирано за ноември 2024 с разширена езикова поддръжка и по-дълъг контекст.", diff --git a/locales/bg-BG/plugin.json b/locales/bg-BG/plugin.json index 7e1b1c17a5..c8234c62eb 100644 --- a/locales/bg-BG/plugin.json +++ b/locales/bg-BG/plugin.json @@ -237,6 +237,14 @@ "builtins.lobe-page-agent.apiName.updateNode": "Актуализиране на възел", "builtins.lobe-page-agent.apiName.wrapNodes": "Обвиване на възли", "builtins.lobe-page-agent.title": "Страница", + "builtins.lobe-self-feedback-intent.apiName.declareSelfFeedbackIntent": "Запишете идея за подобрение", + "builtins.lobe-self-feedback-intent.inspector.gap.proposal": "Предложете подобрение", + "builtins.lobe-self-feedback-intent.inspector.memory.write": "Запишете предпочитание", + "builtins.lobe-self-feedback-intent.inspector.rejected": "Не е записано", + "builtins.lobe-self-feedback-intent.inspector.skill.consolidate": "Организирайте методи", + "builtins.lobe-self-feedback-intent.inspector.skill.create": "Открит нов метод", + "builtins.lobe-self-feedback-intent.inspector.skill.refine": "Подобрете метода", + "builtins.lobe-self-feedback-intent.title": "Идеи за подобрение", "builtins.lobe-skill-store.apiName.importFromMarket": "Импортиране от пазара", "builtins.lobe-skill-store.apiName.importSkill": "Импортиране на умение", "builtins.lobe-skill-store.apiName.searchSkill": "Търсене на умения", diff --git a/locales/bg-BG/providers.json b/locales/bg-BG/providers.json index 43b17d4992..f4d1d7b7d6 100644 --- a/locales/bg-BG/providers.json +++ b/locales/bg-BG/providers.json @@ -33,6 +33,7 @@ "jina.description": "Основана през 2020 г., Jina AI е водеща компания в областта на търсещия AI. Технологичният ѝ стек включва векторни модели, преоценители и малки езикови модели за създаване на надеждни генеративни и мултимодални търсещи приложения.", "kimicodingplan.description": "Kimi Code от Moonshot AI предоставя достъп до модели Kimi, включително K2.5, за задачи, свързани с програмиране.", "lmstudio.description": "LM Studio е десктоп приложение за разработка и експериментиране с LLM на вашия компютър.", + "lobehub.description": "LobeHub Cloud използва официални API, за да осъществява достъп до AI модели и измерва използването чрез Кредити, свързани с токените на модела.", "longcat.description": "LongCat е серия от големи модели за генеративен AI, независимо разработени от Meituan. Той е създаден да подобри вътрешната продуктивност на предприятието и да позволи иновативни приложения чрез ефективна изчислителна архитектура и силни мултимодални възможности.", "minimax.description": "Основана през 2021 г., MiniMax създава универсален AI с мултимодални базови модели, включително текстови модели с трилиони параметри, речеви и визуални модели, както и приложения като Hailuo AI.", "minimaxcodingplan.description": "MiniMax Token Plan предоставя достъп до модели MiniMax, включително M2.7, за задачи, свързани с програмиране, чрез абонамент с фиксирана такса.", diff --git a/locales/bg-BG/subscription.json b/locales/bg-BG/subscription.json index 1008ab8c6a..eae4d80e1d 100644 --- a/locales/bg-BG/subscription.json +++ b/locales/bg-BG/subscription.json @@ -41,7 +41,9 @@ "credits.autoTopUp.monthlyLimitDesc": "Максималната сума, която може да бъде автоматично начислена на месец. Оставете празно за без лимит", "credits.autoTopUp.monthlyLimitPlaceholder": "Без лимит", "credits.autoTopUp.monthlyTopUpAmount": "Месечна сума за презареждане", + "credits.autoTopUp.noCustomerHint": "Закупете кредити веднъж, за да запазите метод за плащане, преди да активирате автоматично зареждане.", "credits.autoTopUp.noPaymentMethodHint": "Няма запазен метод на плащане. Автоматичното зареждане изисква запазена карта за автоматично таксуване.", + "credits.autoTopUp.purchaseCredits": "Закупете кредити", "credits.autoTopUp.saveError": "Неуспешно запазване на настройките за автоматично презареждане", "credits.autoTopUp.saveSuccess": "Настройките за автоматично презареждане са запазени", "credits.autoTopUp.setupPaymentMethod": "Добавяне на метод на плащане", @@ -83,6 +85,7 @@ "credits.packages.title": "Моите пакети с кредити", "credits.topUp.cancel": "Отказ", "credits.topUp.custom": "По избор", + "credits.topUp.freeFeeHint": "Зарежданията на безплатния план включват такса за услуга от {{fee}} на 1M кредити.", "credits.topUp.maxAmountError": "Сумата за еднокупуване не може да надвишава ${{max}}", "credits.topUp.purchaseError": "Неуспешна покупка, моля опитайте отново по-късно", "credits.topUp.purchaseNow": "Купи сега", @@ -120,6 +123,9 @@ "keyMissMatch.button": "Възстанови използването и продължи разговора", "keyMissMatch.description": "Поради временна системна грешка, текущото използване на абонамента ви е временно неактивно. Моля, натиснете бутона по-долу, за да възстановите използването и да продължите разговора. Ако това се случва често, моля свържете се с нас по имейл (support@lobehub.com)", "keyMissMatch.title": "Възстанови използването на абонамента сега", + "limitation.chat.budgetReady.action": "Продължете чата", + "limitation.chat.budgetReady.desc": "Вашите налични кредити вече покриват тази заявка.", + "limitation.chat.budgetReady.title": "Кредити готови", "limitation.chat.success.action": "Продължи разговора", "limitation.chat.success.desc": "Вашият абонамент {{plan}} е успешно надграден. Насладете се на AI разговори. Текущият ви план включва:", "limitation.chat.success.title": "Успешна надстройка", @@ -134,14 +140,23 @@ "limitation.hobby.docs": "Преглед на документацията за конфигурация", "limitation.hobby.tip": "Не забравяйте да превключите към модел с персонализиран API ключ", "limitation.hobby.title": "Моля, конфигурирайте API за моделна услуга", + "limitation.image.budgetReady.action": "Продължете генерирането", + "limitation.image.budgetReady.desc": "Вашите налични кредити вече покриват това генериране.", + "limitation.image.budgetReady.title": "Кредити готови", "limitation.image.success.action": "Продължи генерирането", "limitation.image.success.desc": "Вашият абонамент {{plan}} е успешно надграден. Насладете се на AI генериране на изображения. Текущият ви план включва:", "limitation.image.success.title": "Успешна надстройка", "limitation.image.topupSuccess.action": "Продължи генерирането", "limitation.image.topupSuccess.desc": "Вашите допълнителни кредити вече са активни. Насладете се на AI генериране на изображения. Текущият ви план включва:", "limitation.image.topupSuccess.title": "Успешно зареждане", + "limitation.insufficientBudget.approximateDesc": "Тази заявка може да изисква повече кредити. Заредете кредити или надградете плана си.", + "limitation.insufficientBudget.available": "Налични кредити", "limitation.insufficientBudget.desc": "Оставащите ви кредити не са достатъчни за прогнозната цена на този модел. Моля, заредете кредити или изберете по-евтин модел.", + "limitation.insufficientBudget.estimatedDesc": "Оценено е, че тази заявка изисква повече кредити. Заредете кредити или надградете плана си.", + "limitation.insufficientBudget.exactDesc": "Тази заявка изисква повече кредити. Заредете кредити или надградете плана си.", + "limitation.insufficientBudget.required": "Необходими кредити", "limitation.insufficientBudget.retry": "Опитайте отново", + "limitation.insufficientBudget.shortfall": "Недостиг на кредити", "limitation.insufficientBudget.title": "Недостатъчни кредити за този модел", "limitation.limited.action": "Надстрой сега", "limitation.limited.advanceFeature": "Надстрой, за да се възползваш от премиум функциите:", @@ -151,6 +166,7 @@ "limitation.limited.title": "Изчерпани изчислителни кредити", "limitation.limited.topup": "Зареди кредити", "limitation.limited.upgrade": "Надстрой до по-висок план", + "limitation.limited.upgradeToPlan": "Надградете до {{plan}}", "limitation.providers.lock.addNew": "Абонирай се сега, за да създаваш персонализирани AI доставчици", "limitation.providers.lock.enableProvider": "Абонирай се сега, за да активираш този AI доставчик", "limitation.providers.lock.menuItem": "Абонирай се сега, за да конфигурираш персонализирана API услуга", @@ -161,6 +177,9 @@ "limitation.providers.prompter.subTitle": "Персонализираната API услуга е достъпна само за платени планове. Надстрой сега, за да използваш глобални водещи модели", "limitation.providers.prompter.title": "Абонирай се сега, за да използваш персонализирана API услуга", "limitation.providers.tooltip": "Персонализираната API услуга е достъпна само за платени планове", + "limitation.video.budgetReady.action": "Продължете генерирането", + "limitation.video.budgetReady.desc": "Вашите налични кредити вече покриват това генериране.", + "limitation.video.budgetReady.title": "Кредити готови", "limitation.video.success.action": "Продължи с генерирането", "limitation.video.success.desc": "Вашият абонамент {{plan}} беше успешно надграден. Насладете се на AI видео генериране. Текущият ви план включва:", "limitation.video.success.title": "Успешно надграждане", diff --git a/locales/de-DE/chat.json b/locales/de-DE/chat.json index 53b670894a..2886acaa73 100644 --- a/locales/de-DE/chat.json +++ b/locales/de-DE/chat.json @@ -196,6 +196,9 @@ "groupWizard.searchTemplates": "Vorlagen durchsuchen...", "groupWizard.title": "Gruppe erstellen", "groupWizard.useTemplate": "Vorlage verwenden", + "heteroAgent.cloudNotConfigured.action": "Konfigurieren", + "heteroAgent.cloudNotConfigured.desc": "Konfigurieren Sie Ihr Claude Code-Token im Agentenprofil, um Nachrichten zu senden.", + "heteroAgent.cloudNotConfigured.title": "Cloud-Anmeldedaten erforderlich", "heteroAgent.cloudRepo.multiSelected": "{{count}} Repositories ausgewählt", "heteroAgent.cloudRepo.noRepos": "Keine Repositories konfiguriert. Fügen Sie diese in den Agenteneinstellungen hinzu.", "heteroAgent.cloudRepo.notSet": "Kein Repository ausgewählt", @@ -467,6 +470,10 @@ "sharePage.error.unauthorized.subtitle": "Bitte melde dich an, um dieses geteilte Thema anzusehen.", "sharePage.error.unauthorized.title": "Anmeldung erforderlich", "sharePageDisclaimer": "Dieser Inhalt wurde von einem Nutzer geteilt und spiegelt nicht die Ansichten von LobeHub wider. LobeHub übernimmt keine Verantwortung für etwaige Folgen, die sich aus diesem geteilten Inhalt ergeben.", + "signalCallbacks.collapse": "Details ausblenden", + "signalCallbacks.empty": "Keine Callback-Nachrichten", + "signalCallbacks.expand": "Details anzeigen", + "signalCallbacks.title": "{{tool}} · {{count}} Callback-Updates", "stt.action": "Spracheingabe", "stt.loading": "Erkennung läuft...", "stt.prettifying": "Wird überarbeitet...", @@ -689,7 +696,6 @@ "thread.closeSubagentThread": "Unteragenten-Unterhaltung einklappen", "thread.divider": "Unterthema", "thread.openSubagentThread": "Komplette Unteragenten-Unterhaltung anzeigen", - "thread.subagentBadge": "Unteragent", "thread.subagentReadOnlyHint": "SubAgent-Unterhaltungen sind schreibgeschützt – die Ausführung wird vom Hauptagenten gesteuert.", "thread.threadMessageCount": "{{messageCount}} Nachrichten", "thread.title": "Unterthema", diff --git a/locales/de-DE/models.json b/locales/de-DE/models.json index d8fa773630..cb05accf06 100644 --- a/locales/de-DE/models.json +++ b/locales/de-DE/models.json @@ -106,6 +106,7 @@ "MiniMax-Hailuo-2.3.description": "Brandneues Videoerzeugungsmodell mit umfassenden Verbesserungen in Körperbewegung, physikalischem Realismus und Befolgung von Anweisungen.", "MiniMax-M1.description": "Ein neues Inhouse-Argumentationsmodell mit 80K Chain-of-Thought und 1M Eingabe, vergleichbar mit führenden globalen Modellen.", "MiniMax-M2-Stable.description": "Entwickelt für effizientes Coden und Agenten-Workflows mit höherer Parallelität für den kommerziellen Einsatz.", + "MiniMax-M2.1-Lightning.description": "Leistungsstarke mehrsprachige Programmierfähigkeiten mit schnellerer und effizienterer Inferenz.", "MiniMax-M2.1-highspeed.description": "Leistungsstarke mehrsprachige Programmierfähigkeiten, umfassend verbesserte Programmiererfahrung. Schneller und effizienter.", "MiniMax-M2.1.description": "MiniMax-M2.1 ist das Flaggschiff unter den Open-Source-Großmodellen von MiniMax und konzentriert sich auf die Lösung komplexer Aufgaben aus der realen Welt. Seine zentralen Stärken liegen in der mehrsprachigen Programmierfähigkeit und der Fähigkeit, als Agent komplexe Aufgaben zu bewältigen.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: Gleiche Leistung wie M2.5 mit schnellerer Inferenz.", @@ -314,13 +315,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku ist das schnellste und kompakteste Modell von Anthropic, entwickelt für nahezu sofortige Antworten mit schneller, präziser Leistung.", "claude-3-opus-20240229.description": "Claude 3 Opus ist das leistungsstärkste Modell von Anthropic für hochkomplexe Aufgaben. Es überzeugt in Leistung, Intelligenz, Sprachfluss und Verständnis.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet bietet eine ausgewogene Kombination aus Intelligenz und Geschwindigkeit für Unternehmensanwendungen. Es liefert hohe Nutzbarkeit bei geringeren Kosten und zuverlässiger Skalierbarkeit.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 ist das schnellste und intelligenteste Haiku-Modell von Anthropic, mit blitzschneller Geschwindigkeit und erweitertem logischen Denken.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 ist das schnellste und intelligenteste Haiku-Modell von Anthropic, mit blitzschneller Geschwindigkeit und erweitertem Denken.", "claude-haiku-4-5.description": "Claude Haiku 4.5 von Anthropic — Next-Gen-Haiku mit verbessertem Reasoning und Vision.", "claude-haiku-4.5.description": "Claude Haiku 4.5 ist das schnellste und intelligenteste Haiku-Modell von Anthropic, mit blitzschneller Geschwindigkeit und erweiterten Denkfähigkeiten.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking ist eine erweiterte Variante, die ihren Denkprozess offenlegen kann.", "claude-opus-4-1-20250805.description": "Claude Opus 4.1 ist das neueste und leistungsfähigste Modell von Anthropic für hochkomplexe Aufgaben, das in Leistung, Intelligenz, Sprachgewandtheit und Verständnis herausragt.", "claude-opus-4-1.description": "Claude Opus 4.1 von Anthropic — Premium-Reasoning-Modell mit tiefgehender Analysefähigkeit.", - "claude-opus-4-20250514.description": "Claude Opus 4 ist das leistungsstärkste Modell von Anthropic für hochkomplexe Aufgaben, das in Leistung, Intelligenz, Sprachgewandtheit und Verständnis überzeugt.", + "claude-opus-4-20250514.description": "Claude Opus 4 ist das leistungsstärkste Modell von Anthropic für hochkomplexe Aufgaben, das in Leistung, Intelligenz, Sprachgewandtheit und Verständnis herausragt.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 ist das Flaggschiffmodell von Anthropic. Es kombiniert herausragende Intelligenz mit skalierbarer Leistung und ist ideal für komplexe Aufgaben, die höchste Qualität bei Antworten und logischem Denken erfordern.", "claude-opus-4-5.description": "Claude Opus 4.5 von Anthropic — Flaggschiffmodell mit erstklassigem Reasoning und Coding.", "claude-opus-4-6.description": "Claude Opus 4.6 von Anthropic — Flaggschiffmodell mit 1M Kontextfenster und erweitertem Reasoning.", @@ -329,7 +330,7 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 ist das intelligenteste Modell von Anthropic für die Entwicklung von Agenten und Programmierung.", "claude-opus-4.6.description": "Claude Opus 4.6 ist das intelligenteste Modell von Anthropic für die Entwicklung von Agenten und Programmierung.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking kann nahezu sofortige Antworten oder schrittweises Denken mit sichtbarem Prozess erzeugen.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 kann nahezu sofortige Antworten oder ausführliches schrittweises Denken mit sichtbarem Prozess erzeugen.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 ist das bisher intelligenteste Modell von Anthropic, das nahezu sofortige Antworten oder erweitertes schrittweises Denken mit fein abgestimmter Kontrolle für API-Nutzer bietet.", "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 ist das bisher intelligenteste Modell von Anthropic.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 von Anthropic — weiterentwickeltes Sonnet mit verbesserter Coding-Leistung.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 von Anthropic — neuestes Sonnet mit überlegener Coding- und Tool-Nutzung.", @@ -403,7 +404,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) ist ein innovatives Modell mit tiefem Sprachverständnis und Interaktionsfähigkeit.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 ist ein Next-Gen-Denkmodell mit stärkerem komplexem Denken und Chain-of-Thought für tiefgreifende Analyseaufgaben.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 ist ein Next-Gen-Modell für logisches Denken mit stärkeren Fähigkeiten für komplexes Denken und Kettenlogik.", - "deepseek-chat.description": "Ein neues Open-Source-Modell, das allgemeine und Code-Fähigkeiten kombiniert. Es bewahrt den allgemeinen Dialog des Chat-Modells und die starken Codierungsfähigkeiten des Coder-Modells, mit besserer Präferenzabstimmung. DeepSeek-V2.5 verbessert auch das Schreiben und das Befolgen von Anweisungen.", + "deepseek-chat.description": "Kompatibilitätsalias für den DeepSeek V4 Flash-Modus ohne Denken. Geplant für die Ausmusterung — verwenden Sie stattdessen DeepSeek V4 Flash.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B ist ein Code-Sprachmodell, trainiert auf 2 B Tokens (87 % Code, 13 % chinesisch/englischer Text). Es bietet ein 16K-Kontextfenster und Fill-in-the-Middle-Aufgaben für projektweite Codevervollständigung und Snippet-Ergänzung.", "deepseek-coder-v2.description": "DeepSeek Coder V2 ist ein Open-Source-MoE-Code-Modell mit starker Leistung bei Programmieraufgaben, vergleichbar mit GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 ist ein Open-Source-MoE-Code-Modell mit starker Leistung bei Programmieraufgaben, vergleichbar mit GPT-4 Turbo.", @@ -425,7 +426,7 @@ "deepseek-r1-fast-online.description": "DeepSeek R1 Schnellversion mit Echtzeit-Websuche – kombiniert 671B-Fähigkeiten mit schneller Reaktion.", "deepseek-r1-online.description": "DeepSeek R1 Vollversion mit 671B Parametern und Echtzeit-Websuche – bietet stärkeres Verständnis und bessere Generierung.", "deepseek-r1.description": "DeepSeek-R1 nutzt Cold-Start-Daten vor dem RL und erreicht vergleichbare Leistungen wie OpenAI-o1 bei Mathematik, Programmierung und logischem Denken.", - "deepseek-reasoner.description": "Ein DeepSeek-Logikmodell, das sich auf komplexe logische Denkaufgaben konzentriert.", + "deepseek-reasoner.description": "Kompatibilitätsalias für den DeepSeek V4 Flash-Denkmodus. Geplant für die Ausmusterung — verwenden Sie stattdessen DeepSeek V4 Flash.", "deepseek-v2.description": "DeepSeek V2 ist ein effizientes MoE-Modell für kostengünstige Verarbeitung.", "deepseek-v2:236b.description": "DeepSeek V2 236B ist das codefokussierte Modell von DeepSeek mit starker Codegenerierung.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 ist ein MoE-Modell mit 671B Parametern und herausragenden Stärken in Programmierung, technischer Kompetenz, Kontextverständnis und Langtextverarbeitung.", @@ -490,6 +491,8 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 ist ein Bildgenerierungsmodell von ByteDance Seed, das Text- und Bildeingaben unterstützt und eine hochgradig kontrollierbare, hochwertige Bildgenerierung ermöglicht. Es erzeugt Bilder aus Texteingaben.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 ist das neueste multimodale Bildmodell von ByteDance, das Text-zu-Bild, Bild-zu-Bild und Batch-Bilderzeugung integriert und dabei Allgemeinwissen und logisches Denken einbezieht. Im Vergleich zur vorherigen Version 4.0 bietet es eine deutlich verbesserte Generierungsqualität, bessere Konsistenz bei der Bearbeitung und Multi-Bild-Fusion. Es ermöglicht eine präzisere Kontrolle über visuelle Details, erzeugt kleine Texte und kleine Gesichter natürlicher und erreicht harmonischere Layouts und Farben, wodurch die Gesamtästhetik verbessert wird.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite ist das neueste Bildgenerierungsmodell von ByteDance. Erstmals integriert es Online-Retrieval-Funktionen, die es ermöglichen, Echtzeit-Webinformationen einzubeziehen und die Aktualität der generierten Bilder zu verbessern. Die Intelligenz des Modells wurde ebenfalls aufgerüstet, um komplexe Anweisungen und visuelle Inhalte präzise zu interpretieren. Darüber hinaus bietet es eine verbesserte globale Wissensabdeckung, Konsistenz bei Referenzen und Generierungsqualität in professionellen Szenarien, um den visuellen Erstellungsbedarf auf Unternehmensebene besser zu erfüllen.", + "dreamina-seedance-2-0-260128.description": "Seedance 2.0 von ByteDance ist das leistungsstärkste Videogenerierungsmodell, das multimodale Referenzvideogenerierung, Videobearbeitung, Videoerweiterung, Text-zu-Video und Bild-zu-Video mit synchronisiertem Audio unterstützt.", + "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast von ByteDance bietet die gleichen Funktionen wie Seedance 2.0 mit schnellerer Generierungsgeschwindigkeit zu einem wettbewerbsfähigeren Preis.", "emohaa.description": "Emohaa ist ein Modell für psychische Gesundheit mit professionellen Beratungsfähigkeiten, das Nutzern hilft, emotionale Probleme zu verstehen.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B ist ein quelloffenes, leichtgewichtiges Modell für lokale und individuell angepasste Bereitstellungen.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview ist ein Vorschau-Modell mit 8K Kontextlänge zur Bewertung von ERNIE 4.5.", @@ -514,7 +517,8 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K ist ein schnelles Denkmodell mit 32K Kontext für komplexe Schlussfolgerungen und mehrstufige Gespräche.", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview ist ein Vorschau-Modell mit Denkfähigkeit zur Bewertung und zum Testen.", "ernie-x1.1.description": "ERNIE X1.1 ist ein Vorschau-Denkmodell für Evaluierung und Tests.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 ist ein Bildgenerierungsmodell von ByteDance Seed, das Text- und Bildeingaben unterstützt und hochkontrollierbare, qualitativ hochwertige Bilder generiert. Es erstellt Bilder basierend auf Textaufforderungen.", + "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5, entwickelt vom ByteDance Seed-Team, unterstützt die Bearbeitung und Komposition mehrerer Bilder. Es bietet verbesserte Konsistenz von Motiven, präzise Befolgung von Anweisungen, räumliches Logikverständnis, ästhetischen Ausdruck, Posterlayout und Logodesign mit hochpräziser Text-Bild-Wiedergabe.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, entwickelt von ByteDance Seed, unterstützt Text- und Bildeingaben für hochkontrollierbare, qualitativ hochwertige Bildgenerierung aus Eingabeaufforderungen.", "fal-ai/flux-kontext/dev.description": "FLUX.1-Modell mit Fokus auf Bildbearbeitung, unterstützt Text- und Bildeingaben.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] akzeptiert Texte und Referenzbilder als Eingabe und ermöglicht gezielte lokale Bearbeitungen sowie komplexe globale Szenentransformationen.", "fal-ai/flux/krea.description": "Flux Krea [dev] ist ein Bildgenerierungsmodell mit ästhetischer Ausrichtung auf realistischere, natürliche Bilder.", @@ -522,8 +526,8 @@ "fal-ai/hunyuan-image/v3.description": "Ein leistungsstarkes natives multimodales Bildgenerierungsmodell.", "fal-ai/imagen4/preview.description": "Hochwertiges Bildgenerierungsmodell von Google.", "fal-ai/nano-banana.description": "Nano Banana ist das neueste, schnellste und effizienteste native multimodale Modell von Google. Es ermöglicht Bildgenerierung und -bearbeitung im Dialog.", - "fal-ai/qwen-image-edit.description": "Ein professionelles Bildbearbeitungsmodell des Qwen-Teams, das semantische und optische Bearbeitungen unterstützt, präzise chinesische und englische Texte bearbeitet und hochwertige Bearbeitungen wie Stilübertragungen und Objektrotation ermöglicht.", - "fal-ai/qwen-image.description": "Ein leistungsstarkes Bildgenerierungsmodell des Qwen-Teams mit beeindruckender chinesischer Textdarstellung und vielfältigen visuellen Stilen.", + "fal-ai/qwen-image-edit.description": "Ein professionelles Bildbearbeitungsmodell vom Qwen-Team, das semantische und optische Bearbeitungen, präzise chinesische/englische Textbearbeitung, Stilübertragung, Drehung und mehr unterstützt.", + "fal-ai/qwen-image.description": "Ein leistungsstarkes Bildgenerierungsmodell vom Qwen-Team mit starker chinesischer Textrendering-Fähigkeit und vielfältigen visuellen Stilen.", "flux-1-schnell.description": "Ein Text-zu-Bild-Modell mit 12 Milliarden Parametern von Black Forest Labs, das latente adversariale Diffusionsdistillation nutzt, um hochwertige Bilder in 1–4 Schritten zu erzeugen. Es konkurriert mit geschlossenen Alternativen und ist unter Apache-2.0 für persönliche, Forschungs- und kommerzielle Nutzung verfügbar.", "flux-dev.description": "Open-Source‑Bildgenerierungsmodell für Forschung und Entwicklung, effizient optimiert für nichtkommerzielle Innovationsforschung.", "flux-kontext-max.description": "Modernste kontextuelle Bildgenerierung und -bearbeitung, kombiniert Text und Bilder für präzise, kohärente Ergebnisse.", @@ -565,7 +569,7 @@ "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) ist Googles Bildgenerierungsmodell und unterstützt auch multimodale Chats.", "gemini-3-pro-preview.description": "Gemini 3 Pro ist Googles leistungsstärkstes Agenten- und Vibe-Coding-Modell. Es bietet reichhaltigere visuelle Inhalte und tiefere Interaktionen auf Basis modernster logischer Fähigkeiten.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) ist Googles schnellstes natives Bildgenerierungsmodell mit Denkunterstützung, konversationaler Bildgenerierung und -bearbeitung.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) ist Googles schnellstes natives Bildgenerierungsmodell mit Unterstützung für Denken, konversationelle Bildgenerierung und -bearbeitung.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) liefert Pro-Level-Bildqualität mit Flash-Geschwindigkeit und unterstützt multimodale Chats.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview ist Googles kosteneffizientestes multimodales Modell, optimiert für hochvolumige agentische Aufgaben, Übersetzung und Datenverarbeitung.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite ist Googles kosteneffizientestes multimodales Modell, optimiert für hochvolumige agentenbasierte Aufgaben, Übersetzungen und Datenverarbeitung.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview verbessert Gemini 3 Pro mit erweiterten Fähigkeiten für logisches Denken und unterstützt mittleres Denklevel.", @@ -730,6 +734,8 @@ "grok-4-fast-reasoning.description": "Wir freuen uns, Grok 4 Fast vorzustellen – unser neuester Fortschritt bei kosteneffizienten Denkmodellen.", "grok-4.20-0309-non-reasoning.description": "Eine Non-Reasoning-Variante für einfache Anwendungsfälle.", "grok-4.20-0309-reasoning.description": "Intelligentes, extrem schnelles Modell, das vor der Antwort aktiv denkt.", + "grok-4.20-beta-0309-non-reasoning.description": "Eine Variante ohne Denkprozesse für einfache Anwendungsfälle.", + "grok-4.20-beta-0309-reasoning.description": "Intelligentes, blitzschnelles Modell, das vor der Antwort überlegt.", "grok-4.20-multi-agent-0309.description": "Ein Team aus 4 oder 16 Agenten, hervorragend für Rechercheaufgaben. Unterstützt derzeit keine clientseitigen Tools. Unterstützt ausschließlich serverseitige xAI-Tools (z. B. X Search, Web Search Tools) und Remote-MCP-Tools.", "grok-4.3.description": "Das wahrheitssuchendste große Sprachmodell der Welt", "grok-4.description": "Das neueste Grok-Flaggschiff mit unvergleichlicher Leistung in Sprache, Mathematik und Logik — ein wahrer Alleskönner. Derzeit verweist es auf grok-4-0709; aufgrund begrenzter Ressourcen ist der Preis vorübergehend 10 % höher als der offizielle Preis und wird voraussichtlich später wieder auf den offiziellen Preis zurückkehren.", @@ -1214,6 +1220,8 @@ "qwq.description": "QwQ ist ein Schlussfolgerungsmodell aus der Qwen-Familie. Im Vergleich zu standardmäßig instruktionstunierten Modellen bietet es überlegene Denk- und Schlussfolgerungsfähigkeiten, die die Leistung bei nachgelagerten Aufgaben deutlich verbessern – insbesondere bei schwierigen Problemen. QwQ-32B ist ein mittelgroßes Modell, das mit führenden Schlussfolgerungsmodellen wie DeepSeek-R1 und o1-mini mithalten kann.", "qwq_32b.description": "Mittelgroßes Schlussfolgerungsmodell aus der Qwen-Familie. Im Vergleich zu standardmäßig instruktionstunierten Modellen steigern QwQs Denk- und Schlussfolgerungsfähigkeiten die Leistung bei nachgelagerten Aufgaben deutlich – insbesondere bei schwierigen Problemen.", "r1-1776.description": "R1-1776 ist eine nachtrainierte Variante von DeepSeek R1, die darauf ausgelegt ist, unzensierte, objektive und faktenbasierte Informationen bereitzustellen.", + "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro von ByteDance unterstützt Text-zu-Video, Bild-zu-Video (erster Frame, erster+letzter Frame) und Audiogenerierung synchronisiert mit visuellen Inhalten.", + "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite von BytePlus bietet webgestützte Generierung für Echtzeitinformationen, verbesserte Interpretation komplexer Eingabeaufforderungen und verbesserte Konsistenz von Referenzen für professionelle visuelle Kreationen.", "solar-mini-ja.description": "Solar Mini (Ja) erweitert Solar Mini mit einem Fokus auf Japanisch und behält dabei eine effiziente und starke Leistung in Englisch und Koreanisch bei.", "solar-mini.description": "Solar Mini ist ein kompaktes LLM, das GPT-3.5 übertrifft. Es bietet starke mehrsprachige Fähigkeiten in Englisch und Koreanisch und ist eine effiziente Lösung mit kleinem Ressourcenbedarf.", "solar-pro.description": "Solar Pro ist ein hochintelligentes LLM von Upstage, das auf Befolgen von Anweisungen auf einer einzelnen GPU ausgelegt ist und IFEval-Werte über 80 erreicht. Derzeit wird Englisch unterstützt; die vollständige Veröffentlichung mit erweitertem Sprachsupport und längeren Kontexten war für November 2024 geplant.", diff --git a/locales/de-DE/plugin.json b/locales/de-DE/plugin.json index e5c23ac430..9f75eeb62f 100644 --- a/locales/de-DE/plugin.json +++ b/locales/de-DE/plugin.json @@ -237,6 +237,14 @@ "builtins.lobe-page-agent.apiName.updateNode": "Knoten aktualisieren", "builtins.lobe-page-agent.apiName.wrapNodes": "Knoten umschließen", "builtins.lobe-page-agent.title": "Seite", + "builtins.lobe-self-feedback-intent.apiName.declareSelfFeedbackIntent": "Verbesserungsidee aufzeichnen", + "builtins.lobe-self-feedback-intent.inspector.gap.proposal": "Verbesserung vorschlagen", + "builtins.lobe-self-feedback-intent.inspector.memory.write": "Präferenz aufzeichnen", + "builtins.lobe-self-feedback-intent.inspector.rejected": "Nicht aufgezeichnet", + "builtins.lobe-self-feedback-intent.inspector.skill.consolidate": "Methoden organisieren", + "builtins.lobe-self-feedback-intent.inspector.skill.create": "Neue Methode gefunden", + "builtins.lobe-self-feedback-intent.inspector.skill.refine": "Methode verbessern", + "builtins.lobe-self-feedback-intent.title": "Verbesserungsideen", "builtins.lobe-skill-store.apiName.importFromMarket": "Aus dem Markt importieren", "builtins.lobe-skill-store.apiName.importSkill": "Fähigkeit importieren", "builtins.lobe-skill-store.apiName.searchSkill": "Fähigkeiten suchen", diff --git a/locales/de-DE/providers.json b/locales/de-DE/providers.json index 0741192bc7..434040880b 100644 --- a/locales/de-DE/providers.json +++ b/locales/de-DE/providers.json @@ -33,6 +33,7 @@ "jina.description": "Jina AI wurde 2020 gegründet und ist ein führendes Unternehmen im Bereich Such-KI. Der Such-Stack umfasst Vektormodelle, Reranker und kleine Sprachmodelle für zuverlässige, hochwertige generative und multimodale Suchanwendungen.", "kimicodingplan.description": "Kimi Code von Moonshot AI bietet Zugriff auf Kimi-Modelle, darunter K2.5, für Coding-Aufgaben.", "lmstudio.description": "LM Studio ist eine Desktop-App zur Entwicklung und zum Experimentieren mit LLMs auf dem eigenen Computer.", + "lobehub.description": "LobeHub Cloud verwendet offizielle APIs, um auf KI-Modelle zuzugreifen, und misst die Nutzung mit Credits, die an Modell-Token gebunden sind.", "longcat.description": "LongCat ist eine Reihe von generativen KI-Großmodellen, die unabhängig von Meituan entwickelt wurden. Sie sind darauf ausgelegt, die Produktivität innerhalb des Unternehmens zu steigern und innovative Anwendungen durch eine effiziente Rechenarchitektur und starke multimodale Fähigkeiten zu ermöglichen.", "minimax.description": "MiniMax wurde 2021 gegründet und entwickelt allgemeine KI mit multimodalen Foundation-Modellen, darunter Textmodelle mit Billionen Parametern, Sprach- und Bildmodelle sowie Apps wie Hailuo AI.", "minimaxcodingplan.description": "Der MiniMax Token Plan bietet Zugriff auf MiniMax-Modelle, darunter M2.7, für Coding-Aufgaben im Rahmen eines Festpreis-Abonnements.", diff --git a/locales/de-DE/subscription.json b/locales/de-DE/subscription.json index 9148ccc18f..f4c929c206 100644 --- a/locales/de-DE/subscription.json +++ b/locales/de-DE/subscription.json @@ -41,7 +41,9 @@ "credits.autoTopUp.monthlyLimitDesc": "Maximaler Betrag, der pro Monat automatisch aufgeladen werden kann. Leer lassen für kein Limit", "credits.autoTopUp.monthlyLimitPlaceholder": "Kein Limit", "credits.autoTopUp.monthlyTopUpAmount": "Monatlicher Aufladebetrag", + "credits.autoTopUp.noCustomerHint": "Kaufen Sie einmalig Credits, um eine Zahlungsmethode zu speichern, bevor Sie die automatische Aufladung aktivieren.", "credits.autoTopUp.noPaymentMethodHint": "Keine Zahlungsmethode hinterlegt. Automatisches Aufladen erfordert eine gespeicherte Karte, um automatisch belastet zu werden.", + "credits.autoTopUp.purchaseCredits": "Credits kaufen", "credits.autoTopUp.saveError": "Fehler beim Speichern der Einstellungen für die automatische Aufladung", "credits.autoTopUp.saveSuccess": "Einstellungen für die automatische Aufladung gespeichert", "credits.autoTopUp.setupPaymentMethod": "Zahlungsmethode hinzufügen", @@ -83,6 +85,7 @@ "credits.packages.title": "Meine Guthabenpakete", "credits.topUp.cancel": "Abbrechen", "credits.topUp.custom": "Benutzerdefiniert", + "credits.topUp.freeFeeHint": "Aufladungen im kostenlosen Tarif beinhalten eine {{fee}} Servicegebühr pro 1M Credits.", "credits.topUp.maxAmountError": "Der Betrag einer einzelnen Aufladung darf ${{max}} nicht überschreiten", "credits.topUp.purchaseError": "Kauf fehlgeschlagen, bitte versuche es später erneut", "credits.topUp.purchaseNow": "Jetzt kaufen", @@ -120,6 +123,9 @@ "keyMissMatch.button": "Nutzung wiederherstellen und Gespräch fortsetzen", "keyMissMatch.description": "Aufgrund eines gelegentlichen Systemfehlers ist deine aktuelle Abonnementnutzung vorübergehend inaktiv. Bitte klicke auf den Button unten, um die Nutzung wiederherzustellen und das Gespräch fortzusetzen. Wenn dies wiederholt auftritt, kontaktiere uns bitte per E-Mail (support@lobehub.com)", "keyMissMatch.title": "Abonnementnutzung jetzt wiederherstellen", + "limitation.chat.budgetReady.action": "Weiter chatten", + "limitation.chat.budgetReady.desc": "Ihre verfügbaren Credits decken jetzt diese Anfrage ab.", + "limitation.chat.budgetReady.title": "Credits bereit", "limitation.chat.success.action": "Weiter chatten", "limitation.chat.success.desc": "Dein {{plan}}-Abonnement wurde erfolgreich aktualisiert. Viel Spaß beim Chatten mit KI. Dein aktueller Plan enthält:", "limitation.chat.success.title": "Upgrade erfolgreich", @@ -134,14 +140,23 @@ "limitation.hobby.docs": "Konfigurationsdokumente anzeigen", "limitation.hobby.tip": "Denke daran, auf ein Modell mit benutzerdefiniertem API-Schlüssel zu wechseln", "limitation.hobby.title": "Bitte Modell-Service-API konfigurieren", + "limitation.image.budgetReady.action": "Weiter generieren", + "limitation.image.budgetReady.desc": "Ihre verfügbaren Credits decken jetzt diese Erstellung ab.", + "limitation.image.budgetReady.title": "Credits bereit", "limitation.image.success.action": "Weiter generieren", "limitation.image.success.desc": "Dein {{plan}}-Abonnement wurde erfolgreich aktualisiert. Viel Spaß bei der KI-Bilderzeugung. Dein aktueller Plan enthält:", "limitation.image.success.title": "Upgrade erfolgreich", "limitation.image.topupSuccess.action": "Weiter generieren", "limitation.image.topupSuccess.desc": "Deine aufgeladenen Guthaben sind jetzt aktiv. Viel Spaß bei der KI-Bilderzeugung. Dein aktueller Plan enthält:", "limitation.image.topupSuccess.title": "Aufladung erfolgreich", + "limitation.insufficientBudget.approximateDesc": "Diese Anfrage könnte mehr Credits benötigen. Laden Sie Credits auf oder aktualisieren Sie Ihren Tarif.", + "limitation.insufficientBudget.available": "Verfügbare Credits", "limitation.insufficientBudget.desc": "Ihre verbleibenden Guthaben reichen nicht aus, um die geschätzten Kosten dieses Modells zu decken. Bitte laden Sie Guthaben auf oder wechseln Sie zu einem günstigeren Modell.", + "limitation.insufficientBudget.estimatedDesc": "Diese Anfrage benötigt voraussichtlich mehr Credits. Laden Sie Credits auf oder aktualisieren Sie Ihren Tarif.", + "limitation.insufficientBudget.exactDesc": "Diese Anfrage benötigt mehr Credits. Laden Sie Credits auf oder aktualisieren Sie Ihren Tarif.", + "limitation.insufficientBudget.required": "Benötigte Credits", "limitation.insufficientBudget.retry": "Erneut versuchen", + "limitation.insufficientBudget.shortfall": "Credit-Mangel", "limitation.insufficientBudget.title": "Unzureichende Guthaben für dieses Modell", "limitation.limited.action": "Jetzt upgraden", "limitation.limited.advanceFeature": "Upgrade für Premium-Funktionen:", @@ -151,6 +166,7 @@ "limitation.limited.title": "Rechenguthaben aufgebraucht", "limitation.limited.topup": "Guthaben aufladen", "limitation.limited.upgrade": "Upgrade auf höheren Plan", + "limitation.limited.upgradeToPlan": "Upgrade auf {{plan}}", "limitation.providers.lock.addNew": "Jetzt abonnieren, um benutzerdefinierte KI-Anbieter zu erstellen", "limitation.providers.lock.enableProvider": "Jetzt abonnieren, um diesen KI-Anbieter zu aktivieren", "limitation.providers.lock.menuItem": "Jetzt abonnieren, um benutzerdefinierten API-Service zu konfigurieren", @@ -161,6 +177,9 @@ "limitation.providers.prompter.subTitle": "Benutzerdefinierter API-Service ist nur in kostenpflichtigen Plänen verfügbar. Upgrade jetzt, um globale Modellanbieter zu nutzen", "limitation.providers.prompter.title": "Jetzt abonnieren, um benutzerdefinierten API-Service zu nutzen", "limitation.providers.tooltip": "Benutzerdefinierter API-Service ist nur in kostenpflichtigen Plänen verfügbar", + "limitation.video.budgetReady.action": "Weiter generieren", + "limitation.video.budgetReady.desc": "Ihre verfügbaren Credits decken jetzt diese Erstellung ab.", + "limitation.video.budgetReady.title": "Credits bereit", "limitation.video.success.action": "Weiter generieren", "limitation.video.success.desc": "Ihr {{plan}}-Abonnement wurde erfolgreich aktualisiert. Viel Spaß mit der KI-Videoerstellung. Ihr aktueller Plan beinhaltet:", "limitation.video.success.title": "Upgrade erfolgreich", diff --git a/locales/en-US/chat.json b/locales/en-US/chat.json index 1fe13bf5ab..e5bbe8a838 100644 --- a/locales/en-US/chat.json +++ b/locales/en-US/chat.json @@ -470,6 +470,10 @@ "sharePage.error.unauthorized.subtitle": "Please sign in to view this shared topic.", "sharePage.error.unauthorized.title": "Sign In Required", "sharePageDisclaimer": "This content is shared by a user and does not represent the views of LobeHub. LobeHub is not responsible for any consequences arising from this shared content.", + "signalCallbacks.collapse": "Hide details", + "signalCallbacks.empty": "No callback messages", + "signalCallbacks.expand": "Show details", + "signalCallbacks.title": "{{tool}} · {{count}} callback updates", "stt.action": "Voice Input", "stt.loading": "Recognizing...", "stt.prettifying": "Polishing...", @@ -692,7 +696,6 @@ "thread.closeSubagentThread": "Collapse SubAgent conversation", "thread.divider": "Subtopic", "thread.openSubagentThread": "View full SubAgent conversation", - "thread.subagentBadge": "SubAgent", "thread.subagentReadOnlyHint": "SubAgent conversations are read-only — execution is driven by the parent agent.", "thread.threadMessageCount": "{{messageCount}} messages", "thread.title": "Subtopic", diff --git a/locales/en-US/models.json b/locales/en-US/models.json index b8dd7e71f2..3c724d77ab 100644 --- a/locales/en-US/models.json +++ b/locales/en-US/models.json @@ -106,6 +106,7 @@ "MiniMax-Hailuo-2.3.description": "Brand-new video generation model with comprehensive upgrades in body motion, physical realism, and instruction following.", "MiniMax-M1.description": "A new in-house reasoning model with 80K chain-of-thought and 1M input, delivering performance comparable to top global models.", "MiniMax-M2-Stable.description": "Built for efficient coding and agent workflows, with higher concurrency for commercial use.", + "MiniMax-M2.1-Lightning.description": "Powerful multilingual programming capabilities with faster and more efficient inference.", "MiniMax-M2.1-highspeed.description": "Powerful multilingual programming capabilities, comprehensively upgraded programming experience. Faster and more efficient.", "MiniMax-M2.1.description": "MiniMax-M2.1 is a flagship open-source large model from MiniMax, focusing on solving complex real-world tasks. Its core strengths are multi-language programming capabilities and the ability to solve complex tasks as an Agent.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: Same performance as M2.5 with faster inference.", @@ -314,13 +315,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku is Anthropic’s fastest and most compact model, designed for near-instant responses with fast, accurate performance.", "claude-3-opus-20240229.description": "Claude 3 Opus is Anthropic’s most powerful model for highly complex tasks, excelling in performance, intelligence, fluency, and comprehension.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet balances intelligence and speed for enterprise workloads, delivering high utility at lower cost and reliable large-scale deployment.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 is Anthropic’s fastest and smartest Haiku model, with lightning speed and extended reasoning.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 is Anthropic's fastest and most intelligent Haiku model, with lightning speed and extended thinking.", "claude-haiku-4-5.description": "Claude Haiku 4.5 by Anthropic — next-gen Haiku with enhanced reasoning and vision.", "claude-haiku-4.5.description": "Claude Haiku 4.5 is Anthropic’s fastest and smartest Haiku model, with lightning speed and extended reasoning.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking is an advanced variant that can reveal its reasoning process.", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1 is Anthropic’s latest and most capable model for highly complex tasks, excelling in performance, intelligence, fluency, and understanding.", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1 is Anthropic's latest and most capable model for highly complex tasks, excelling in performance, intelligence, fluency, and understanding.", "claude-opus-4-1.description": "Claude Opus 4.1 by Anthropic — premium reasoning model with deep analysis capabilities.", - "claude-opus-4-20250514.description": "Claude Opus 4 is Anthropic’s most powerful model for highly complex tasks, excelling in performance, intelligence, fluency, and comprehension.", + "claude-opus-4-20250514.description": "Claude Opus 4 is Anthropic's most powerful model for highly complex tasks, excelling in performance, intelligence, fluency, and understanding.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 is Anthropic’s flagship model, combining outstanding intelligence with scalable performance, ideal for complex tasks requiring the highest-quality responses and reasoning.", "claude-opus-4-5.description": "Claude Opus 4.5 by Anthropic — flagship model with top-tier reasoning and coding.", "claude-opus-4-6.description": "Claude Opus 4.6 by Anthropic — 1M context window flagship with advanced reasoning.", @@ -329,8 +330,8 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 is Anthropic’s most intelligent model for building agents and coding.", "claude-opus-4.6.description": "Claude Opus 4.6 is Anthropic’s most intelligent model for building agents and coding.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking can produce near-instant responses or extended step-by-step thinking with visible process.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 can produce near-instant responses or extended step-by-step thinking with visible process.", - "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 is Anthropic’s most intelligent model to date.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 is Anthropic's most intelligent model to date, offering near-instant responses or extended step-by-step thinking with fine-grained control for API users.", + "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 is Anthropic's most intelligent model to date.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 by Anthropic — improved Sonnet with enhanced coding performance.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 by Anthropic — latest Sonnet with superior coding and tool use.", "claude-sonnet-4.5.description": "Claude Sonnet 4.5 is Anthropic’s most intelligent model to date.", @@ -403,7 +404,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) is an innovative model offering deep language understanding and interaction.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 is a next-gen reasoning model with stronger complex reasoning and chain-of-thought for deep analysis tasks.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 is a next-gen reasoning model with stronger complex reasoning and chain-of-thought capabilities.", - "deepseek-chat.description": "A new open-source model combining general and code abilities. It preserves the chat model’s general dialogue and the coder model’s strong coding, with better preference alignment. DeepSeek-V2.5 also improves writing and instruction following.", + "deepseek-chat.description": "Compatibility alias for DeepSeek V4 Flash non-thinking mode. Slated for deprecation — use DeepSeek V4 Flash instead.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B is a code language model trained on 2T tokens (87% code, 13% Chinese/English text). It introduces a 16K context window and fill-in-the-middle tasks, providing project-level code completion and snippet infilling.", "deepseek-coder-v2.description": "DeepSeek Coder V2 is an open-source MoE code model that performs strongly on coding tasks, comparable to GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 is an open-source MoE code model that performs strongly on coding tasks, comparable to GPT-4 Turbo.", @@ -425,7 +426,7 @@ "deepseek-r1-fast-online.description": "DeepSeek R1 fast full version with real-time web search, combining 671B-scale capability and faster response.", "deepseek-r1-online.description": "DeepSeek R1 full version with 671B parameters and real-time web search, offering stronger understanding and generation.", "deepseek-r1.description": "DeepSeek-R1 uses cold-start data before RL and performs comparably to OpenAI-o1 on math, coding, and reasoning.", - "deepseek-reasoner.description": "A DeepSeek reasoning model focused on complex logical reasoning tasks.", + "deepseek-reasoner.description": "Compatibility alias for DeepSeek V4 Flash thinking mode. Slated for deprecation — use DeepSeek V4 Flash instead.", "deepseek-v2.description": "DeepSeek V2 is an efficient MoE model for cost-effective processing.", "deepseek-v2:236b.description": "DeepSeek V2 236B is DeepSeek’s code-focused model with strong code generation.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 is a 671B-parameter MoE model with standout strengths in programming and technical capability, context understanding, and long-text handling.", @@ -490,6 +491,8 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 is an image generation model from ByteDance Seed, supporting text and image inputs with highly controllable, high-quality image generation. It generates images from text prompts.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 is ByteDance’s latest multimodal image model, integrating text-to-image, image-to-image, and batch image generation capabilities, while incorporating commonsense and reasoning abilities. Compared to the previous 4.0 version, it delivers significantly improved generation quality, with better editing consistency and multi-image fusion. It offers more precise control over visual details, producing small text and small faces more naturally, and achieves more harmonious layout and color, enhancing overall aesthetics.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite is ByteDance’s latest image-generation model. For the first time, it integrates online retrieval capabilities, allowing it to incorporate real-time web information and enhance the timeliness of generated images. The model’s intelligence has also been upgraded, enabling precise interpretation of complex instructions and visual content. Additionally, it offers improved global knowledge coverage, reference consistency, and generation quality in professional scenarios, better meeting enterprise-level visual creation needs.", + "dreamina-seedance-2-0-260128.description": "Seedance 2.0 by ByteDance is the most powerful video generation model, supporting multimodal reference video generation, video editing, video extension, text-to-video, and image-to-video with synchronized audio.", + "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast by ByteDance offers the same capabilities as Seedance 2.0 with faster generation speeds at a more competitive price.", "emohaa.description": "Emohaa is a mental health model with professional counseling abilities to help users understand emotional issues.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B is an open-source lightweight model for local and customized deployment.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview is an 8K context preview model for evaluating ERNIE 4.5.", @@ -514,7 +517,8 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K is a fast thinking model with 32K context for complex reasoning and multi-turn chat.", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview is a thinking-model preview for evaluation and testing.", "ernie-x1.1.description": "ERNIE X1.1 is a thinking-model preview for evaluation and testing.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 is an image generation model from ByteDance Seed, supporting text and image inputs with highly controllable, high-quality image generation. It generates images from text prompts.", + "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5, built by ByteDance Seed team, supports multi-image editing and composition. Features enhanced subject consistency, precise instruction following, spatial logic understanding, aesthetic expression, poster layout and logo design with high-precision text-image rendering.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, built by ByteDance Seed, supports text and image inputs for highly controllable, high-quality image generation from prompts.", "fal-ai/flux-kontext/dev.description": "FLUX.1 model focused on image editing, supporting text and image inputs.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] accepts text and reference images as input, enabling targeted local edits and complex global scene transformations.", "fal-ai/flux/krea.description": "Flux Krea [dev] is an image generation model with an aesthetic bias toward more realistic, natural images.", @@ -522,8 +526,8 @@ "fal-ai/hunyuan-image/v3.description": "A powerful native multimodal image generation model.", "fal-ai/imagen4/preview.description": "High-quality image generation model from Google.", "fal-ai/nano-banana.description": "Nano Banana is Google’s newest, fastest, and most efficient native multimodal model, enabling image generation and editing through conversation.", - "fal-ai/qwen-image-edit.description": "A professional image editing model from the Qwen team that supports semantic and appearance edits, precisely edits Chinese and English text, and enables high-quality edits such as style transfer and object rotation.", - "fal-ai/qwen-image.description": "A powerful image generation model from the Qwen team with impressive Chinese text rendering and diverse visual styles.", + "fal-ai/qwen-image-edit.description": "A professional image editing model from the Qwen team, supporting semantic and appearance edits, precise Chinese/English text editing, style transfer, rotation, and more.", + "fal-ai/qwen-image.description": "A powerful image generation model from the Qwen team with strong Chinese text rendering and diverse visual styles.", "flux-1-schnell.description": "A 12B-parameter text-to-image model from Black Forest Labs using latent adversarial diffusion distillation to generate high-quality images in 1-4 steps. It rivals closed alternatives and is released under Apache-2.0 for personal, research, and commercial use.", "flux-dev.description": "Open-source R&D image generation model, efficiently optimized for non-commercial innovation research.", "flux-kontext-max.description": "State-of-the-art contextual image generation and editing, combining text and images for precise, coherent results.", @@ -562,10 +566,10 @@ "gemini-3-flash-preview.description": "Gemini 3 Flash is the smartest model built for speed, combining cutting-edge intelligence with excellent search grounding.", "gemini-3-flash.description": "Gemini 3 Flash by Google — ultra-fast model with multimodal input support.", "gemini-3-pro-image-preview.description": "Gemini 3 Pro Image (Nano Banana Pro) is Google's image generation model that also supports multimodal dialogue.", - "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) is Google’s image generation model and also supports multimodal chat.", + "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) is Google's image generation model and also supports multimodal chat.", "gemini-3-pro-preview.description": "Gemini 3 Pro is Google’s most powerful agent and vibe-coding model, delivering richer visuals and deeper interaction on top of state-of-the-art reasoning.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) is Google's fastest native image generation model with thinking support, conversational image generation and editing.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) is Google's fastest native image generation model with thinking support, conversational image generation and editing.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) delivers Pro-level image quality at Flash speed with multimodal chat support.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview is Google's most cost-efficient multimodal model, optimized for high-volume agentic tasks, translation, and data processing.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite is Google's most cost-efficient multimodal model, optimized for high-volume agentic tasks, translation, and data processing.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview improves on Gemini 3 Pro with enhanced reasoning capabilities and adds medium thinking level support.", @@ -730,6 +734,8 @@ "grok-4-fast-reasoning.description": "We’re excited to release Grok 4 Fast, our latest progress in cost-effective reasoning models.", "grok-4.20-0309-non-reasoning.description": "A non-reasoning variant for simple use cases", "grok-4.20-0309-reasoning.description": "Intelligent, blazing-fast model that reasons before responding", + "grok-4.20-beta-0309-non-reasoning.description": "A non-reasoning variant for simple use cases", + "grok-4.20-beta-0309-reasoning.description": "Intelligent, blazing-fast model that reasons before responding", "grok-4.20-multi-agent-0309.description": "A team of 4 or 16 agents, Excels at research use cases, Does not currently support client-side tools. Only supports xAI server side tools (eg X Search, Web Search tools) and remote MCP tools.", "grok-4.3.description": "The most truth-seeking large language model in the world", "grok-4.description": "Latest Grok flagship with unmatched performance in language, math, and reasoning — a true all-rounder. Currently points to grok-4-0709; due to limited resources it is temporarily 10% higher than official pricing and is expected to return to official price later.", @@ -1214,6 +1220,8 @@ "qwq.description": "QwQ is a reasoning model in the Qwen family. Compared with standard instruction-tuned models, it brings thinking and reasoning abilities that significantly improve downstream performance, especially on hard problems. QwQ-32B is a mid-sized reasoning model that competes well with top reasoning models like DeepSeek-R1 and o1-mini.", "qwq_32b.description": "Mid-sized reasoning model in the Qwen family. Compared with standard instruction-tuned models, QwQ’s thinking and reasoning abilities significantly boost downstream performance, especially on hard problems.", "r1-1776.description": "R1-1776 is a post-trained variant of DeepSeek R1 designed to provide uncensored, unbiased factual information.", + "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro by ByteDance supports text-to-video, image-to-video (first frame, first+last frame), and audio generation synchronized with visuals.", + "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite by BytePlus features web-retrieval-augmented generation for real-time information, enhanced complex prompt interpretation, and improved reference consistency for professional visual creation.", "solar-mini-ja.description": "Solar Mini (Ja) extends Solar Mini with a focus on Japanese while maintaining efficient, strong performance in English and Korean.", "solar-mini.description": "Solar Mini is a compact LLM that outperforms GPT-3.5, with strong multilingual capability supporting English and Korean, offering an efficient small-footprint solution.", "solar-pro.description": "Solar Pro is a high-intelligence LLM from Upstage, focused on instruction following on a single GPU, with IFEval scores above 80. It currently supports English; the full release was planned for November 2024 with expanded language support and longer context.", diff --git a/locales/en-US/providers.json b/locales/en-US/providers.json index cdbf854011..ece32d6726 100644 --- a/locales/en-US/providers.json +++ b/locales/en-US/providers.json @@ -33,6 +33,7 @@ "jina.description": "Founded in 2020, Jina AI is a leading search AI company. Its search stack includes vector models, rerankers, and small language models to build reliable, high-quality generative and multimodal search apps.", "kimicodingplan.description": "Kimi Code from Moonshot AI provides access to Kimi models including K2.5 for coding tasks.", "lmstudio.description": "LM Studio is a desktop app for developing and experimenting with LLMs on your computer.", + "lobehub.description": "LobeHub Cloud uses official APIs to access AI models and measures usage with Credits tied to model tokens.", "longcat.description": "LongCat is a series of generative AI large models independently developed by Meituan. It is designed to enhance internal enterprise productivity and enable innovative applications through an efficient computational architecture and strong multimodal capabilities.", "minimax.description": "Founded in 2021, MiniMax builds general-purpose AI with multimodal foundation models, including trillion-parameter MoE text models, speech models, and vision models, along with apps like Hailuo AI.", "minimaxcodingplan.description": "MiniMax Token Plan provides access to MiniMax models including M2.7 for coding tasks via a fixed-fee subscription.", diff --git a/locales/en-US/subscription.json b/locales/en-US/subscription.json index b06623b0de..67fa0138a8 100644 --- a/locales/en-US/subscription.json +++ b/locales/en-US/subscription.json @@ -123,6 +123,9 @@ "keyMissMatch.button": "Restore usage and continue conversation", "keyMissMatch.description": "Due to an occasional system failure, your current subscription usage is temporarily inactive. Please click the button below to restore usage and continue the conversation. If this happens repeatedly, please contact us via email (support@lobehub.com)", "keyMissMatch.title": "Restore Subscription Usage Now", + "limitation.chat.budgetReady.action": "Continue Chatting", + "limitation.chat.budgetReady.desc": "Your available credits now cover this request.", + "limitation.chat.budgetReady.title": "Credits Ready", "limitation.chat.success.action": "Continue Chatting", "limitation.chat.success.desc": "Your {{plan}} subscription has been upgraded successfully. Enjoy AI chatting. Your current plan includes:", "limitation.chat.success.title": "Upgrade Successful", @@ -137,15 +140,24 @@ "limitation.hobby.docs": "View configuration docs", "limitation.hobby.tip": "Remember to switch to a model with custom API Key", "limitation.hobby.title": "Please Configure Model Service API", + "limitation.image.budgetReady.action": "Continue Generating", + "limitation.image.budgetReady.desc": "Your available credits now cover this generation.", + "limitation.image.budgetReady.title": "Credits Ready", "limitation.image.success.action": "Continue Generating", "limitation.image.success.desc": "Your {{plan}} subscription has been upgraded successfully. Enjoy AI image generation. Your current plan includes:", "limitation.image.success.title": "Upgrade Successful", "limitation.image.topupSuccess.action": "Continue Generating", "limitation.image.topupSuccess.desc": "Your top-up credits are now active. Enjoy AI image generation. Your current plan includes:", "limitation.image.topupSuccess.title": "Top-up Successful", - "limitation.insufficientBudget.desc": "Your remaining credits are not enough for the estimated cost of this model. Please top up credits or switch to a less expensive model.", + "limitation.insufficientBudget.approximateDesc": "This request may need more credits. Top up credits or upgrade your plan.", + "limitation.insufficientBudget.available": "Available Credits", + "limitation.insufficientBudget.desc": "Your credits are not enough to continue. Top up credits or upgrade your plan.", + "limitation.insufficientBudget.estimatedDesc": "This request is estimated to need more credits. Top up credits or upgrade your plan.", + "limitation.insufficientBudget.exactDesc": "This request needs more credits. Top up credits or upgrade your plan.", + "limitation.insufficientBudget.required": "Required Credits", "limitation.insufficientBudget.retry": "Retry", - "limitation.insufficientBudget.title": "Insufficient Credits for This Model", + "limitation.insufficientBudget.shortfall": "Credit Shortfall", + "limitation.insufficientBudget.title": "Insufficient Credits", "limitation.limited.action": "Upgrade Now", "limitation.limited.advanceFeature": "Upgrade to enjoy premium features:", "limitation.limited.desc": "Your {{plan}} computing credits have been exhausted. Upgrade now to get more credits.", @@ -154,6 +166,7 @@ "limitation.limited.title": "Computing Credits Exhausted", "limitation.limited.topup": "Top-Up Credits", "limitation.limited.upgrade": "Upgrade to Higher Plan", + "limitation.limited.upgradeToPlan": "Upgrade to {{plan}}", "limitation.providers.lock.addNew": "Subscribe now to create custom AI providers", "limitation.providers.lock.enableProvider": "Subscribe now to enable this AI provider", "limitation.providers.lock.menuItem": "Subscribe now to configure custom API service", @@ -164,6 +177,9 @@ "limitation.providers.prompter.subTitle": "Custom API service is only available for paid plans. Upgrade now to enjoy global mainstream model services", "limitation.providers.prompter.title": "Subscribe now to use custom API service", "limitation.providers.tooltip": "Custom API service is only available for paid plans", + "limitation.video.budgetReady.action": "Continue Generating", + "limitation.video.budgetReady.desc": "Your available credits now cover this generation.", + "limitation.video.budgetReady.title": "Credits Ready", "limitation.video.success.action": "Continue Generating", "limitation.video.success.desc": "Your {{plan}} subscription has been upgraded successfully. Enjoy AI video generation. Your current plan includes:", "limitation.video.success.title": "Upgrade Successful", diff --git a/locales/es-ES/chat.json b/locales/es-ES/chat.json index 90626dcce3..a70f138d2c 100644 --- a/locales/es-ES/chat.json +++ b/locales/es-ES/chat.json @@ -196,6 +196,9 @@ "groupWizard.searchTemplates": "Buscar plantillas...", "groupWizard.title": "Crear grupo", "groupWizard.useTemplate": "Usar plantilla", + "heteroAgent.cloudNotConfigured.action": "Configurar", + "heteroAgent.cloudNotConfigured.desc": "Configura tu token de Claude Code en el perfil del agente para comenzar a enviar mensajes.", + "heteroAgent.cloudNotConfigured.title": "Se requieren credenciales de la nube", "heteroAgent.cloudRepo.multiSelected": "{{count}} repositorios seleccionados", "heteroAgent.cloudRepo.noRepos": "No hay repositorios configurados. Agrégalos en la configuración del agente.", "heteroAgent.cloudRepo.notSet": "Ningún repositorio seleccionado", @@ -467,6 +470,10 @@ "sharePage.error.unauthorized.subtitle": "Por favor, inicia sesión para ver este tema compartido.", "sharePage.error.unauthorized.title": "Inicio de sesión requerido", "sharePageDisclaimer": "Este contenido ha sido compartido por un usuario y no representa las opiniones de LobeHub. LobeHub no se hace responsable de las consecuencias derivadas de este contenido compartido.", + "signalCallbacks.collapse": "Ocultar detalles", + "signalCallbacks.empty": "No hay mensajes de callback", + "signalCallbacks.expand": "Mostrar detalles", + "signalCallbacks.title": "{{tool}} · {{count}} actualizaciones de callback", "stt.action": "Entrada por voz", "stt.loading": "Reconociendo...", "stt.prettifying": "Puliendo...", @@ -689,7 +696,6 @@ "thread.closeSubagentThread": "Contraer conversación del subagente", "thread.divider": "Subtema", "thread.openSubagentThread": "Ver conversación completa del subagente", - "thread.subagentBadge": "Subagente", "thread.subagentReadOnlyHint": "Las conversaciones del SubAgente son de solo lectura: la ejecución está dirigida por el agente principal.", "thread.threadMessageCount": "{{messageCount}} mensajes", "thread.title": "Subtema", diff --git a/locales/es-ES/models.json b/locales/es-ES/models.json index 4f07f218ab..fca8e2cdb9 100644 --- a/locales/es-ES/models.json +++ b/locales/es-ES/models.json @@ -106,6 +106,7 @@ "MiniMax-Hailuo-2.3.description": "Nuevo modelo de generación de video con mejoras integrales en movimiento corporal, realismo físico y seguimiento de instrucciones.", "MiniMax-M1.description": "Nuevo modelo de razonamiento interno con 80K de cadena de pensamiento y 1M de entrada, con rendimiento comparable a los mejores modelos globales.", "MiniMax-M2-Stable.description": "Diseñado para codificación eficiente y flujos de trabajo de agentes, con mayor concurrencia para uso comercial.", + "MiniMax-M2.1-Lightning.description": "Potentes capacidades de programación multilingüe con inferencia más rápida y eficiente.", "MiniMax-M2.1-highspeed.description": "Potentes capacidades de programación multilingüe, con una experiencia de programación completamente mejorada. Más rápido y eficiente.", "MiniMax-M2.1.description": "MiniMax-M2.1 es un modelo insignia de código abierto de MiniMax, enfocado en resolver tareas complejas del mundo real. Sus principales fortalezas son sus capacidades de programación multilingüe y su habilidad para resolver tareas complejas como un Agente.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: Mismo rendimiento que M2.5 con inferencia más rápida.", @@ -314,13 +315,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku es el modelo más rápido y compacto de Anthropic, diseñado para respuestas casi instantáneas con rendimiento rápido y preciso.", "claude-3-opus-20240229.description": "Claude 3 Opus es el modelo más potente de Anthropic para tareas altamente complejas, destacando en rendimiento, inteligencia, fluidez y comprensión.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet equilibra inteligencia y velocidad para cargas de trabajo empresariales, ofreciendo alta utilidad a menor costo y despliegue confiable a gran escala.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 es el modelo Haiku más rápido e inteligente de Anthropic, con velocidad relámpago y razonamiento extendido.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 es el modelo Haiku más rápido e inteligente de Anthropic, con velocidad relámpago y pensamiento extendido.", "claude-haiku-4-5.description": "Claude Haiku 4.5 de Anthropic: Haiku de nueva generación con razonamiento y visión mejorados.", "claude-haiku-4.5.description": "Claude Haiku 4.5 es el modelo Haiku más rápido e inteligente de Anthropic, con velocidad relámpago y razonamiento extendido.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking es una variante avanzada que puede mostrar su proceso de razonamiento.", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1 es el modelo más reciente y capaz de Anthropic para tareas altamente complejas, destacando en rendimiento, inteligencia, fluidez y comprensión.", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1 es el modelo más reciente y avanzado de Anthropic para tareas altamente complejas, destacando en rendimiento, inteligencia, fluidez y comprensión.", "claude-opus-4-1.description": "Claude Opus 4.1 de Anthropic: modelo de razonamiento premium con profundas capacidades de análisis.", - "claude-opus-4-20250514.description": "Claude Opus 4 es el modelo más potente de Anthropic para tareas altamente complejas, sobresaliendo en rendimiento, inteligencia, fluidez y comprensión.", + "claude-opus-4-20250514.description": "Claude Opus 4 es el modelo más poderoso de Anthropic para tareas altamente complejas, destacando en rendimiento, inteligencia, fluidez y comprensión.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 es el modelo insignia de Anthropic, combinando inteligencia excepcional con rendimiento escalable, ideal para tareas complejas que requieren respuestas y razonamiento de la más alta calidad.", "claude-opus-4-5.description": "Claude Opus 4.5 de Anthropic: modelo insignia con razonamiento y programación de primer nivel.", "claude-opus-4-6.description": "Claude Opus 4.6 de Anthropic: modelo insignia con ventana de contexto de 1M y razonamiento avanzado.", @@ -329,7 +330,7 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 es el modelo más inteligente de Anthropic para construir agentes y programar.", "claude-opus-4.6.description": "Claude Opus 4.6 es el modelo más inteligente de Anthropic para construir agentes y programar.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking puede generar respuestas casi instantáneas o pensamiento paso a paso extendido con proceso visible.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 puede generar respuestas casi instantáneas o razonamientos detallados paso a paso con un proceso visible.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 es el modelo más inteligente de Anthropic hasta la fecha, ofreciendo respuestas casi instantáneas o pensamiento paso a paso extendido con control detallado para usuarios de API.", "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 es el modelo más inteligente de Anthropic hasta la fecha.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 de Anthropic: versión mejorada de Sonnet con mayor rendimiento en programación.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 de Anthropic: última versión de Sonnet con programación superior y uso avanzado de herramientas.", @@ -403,7 +404,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) es un modelo innovador que ofrece una comprensión profunda del lenguaje y una interacción avanzada.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 es un modelo de razonamiento de nueva generación con capacidades mejoradas para razonamiento complejo y cadenas de pensamiento, ideal para tareas de análisis profundo.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 es un modelo de razonamiento de próxima generación con capacidades mejoradas de razonamiento complejo y cadenas de pensamiento.", - "deepseek-chat.description": "Un nuevo modelo de código abierto que combina habilidades generales y de programación. Preserva el diálogo general del modelo de chat y la sólida capacidad de codificación del modelo de programación, con mejor alineación de preferencias. DeepSeek-V2.5 también mejora la escritura y el seguimiento de instrucciones.", + "deepseek-chat.description": "Alias de compatibilidad para el modo sin razonamiento de DeepSeek V4 Flash. Programado para ser descontinuado: utiliza DeepSeek V4 Flash en su lugar.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B es un modelo de lenguaje para código entrenado con 2T de tokens (87% código, 13% texto en chino/inglés). Introduce una ventana de contexto de 16K y tareas de completado intermedio, ofreciendo completado de código a nivel de proyecto y relleno de fragmentos.", "deepseek-coder-v2.description": "DeepSeek Coder V2 es un modelo de código MoE de código abierto que tiene un rendimiento sólido en tareas de programación, comparable a GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 es un modelo de código MoE de código abierto que tiene un rendimiento sólido en tareas de programación, comparable a GPT-4 Turbo.", @@ -425,7 +426,7 @@ "deepseek-r1-fast-online.description": "Versión completa rápida de DeepSeek R1 con búsqueda web en tiempo real, combinando capacidad a escala 671B y respuesta ágil.", "deepseek-r1-online.description": "Versión completa de DeepSeek R1 con 671B de parámetros y búsqueda web en tiempo real, ofreciendo mejor comprensión y generación.", "deepseek-r1.description": "DeepSeek-R1 utiliza datos de arranque en frío antes del aprendizaje por refuerzo y tiene un rendimiento comparable a OpenAI-o1 en matemáticas, programación y razonamiento.", - "deepseek-reasoner.description": "Un modelo de razonamiento DeepSeek enfocado en tareas de razonamiento lógico complejo.", + "deepseek-reasoner.description": "Alias de compatibilidad para el modo de razonamiento de DeepSeek V4 Flash. Programado para ser descontinuado: utiliza DeepSeek V4 Flash en su lugar.", "deepseek-v2.description": "DeepSeek V2 es un modelo MoE eficiente para procesamiento rentable.", "deepseek-v2:236b.description": "DeepSeek V2 236B es el modelo de DeepSeek centrado en código con fuerte generación de código.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 es un modelo MoE con 671 mil millones de parámetros, con fortalezas destacadas en programación, capacidad técnica, comprensión de contexto y manejo de textos largos.", @@ -490,6 +491,8 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 es un modelo de generación de imágenes de ByteDance Seed que admite entradas de texto e imagen con generación de imágenes de alta calidad y altamente controlable. Genera imágenes a partir de indicaciones de texto.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 es el último modelo multimodal de imágenes de ByteDance, que integra capacidades de texto a imagen, imagen a imagen y generación de imágenes por lotes, mientras incorpora sentido común y habilidades de razonamiento. En comparación con la versión 4.0 anterior, ofrece una calidad de generación significativamente mejorada, con mayor consistencia en la edición y fusión de múltiples imágenes. Proporciona un control más preciso sobre los detalles visuales, produciendo texto y rostros pequeños de manera más natural, y logra una disposición y color más armoniosos, mejorando la estética general.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite es el último modelo de generación de imágenes de ByteDance. Por primera vez, integra capacidades de recuperación en línea, permitiendo incorporar información web en tiempo real y mejorar la actualidad de las imágenes generadas. La inteligencia del modelo también ha sido mejorada, permitiendo una interpretación precisa de instrucciones complejas y contenido visual. Además, ofrece una mejor cobertura de conocimiento global, consistencia de referencia y calidad de generación en escenarios profesionales, satisfaciendo mejor las necesidades de creación visual a nivel empresarial.", + "dreamina-seedance-2-0-260128.description": "Seedance 2.0 de ByteDance es el modelo de generación de video más poderoso, compatible con generación de video multimodal de referencia, edición de video, extensión de video, texto a video e imagen a video con audio sincronizado.", + "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast de ByteDance ofrece las mismas capacidades que Seedance 2.0 con velocidades de generación más rápidas a un precio más competitivo.", "emohaa.description": "Emohaa es un modelo de salud mental con capacidades profesionales de asesoramiento para ayudar a los usuarios a comprender problemas emocionales.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B es un modelo ligero de código abierto para implementación local y personalizada.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview es un modelo de vista previa con contexto de 8K para evaluar ERNIE 4.5.", @@ -514,7 +517,8 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K es un modelo de pensamiento rápido con contexto de 32K para razonamiento complejo y chat de múltiples turnos.", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview es una vista previa del modelo de pensamiento para evaluación y pruebas.", "ernie-x1.1.description": "ERNIE X1.1 es un modelo de pensamiento en vista previa para evaluación y pruebas.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 es un modelo de generación de imágenes de ByteDance Seed, que admite entradas de texto e imagen con generación de imágenes altamente controlable y de alta calidad. Genera imágenes a partir de indicaciones de texto.", + "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5, desarrollado por el equipo Seed de ByteDance, admite edición y composición de múltiples imágenes. Incluye consistencia mejorada de sujetos, seguimiento preciso de instrucciones, comprensión de lógica espacial, expresión estética, diseño de carteles y logotipos con renderizado de texto-imagen de alta precisión.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, desarrollado por ByteDance Seed, admite entradas de texto e imagen para generación de imágenes altamente controlable y de alta calidad a partir de indicaciones.", "fal-ai/flux-kontext/dev.description": "Modelo FLUX.1 centrado en la edición de imágenes, compatible con entradas de texto e imagen.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] acepta texto e imágenes de referencia como entrada, permitiendo ediciones locales dirigidas y transformaciones globales complejas de escenas.", "fal-ai/flux/krea.description": "Flux Krea [dev] es un modelo de generación de imágenes con una inclinación estética hacia imágenes más realistas y naturales.", @@ -522,8 +526,8 @@ "fal-ai/hunyuan-image/v3.description": "Un potente modelo nativo multimodal de generación de imágenes.", "fal-ai/imagen4/preview.description": "Modelo de generación de imágenes de alta calidad de Google.", "fal-ai/nano-banana.description": "Nano Banana es el modelo multimodal nativo más nuevo, rápido y eficiente de Google, que permite generación y edición de imágenes mediante conversación.", - "fal-ai/qwen-image-edit.description": "Un modelo profesional de edición de imágenes del equipo Qwen que admite ediciones semánticas y de apariencia, edita con precisión texto en chino e inglés, y permite ediciones de alta calidad como transferencia de estilo y rotación de objetos.", - "fal-ai/qwen-image.description": "Un potente modelo de generación de imágenes del equipo Qwen con impresionante renderizado de texto en chino y estilos visuales diversos.", + "fal-ai/qwen-image-edit.description": "Un modelo profesional de edición de imágenes del equipo Qwen, que admite ediciones semánticas y de apariencia, edición precisa de texto en chino/inglés, transferencia de estilo, rotación y más.", + "fal-ai/qwen-image.description": "Un modelo poderoso de generación de imágenes del equipo Qwen con un fuerte renderizado de texto en chino y estilos visuales diversos.", "flux-1-schnell.description": "Modelo de texto a imagen con 12 mil millones de parámetros de Black Forest Labs que utiliza destilación difusiva adversarial latente para generar imágenes de alta calidad en 1 a 4 pasos. Compite con alternativas cerradas y se lanza bajo licencia Apache-2.0 para uso personal, de investigación y comercial.", "flux-dev.description": "Modelo de generación de imágenes de I+D de código abierto, optimizado de forma eficiente para investigación innovadora no comercial.", "flux-kontext-max.description": "Generación y edición de imágenes contextual de última generación, combinando texto e imágenes para resultados precisos y coherentes.", @@ -565,7 +569,7 @@ "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) es el modelo de generación de imágenes de Google y también admite chat multimodal.", "gemini-3-pro-preview.description": "Gemini 3 Pro es el agente más potente de Google y modelo de codificación emocional, que ofrece visuales más ricos e interacción más profunda sobre un razonamiento de última generación.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) es el modelo nativo de generación de imágenes más rápido de Google con soporte de pensamiento, generación conversacional de imágenes y edición.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) es el modelo nativo de generación de imágenes más rápido de Google con soporte de razonamiento, generación conversacional de imágenes y edición.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) ofrece calidad de imagen a nivel Pro a velocidad Flash con soporte para chat multimodal.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview es el modelo multimodal más rentable de Google, optimizado para tareas agentivas de alto volumen, traducción y procesamiento de datos.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite es el modelo multimodal más eficiente en costos de Google, optimizado para tareas agentivas de alto volumen, traducción y procesamiento de datos.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview mejora las capacidades de razonamiento de Gemini 3 Pro y añade soporte para un nivel de pensamiento medio.", @@ -730,6 +734,8 @@ "grok-4-fast-reasoning.description": "Nos complace lanzar Grok 4 Fast, nuestro último avance en modelos de razonamiento rentables.", "grok-4.20-0309-non-reasoning.description": "Variante sin razonamiento para casos de uso simples.", "grok-4.20-0309-reasoning.description": "Modelo inteligente y rapidísimo que razona antes de responder.", + "grok-4.20-beta-0309-non-reasoning.description": "Una variante sin razonamiento para casos de uso simples.", + "grok-4.20-beta-0309-reasoning.description": "Modelo inteligente y ultrarrápido que razona antes de responder.", "grok-4.20-multi-agent-0309.description": "Equipo de 4 o 16 agentes. Destaca en casos de investigación. No admite herramientas del lado del cliente. Solo admite herramientas del lado del servidor de xAI (como X Search, Web Search) y herramientas MCP remotas.", "grok-4.3.description": "El modelo de lenguaje grande más orientado a la verdad en el mundo.", "grok-4.description": "Último modelo insignia de Grok con un rendimiento inigualable en lenguaje, matemáticas y razonamiento — un verdadero todoterreno. Actualmente apunta a grok-4-0709; debido a recursos limitados, tiene un precio temporalmente un 10% más alto que el oficial y se espera que regrese al precio oficial más adelante.", @@ -1214,6 +1220,8 @@ "qwq.description": "QwQ es un modelo de razonamiento de la familia Qwen. En comparación con los modelos estándar ajustados por instrucciones, ofrece capacidades de pensamiento y razonamiento que mejoran significativamente el rendimiento en tareas difíciles. QwQ-32B es un modelo de razonamiento de tamaño medio que compite con los mejores modelos como DeepSeek-R1 y o1-mini.", "qwq_32b.description": "Modelo de razonamiento de tamaño medio de la familia Qwen. En comparación con los modelos estándar ajustados por instrucciones, las capacidades de pensamiento y razonamiento de QwQ mejoran significativamente el rendimiento en tareas difíciles.", "r1-1776.description": "R1-1776 es una variante postentrenada de DeepSeek R1 diseñada para proporcionar información factual sin censura ni sesgo.", + "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro de ByteDance admite texto a video, imagen a video (primer cuadro, primer+último cuadro) y generación de audio sincronizado con visuales.", + "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite de BytePlus incluye generación aumentada con recuperación web para información en tiempo real, interpretación mejorada de indicaciones complejas y mayor consistencia de referencia para creación visual profesional.", "solar-mini-ja.description": "Solar Mini (Ja) amplía Solar Mini con un enfoque en japonés, manteniendo un rendimiento eficiente y sólido en inglés y coreano.", "solar-mini.description": "Solar Mini es un modelo LLM compacto que supera a GPT-3.5, con una sólida capacidad multilingüe compatible con inglés y coreano, ofreciendo una solución eficiente de bajo consumo.", "solar-pro.description": "Solar Pro es un LLM de alta inteligencia de Upstage, enfocado en el seguimiento de instrucciones en una sola GPU, con puntuaciones IFEval superiores a 80. Actualmente admite inglés; el lanzamiento completo estaba previsto para noviembre de 2024 con soporte de idiomas ampliado y contexto más largo.", diff --git a/locales/es-ES/plugin.json b/locales/es-ES/plugin.json index bb45e28a48..39e2724f68 100644 --- a/locales/es-ES/plugin.json +++ b/locales/es-ES/plugin.json @@ -237,6 +237,14 @@ "builtins.lobe-page-agent.apiName.updateNode": "Actualizar nodo", "builtins.lobe-page-agent.apiName.wrapNodes": "Envolver nodos", "builtins.lobe-page-agent.title": "Página", + "builtins.lobe-self-feedback-intent.apiName.declareSelfFeedbackIntent": "Registrar idea de mejora", + "builtins.lobe-self-feedback-intent.inspector.gap.proposal": "Sugerir mejora", + "builtins.lobe-self-feedback-intent.inspector.memory.write": "Registrar preferencia", + "builtins.lobe-self-feedback-intent.inspector.rejected": "No registrado", + "builtins.lobe-self-feedback-intent.inspector.skill.consolidate": "Organizar métodos", + "builtins.lobe-self-feedback-intent.inspector.skill.create": "Nuevo método encontrado", + "builtins.lobe-self-feedback-intent.inspector.skill.refine": "Mejorar método", + "builtins.lobe-self-feedback-intent.title": "Ideas de mejora", "builtins.lobe-skill-store.apiName.importFromMarket": "Importar del Mercado", "builtins.lobe-skill-store.apiName.importSkill": "Importar Habilidad", "builtins.lobe-skill-store.apiName.searchSkill": "Buscar Habilidades", diff --git a/locales/es-ES/providers.json b/locales/es-ES/providers.json index c4ef10fb49..310682cd8a 100644 --- a/locales/es-ES/providers.json +++ b/locales/es-ES/providers.json @@ -33,6 +33,7 @@ "jina.description": "Fundada en 2020, Jina AI es una empresa líder en búsqueda con IA. Su pila de búsqueda incluye modelos vectoriales, reordenadores y pequeños modelos de lenguaje para construir aplicaciones generativas y multimodales confiables y de alta calidad.", "kimicodingplan.description": "Kimi Code de Moonshot AI proporciona acceso a los modelos Kimi, incluidos K2.5, para tareas de codificación.", "lmstudio.description": "LM Studio es una aplicación de escritorio para desarrollar y experimentar con LLMs en tu ordenador.", + "lobehub.description": "LobeHub Cloud utiliza APIs oficiales para acceder a modelos de IA y mide el uso con Créditos vinculados a los tokens del modelo.", "longcat.description": "LongCat es una serie de modelos grandes de inteligencia artificial generativa desarrollados de manera independiente por Meituan. Está diseñado para mejorar la productividad interna de la empresa y permitir aplicaciones innovadoras mediante una arquitectura computacional eficiente y sólidas capacidades multimodales.", "minimax.description": "Fundada en 2021, MiniMax desarrolla IA de propósito general con modelos fundacionales multimodales, incluyendo modelos de texto MoE con billones de parámetros, modelos de voz y visión, junto con aplicaciones como Hailuo AI.", "minimaxcodingplan.description": "El Plan de Tokens MiniMax proporciona acceso a los modelos MiniMax, incluidos M2.7, para tareas de codificación mediante una suscripción de tarifa fija.", diff --git a/locales/es-ES/subscription.json b/locales/es-ES/subscription.json index 6c02030dc0..9ca7e5c7be 100644 --- a/locales/es-ES/subscription.json +++ b/locales/es-ES/subscription.json @@ -41,7 +41,9 @@ "credits.autoTopUp.monthlyLimitDesc": "Cantidad máxima que se puede cargar automáticamente por mes. Déjalo vacío para no establecer un límite", "credits.autoTopUp.monthlyLimitPlaceholder": "Sin límite", "credits.autoTopUp.monthlyTopUpAmount": "Monto de Recarga Mensual", + "credits.autoTopUp.noCustomerHint": "Compra créditos una vez para guardar un método de pago antes de habilitar la recarga automática.", "credits.autoTopUp.noPaymentMethodHint": "No hay un método de pago registrado. La recarga automática necesita una tarjeta guardada para realizar cargos automáticamente.", + "credits.autoTopUp.purchaseCredits": "Comprar Créditos", "credits.autoTopUp.saveError": "Error al guardar la configuración de recarga automática", "credits.autoTopUp.saveSuccess": "Configuración de recarga automática guardada", "credits.autoTopUp.setupPaymentMethod": "Agregar método de pago", @@ -83,6 +85,7 @@ "credits.packages.title": "Mis Paquetes de Créditos", "credits.topUp.cancel": "Cancelar", "credits.topUp.custom": "Personalizado", + "credits.topUp.freeFeeHint": "Las recargas del plan gratuito incluyen una tarifa de servicio de {{fee}} por cada 1M de créditos.", "credits.topUp.maxAmountError": "El monto de compra no puede exceder ${{max}}", "credits.topUp.purchaseError": "Error en la compra, por favor intenta más tarde", "credits.topUp.purchaseNow": "Comprar Ahora", @@ -120,6 +123,9 @@ "keyMissMatch.button": "Restaurar uso y continuar conversación", "keyMissMatch.description": "Debido a una falla ocasional del sistema, el uso de tu suscripción actual está temporalmente inactivo. Haz clic en el botón de abajo para restaurar el uso y continuar la conversación. Si esto ocurre repetidamente, contáctanos por correo electrónico (support@lobehub.com)", "keyMissMatch.title": "Restaurar Uso de Suscripción Ahora", + "limitation.chat.budgetReady.action": "Continuar Chateando", + "limitation.chat.budgetReady.desc": "Tus créditos disponibles ahora cubren esta solicitud.", + "limitation.chat.budgetReady.title": "Créditos Listos", "limitation.chat.success.action": "Seguir Chateando", "limitation.chat.success.desc": "Tu suscripción {{plan}} se ha actualizado con éxito. Disfruta del chat con IA. Tu plan actual incluye:", "limitation.chat.success.title": "Actualización Exitosa", @@ -134,14 +140,23 @@ "limitation.hobby.docs": "Ver documentación de configuración", "limitation.hobby.tip": "Recuerda cambiar a un modelo con clave API personalizada", "limitation.hobby.title": "Por favor Configura la API del Servicio de Modelos", + "limitation.image.budgetReady.action": "Continuar Generando", + "limitation.image.budgetReady.desc": "Tus créditos disponibles ahora cubren esta generación.", + "limitation.image.budgetReady.title": "Créditos Listos", "limitation.image.success.action": "Seguir Generando", "limitation.image.success.desc": "Tu suscripción {{plan}} se ha actualizado con éxito. Disfruta de la generación de imágenes con IA. Tu plan actual incluye:", "limitation.image.success.title": "Actualización Exitosa", "limitation.image.topupSuccess.action": "Seguir Generando", "limitation.image.topupSuccess.desc": "Tus créditos recargados están activos. Disfruta de la generación de imágenes con IA. Tu plan actual incluye:", "limitation.image.topupSuccess.title": "Recarga Exitosa", + "limitation.insufficientBudget.approximateDesc": "Es posible que esta solicitud necesite más créditos. Recarga créditos o mejora tu plan.", + "limitation.insufficientBudget.available": "Créditos Disponibles", "limitation.insufficientBudget.desc": "Tus créditos restantes no son suficientes para el costo estimado de este modelo. Por favor, recarga créditos o cambia a un modelo menos costoso.", + "limitation.insufficientBudget.estimatedDesc": "Se estima que esta solicitud necesita más créditos. Recarga créditos o mejora tu plan.", + "limitation.insufficientBudget.exactDesc": "Esta solicitud necesita más créditos. Recarga créditos o mejora tu plan.", + "limitation.insufficientBudget.required": "Créditos Requeridos", "limitation.insufficientBudget.retry": "Reintentar", + "limitation.insufficientBudget.shortfall": "Déficit de Créditos", "limitation.insufficientBudget.title": "Créditos Insuficientes para Este Modelo", "limitation.limited.action": "Mejorar Ahora", "limitation.limited.advanceFeature": "Mejora para disfrutar de funciones premium:", @@ -151,6 +166,7 @@ "limitation.limited.title": "Créditos de Cómputo Agotados", "limitation.limited.topup": "Recargar Créditos", "limitation.limited.upgrade": "Mejorar a un Plan Superior", + "limitation.limited.upgradeToPlan": "Mejorar al plan {{plan}}", "limitation.providers.lock.addNew": "Suscríbete ahora para crear proveedores de IA personalizados", "limitation.providers.lock.enableProvider": "Suscríbete ahora para habilitar este proveedor de IA", "limitation.providers.lock.menuItem": "Suscríbete ahora para configurar el servicio API personalizado", @@ -161,6 +177,9 @@ "limitation.providers.prompter.subTitle": "El servicio API personalizado solo está disponible en planes de pago. Mejora ahora para disfrutar de servicios de modelos globales", "limitation.providers.prompter.title": "Suscríbete ahora para usar el servicio API personalizado", "limitation.providers.tooltip": "El servicio API personalizado solo está disponible en planes de pago", + "limitation.video.budgetReady.action": "Continuar Generando", + "limitation.video.budgetReady.desc": "Tus créditos disponibles ahora cubren esta generación.", + "limitation.video.budgetReady.title": "Créditos Listos", "limitation.video.success.action": "Continuar generando", "limitation.video.success.desc": "Tu suscripción {{plan}} se ha actualizado correctamente. Disfruta de la generación de videos con IA. Tu plan actual incluye:", "limitation.video.success.title": "Actualización exitosa", diff --git a/locales/fa-IR/chat.json b/locales/fa-IR/chat.json index 8c37739f24..975c8e4e61 100644 --- a/locales/fa-IR/chat.json +++ b/locales/fa-IR/chat.json @@ -196,6 +196,9 @@ "groupWizard.searchTemplates": "جستجوی الگوها...", "groupWizard.title": "ایجاد گروه", "groupWizard.useTemplate": "استفاده از الگو", + "heteroAgent.cloudNotConfigured.action": "پیکربندی", + "heteroAgent.cloudNotConfigured.desc": "توکن Claude Code خود را در پروفایل عامل پیکربندی کنید تا ارسال پیام‌ها را شروع کنید.", + "heteroAgent.cloudNotConfigured.title": "اعتبارنامه‌های ابری مورد نیاز است", "heteroAgent.cloudRepo.multiSelected": "{{count}} مخزن انتخاب شده است", "heteroAgent.cloudRepo.noRepos": "هیچ مخزنی تنظیم نشده است. آنها را در تنظیمات عامل اضافه کنید.", "heteroAgent.cloudRepo.notSet": "هیچ مخزنی انتخاب نشده است", @@ -467,6 +470,10 @@ "sharePage.error.unauthorized.subtitle": "لطفاً برای مشاهده این موضوع اشتراک‌گذاری‌شده وارد شوید.", "sharePage.error.unauthorized.title": "ورود لازم است", "sharePageDisclaimer": "این محتوا توسط یک کاربر به اشتراک گذاشته شده و دیدگاه‌های LobeHub را نمایندگی نمی‌کند. LobeHub مسئولیتی در قبال پیامدهای ناشی از این محتوای اشتراک‌گذاری‌شده ندارد.", + "signalCallbacks.collapse": "پنهان کردن جزئیات", + "signalCallbacks.empty": "هیچ پیام بازگشتی وجود ندارد", + "signalCallbacks.expand": "نمایش جزئیات", + "signalCallbacks.title": "{{tool}} · {{count}} به‌روزرسانی‌های بازگشتی", "stt.action": "ورودی صوتی", "stt.loading": "در حال شناسایی...", "stt.prettifying": "در حال ویرایش...", @@ -689,7 +696,6 @@ "thread.closeSubagentThread": "بستن گفتگوی عامل فرعی", "thread.divider": "زیرموضوع", "thread.openSubagentThread": "مشاهده کامل گفتگوی عامل فرعی", - "thread.subagentBadge": "عامل فرعی", "thread.subagentReadOnlyHint": "مکالمات SubAgent فقط خواندنی هستند — اجرا توسط عامل اصلی هدایت می‌شود.", "thread.threadMessageCount": "{{messageCount}} پیام", "thread.title": "زیرموضوع", diff --git a/locales/fa-IR/models.json b/locales/fa-IR/models.json index 5cc63214f6..368e8ede83 100644 --- a/locales/fa-IR/models.json +++ b/locales/fa-IR/models.json @@ -106,6 +106,7 @@ "MiniMax-Hailuo-2.3.description": "مدل جدید تولید ویدئو با ارتقاهای جامع در حرکت بدن، واقع‌گرایی فیزیکی و پیروی از دستورالعمل‌ها.", "MiniMax-M1.description": "یک مدل استدلالی داخلی جدید با ۸۰ هزار زنجیره تفکر و ورودی ۱ میلیون توکن، با عملکردی در سطح مدل‌های برتر جهانی.", "MiniMax-M2-Stable.description": "طراحی‌شده برای کدنویسی کارآمد و جریان‌های کاری عامل‌محور، با هم‌زمانی بالاتر برای استفاده تجاری.", + "MiniMax-M2.1-Lightning.description": "قابلیت‌های برنامه‌نویسی چندزبانه قدرتمند با استنتاج سریع‌تر و کارآمدتر.", "MiniMax-M2.1-highspeed.description": "قابلیت‌های برنامه‌نویسی چندزبانه قدرتمند، تجربه برنامه‌نویسی کاملاً ارتقاء یافته. سریع‌تر و کارآمدتر.", "MiniMax-M2.1.description": "MiniMax-M2.1 یک مدل بزرگ متن‌باز پیشرفته از MiniMax است که بر حل وظایف پیچیده دنیای واقعی تمرکز دارد. نقاط قوت اصلی آن شامل توانایی برنامه‌نویسی چندزبانه و قابلیت عمل به‌عنوان یک عامل هوشمند برای حل مسائل پیچیده است.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: همان عملکرد M2.5 با استنتاج سریع‌تر.", @@ -314,13 +315,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku سریع‌ترین و فشرده‌ترین مدل Anthropic است که برای پاسخ‌های تقریباً فوری با عملکرد سریع و دقیق طراحی شده است.", "claude-3-opus-20240229.description": "Claude 3 Opus قدرتمندترین مدل Anthropic برای وظایف بسیار پیچیده است که در عملکرد، هوش، روانی و درک زبان برتری دارد.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet تعادل بین هوش و سرعت را برای بارهای کاری سازمانی برقرار می‌کند و با هزینه کمتر، بهره‌وری بالا و استقرار قابل اعتماد در مقیاس وسیع را ارائه می‌دهد.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 سریع‌ترین و هوشمندترین مدل Haiku از Anthropic است که با سرعت فوق‌العاده و توانایی استدلال پیشرفته ارائه می‌شود.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 سریع‌ترین و هوشمندترین مدل هایکو Anthropic است، با سرعت فوق‌العاده و توانایی تفکر گسترده.", "claude-haiku-4-5.description": "Claude Haiku 4.5 از Anthropic — نسل جدید Haiku با استدلال و پردازش تصویری پیشرفته.", "claude-haiku-4.5.description": "Claude Haiku 4.5 سریع‌ترین و هوشمندترین مدل Haiku از Anthropic است که با سرعت برق‌آسا و توانایی استدلال پیشرفته ارائه می‌شود.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking یک نسخه پیشرفته است که می‌تواند فرآیند استدلال خود را آشکار کند.", "claude-opus-4-1-20250805.description": "Claude Opus 4.1 جدیدترین و توانمندترین مدل Anthropic برای وظایف بسیار پیچیده است که در عملکرد، هوش، روانی و درک برتری دارد.", "claude-opus-4-1.description": "Claude Opus 4.1 از Anthropic — مدل استدلال سطح‌بالا با توانایی تحلیل عمیق.", - "claude-opus-4-20250514.description": "Claude Opus 4 قدرتمندترین مدل Anthropic برای وظایف بسیار پیچیده است که در عملکرد، هوش، روانی و فهم برتری دارد.", + "claude-opus-4-20250514.description": "Claude Opus 4 قدرتمندترین مدل Anthropic برای وظایف بسیار پیچیده است که در عملکرد، هوش، روانی و درک برتری دارد.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 مدل پرچم‌دار Anthropic است که هوش برجسته را با عملکرد مقیاس‌پذیر ترکیب می‌کند و برای وظایف پیچیده‌ای که نیاز به پاسخ‌های باکیفیت و استدلال دارند، ایده‌آل است.", "claude-opus-4-5.description": "Claude Opus 4.5 از Anthropic — مدل پرچم‌دار با استدلال و کدنویسی سطح‌بالا.", "claude-opus-4-6.description": "Claude Opus 4.6 از Anthropic — مدل پرچم‌دار با پنجره زمینه ۱ میلیون و توانایی استدلال پیشرفته.", @@ -329,7 +330,7 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 هوشمندترین مدل Anthropic برای ساخت عوامل و کدنویسی است.", "claude-opus-4.6.description": "Claude Opus 4.6 هوشمندترین مدل Anthropic برای ساخت عوامل و کدنویسی است.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking می‌تواند پاسخ‌های تقریباً فوری یا تفکر گام‌به‌گام طولانی با فرآیند قابل مشاهده تولید کند.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 می‌تواند پاسخ‌های تقریباً فوری یا تفکر گام‌به‌گام گسترده با فرآیند قابل مشاهده تولید کند.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 هوشمندترین مدل Anthropic تا به امروز است که پاسخ‌های تقریباً فوری یا تفکر گام‌به‌گام گسترده با کنترل دقیق برای کاربران API ارائه می‌دهد.", "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 هوشمندترین مدل Anthropic تا به امروز است.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 از Anthropic — نسخه بهبود‌یافته Sonnet با عملکرد بهتر در کدنویسی.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 از Anthropic — جدیدترین Sonnet با کدنویسی برتر و استفاده بهتر از ابزار.", @@ -403,7 +404,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) یک مدل نوآورانه با درک عمیق زبان و تعامل است.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 یک مدل استدلال نسل بعدی با توانایی استدلال پیچیده و زنجیره تفکر برای وظایف تحلیلی عمیق است.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 یک مدل استدلال نسل بعدی با قابلیت‌های استدلال پیچیده‌تر و زنجیره‌ای از تفکر است.", - "deepseek-chat.description": "یک مدل متن‌باز جدید که توانایی‌های عمومی و کدنویسی را ترکیب می‌کند. این مدل گفتگوی عمومی مدل چت و کدنویسی قوی مدل کدنویس را حفظ کرده و با هم‌ترازی بهتر ترجیحات ارائه می‌دهد. DeepSeek-V2.5 همچنین نوشتن و پیروی از دستورالعمل‌ها را بهبود می‌بخشد.", + "deepseek-chat.description": "نام مستعار سازگار برای حالت غیرتفکری DeepSeek V4 Flash. برنامه‌ریزی شده برای توقف استفاده — به جای آن از DeepSeek V4 Flash استفاده کنید.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B یک مدل زبان برنامه‌نویسی است که با ۲ تریلیون توکن (۸۷٪ کد، ۱۳٪ متن چینی/انگلیسی) آموزش دیده است. این مدل دارای پنجره متنی ۱۶K و وظایف تکمیل در میانه است که تکمیل کد در سطح پروژه و پر کردن قطعات کد را فراهم می‌کند.", "deepseek-coder-v2.description": "DeepSeek Coder V2 یک مدل کدنویسی MoE متن‌باز است که در وظایف برنامه‌نویسی عملکردی هم‌سطح با GPT-4 Turbo دارد.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 یک مدل کدنویسی MoE متن‌باز است که در وظایف برنامه‌نویسی عملکردی هم‌سطح با GPT-4 Turbo دارد.", @@ -425,7 +426,7 @@ "deepseek-r1-fast-online.description": "نسخه کامل سریع DeepSeek R1 با جستجوی وب در زمان واقعی که توانایی در مقیاس ۶۷۱B را با پاسخ‌دهی سریع‌تر ترکیب می‌کند.", "deepseek-r1-online.description": "نسخه کامل DeepSeek R1 با ۶۷۱ میلیارد پارامتر و جستجوی وب در زمان واقعی که درک و تولید قوی‌تری را ارائه می‌دهد.", "deepseek-r1.description": "DeepSeek-R1 پیش از یادگیری تقویتی از داده‌های شروع سرد استفاده می‌کند و در وظایف ریاضی، کدنویسی و استدلال عملکردی هم‌سطح با OpenAI-o1 دارد.", - "deepseek-reasoner.description": "یک مدل استدلال DeepSeek که بر وظایف استدلال منطقی پیچیده متمرکز است.", + "deepseek-reasoner.description": "نام مستعار سازگار برای حالت تفکری DeepSeek V4 Flash. برنامه‌ریزی شده برای توقف استفاده — به جای آن از DeepSeek V4 Flash استفاده کنید.", "deepseek-v2.description": "DeepSeek V2 یک مدل MoE کارآمد است که پردازش مقرون‌به‌صرفه را امکان‌پذیر می‌سازد.", "deepseek-v2:236b.description": "DeepSeek V2 236B مدل متمرکز بر کدنویسی DeepSeek است که توانایی بالایی در تولید کد دارد.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 یک مدل MoE با ۶۷۱ میلیارد پارامتر است که در برنامه‌نویسی، توانایی‌های فنی، درک زمینه و پردازش متون بلند عملکرد برجسته‌ای دارد.", @@ -490,6 +491,8 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 یک مدل تولید تصویر از ByteDance Seed است که از ورودی‌های متن و تصویر پشتیبانی می‌کند و تولید تصویر با کیفیت بالا و قابل کنترل را ارائه می‌دهد. این مدل تصاویر را از دستورات متنی تولید می‌کند.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 جدیدترین مدل چندوجهی تصویر ByteDance است که قابلیت‌های تبدیل متن به تصویر، تصویر به تصویر و تولید دسته‌ای تصاویر را ادغام می‌کند و توانایی‌های استدلال و دانش عمومی را نیز در بر می‌گیرد. در مقایسه با نسخه قبلی 4.0، کیفیت تولید به‌طور قابل‌توجهی بهبود یافته است، با سازگاری بهتر در ویرایش و ترکیب چند تصویر. کنترل دقیق‌تری بر جزئیات بصری ارائه می‌دهد، متن‌های کوچک و چهره‌های کوچک را به‌طور طبیعی‌تر تولید می‌کند و به هماهنگی بهتر در چیدمان و رنگ دست می‌یابد، که زیبایی کلی را افزایش می‌دهد.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite جدیدترین مدل تولید تصویر ByteDance است. برای اولین بار، قابلیت‌های بازیابی آنلاین را ادغام کرده است که به آن امکان می‌دهد اطلاعات وب لحظه‌ای را وارد کند و به‌موقع بودن تصاویر تولید شده را افزایش دهد. هوش مدل نیز ارتقا یافته است، که تفسیر دقیق دستورالعمل‌های پیچیده و محتوای بصری را امکان‌پذیر می‌کند. علاوه بر این، پوشش دانش جهانی، سازگاری مرجع و کیفیت تولید در سناریوهای حرفه‌ای بهبود یافته است، که نیازهای خلق بصری در سطح سازمانی را بهتر برآورده می‌کند.", + "dreamina-seedance-2-0-260128.description": "Seedance 2.0 توسط ByteDance قدرتمندترین مدل تولید ویدئو است که از تولید ویدئو با مرجع چندوجهی، ویرایش ویدئو، گسترش ویدئو، متن به ویدئو و تصویر به ویدئو با صدای همگام‌سازی شده پشتیبانی می‌کند.", + "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast توسط ByteDance همان قابلیت‌های Seedance 2.0 را با سرعت تولید بالاتر و قیمت رقابتی‌تر ارائه می‌دهد.", "emohaa.description": "Emohaa یک مدل سلامت روان با توانایی مشاوره حرفه‌ای است که به کاربران در درک مسائل احساسی کمک می‌کند.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B یک مدل سبک متن‌باز برای استقرار محلی و سفارشی‌سازی شده است.", "ernie-4.5-8k-preview.description": "پیش‌نمایش مدل با پنجره متنی ۸هزار توکن برای ارزیابی ERNIE 4.5.", @@ -514,7 +517,8 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K یک مدل تفکر سریع با زمینه ۳۲K برای استدلال پیچیده و گفت‌وگوی چندمرحله‌ای است.", "ernie-x1.1-preview.description": "پیش‌نمایش ERNIE X1.1 یک مدل تفکر برای ارزیابی و آزمایش است.", "ernie-x1.1.description": "ERNIE X1.1 یک مدل تفکر پیش‌نمایش برای ارزیابی و آزمایش است.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 یک مدل تولید تصویر از ByteDance Seed است که از ورودی‌های متنی و تصویری پشتیبانی می‌کند و تولید تصاویر با کیفیت بالا و قابل کنترل را ارائه می‌دهد. این مدل تصاویر را از دستورات متنی تولید می‌کند.", + "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5 که توسط تیم Seed ByteDance ساخته شده است، از ویرایش و ترکیب چندتصویری پشتیبانی می‌کند. ویژگی‌ها شامل سازگاری بهتر موضوع، پیروی دقیق از دستورات، درک منطق فضایی، بیان زیبایی‌شناختی، طراحی پوستر و لوگو با رندر دقیق متن-تصویر است.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 که توسط تیم Seed ByteDance ساخته شده است، از ورودی‌های متن و تصویر برای تولید تصاویر باکیفیت و قابل‌کنترل از طریق دستورات پشتیبانی می‌کند.", "fal-ai/flux-kontext/dev.description": "مدل FLUX.1 با تمرکز بر ویرایش تصویر که از ورودی‌های متنی و تصویری پشتیبانی می‌کند.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] ورودی‌های متنی و تصاویر مرجع را می‌پذیرد و امکان ویرایش‌های محلی هدفمند و تغییرات پیچیده در صحنه کلی را فراهم می‌کند.", "fal-ai/flux/krea.description": "Flux Krea [dev] یک مدل تولید تصویر با تمایل زیبایی‌شناسی به تصاویر طبیعی و واقع‌گرایانه‌تر است.", @@ -522,8 +526,8 @@ "fal-ai/hunyuan-image/v3.description": "یک مدل قدرتمند بومی چندوجهی برای تولید تصویر.", "fal-ai/imagen4/preview.description": "مدل تولید تصویر با کیفیت بالا از گوگل.", "fal-ai/nano-banana.description": "Nano Banana جدیدترین، سریع‌ترین و کارآمدترین مدل چندوجهی بومی گوگل است که امکان تولید و ویرایش تصویر از طریق مکالمه را فراهم می‌کند.", - "fal-ai/qwen-image-edit.description": "یک مدل ویرایش تصویر حرفه‌ای از تیم Qwen که از ویرایش‌های معنایی و ظاهری پشتیبانی می‌کند، متن‌های چینی و انگلیسی را با دقت ویرایش می‌کند و ویرایش‌های با کیفیت بالا مانند انتقال سبک و چرخش اشیاء را ممکن می‌سازد.", - "fal-ai/qwen-image.description": "یک مدل قدرتمند تولید تصویر از تیم Qwen با قابلیت‌های برجسته در رندر متن چینی و سبک‌های بصری متنوع.", + "fal-ai/qwen-image-edit.description": "مدل ویرایش تصویر حرفه‌ای از تیم Qwen که از ویرایش‌های معنایی و ظاهری، ویرایش دقیق متن‌های چینی/انگلیسی، انتقال سبک، چرخش و موارد دیگر پشتیبانی می‌کند.", + "fal-ai/qwen-image.description": "مدل قدرتمند تولید تصویر از تیم Qwen با قابلیت رندر قوی متن چینی و سبک‌های بصری متنوع.", "flux-1-schnell.description": "مدل تبدیل متن به تصویر با ۱۲ میلیارد پارامتر از Black Forest Labs که از تقطیر انتشار تقابلی نهفته برای تولید تصاویر با کیفیت بالا در ۱ تا ۴ مرحله استفاده می‌کند. این مدل با جایگزین‌های بسته رقابت می‌کند و تحت مجوز Apache-2.0 برای استفاده شخصی، تحقیقاتی و تجاری منتشر شده است.", "flux-dev.description": "مدل تولید تصویر متن‌باز برای تحقیق و توسعه، به‌طور کارآمد برای پژوهش‌های نوآورانهٔ غیرتجاری بهینه‌سازی شده است.", "flux-kontext-max.description": "تولید و ویرایش تصویر متنی-زمینه‌ای پیشرفته که متن و تصویر را برای نتایج دقیق و منسجم ترکیب می‌کند.", @@ -562,10 +566,10 @@ "gemini-3-flash-preview.description": "Gemini 3 Flash هوشمندترین مدل طراحی‌شده برای سرعت است که هوش پیشرفته را با قابلیت جست‌وجوی دقیق ترکیب می‌کند.", "gemini-3-flash.description": "Gemini 3 Flash از Google — مدل بسیار سریع با پشتیبانی ورودی چندوجهی.", "gemini-3-pro-image-preview.description": "Gemini 3 Pro Image (Nano Banana Pro) مدل تولید تصویر گوگل است که از گفتگوی چندوجهی نیز پشتیبانی می‌کند.", - "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) مدل تولید تصویر گوگل است که از چت چندوجهی نیز پشتیبانی می‌کند.", + "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) مدل تولید تصویر گوگل است و همچنین از چت چندوجهی پشتیبانی می‌کند.", "gemini-3-pro-preview.description": "Gemini 3 Pro قدرتمندترین مدل عامل و کدنویسی احساسی گوگل است که تعاملات بصری غنی‌تر و تعامل عمیق‌تری را بر پایه استدلال پیشرفته ارائه می‌دهد.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) سریع‌ترین مدل تولید تصویر بومی گوگل با پشتیبانی از تفکر، تولید و ویرایش تصویر مکالمه‌ای است.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) سریع‌ترین مدل تولید تصویر بومی گوگل است که از تفکر، تولید و ویرایش تصاویر در مکالمات پشتیبانی می‌کند.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) کیفیت تصویر در سطح حرفه‌ای را با سرعت Flash و پشتیبانی از چت چندوجهی ارائه می‌دهد.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview اقتصادی‌ترین مدل چندوجهی گوگل است که برای وظایف عامل‌محور با حجم بالا، ترجمه و پردازش داده‌ها بهینه شده است.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite اقتصادی‌ترین مدل چندوجهی گوگل است که برای وظایف عاملی با حجم بالا، ترجمه و پردازش داده بهینه شده است.", "gemini-3.1-pro-preview.description": "پیش‌نمایش Gemini 3.1 Pro قابلیت‌های استدلال بهبود یافته را به Gemini 3 Pro اضافه می‌کند و از سطح تفکر متوسط پشتیبانی می‌کند.", @@ -730,6 +734,8 @@ "grok-4-fast-reasoning.description": "با افتخار Grok 4 Fast را معرفی می‌کنیم، جدیدترین پیشرفت ما در مدل‌های استدلال مقرون‌به‌صرفه.", "grok-4.20-0309-non-reasoning.description": "نسخه بدون استدلال برای کاربردهای ساده.", "grok-4.20-0309-reasoning.description": "مدلی هوشمند و بسیار سریع که قبل از پاسخ استدلال می‌کند.", + "grok-4.20-beta-0309-non-reasoning.description": "نسخه غیرتفکری برای موارد استفاده ساده.", + "grok-4.20-beta-0309-reasoning.description": "مدل هوشمند و فوق‌العاده سریع که قبل از پاسخ‌دهی استدلال می‌کند.", "grok-4.20-multi-agent-0309.description": "مجموعه‌ای از ۴ یا ۱۶ ایجنت که در پژوهش عملکرد عالی دارد. در حال حاضر از ابزارهای سمت کاربر پشتیبانی نمی‌کند و تنها ابزارهای سمت سرور xAI (مانند X Search و Web Search) و ابزارهای MCP از راه دور را پشتیبانی می‌کند.", "grok-4.3.description": "حقیقت‌جویانه‌ترین مدل زبان بزرگ در جهان", "grok-4.description": "جدیدترین مدل پرچمدار Grok با عملکرد بی‌نظیر در زبان، ریاضیات و استدلال — یک مدل همه‌جانبه واقعی. در حال حاضر به grok-4-0709 اشاره دارد؛ به دلیل منابع محدود، قیمت آن موقتاً ۱۰٪ بالاتر از قیمت رسمی است و انتظار می‌رود به قیمت رسمی بازگردد.", @@ -1214,6 +1220,8 @@ "qwq.description": "QwQ یک مدل استدلال در خانواده Qwen است. در مقایسه با مدل‌های تنظیم‌شده با دستورالعمل استاندارد، توانایی تفکر و استدلال آن عملکرد پایین‌دستی را به‌ویژه در مسائل دشوار به‌طور قابل توجهی بهبود می‌بخشد. QwQ-32B یک مدل استدلال میان‌رده است که با مدل‌های برتر مانند DeepSeek-R1 و o1-mini رقابت می‌کند.", "qwq_32b.description": "مدل استدلال میان‌رده در خانواده Qwen. در مقایسه با مدل‌های تنظیم‌شده با دستورالعمل استاندارد، توانایی تفکر و استدلال QwQ عملکرد پایین‌دستی را به‌ویژه در مسائل دشوار به‌طور قابل توجهی بهبود می‌بخشد.", "r1-1776.description": "R1-1776 نسخه پس‌آموزشی مدل DeepSeek R1 است که برای ارائه اطلاعات واقعی، بدون سانسور و بی‌طرف طراحی شده است.", + "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro توسط ByteDance از متن به ویدئو، تصویر به ویدئو (فریم اول، فریم اول+آخر) و تولید صدا همگام با تصاویر پشتیبانی می‌کند.", + "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite توسط BytePlus دارای تولید تقویت‌شده با بازیابی وب برای اطلاعات بلادرنگ، تفسیر پیشرفته دستورات پیچیده و بهبود سازگاری مرجع برای خلق بصری حرفه‌ای است.", "solar-mini-ja.description": "Solar Mini (ژاپنی) نسخه‌ای از Solar Mini با تمرکز بر زبان ژاپنی است که در عین حال عملکرد قوی و کارآمدی در زبان‌های انگلیسی و کره‌ای حفظ می‌کند.", "solar-mini.description": "Solar Mini یک مدل زبانی فشرده است که عملکردی بهتر از GPT-3.5 دارد و با پشتیبانی چندزبانه قوی از زبان‌های انگلیسی و کره‌ای، راه‌حلی کارآمد با حجم کم ارائه می‌دهد.", "solar-pro.description": "Solar Pro یک مدل زبانی هوشمند از Upstage است که برای پیروی از دستورالعمل‌ها روی یک GPU طراحی شده و امتیاز IFEval بالای ۸۰ دارد. در حال حاضر از زبان انگلیسی پشتیبانی می‌کند؛ انتشار کامل آن برای نوامبر ۲۰۲۴ با پشتیبانی زبانی گسترده‌تر و زمینه طولانی‌تر برنامه‌ریزی شده است.", diff --git a/locales/fa-IR/plugin.json b/locales/fa-IR/plugin.json index 2470787301..abdc526b87 100644 --- a/locales/fa-IR/plugin.json +++ b/locales/fa-IR/plugin.json @@ -237,6 +237,14 @@ "builtins.lobe-page-agent.apiName.updateNode": "به‌روزرسانی گره", "builtins.lobe-page-agent.apiName.wrapNodes": "بسته‌بندی گره‌ها", "builtins.lobe-page-agent.title": "صفحه", + "builtins.lobe-self-feedback-intent.apiName.declareSelfFeedbackIntent": "ثبت ایده بهبود", + "builtins.lobe-self-feedback-intent.inspector.gap.proposal": "پیشنهاد بهبود", + "builtins.lobe-self-feedback-intent.inspector.memory.write": "ثبت ترجیح", + "builtins.lobe-self-feedback-intent.inspector.rejected": "ثبت نشد", + "builtins.lobe-self-feedback-intent.inspector.skill.consolidate": "سازماندهی روش‌ها", + "builtins.lobe-self-feedback-intent.inspector.skill.create": "روش جدید یافت شد", + "builtins.lobe-self-feedback-intent.inspector.skill.refine": "بهبود روش", + "builtins.lobe-self-feedback-intent.title": "ایده‌های بهبود", "builtins.lobe-skill-store.apiName.importFromMarket": "وارد کردن از بازار", "builtins.lobe-skill-store.apiName.importSkill": "وارد کردن مهارت", "builtins.lobe-skill-store.apiName.searchSkill": "جستجوی مهارت‌ها", diff --git a/locales/fa-IR/providers.json b/locales/fa-IR/providers.json index d06eb4a87b..54dc2d7229 100644 --- a/locales/fa-IR/providers.json +++ b/locales/fa-IR/providers.json @@ -33,6 +33,7 @@ "jina.description": "Jina AI که در سال 2020 تأسیس شد، یک شرکت پیشرو در زمینه جستجوی هوش مصنوعی است. پشته جستجوی آن شامل مدل‌های برداری، رتبه‌بندها و مدل‌های زبانی کوچک برای ساخت اپلیکیشن‌های جستجوی مولد و چندوجهی با کیفیت بالا است.", "kimicodingplan.description": "Kimi Code از Moonshot AI دسترسی به مدل‌های Kimi شامل K2.5 را برای وظایف کدنویسی فراهم می‌کند.", "lmstudio.description": "LM Studio یک اپلیکیشن دسکتاپ برای توسعه و آزمایش مدل‌های زبانی بزرگ روی رایانه شخصی شماست.", + "lobehub.description": "LobeHub Cloud از APIهای رسمی برای دسترسی به مدل‌های هوش مصنوعی استفاده می‌کند و مصرف را با اعتباراتی که به توکن‌های مدل مرتبط هستند، اندازه‌گیری می‌کند.", "longcat.description": "لانگ‌کت مجموعه‌ای از مدل‌های بزرگ هوش مصنوعی تولیدی است که به‌طور مستقل توسط میتوآن توسعه داده شده است. این مدل‌ها برای افزایش بهره‌وری داخلی شرکت و امکان‌پذیر کردن کاربردهای نوآورانه از طریق معماری محاسباتی کارآمد و قابلیت‌های چندوجهی قدرتمند طراحی شده‌اند.", "minimax.description": "MiniMax که در سال 2021 تأسیس شد، هوش مصنوعی چندمنظوره با مدل‌های پایه چندوجهی از جمله مدل‌های متنی با پارامترهای تریلیونی، مدل‌های گفتاری و تصویری توسعه می‌دهد و اپ‌هایی مانند Hailuo AI را ارائه می‌کند.", "minimaxcodingplan.description": "طرح توکن MiniMax دسترسی به مدل‌های MiniMax شامل M2.7 را برای وظایف کدنویسی از طریق اشتراک با هزینه ثابت فراهم می‌کند.", diff --git a/locales/fa-IR/subscription.json b/locales/fa-IR/subscription.json index 244c0d26cb..ea97456b27 100644 --- a/locales/fa-IR/subscription.json +++ b/locales/fa-IR/subscription.json @@ -41,7 +41,9 @@ "credits.autoTopUp.monthlyLimitDesc": "حداکثر مبلغی که می‌تواند به صورت خودکار در هر ماه شارژ شود. برای عدم محدودیت خالی بگذارید", "credits.autoTopUp.monthlyLimitPlaceholder": "بدون محدودیت", "credits.autoTopUp.monthlyTopUpAmount": "مبلغ شارژ ماهانه", + "credits.autoTopUp.noCustomerHint": "برای فعال کردن شارژ خودکار، ابتدا یک بار اعتبار خریداری کنید تا روش پرداخت ذخیره شود.", "credits.autoTopUp.noPaymentMethodHint": "هیچ روش پرداختی در پرونده موجود نیست. برای شارژ خودکار، نیاز به یک کارت ذخیره‌شده است که به‌طور خودکار شارژ شود.", + "credits.autoTopUp.purchaseCredits": "خرید اعتبار", "credits.autoTopUp.saveError": "ذخیره تنظیمات شارژ خودکار ناموفق بود", "credits.autoTopUp.saveSuccess": "تنظیمات شارژ خودکار ذخیره شد", "credits.autoTopUp.setupPaymentMethod": "افزودن روش پرداخت", @@ -83,6 +85,7 @@ "credits.packages.title": "بسته‌های اعتباری من", "credits.topUp.cancel": "لغو", "credits.topUp.custom": "سفارشی", + "credits.topUp.freeFeeHint": "شارژ طرح رایگان شامل هزینه خدمات {{fee}} به ازای هر ۱ میلیون اعتبار است.", "credits.topUp.maxAmountError": "مبلغ خرید نمی‌تواند بیش از ${{max}} باشد", "credits.topUp.purchaseError": "خرید ناموفق بود، لطفاً بعداً دوباره تلاش کنید", "credits.topUp.purchaseNow": "اکنون خرید کنید", @@ -120,6 +123,9 @@ "keyMissMatch.button": "بازیابی مصرف و ادامه گفتگو", "keyMissMatch.description": "به دلیل یک خطای سیستمی موقت، مصرف اشتراک شما غیرفعال شده است. لطفاً برای ادامه گفتگو دکمه زیر را بزنید. در صورت تکرار، با ما تماس بگیرید (support@lobehub.com)", "keyMissMatch.title": "اکنون مصرف اشتراک را بازیابی کنید", + "limitation.chat.budgetReady.action": "ادامه چت", + "limitation.chat.budgetReady.desc": "اعتبارات موجود شما اکنون این درخواست را پوشش می‌دهند.", + "limitation.chat.budgetReady.title": "اعتبارات آماده", "limitation.chat.success.action": "ادامه گفتگو", "limitation.chat.success.desc": "اشتراک {{plan}} شما با موفقیت ارتقا یافت. از گفتگو با هوش مصنوعی لذت ببرید. پلن فعلی شما شامل:", "limitation.chat.success.title": "ارتقا با موفقیت انجام شد", @@ -134,14 +140,23 @@ "limitation.hobby.docs": "مشاهده مستندات پیکربندی", "limitation.hobby.tip": "فراموش نکنید به مدلی با کلید API سفارشی تغییر دهید", "limitation.hobby.title": "لطفاً API سرویس مدل را پیکربندی کنید", + "limitation.image.budgetReady.action": "ادامه تولید", + "limitation.image.budgetReady.desc": "اعتبارات موجود شما اکنون این تولید را پوشش می‌دهند.", + "limitation.image.budgetReady.title": "اعتبارات آماده", "limitation.image.success.action": "ادامه تولید", "limitation.image.success.desc": "اشتراک {{plan}} شما با موفقیت ارتقا یافت. از تولید تصویر با هوش مصنوعی لذت ببرید. پلن فعلی شما شامل:", "limitation.image.success.title": "ارتقا با موفقیت انجام شد", "limitation.image.topupSuccess.action": "ادامه تولید", "limitation.image.topupSuccess.desc": "اعتبار شارژ شده شما فعال شد. از تولید تصویر با هوش مصنوعی لذت ببرید. پلن فعلی شما شامل:", "limitation.image.topupSuccess.title": "شارژ با موفقیت انجام شد", + "limitation.insufficientBudget.approximateDesc": "این درخواست ممکن است به اعتبارات بیشتری نیاز داشته باشد. اعتبار شارژ کنید یا طرح خود را ارتقا دهید.", + "limitation.insufficientBudget.available": "اعتبارات موجود", "limitation.insufficientBudget.desc": "اعتبارات باقی‌مانده شما برای هزینه تخمینی این مدل کافی نیست. لطفاً اعتبار خود را افزایش دهید یا به مدلی ارزان‌تر تغییر دهید.", + "limitation.insufficientBudget.estimatedDesc": "برآورد می‌شود که این درخواست به اعتبارات بیشتری نیاز داشته باشد. اعتبار شارژ کنید یا طرح خود را ارتقا دهید.", + "limitation.insufficientBudget.exactDesc": "این درخواست به اعتبارات بیشتری نیاز دارد. اعتبار شارژ کنید یا طرح خود را ارتقا دهید.", + "limitation.insufficientBudget.required": "اعتبارات مورد نیاز", "limitation.insufficientBudget.retry": "تلاش مجدد", + "limitation.insufficientBudget.shortfall": "کمبود اعتبار", "limitation.insufficientBudget.title": "اعتبارات ناکافی برای این مدل", "limitation.limited.action": "اکنون ارتقا دهید", "limitation.limited.advanceFeature": "برای استفاده از ویژگی‌های پیشرفته ارتقا دهید:", @@ -151,6 +166,7 @@ "limitation.limited.title": "اعتبار محاسباتی به پایان رسیده است", "limitation.limited.topup": "شارژ اعتبار", "limitation.limited.upgrade": "ارتقا به پلن بالاتر", + "limitation.limited.upgradeToPlan": "ارتقا به {{plan}}", "limitation.providers.lock.addNew": "برای ایجاد ارائه‌دهندگان سفارشی هوش مصنوعی، اشتراک تهیه کنید", "limitation.providers.lock.enableProvider": "برای فعال‌سازی این ارائه‌دهنده هوش مصنوعی، اشتراک تهیه کنید", "limitation.providers.lock.menuItem": "برای پیکربندی سرویس API سفارشی، اشتراک تهیه کنید", @@ -161,6 +177,9 @@ "limitation.providers.prompter.subTitle": "سرویس API سفارشی فقط در طرح‌های پولی در دسترس است. برای استفاده از خدمات مدل‌های مطرح جهانی، هم‌اکنون ارتقاء دهید", "limitation.providers.prompter.title": "برای استفاده از سرویس API سفارشی، اشتراک تهیه کنید", "limitation.providers.tooltip": "سرویس API سفارشی فقط در طرح‌های پولی در دسترس است", + "limitation.video.budgetReady.action": "ادامه تولید", + "limitation.video.budgetReady.desc": "اعتبارات موجود شما اکنون این تولید را پوشش می‌دهند.", + "limitation.video.budgetReady.title": "اعتبارات آماده", "limitation.video.success.action": "ادامه تولید", "limitation.video.success.desc": "اشتراک {{plan}} شما با موفقیت ارتقا یافت. از تولید ویدئوی هوش مصنوعی لذت ببرید. طرح فعلی شما شامل موارد زیر است:", "limitation.video.success.title": "ارتقا با موفقیت انجام شد", diff --git a/locales/fr-FR/chat.json b/locales/fr-FR/chat.json index 3027a8ceb1..b0ae4d79f0 100644 --- a/locales/fr-FR/chat.json +++ b/locales/fr-FR/chat.json @@ -196,6 +196,9 @@ "groupWizard.searchTemplates": "Rechercher des modèles...", "groupWizard.title": "Créer un groupe", "groupWizard.useTemplate": "Utiliser un modèle", + "heteroAgent.cloudNotConfigured.action": "Configurer", + "heteroAgent.cloudNotConfigured.desc": "Configurez votre jeton Claude Code dans le profil de l'agent pour commencer à envoyer des messages.", + "heteroAgent.cloudNotConfigured.title": "Identifiants cloud requis", "heteroAgent.cloudRepo.multiSelected": "{{count}} dépôts sélectionnés", "heteroAgent.cloudRepo.noRepos": "Aucun dépôt configuré. Ajoutez-les dans les paramètres de l'agent.", "heteroAgent.cloudRepo.notSet": "Aucun dépôt sélectionné", @@ -467,6 +470,10 @@ "sharePage.error.unauthorized.subtitle": "Veuillez vous connecter pour consulter ce sujet partagé.", "sharePage.error.unauthorized.title": "Connexion requise", "sharePageDisclaimer": "Ce contenu est partagé par un utilisateur et ne reflète pas les opinions de LobeHub. LobeHub n’est pas responsable des conséquences liées à ce contenu partagé.", + "signalCallbacks.collapse": "Masquer les détails", + "signalCallbacks.empty": "Aucun message de rappel", + "signalCallbacks.expand": "Afficher les détails", + "signalCallbacks.title": "{{tool}} · {{count}} mises à jour de rappel", "stt.action": "Entrée vocale", "stt.loading": "Reconnaissance en cours...", "stt.prettifying": "Amélioration...", @@ -689,7 +696,6 @@ "thread.closeSubagentThread": "Réduire la conversation du sous-agent", "thread.divider": "Sous-sujet", "thread.openSubagentThread": "Afficher la conversation complète du sous-agent", - "thread.subagentBadge": "Sous-agent", "thread.subagentReadOnlyHint": "Les conversations des sous-agents sont en lecture seule — l'exécution est pilotée par l'agent parent.", "thread.threadMessageCount": "{{messageCount}} messages", "thread.title": "Sous-sujet", diff --git a/locales/fr-FR/models.json b/locales/fr-FR/models.json index 05889ed51a..9360265c2a 100644 --- a/locales/fr-FR/models.json +++ b/locales/fr-FR/models.json @@ -106,6 +106,7 @@ "MiniMax-Hailuo-2.3.description": "Nouveau modèle de génération vidéo avec des améliorations complètes dans les mouvements corporels, le réalisme physique et le suivi des instructions.", "MiniMax-M1.description": "Un nouveau modèle de raisonnement interne avec 80 000 chaînes de pensée et 1 million d’entrées, offrant des performances comparables aux meilleurs modèles mondiaux.", "MiniMax-M2-Stable.description": "Conçu pour un codage efficace et des flux de travail d’agents, avec une plus grande simultanéité pour un usage commercial.", + "MiniMax-M2.1-Lightning.description": "Capacités de programmation multilingues puissantes avec une inférence plus rapide et plus efficace.", "MiniMax-M2.1-highspeed.description": "Des capacités de programmation multilingues puissantes, offrant une expérience de programmation entièrement améliorée. Plus rapide et plus efficace.", "MiniMax-M2.1.description": "MiniMax-M2.1 est un modèle phare open source de MiniMax, conçu pour résoudre des tâches complexes du monde réel. Ses principaux atouts résident dans ses capacités de programmation multilingue et sa faculté à résoudre des problèmes complexes en tant qu'agent.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed : Même performance que M2.5 avec une inférence plus rapide.", @@ -314,7 +315,7 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku est le modèle le plus rapide et le plus compact d’Anthropic, conçu pour des réponses quasi instantanées avec des performances rapides et précises.", "claude-3-opus-20240229.description": "Claude 3 Opus est le modèle le plus puissant d’Anthropic pour les tâches complexes, excellent en performance, intelligence, fluidité et compréhension.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet équilibre intelligence et rapidité pour les charges de travail en entreprise, offrant une grande utilité à moindre coût et un déploiement fiable à grande échelle.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 est le modèle Haiku le plus rapide et le plus intelligent d'Anthropic, avec une vitesse fulgurante et un raisonnement étendu.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 est le modèle Haiku le plus rapide et le plus intelligent d'Anthropic, avec une vitesse fulgurante et une réflexion approfondie.", "claude-haiku-4-5.description": "Claude Haiku 4.5 par Anthropic — modèle Haiku de nouvelle génération avec un raisonnement et une vision améliorés.", "claude-haiku-4.5.description": "Claude Haiku 4.5 est le modèle Haiku le plus rapide et le plus intelligent d’Anthropic, avec une vitesse fulgurante et un raisonnement étendu.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking est une variante avancée capable de révéler son processus de raisonnement.", @@ -329,7 +330,7 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 est le modèle le plus intelligent d’Anthropic pour la création d’agents et le codage.", "claude-opus-4.6.description": "Claude Opus 4.6 est le modèle le plus intelligent d’Anthropic pour la création d’agents et le codage.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking peut produire des réponses quasi instantanées ou une réflexion détaillée étape par étape avec un processus visible.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 peut produire des réponses quasi-instantanées ou un raisonnement détaillé étape par étape avec un processus visible.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 est le modèle le plus intelligent d'Anthropic à ce jour, offrant des réponses quasi-instantanées ou une réflexion détaillée étape par étape avec un contrôle précis pour les utilisateurs d'API.", "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 est le modèle le plus intelligent d'Anthropic à ce jour.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 par Anthropic — Sonnet amélioré avec des performances de codage accrues.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 par Anthropic — dernier modèle Sonnet avec un codage supérieur et une utilisation d'outils avancée.", @@ -403,7 +404,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) est un modèle innovant offrant une compréhension linguistique approfondie et une interaction fluide.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 est un modèle de raisonnement nouvelle génération avec un raisonnement complexe renforcé et une chaîne de pensée pour les tâches d’analyse approfondie.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 est un modèle de raisonnement de nouvelle génération avec des capacités renforcées de raisonnement complexe et de chaîne de pensée.", - "deepseek-chat.description": "Un nouveau modèle open-source combinant des capacités générales et de codage. Il préserve le dialogue général du modèle de chat et les solides compétences en codage du modèle de programmation, avec un meilleur alignement des préférences. DeepSeek-V2.5 améliore également l'écriture et le suivi des instructions.", + "deepseek-chat.description": "Alias de compatibilité pour le mode non-réflexif de DeepSeek V4 Flash. Prévu pour être déprécié — utilisez DeepSeek V4 Flash à la place.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B est un modèle de langage pour le code entraîné sur 2T de tokens (87 % de code, 13 % de texte en chinois/anglais). Il introduit une fenêtre de contexte de 16K et des tâches de remplissage au milieu, offrant une complétion de code à l’échelle du projet et un remplissage de fragments.", "deepseek-coder-v2.description": "DeepSeek Coder V2 est un modèle de code MoE open source performant sur les tâches de programmation, comparable à GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 est un modèle de code MoE open source performant sur les tâches de programmation, comparable à GPT-4 Turbo.", @@ -425,7 +426,7 @@ "deepseek-r1-fast-online.description": "Version complète rapide de DeepSeek R1 avec recherche web en temps réel, combinant des capacités à l’échelle de 671B et des réponses plus rapides.", "deepseek-r1-online.description": "Version complète de DeepSeek R1 avec 671B de paramètres et recherche web en temps réel, offrant une meilleure compréhension et génération.", "deepseek-r1.description": "DeepSeek-R1 utilise des données de démarrage à froid avant l’apprentissage par renforcement et affiche des performances comparables à OpenAI-o1 en mathématiques, codage et raisonnement.", - "deepseek-reasoner.description": "Un modèle de raisonnement DeepSeek axé sur les tâches de raisonnement logique complexes.", + "deepseek-reasoner.description": "Alias de compatibilité pour le mode réflexif de DeepSeek V4 Flash. Prévu pour être déprécié — utilisez DeepSeek V4 Flash à la place.", "deepseek-v2.description": "DeepSeek V2 est un modèle MoE efficace pour un traitement économique.", "deepseek-v2:236b.description": "DeepSeek V2 236B est le modèle axé sur le code de DeepSeek avec une forte génération de code.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 est un modèle MoE de 671B paramètres avec des points forts en programmation, compréhension du contexte et traitement de longs textes.", @@ -490,6 +491,8 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 est un modèle de génération d’image de ByteDance Seed, prenant en charge les entrées texte et image avec une génération d’image de haute qualité et hautement contrôlable. Il génère des images à partir d’invites textuelles.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 est le dernier modèle d'image multimodal de ByteDance, intégrant des capacités de génération de texte en image, d'image en image et de génération d'images par lots, tout en incorporant des compétences en raisonnement et en bon sens. Par rapport à la version précédente 4.0, il offre une qualité de génération nettement améliorée, avec une meilleure cohérence d'édition et une fusion multi-images. Il permet un contrôle plus précis des détails visuels, produisant des textes et des visages plus petits de manière plus naturelle, et atteint une mise en page et des couleurs plus harmonieuses, améliorant l'esthétique globale.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite est le dernier modèle de génération d'images de ByteDance. Pour la première fois, il intègre des capacités de recherche en ligne, lui permettant d'incorporer des informations web en temps réel et d'améliorer la pertinence des images générées. L'intelligence du modèle a également été améliorée, permettant une interprétation précise des instructions complexes et du contenu visuel. De plus, il offre une meilleure couverture des connaissances globales, une cohérence des références et une qualité de génération dans des scénarios professionnels, répondant mieux aux besoins de création visuelle au niveau des entreprises.", + "dreamina-seedance-2-0-260128.description": "Seedance 2.0 de ByteDance est le modèle de génération vidéo le plus puissant, prenant en charge la génération de vidéos multimodales de référence, le montage vidéo, l'extension vidéo, la conversion texte en vidéo et image en vidéo avec audio synchronisé.", + "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast de ByteDance offre les mêmes capacités que Seedance 2.0 avec des vitesses de génération plus rapides à un prix plus compétitif.", "emohaa.description": "Emohaa est un modèle de santé mentale doté de compétences professionnelles en conseil pour aider les utilisateurs à comprendre leurs problèmes émotionnels.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B est un modèle léger open source conçu pour un déploiement local et personnalisé.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview est un modèle de prévisualisation avec contexte 8K pour l’évaluation d’ERNIE 4.5.", @@ -514,7 +517,8 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K est un modèle de réflexion rapide avec un contexte de 32K pour le raisonnement complexe et les dialogues multi-tours.", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview est une préversion de modèle de réflexion pour l’évaluation et les tests.", "ernie-x1.1.description": "ERNIE X1.1 est un modèle de réflexion en aperçu pour évaluation et test.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 est un modèle de génération d'images de ByteDance Seed, prenant en charge les entrées texte et image avec une génération d'images hautement contrôlable et de haute qualité. Il génère des images à partir de descriptions textuelles.", + "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5, développé par l'équipe Seed de ByteDance, prend en charge l'édition et la composition multi-images. Inclut une meilleure cohérence des sujets, un suivi précis des instructions, une compréhension de la logique spatiale, une expression esthétique, une mise en page de posters et la conception de logos avec un rendu texte-image de haute précision.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, développé par ByteDance Seed, prend en charge les entrées texte et image pour une génération d'images hautement contrôlable et de haute qualité à partir de prompts.", "fal-ai/flux-kontext/dev.description": "Modèle FLUX.1 axé sur l’édition d’images, prenant en charge les entrées texte et image.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] accepte des textes et des images de référence en entrée, permettant des modifications locales ciblées et des transformations globales complexes de scènes.", "fal-ai/flux/krea.description": "Flux Krea [dev] est un modèle de génération d’images avec une préférence esthétique pour des images plus réalistes et naturelles.", @@ -522,8 +526,8 @@ "fal-ai/hunyuan-image/v3.description": "Un puissant modèle natif multimodal de génération d’images.", "fal-ai/imagen4/preview.description": "Modèle de génération d’images de haute qualité développé par Google.", "fal-ai/nano-banana.description": "Nano Banana est le modèle multimodal natif le plus récent, le plus rapide et le plus efficace de Google, permettant la génération et l’édition d’images via la conversation.", - "fal-ai/qwen-image-edit.description": "Un modèle professionnel d'édition d'images de l'équipe Qwen qui prend en charge les modifications sémantiques et d'apparence, édite précisément le texte en chinois et en anglais, et permet des modifications de haute qualité telles que le transfert de style et la rotation d'objets.", - "fal-ai/qwen-image.description": "Un modèle puissant de génération d'images de l'équipe Qwen avec un rendu impressionnant du texte en chinois et des styles visuels variés.", + "fal-ai/qwen-image-edit.description": "Un modèle professionnel d'édition d'images de l'équipe Qwen, prenant en charge les modifications sémantiques et d'apparence, l'édition précise de texte en chinois/anglais, le transfert de style, la rotation et plus encore.", + "fal-ai/qwen-image.description": "Un modèle puissant de génération d'images de l'équipe Qwen avec un rendu texte chinois robuste et des styles visuels variés.", "flux-1-schnell.description": "Modèle texte-vers-image à 12 milliards de paramètres de Black Forest Labs utilisant la distillation par diffusion latente adversariale pour générer des images de haute qualité en 1 à 4 étapes. Il rivalise avec les alternatives propriétaires et est publié sous licence Apache-2.0 pour un usage personnel, de recherche et commercial.", "flux-dev.description": "Modèle open source de génération d’images destiné à la R&D, optimisé efficacement pour la recherche d’innovation non commerciale.", "flux-kontext-max.description": "Génération et édition d’images contextuelles de pointe, combinant texte et images pour des résultats précis et cohérents.", @@ -565,7 +569,7 @@ "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) est le modèle de génération d'images de Google et prend également en charge le chat multimodal.", "gemini-3-pro-preview.description": "Gemini 3 Pro est le modèle agent et de codage le plus puissant de Google, offrant des visuels enrichis et une interaction plus poussée grâce à un raisonnement de pointe.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) est le modèle de génération d'images natif le plus rapide de Google avec prise en charge de la réflexion, génération et édition d'images conversationnelles.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) est le modèle de génération d'images natif le plus rapide de Google avec prise en charge de la réflexion, génération et édition d'images conversationnelles.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) offre une qualité d'image de niveau Pro à une vitesse Flash avec prise en charge du chat multimodal.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview est le modèle multimodal le plus économique de Google, optimisé pour les tâches agentiques à haut volume, la traduction et le traitement des données.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite est le modèle multimodal le plus économique de Google, optimisé pour les tâches agentiques à haut volume, la traduction et le traitement des données.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview améliore Gemini 3 Pro avec des capacités de raisonnement renforcées et ajoute un support de niveau de réflexion moyen.", @@ -730,6 +734,8 @@ "grok-4-fast-reasoning.description": "Nous sommes ravis de présenter Grok 4 Fast, notre dernière avancée en matière de modèles de raisonnement économiques.", "grok-4.20-0309-non-reasoning.description": "Une variante sans raisonnement pour des cas d'utilisation simples.", "grok-4.20-0309-reasoning.description": "Modèle intelligent et ultra-rapide qui raisonne avant de répondre.", + "grok-4.20-beta-0309-non-reasoning.description": "Une variante non-réflexive pour des cas d'utilisation simples.", + "grok-4.20-beta-0309-reasoning.description": "Modèle intelligent et ultra-rapide qui réfléchit avant de répondre.", "grok-4.20-multi-agent-0309.description": "Une équipe de 4 ou 16 agents, excelle dans les cas d'utilisation de recherche. Ne prend actuellement pas en charge les outils côté client. Prend uniquement en charge les outils côté serveur xAI (par exemple X Search, outils de recherche Web) et les outils MCP distants.", "grok-4.3.description": "Le modèle de langage de grande taille le plus axé sur la vérité au monde", "grok-4.description": "Dernier modèle phare Grok avec des performances inégalées en langage, mathématiques et raisonnement — un véritable polyvalent. Actuellement pointé vers grok-4-0709 ; en raison de ressources limitées, son prix est temporairement 10 % plus élevé que le tarif officiel et devrait revenir au prix officiel ultérieurement.", @@ -1214,6 +1220,8 @@ "qwq.description": "QwQ est un modèle de raisonnement de la famille Qwen. Comparé aux modèles classiques ajustés par instruction, il apporte des capacités de réflexion et de raisonnement qui améliorent considérablement les performances en aval, notamment sur les problèmes complexes. QwQ-32B est un modèle de raisonnement de taille moyenne qui rivalise avec les meilleurs modèles comme DeepSeek-R1 et o1-mini.", "qwq_32b.description": "Modèle de raisonnement de taille moyenne de la famille Qwen. Comparé aux modèles classiques ajustés par instruction, les capacités de réflexion et de raisonnement de QwQ améliorent considérablement les performances en aval, notamment sur les problèmes complexes.", "r1-1776.description": "R1-1776 est une variante post-entraînée de DeepSeek R1 conçue pour fournir des informations factuelles non censurées et impartiales.", + "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro de ByteDance prend en charge la conversion texte en vidéo, image en vidéo (première image, première+dernière image) et la génération audio synchronisée avec les visuels.", + "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite par BytePlus propose une génération augmentée par récupération web pour des informations en temps réel, une interprétation améliorée des prompts complexes et une meilleure cohérence des références pour la création visuelle professionnelle.", "solar-mini-ja.description": "Solar Mini (Ja) étend Solar Mini avec un accent sur le japonais tout en maintenant des performances efficaces et solides en anglais et en coréen.", "solar-mini.description": "Solar Mini est un modèle LLM compact surpassant GPT-3.5, avec de solides capacités multilingues en anglais et en coréen, offrant une solution efficace à faible empreinte.", "solar-pro.description": "Solar Pro est un LLM intelligent développé par Upstage, axé sur le suivi d'instructions sur un seul GPU, avec des scores IFEval supérieurs à 80. Il prend actuellement en charge l'anglais ; la version complète est prévue pour novembre 2024 avec un support linguistique élargi et un contexte plus long.", diff --git a/locales/fr-FR/plugin.json b/locales/fr-FR/plugin.json index 8031323790..f62a25600d 100644 --- a/locales/fr-FR/plugin.json +++ b/locales/fr-FR/plugin.json @@ -237,6 +237,14 @@ "builtins.lobe-page-agent.apiName.updateNode": "Mettre à jour le nœud", "builtins.lobe-page-agent.apiName.wrapNodes": "Associer les nœuds", "builtins.lobe-page-agent.title": "Page", + "builtins.lobe-self-feedback-intent.apiName.declareSelfFeedbackIntent": "Enregistrer une idée d'amélioration", + "builtins.lobe-self-feedback-intent.inspector.gap.proposal": "Suggérer une amélioration", + "builtins.lobe-self-feedback-intent.inspector.memory.write": "Enregistrer une préférence", + "builtins.lobe-self-feedback-intent.inspector.rejected": "Non enregistré", + "builtins.lobe-self-feedback-intent.inspector.skill.consolidate": "Organiser les méthodes", + "builtins.lobe-self-feedback-intent.inspector.skill.create": "Nouvelle méthode trouvée", + "builtins.lobe-self-feedback-intent.inspector.skill.refine": "Améliorer la méthode", + "builtins.lobe-self-feedback-intent.title": "Idées d'amélioration", "builtins.lobe-skill-store.apiName.importFromMarket": "Importer depuis le Marché", "builtins.lobe-skill-store.apiName.importSkill": "Importer une Compétence", "builtins.lobe-skill-store.apiName.searchSkill": "Rechercher des Compétences", diff --git a/locales/fr-FR/providers.json b/locales/fr-FR/providers.json index cc72e426df..ba3e58742e 100644 --- a/locales/fr-FR/providers.json +++ b/locales/fr-FR/providers.json @@ -33,6 +33,7 @@ "jina.description": "Fondée en 2020, Jina AI est une entreprise leader en IA de recherche. Sa pile technologique comprend des modèles vectoriels, des rerankers et de petits modèles linguistiques pour créer des applications de recherche générative et multimodale fiables et de haute qualité.", "kimicodingplan.description": "Kimi Code de Moonshot AI offre un accès aux modèles Kimi, y compris K2.5, pour des tâches de codage.", "lmstudio.description": "LM Studio est une application de bureau pour développer et expérimenter avec des LLMs sur votre ordinateur.", + "lobehub.description": "LobeHub Cloud utilise des API officielles pour accéder aux modèles d'IA et mesure l'utilisation avec des Crédits liés aux jetons des modèles.", "longcat.description": "LongCat est une série de grands modèles d'IA générative développés indépendamment par Meituan. Elle est conçue pour améliorer la productivité interne de l'entreprise et permettre des applications innovantes grâce à une architecture informatique efficace et de puissantes capacités multimodales.", "minimax.description": "Fondée en 2021, MiniMax développe une IA généraliste avec des modèles fondamentaux multimodaux, incluant des modèles texte MoE à un billion de paramètres, des modèles vocaux et visuels, ainsi que des applications comme Hailuo AI.", "minimaxcodingplan.description": "Le plan de jetons MiniMax offre un accès aux modèles MiniMax, y compris M2.7, pour des tâches de codage via un abonnement à tarif fixe.", diff --git a/locales/fr-FR/subscription.json b/locales/fr-FR/subscription.json index 184313ae31..a76c9e24be 100644 --- a/locales/fr-FR/subscription.json +++ b/locales/fr-FR/subscription.json @@ -41,7 +41,9 @@ "credits.autoTopUp.monthlyLimitDesc": "Montant maximum pouvant être facturé automatiquement par mois. Laissez vide pour aucune limite", "credits.autoTopUp.monthlyLimitPlaceholder": "Aucune limite", "credits.autoTopUp.monthlyTopUpAmount": "Montant de recharge mensuelle", + "credits.autoTopUp.noCustomerHint": "Achetez des crédits une fois pour enregistrer un mode de paiement avant d'activer le rechargement automatique.", "credits.autoTopUp.noPaymentMethodHint": "Aucun moyen de paiement enregistré. Le rechargement automatique nécessite une carte enregistrée pour être facturé automatiquement.", + "credits.autoTopUp.purchaseCredits": "Acheter des crédits", "credits.autoTopUp.saveError": "Échec de l'enregistrement des paramètres de recharge automatique", "credits.autoTopUp.saveSuccess": "Paramètres de recharge automatique enregistrés", "credits.autoTopUp.setupPaymentMethod": "Ajouter un moyen de paiement", @@ -83,6 +85,7 @@ "credits.packages.title": "Mes forfaits de crédits", "credits.topUp.cancel": "Annuler", "credits.topUp.custom": "Personnalisé", + "credits.topUp.freeFeeHint": "Les rechargements du plan gratuit incluent des frais de service de {{fee}} par 1M de crédits.", "credits.topUp.maxAmountError": "Le montant d'achat unique ne peut pas dépasser ${{max}}", "credits.topUp.purchaseError": "Échec de l'achat, veuillez réessayer plus tard", "credits.topUp.purchaseNow": "Acheter maintenant", @@ -120,6 +123,9 @@ "keyMissMatch.button": "Restaurer l'utilisation et continuer la conversation", "keyMissMatch.description": "En raison d'une défaillance système occasionnelle, l'utilisation de votre abonnement est temporairement inactive. Veuillez cliquer sur le bouton ci-dessous pour restaurer l'utilisation et continuer la conversation. Si cela se reproduit, veuillez nous contacter par e-mail (support@lobehub.com)", "keyMissMatch.title": "Restaurer l'utilisation de l'abonnement maintenant", + "limitation.chat.budgetReady.action": "Continuer à discuter", + "limitation.chat.budgetReady.desc": "Vos crédits disponibles couvrent désormais cette demande.", + "limitation.chat.budgetReady.title": "Crédits disponibles", "limitation.chat.success.action": "Continuer à discuter", "limitation.chat.success.desc": "Votre abonnement {{plan}} a été mis à niveau avec succès. Profitez du chat IA. Votre forfait actuel inclut :", "limitation.chat.success.title": "Mise à niveau réussie", @@ -134,14 +140,23 @@ "limitation.hobby.docs": "Voir la documentation de configuration", "limitation.hobby.tip": "N'oubliez pas de passer à un modèle avec clé API personnalisée", "limitation.hobby.title": "Veuillez configurer l'API du service de modèle", + "limitation.image.budgetReady.action": "Continuer la génération", + "limitation.image.budgetReady.desc": "Vos crédits disponibles couvrent désormais cette génération.", + "limitation.image.budgetReady.title": "Crédits disponibles", "limitation.image.success.action": "Continuer la génération", "limitation.image.success.desc": "Votre abonnement {{plan}} a été mis à niveau avec succès. Profitez de la génération d'images IA. Votre forfait actuel inclut :", "limitation.image.success.title": "Mise à niveau réussie", "limitation.image.topupSuccess.action": "Continuer la génération", "limitation.image.topupSuccess.desc": "Vos crédits prépayés sont maintenant actifs. Profitez de la génération d'images IA. Votre forfait actuel inclut :", "limitation.image.topupSuccess.title": "Recharge réussie", + "limitation.insufficientBudget.approximateDesc": "Cette demande pourrait nécessiter plus de crédits. Rechargez vos crédits ou passez à un plan supérieur.", + "limitation.insufficientBudget.available": "Crédits disponibles", "limitation.insufficientBudget.desc": "Vos crédits restants ne suffisent pas pour le coût estimé de ce modèle. Veuillez recharger vos crédits ou choisir un modèle moins coûteux.", + "limitation.insufficientBudget.estimatedDesc": "Cette demande est estimée nécessiter plus de crédits. Rechargez vos crédits ou passez à un plan supérieur.", + "limitation.insufficientBudget.exactDesc": "Cette demande nécessite plus de crédits. Rechargez vos crédits ou passez à un plan supérieur.", + "limitation.insufficientBudget.required": "Crédits requis", "limitation.insufficientBudget.retry": "Réessayer", + "limitation.insufficientBudget.shortfall": "Manque de crédits", "limitation.insufficientBudget.title": "Crédits insuffisants pour ce modèle", "limitation.limited.action": "Mettre à niveau maintenant", "limitation.limited.advanceFeature": "Passez à un forfait supérieur pour profiter des fonctionnalités premium :", @@ -151,6 +166,7 @@ "limitation.limited.title": "Crédits de calcul épuisés", "limitation.limited.topup": "Recharger les crédits", "limitation.limited.upgrade": "Passer à un forfait supérieur", + "limitation.limited.upgradeToPlan": "Passer au plan {{plan}}", "limitation.providers.lock.addNew": "Abonnez-vous maintenant pour créer des fournisseurs IA personnalisés", "limitation.providers.lock.enableProvider": "Abonnez-vous maintenant pour activer ce fournisseur IA", "limitation.providers.lock.menuItem": "Abonnez-vous maintenant pour configurer un service API personnalisé", @@ -161,6 +177,9 @@ "limitation.providers.prompter.subTitle": "Le service API personnalisé est uniquement disponible pour les forfaits payants. Passez à un forfait supérieur pour profiter des services de modèles mondiaux", "limitation.providers.prompter.title": "Abonnez-vous maintenant pour utiliser le service API personnalisé", "limitation.providers.tooltip": "Le service API personnalisé est uniquement disponible pour les forfaits payants", + "limitation.video.budgetReady.action": "Continuer la génération", + "limitation.video.budgetReady.desc": "Vos crédits disponibles couvrent désormais cette génération.", + "limitation.video.budgetReady.title": "Crédits disponibles", "limitation.video.success.action": "Continuer la génération", "limitation.video.success.desc": "Votre abonnement {{plan}} a été mis à niveau avec succès. Profitez de la génération de vidéos par IA. Votre forfait actuel comprend :", "limitation.video.success.title": "Mise à niveau réussie", diff --git a/locales/it-IT/chat.json b/locales/it-IT/chat.json index 82736fbf94..6c6c245d8f 100644 --- a/locales/it-IT/chat.json +++ b/locales/it-IT/chat.json @@ -196,6 +196,9 @@ "groupWizard.searchTemplates": "Cerca modelli...", "groupWizard.title": "Crea Gruppo", "groupWizard.useTemplate": "Usa Modello", + "heteroAgent.cloudNotConfigured.action": "Configura", + "heteroAgent.cloudNotConfigured.desc": "Configura il token Claude Code nel profilo dell'agente per iniziare a inviare messaggi.", + "heteroAgent.cloudNotConfigured.title": "Credenziali cloud richieste", "heteroAgent.cloudRepo.multiSelected": "{{count}} repository selezionati", "heteroAgent.cloudRepo.noRepos": "Nessun repository configurato. Aggiungili nelle impostazioni dell'agente.", "heteroAgent.cloudRepo.notSet": "Nessun repository selezionato", @@ -467,6 +470,10 @@ "sharePage.error.unauthorized.subtitle": "Accedi per visualizzare questo argomento condiviso.", "sharePage.error.unauthorized.title": "Accesso richiesto", "sharePageDisclaimer": "Questo contenuto è stato condiviso da un utente e non rappresenta le opinioni di LobeHub. LobeHub non è responsabile per eventuali conseguenze derivanti da questo contenuto condiviso.", + "signalCallbacks.collapse": "Nascondi dettagli", + "signalCallbacks.empty": "Nessun messaggio di callback", + "signalCallbacks.expand": "Mostra dettagli", + "signalCallbacks.title": "{{tool}} · {{count}} aggiornamenti di callback", "stt.action": "Input vocale", "stt.loading": "Riconoscimento in corso...", "stt.prettifying": "Ottimizzazione...", @@ -689,7 +696,6 @@ "thread.closeSubagentThread": "Comprimi conversazione subagente", "thread.divider": "Sottotema", "thread.openSubagentThread": "Mostra conversazione completa del subagente", - "thread.subagentBadge": "Subagente", "thread.subagentReadOnlyHint": "Le conversazioni del SubAgent sono di sola lettura — l'esecuzione è guidata dall'agente principale.", "thread.threadMessageCount": "{{messageCount}} messaggi", "thread.title": "Sottotema", diff --git a/locales/it-IT/models.json b/locales/it-IT/models.json index ef93696794..75b60b3e1d 100644 --- a/locales/it-IT/models.json +++ b/locales/it-IT/models.json @@ -106,6 +106,7 @@ "MiniMax-Hailuo-2.3.description": "Nuovo modello di generazione video con aggiornamenti completi nei movimenti del corpo, realismo fisico e aderenza alle istruzioni.", "MiniMax-M1.description": "Nuovo modello di ragionamento proprietario con 80K chain-of-thought e 1M di input, con prestazioni comparabili ai migliori modelli globali.", "MiniMax-M2-Stable.description": "Progettato per flussi di lavoro di codifica e agenti efficienti, con maggiore concorrenza per l'uso commerciale.", + "MiniMax-M2.1-Lightning.description": "Potenti capacità di programmazione multilingue con inferenza più rapida ed efficiente.", "MiniMax-M2.1-highspeed.description": "Potenti capacità di programmazione multilingue, esperienza di programmazione completamente aggiornata. Più veloce ed efficiente.", "MiniMax-M2.1.description": "MiniMax-M2.1 è un modello open-source di punta di MiniMax, progettato per affrontare compiti complessi del mondo reale. I suoi punti di forza principali sono le capacità di programmazione multilingue e la risoluzione di compiti complessi come agente.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: Stesse prestazioni di M2.5 con inferenza più veloce.", @@ -314,7 +315,7 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku è il modello più veloce e compatto di Anthropic, progettato per risposte quasi istantanee con prestazioni rapide e accurate.", "claude-3-opus-20240229.description": "Claude 3 Opus è il modello più potente di Anthropic per compiti altamente complessi, eccellendo in prestazioni, intelligenza, fluidità e comprensione.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet bilancia intelligenza e velocità per carichi di lavoro aziendali, offrendo alta utilità a costi inferiori e distribuzione affidabile su larga scala.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 è il modello Haiku più veloce e intelligente di Anthropic, con velocità fulminea e capacità di ragionamento estese.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 è il modello Haiku più veloce e intelligente di Anthropic, con velocità fulminea e pensiero esteso.", "claude-haiku-4-5.description": "Claude Haiku 4.5 di Anthropic — Haiku di nuova generazione con ragionamento e visione migliorati.", "claude-haiku-4.5.description": "Claude Haiku 4.5 è il modello Haiku più veloce e intelligente di Anthropic, con velocità fulminea e capacità di ragionamento estese.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking è una variante avanzata in grado di mostrare il proprio processo di ragionamento.", @@ -329,8 +330,8 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 è il modello più intelligente di Anthropic per la creazione di agenti e la programmazione.", "claude-opus-4.6.description": "Claude Opus 4.6 è il modello più intelligente di Anthropic per la creazione di agenti e la programmazione.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking può produrre risposte quasi istantanee o riflessioni estese passo dopo passo con processo visibile.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 può produrre risposte quasi istantanee o pensieri estesi passo dopo passo con un processo visibile.", - "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 è il modello più intelligente mai creato da Anthropic.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 è il modello più intelligente di Anthropic fino ad oggi, offrendo risposte quasi istantanee o pensiero esteso passo dopo passo con controllo dettagliato per gli utenti API.", + "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 è il modello più intelligente di Anthropic fino ad oggi.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 di Anthropic — Sonnet migliorato con prestazioni di codifica avanzate.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 di Anthropic — ultimo Sonnet con codifica superiore e utilizzo di strumenti.", "claude-sonnet-4.5.description": "Claude Sonnet 4.5 è il modello più intelligente mai creato da Anthropic.", @@ -403,7 +404,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) è un modello innovativo che offre una profonda comprensione linguistica e interazione.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 è un modello di nuova generazione per il ragionamento, con capacità avanzate di ragionamento complesso e chain-of-thought per compiti di analisi approfondita.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 è un modello di ragionamento di nuova generazione con capacità avanzate di ragionamento complesso e catena di pensiero.", - "deepseek-chat.description": "Un nuovo modello open-source che combina capacità generali e di codifica. Preserva il dialogo generale del modello di chat e la forte capacità di codifica del modello coder, con un migliore allineamento delle preferenze. DeepSeek-V2.5 migliora anche la scrittura e il seguito delle istruzioni.", + "deepseek-chat.description": "Alias di compatibilità per la modalità non pensante di DeepSeek V4 Flash. Destinato alla deprecazione — utilizzare DeepSeek V4 Flash invece.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B è un modello linguistico per il codice addestrato su 2 trilioni di token (87% codice, 13% testo in cinese/inglese). Introduce una finestra di contesto da 16K e compiti di completamento intermedio, offrendo completamento di codice a livello di progetto e riempimento di snippet.", "deepseek-coder-v2.description": "DeepSeek Coder V2 è un modello MoE open-source per il codice che ottiene ottimi risultati nei compiti di programmazione, comparabile a GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 è un modello MoE open-source per il codice che ottiene ottimi risultati nei compiti di programmazione, comparabile a GPT-4 Turbo.", @@ -425,7 +426,7 @@ "deepseek-r1-fast-online.description": "DeepSeek R1 versione completa veloce con ricerca web in tempo reale, che combina capacità su scala 671B e risposte rapide.", "deepseek-r1-online.description": "DeepSeek R1 versione completa con 671 miliardi di parametri e ricerca web in tempo reale, che offre una comprensione e generazione più avanzate.", "deepseek-r1.description": "DeepSeek-R1 utilizza dati cold-start prima dell'RL e ottiene prestazioni comparabili a OpenAI-o1 in matematica, programmazione e ragionamento.", - "deepseek-reasoner.description": "Un modello di ragionamento DeepSeek focalizzato su compiti di ragionamento logico complessi.", + "deepseek-reasoner.description": "Alias di compatibilità per la modalità pensante di DeepSeek V4 Flash. Destinato alla deprecazione — utilizzare DeepSeek V4 Flash invece.", "deepseek-v2.description": "DeepSeek V2 è un modello MoE efficiente per un'elaborazione conveniente.", "deepseek-v2:236b.description": "DeepSeek V2 236B è il modello DeepSeek focalizzato sul codice con forte capacità di generazione.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 è un modello MoE con 671 miliardi di parametri, con punti di forza nella programmazione, capacità tecnica, comprensione del contesto e gestione di testi lunghi.", @@ -490,6 +491,8 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 è un modello di generazione di immagini di ByteDance Seed, che supporta input di testo e immagini con generazione di immagini di alta qualità e altamente controllabile. Genera immagini da prompt testuali.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 è l'ultimo modello multimodale di ByteDance, che integra capacità di generazione da testo a immagine, immagine a immagine e generazione di immagini in batch, incorporando anche conoscenze di senso comune e capacità di ragionamento. Rispetto alla versione precedente 4.0, offre una qualità di generazione significativamente migliorata, con una maggiore coerenza nell'editing e nella fusione di più immagini. Offre un controllo più preciso sui dettagli visivi, producendo testi e volti piccoli in modo più naturale, e raggiunge una disposizione e una colorazione più armoniose, migliorando l'estetica complessiva.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite è l'ultimo modello di generazione di immagini di ByteDance. Per la prima volta, integra capacità di recupero online, consentendo di incorporare informazioni web in tempo reale e migliorare la tempestività delle immagini generate. L'intelligenza del modello è stata inoltre aggiornata, consentendo un'interpretazione precisa di istruzioni complesse e contenuti visivi. Inoltre, offre una copertura globale della conoscenza migliorata, una maggiore coerenza di riferimento e una qualità di generazione superiore in scenari professionali, soddisfacendo meglio le esigenze di creazione visiva a livello aziendale.", + "dreamina-seedance-2-0-260128.description": "Seedance 2.0 di ByteDance è il modello di generazione video più potente, supportando la generazione di video multimodali di riferimento, editing video, estensione video, testo-a-video e immagine-a-video con audio sincronizzato.", + "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast di ByteDance offre le stesse capacità di Seedance 2.0 con velocità di generazione più rapide a un prezzo più competitivo.", "emohaa.description": "Emohaa è un modello per la salute mentale con capacità di consulenza professionale per aiutare gli utenti a comprendere le problematiche emotive.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B è un modello open-source leggero per implementazioni locali e personalizzate.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview è un modello di anteprima con finestra contestuale da 8K per la valutazione di ERNIE 4.5.", @@ -514,7 +517,8 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K è un modello di pensiero veloce con contesto da 32K per ragionamento complesso e chat multi-turno.", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview è un’anteprima del modello di pensiero per valutazioni e test.", "ernie-x1.1.description": "ERNIE X1.1 è un'anteprima del modello di pensiero per valutazione e test.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 è un modello di generazione di immagini di ByteDance Seed, che supporta input di testo e immagini con generazione di immagini altamente controllabile e di alta qualità. Genera immagini a partire da prompt testuali.", + "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5, sviluppato dal team Seed di ByteDance, supporta l'editing e la composizione multi-immagine. Caratteristiche includono coerenza del soggetto migliorata, esecuzione precisa delle istruzioni, comprensione della logica spaziale, espressione estetica, layout di poster e design di loghi con rendering testo-immagine ad alta precisione.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, sviluppato da ByteDance Seed, supporta input di testo e immagini per una generazione di immagini altamente controllabile e di alta qualità a partire dai prompt.", "fal-ai/flux-kontext/dev.description": "FLUX.1 è un modello focalizzato sull’editing di immagini, che supporta input di testo e immagini.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] accetta testo e immagini di riferimento come input, consentendo modifiche locali mirate e trasformazioni complesse della scena globale.", "fal-ai/flux/krea.description": "Flux Krea [dev] è un modello di generazione di immagini con una preferenza estetica per immagini più realistiche e naturali.", @@ -522,8 +526,8 @@ "fal-ai/hunyuan-image/v3.description": "Un potente modello nativo multimodale per la generazione di immagini.", "fal-ai/imagen4/preview.description": "Modello di generazione di immagini di alta qualità sviluppato da Google.", "fal-ai/nano-banana.description": "Nano Banana è il modello multimodale nativo più recente, veloce ed efficiente di Google, che consente la generazione e l’editing di immagini tramite conversazione.", - "fal-ai/qwen-image-edit.description": "Un modello professionale di editing di immagini del team Qwen che supporta modifiche semantiche e di aspetto, modifica con precisione testo in cinese e inglese, e consente modifiche di alta qualità come trasferimento di stile e rotazione di oggetti.", - "fal-ai/qwen-image.description": "Un potente modello di generazione di immagini del team Qwen con impressionante rendering di testo in cinese e stili visivi diversificati.", + "fal-ai/qwen-image-edit.description": "Un modello professionale di editing immagini del team Qwen, che supporta modifiche semantiche e di aspetto, editing preciso di testo in cinese/inglese, trasferimento di stile, rotazione e altro.", + "fal-ai/qwen-image.description": "Un potente modello di generazione immagini del team Qwen con una forte resa del testo in cinese e stili visivi diversificati.", "flux-1-schnell.description": "Modello testo-immagine da 12 miliardi di parametri di Black Forest Labs che utilizza la distillazione latente avversariale per generare immagini di alta qualità in 1-4 passaggi. Con licenza Apache-2.0 per uso personale, di ricerca e commerciale.", "flux-dev.description": "Modello open-source per la ricerca e sviluppo nella generazione di immagini, ottimizzato in modo efficiente per la ricerca innovativa non commerciale.", "flux-kontext-max.description": "Generazione ed editing di immagini contestuali all’avanguardia, combinando testo e immagini per risultati precisi e coerenti.", @@ -562,10 +566,10 @@ "gemini-3-flash-preview.description": "Gemini 3 Flash è il modello più intelligente progettato per la velocità, che combina intelligenza all'avanguardia con un eccellente ancoraggio alla ricerca.", "gemini-3-flash.description": "Gemini 3 Flash di Google — modello ultra-veloce con supporto per input multimodali.", "gemini-3-pro-image-preview.description": "Gemini 3 Pro Image (Nano Banana Pro) è il modello di generazione di immagini di Google che supporta anche il dialogo multimodale.", - "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) è il modello di generazione di immagini di Google e supporta anche la chat multimodale.", + "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) è il modello di generazione immagini di Google e supporta anche la chat multimodale.", "gemini-3-pro-preview.description": "Gemini 3 Pro è il modello più potente di Google per agenti e codifica creativa, offrendo visuali più ricche e interazioni più profonde grazie a un ragionamento all'avanguardia.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) è il modello di generazione di immagini nativo più veloce di Google con supporto al pensiero, generazione e modifica di immagini conversazionali.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) è il modello di generazione di immagini nativo più veloce di Google con supporto al pensiero, generazione e modifica di immagini conversazionali.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) offre qualità di immagine di livello Pro a velocità Flash con supporto per la chat multimodale.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview è il modello multimodale più economico di Google, ottimizzato per compiti agentici ad alto volume, traduzione e elaborazione dati.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite è il modello multimodale più economico di Google, ottimizzato per compiti agentici ad alto volume, traduzione e elaborazione dati.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview migliora Gemini 3 Pro con capacità di ragionamento avanzate e aggiunge supporto per un livello di pensiero medio.", @@ -730,6 +734,8 @@ "grok-4-fast-reasoning.description": "Siamo entusiasti di presentare Grok 4 Fast, il nostro ultimo progresso nei modelli di ragionamento a basso costo.", "grok-4.20-0309-non-reasoning.description": "Una variante senza ragionamento per casi d'uso semplici.", "grok-4.20-0309-reasoning.description": "Modello intelligente e velocissimo che ragiona prima di rispondere.", + "grok-4.20-beta-0309-non-reasoning.description": "Una variante non pensante per casi d'uso semplici.", + "grok-4.20-beta-0309-reasoning.description": "Modello intelligente e ultra-veloce che ragiona prima di rispondere.", "grok-4.20-multi-agent-0309.description": "Un team di 4 o 16 agenti, eccelle nei casi d'uso di ricerca, non supporta attualmente strumenti client-side. Supporta solo strumenti server-side xAI (es. X Search, strumenti di ricerca web) e strumenti MCP remoti.", "grok-4.3.description": "Il modello linguistico di grandi dimensioni più orientato alla verità al mondo", "grok-4.description": "Ultimo modello di punta Grok con prestazioni ineguagliabili in linguaggio, matematica e ragionamento — un vero tuttofare. Attualmente punta a grok-4-0709; a causa di risorse limitate, il prezzo è temporaneamente superiore del 10% rispetto al prezzo ufficiale e si prevede un ritorno al prezzo ufficiale in seguito.", @@ -1214,6 +1220,8 @@ "qwq.description": "QwQ è un modello di ragionamento della famiglia Qwen. Rispetto ai modelli standard ottimizzati per istruzioni, offre capacità di pensiero e ragionamento che migliorano significativamente le prestazioni nei compiti difficili. QwQ-32B è un modello di medie dimensioni che compete con i migliori modelli di ragionamento come DeepSeek-R1 e o1-mini.", "qwq_32b.description": "Modello di ragionamento di medie dimensioni della famiglia Qwen. Rispetto ai modelli standard ottimizzati per istruzioni, le capacità di pensiero e ragionamento di QwQ migliorano significativamente le prestazioni nei compiti difficili.", "r1-1776.description": "R1-1776 è una variante post-addestrata di DeepSeek R1 progettata per fornire informazioni fattuali non censurate e imparziali.", + "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro di ByteDance supporta testo-a-video, immagine-a-video (prima immagine, prima+ultima immagine) e generazione audio sincronizzata con i visual.", + "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite di BytePlus offre generazione aumentata da recupero web per informazioni in tempo reale, interpretazione migliorata di prompt complessi e maggiore coerenza di riferimento per la creazione visiva professionale.", "solar-mini-ja.description": "Solar Mini (Ja) estende Solar Mini con un focus sul giapponese, mantenendo prestazioni efficienti e solide in inglese e coreano.", "solar-mini.description": "Solar Mini è un LLM compatto che supera GPT-3.5, con forte capacità multilingue in inglese e coreano, offrendo una soluzione efficiente e leggera.", "solar-pro.description": "Solar Pro è un LLM ad alta intelligenza di Upstage, focalizzato sull’esecuzione di istruzioni su una singola GPU, con punteggi IFEval superiori a 80. Attualmente supporta l’inglese; il rilascio completo è previsto per novembre 2024 con supporto linguistico ampliato e contesto più lungo.", diff --git a/locales/it-IT/plugin.json b/locales/it-IT/plugin.json index ee26e37f3b..55c2680cf9 100644 --- a/locales/it-IT/plugin.json +++ b/locales/it-IT/plugin.json @@ -237,6 +237,14 @@ "builtins.lobe-page-agent.apiName.updateNode": "Aggiorna nodo", "builtins.lobe-page-agent.apiName.wrapNodes": "Raggruppa nodi", "builtins.lobe-page-agent.title": "Pagina", + "builtins.lobe-self-feedback-intent.apiName.declareSelfFeedbackIntent": "Registra idea di miglioramento", + "builtins.lobe-self-feedback-intent.inspector.gap.proposal": "Suggerisci miglioramento", + "builtins.lobe-self-feedback-intent.inspector.memory.write": "Registra preferenza", + "builtins.lobe-self-feedback-intent.inspector.rejected": "Non registrato", + "builtins.lobe-self-feedback-intent.inspector.skill.consolidate": "Organizza metodi", + "builtins.lobe-self-feedback-intent.inspector.skill.create": "Nuovo metodo trovato", + "builtins.lobe-self-feedback-intent.inspector.skill.refine": "Migliora metodo", + "builtins.lobe-self-feedback-intent.title": "Idee di miglioramento", "builtins.lobe-skill-store.apiName.importFromMarket": "Importa dal Mercato", "builtins.lobe-skill-store.apiName.importSkill": "Importa Abilità", "builtins.lobe-skill-store.apiName.searchSkill": "Cerca Abilità", diff --git a/locales/it-IT/providers.json b/locales/it-IT/providers.json index aca1986b82..c586b43c52 100644 --- a/locales/it-IT/providers.json +++ b/locales/it-IT/providers.json @@ -33,6 +33,7 @@ "jina.description": "Fondata nel 2020, Jina AI è un'azienda leader nell'AI per la ricerca. Il suo stack include modelli vettoriali, reranker e piccoli modelli linguistici per costruire app di ricerca generativa e multimodale affidabili e di alta qualità.", "kimicodingplan.description": "Kimi Code di Moonshot AI offre accesso ai modelli Kimi, inclusi K2.5, per attività di codifica.", "lmstudio.description": "LM Studio è un'app desktop per sviluppare e sperimentare con LLM direttamente sul tuo computer.", + "lobehub.description": "LobeHub Cloud utilizza API ufficiali per accedere ai modelli di intelligenza artificiale e misura l'utilizzo con Crediti legati ai token del modello.", "longcat.description": "LongCat è una serie di modelli AI generativi di grandi dimensioni sviluppati indipendentemente da Meituan. È progettato per migliorare la produttività interna dell'azienda e consentire applicazioni innovative attraverso un'architettura computazionale efficiente e potenti capacità multimodali.", "minimax.description": "Fondata nel 2021, MiniMax sviluppa AI generali con modelli fondamentali multimodali, inclusi modelli testuali MoE da trilioni di parametri, modelli vocali e visivi, oltre ad app come Hailuo AI.", "minimaxcodingplan.description": "Il piano di token MiniMax offre accesso ai modelli MiniMax, inclusi M2.7, per attività di codifica tramite un abbonamento a tariffa fissa.", diff --git a/locales/it-IT/subscription.json b/locales/it-IT/subscription.json index 08ae1e6e77..55f325400d 100644 --- a/locales/it-IT/subscription.json +++ b/locales/it-IT/subscription.json @@ -41,7 +41,9 @@ "credits.autoTopUp.monthlyLimitDesc": "Importo massimo che può essere addebitato automaticamente al mese. Lascia vuoto per nessun limite", "credits.autoTopUp.monthlyLimitPlaceholder": "Nessun limite", "credits.autoTopUp.monthlyTopUpAmount": "Importo di ricarica mensile", + "credits.autoTopUp.noCustomerHint": "Acquista crediti una volta per salvare un metodo di pagamento prima di abilitare il ricaricamento automatico.", "credits.autoTopUp.noPaymentMethodHint": "Nessun metodo di pagamento registrato. La ricarica automatica richiede una carta salvata per l'addebito automatico.", + "credits.autoTopUp.purchaseCredits": "Acquista Crediti", "credits.autoTopUp.saveError": "Impossibile salvare le impostazioni di ricarica automatica", "credits.autoTopUp.saveSuccess": "Impostazioni di ricarica automatica salvate", "credits.autoTopUp.setupPaymentMethod": "Aggiungi Metodo di Pagamento", @@ -83,6 +85,7 @@ "credits.packages.title": "I Miei Pacchetti Crediti", "credits.topUp.cancel": "Annulla", "credits.topUp.custom": "Personalizzato", + "credits.topUp.freeFeeHint": "Le ricariche del piano gratuito includono una commissione di servizio di {{fee}} per 1M di crediti.", "credits.topUp.maxAmountError": "L'importo massimo per singolo acquisto non può superare ${{max}}", "credits.topUp.purchaseError": "Acquisto non riuscito, riprova più tardi", "credits.topUp.purchaseNow": "Acquista Ora", @@ -120,6 +123,9 @@ "keyMissMatch.button": "Ripristina l'utilizzo e continua la conversazione", "keyMissMatch.description": "A causa di un guasto occasionale del sistema, l'utilizzo del tuo abbonamento è temporaneamente inattivo. Clicca sul pulsante qui sotto per ripristinare l'utilizzo e continuare la conversazione. Se il problema si ripete, contattaci via email (support@lobehub.com)", "keyMissMatch.title": "Ripristina subito l'utilizzo dell'abbonamento", + "limitation.chat.budgetReady.action": "Continua a Chattare", + "limitation.chat.budgetReady.desc": "I tuoi crediti disponibili ora coprono questa richiesta.", + "limitation.chat.budgetReady.title": "Crediti Disponibili", "limitation.chat.success.action": "Continua a chattare", "limitation.chat.success.desc": "Il tuo abbonamento {{plan}} è stato aggiornato con successo. Goditi le conversazioni con l'IA. Il tuo piano attuale include:", "limitation.chat.success.title": "Aggiornamento riuscito", @@ -134,14 +140,23 @@ "limitation.hobby.docs": "Visualizza documentazione di configurazione", "limitation.hobby.tip": "Ricorda di passare a un modello con API Key personalizzata", "limitation.hobby.title": "Configura l'API del servizio modello", + "limitation.image.budgetReady.action": "Continua a Generare", + "limitation.image.budgetReady.desc": "I tuoi crediti disponibili ora coprono questa generazione.", + "limitation.image.budgetReady.title": "Crediti Disponibili", "limitation.image.success.action": "Continua a generare", "limitation.image.success.desc": "Il tuo abbonamento {{plan}} è stato aggiornato con successo. Goditi la generazione di immagini con l'IA. Il tuo piano attuale include:", "limitation.image.success.title": "Aggiornamento riuscito", "limitation.image.topupSuccess.action": "Continua a generare", "limitation.image.topupSuccess.desc": "I tuoi crediti ricaricati sono ora attivi. Goditi la generazione di immagini con l'IA. Il tuo piano attuale include:", "limitation.image.topupSuccess.title": "Ricarica riuscita", + "limitation.insufficientBudget.approximateDesc": "Questa richiesta potrebbe richiedere più crediti. Ricarica i crediti o aggiorna il tuo piano.", + "limitation.insufficientBudget.available": "Crediti Disponibili", "limitation.insufficientBudget.desc": "I tuoi crediti rimanenti non sono sufficienti per il costo stimato di questo modello. Per favore, ricarica i crediti o passa a un modello meno costoso.", + "limitation.insufficientBudget.estimatedDesc": "Si stima che questa richiesta necessiti di più crediti. Ricarica i crediti o aggiorna il tuo piano.", + "limitation.insufficientBudget.exactDesc": "Questa richiesta necessita di più crediti. Ricarica i crediti o aggiorna il tuo piano.", + "limitation.insufficientBudget.required": "Crediti Necessari", "limitation.insufficientBudget.retry": "Riprova", + "limitation.insufficientBudget.shortfall": "Deficit di Crediti", "limitation.insufficientBudget.title": "Crediti Insufficienti per Questo Modello", "limitation.limited.action": "Aggiorna ora", "limitation.limited.advanceFeature": "Aggiorna per accedere alle funzionalità premium:", @@ -151,6 +166,7 @@ "limitation.limited.title": "Crediti di calcolo esauriti", "limitation.limited.topup": "Ricarica crediti", "limitation.limited.upgrade": "Passa a un piano superiore", + "limitation.limited.upgradeToPlan": "Aggiorna al piano {{plan}}", "limitation.providers.lock.addNew": "Abbonati ora per creare provider IA personalizzati", "limitation.providers.lock.enableProvider": "Abbonati ora per abilitare questo provider IA", "limitation.providers.lock.menuItem": "Abbonati ora per configurare il servizio API personalizzato", @@ -161,6 +177,9 @@ "limitation.providers.prompter.subTitle": "Il servizio API personalizzato è disponibile solo con piani a pagamento. Aggiorna ora per accedere ai principali modelli globali", "limitation.providers.prompter.title": "Abbonati ora per usare il servizio API personalizzato", "limitation.providers.tooltip": "Il servizio API personalizzato è disponibile solo con piani a pagamento", + "limitation.video.budgetReady.action": "Continua a Generare", + "limitation.video.budgetReady.desc": "I tuoi crediti disponibili ora coprono questa generazione.", + "limitation.video.budgetReady.title": "Crediti Disponibili", "limitation.video.success.action": "Continua a generare", "limitation.video.success.desc": "Il tuo abbonamento {{plan}} è stato aggiornato con successo. Goditi la generazione di video con l'IA. Il tuo piano attuale include:", "limitation.video.success.title": "Aggiornamento riuscito", diff --git a/locales/ja-JP/chat.json b/locales/ja-JP/chat.json index e3924234d0..548a2c1d05 100644 --- a/locales/ja-JP/chat.json +++ b/locales/ja-JP/chat.json @@ -196,6 +196,9 @@ "groupWizard.searchTemplates": "テンプレートを検索…", "groupWizard.title": "グループを作成", "groupWizard.useTemplate": "テンプレートを使用", + "heteroAgent.cloudNotConfigured.action": "設定する", + "heteroAgent.cloudNotConfigured.desc": "メッセージの送信を開始するには、エージェントプロファイルでClaude Codeトークンを設定してください。", + "heteroAgent.cloudNotConfigured.title": "クラウド認証情報が必要です", "heteroAgent.cloudRepo.multiSelected": "{{count}} 個のリポジトリが選択されました", "heteroAgent.cloudRepo.noRepos": "リポジトリが設定されていません。エージェント設定で追加してください。", "heteroAgent.cloudRepo.notSet": "リポジトリが選択されていません", @@ -467,6 +470,10 @@ "sharePage.error.unauthorized.subtitle": "この共有されたトピックを表示するには、サインインしてください。", "sharePage.error.unauthorized.title": "サインインが必要です", "sharePageDisclaimer": "このコンテンツはユーザーによって共有されたものであり、LobeHub の見解を示すものではありません。LobeHub は、この共有コンテンツに起因するいかなる結果についても責任を負いません。", + "signalCallbacks.collapse": "詳細を非表示", + "signalCallbacks.empty": "コールバックメッセージはありません", + "signalCallbacks.expand": "詳細を表示", + "signalCallbacks.title": "{{tool}} · {{count}}件のコールバック更新", "stt.action": "音声入力", "stt.loading": "認識中…", "stt.prettifying": "推敲中…", @@ -689,7 +696,6 @@ "thread.closeSubagentThread": "サブエージェント会話を折りたたむ", "thread.divider": "サブトピック", "thread.openSubagentThread": "サブエージェント会話をすべて表示", - "thread.subagentBadge": "サブエージェント", "thread.subagentReadOnlyHint": "サブエージェントの会話は読み取り専用です。実行は親エージェントによって制御されます。", "thread.threadMessageCount": "{{messageCount}} 件のメッセージ", "thread.title": "サブトピック", diff --git a/locales/ja-JP/models.json b/locales/ja-JP/models.json index 94a3583d1d..a36ead92c8 100644 --- a/locales/ja-JP/models.json +++ b/locales/ja-JP/models.json @@ -106,6 +106,7 @@ "MiniMax-Hailuo-2.3.description": "身体動作、物理的リアリズム、指示追従性において全面的にアップグレードされた新しいビデオ生成モデル。", "MiniMax-M1.description": "80Kの思考連鎖と1Mの入力を備えた新しい社内推論モデルで、世界トップクラスのモデルに匹敵する性能を発揮します。", "MiniMax-M2-Stable.description": "効率的なコーディングとエージェントワークフローのために設計され、商用利用における高い同時実行性を実現します。", + "MiniMax-M2.1-Lightning.description": "強力な多言語プログラミング機能を備え、より高速かつ効率的な推論を実現。", "MiniMax-M2.1-highspeed.description": "強力な多言語プログラミング機能を備え、プログラミング体験を包括的に向上。より高速かつ効率的です。", "MiniMax-M2.1.description": "MiniMax-M2.1は、MiniMaxが開発したフラッグシップのオープンソース大規模モデルで、複雑な現実世界のタスク解決に特化しています。多言語プログラミング能力とエージェントとしての高度なタスク処理能力が主な強みです。", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: M2.5と同等の性能で推論速度が向上。", @@ -314,13 +315,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haikuは、Anthropicの最速かつ最小のモデルで、即時応答と高速かつ正確な性能を実現するよう設計されています。", "claude-3-opus-20240229.description": "Claude 3 Opusは、Anthropicの最も強力なモデルで、非常に複雑なタスクにおいて卓越した性能、知性、流暢さ、理解力を発揮します。", "claude-3-sonnet-20240229.description": "Claude 3 Sonnetは、知性と速度のバランスを取り、エンタープライズ向けのワークロードにおいて高い実用性とコスト効率、信頼性のある大規模展開を実現します。", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5は、Anthropicの最速かつ最も賢いHaikuモデルで、驚異的なスピードと高度な推論能力を備えています。", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5はAnthropicの最速かつ最も知的なHaikuモデルで、驚異的な速度と拡張された思考能力を備えています。", "claude-haiku-4-5.description": "Claude Haiku 4.5 by Anthropic — 次世代Haikuモデルで、推論能力とビジョンが強化されています。", "claude-haiku-4.5.description": "Claude Haiku 4.5は、Anthropicの最速かつ最も賢いHaikuモデルで、驚異的なスピードと高度な推論能力を備えています。", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinkingは、推論プロセスを可視化できる高度なバリアントです。", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1は、Anthropicの最新かつ最も高性能なモデルで、非常に複雑なタスクにおいて卓越したパフォーマンス、知性、流暢さ、理解力を発揮します。", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1はAnthropicの最新かつ最も高度なモデルで、非常に複雑なタスクにおいて卓越した性能、知性、流暢さ、理解力を発揮します。", "claude-opus-4-1.description": "Claude Opus 4.1 by Anthropic — 深い分析能力を備えたプレミアム推論モデル。", - "claude-opus-4-20250514.description": "Claude Opus 4は、Anthropicの最も強力なモデルで、非常に複雑なタスクにおいて卓越したパフォーマンス、知性、流暢さ、理解力を発揮します。", + "claude-opus-4-20250514.description": "Claude Opus 4はAnthropicの最も強力なモデルで、非常に複雑なタスクにおいて卓越した性能、知性、流暢さ、理解力を発揮します。", "claude-opus-4-5-20251101.description": "Claude Opus 4.5は、Anthropicのフラッグシップモデルで、卓越した知性とスケーラブルな性能を兼ね備え、最高品質の応答と推論が求められる複雑なタスクに最適です。", "claude-opus-4-5.description": "Claude Opus 4.5 by Anthropic — 最高レベルの推論とコーディング能力を備えたフラッグシップモデル。", "claude-opus-4-6.description": "Claude Opus 4.6 by Anthropic — 1Mコンテキストウィンドウを備えたフラッグシップモデルで、高度な推論能力を提供します。", @@ -329,8 +330,8 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6は、エージェント構築やコーディングにおいてAnthropicの最も知的なモデルです。", "claude-opus-4.6.description": "Claude Opus 4.6は、エージェント構築やコーディングにおいてAnthropicの最も知的なモデルです。", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinkingは、即時応答または段階的な思考プロセスを可視化しながら出力できます。", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4は、瞬時の応答や、プロセスを可視化した段階的な思考を提供できます。", - "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5は、これまでで最も知的なAnthropicモデルです。", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4はAnthropicのこれまでで最も知的なモデルで、ほぼ瞬時の応答や詳細なステップバイステップの思考を提供し、APIユーザー向けに細かい制御が可能です。", + "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5はAnthropicのこれまでで最も知的なモデルです。", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 by Anthropic — コーディング性能が向上した改良版Sonnet。", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 by Anthropic — 最新のSonnetモデルで、優れたコーディングとツール使用能力を備えています。", "claude-sonnet-4.5.description": "Claude Sonnet 4.5は、これまでで最も知的なAnthropicのモデルです。", @@ -403,7 +404,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat(67B)は、深い言語理解と対話能力を提供する革新的なモデルです。", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 は次世代の推論モデルで、複雑な推論と連想思考に優れ、深い分析タスクに対応します。", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2は次世代推論モデルで、複雑な推論と連鎖的思考能力が強化されています。", - "deepseek-chat.description": "一般的な対話能力とコーディング能力を組み合わせた新しいオープンソースモデルです。チャットモデルの一般的な対話能力と、コーダーモデルの強力なコーディング能力を保持し、より良い嗜好調整を実現しています。DeepSeek-V2.5は、文章作成や指示の追従能力も向上しています。", + "deepseek-chat.description": "DeepSeek V4 Flash非思考モードの互換性エイリアス。廃止予定 — DeepSeek V4 Flashを使用してください。", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B は 2T トークン(コード 87%、中英テキスト 13%)で学習されたコード言語モデルです。16K のコンテキストウィンドウと Fill-in-the-Middle タスクを導入し、プロジェクトレベルのコード補完とスニペット補完を提供します。", "deepseek-coder-v2.description": "DeepSeek Coder V2 はオープンソースの MoE コードモデルで、コーディングタスクにおいて GPT-4 Turbo に匹敵する性能を発揮します。", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 はオープンソースの MoE コードモデルで、コーディングタスクにおいて GPT-4 Turbo に匹敵する性能を発揮します。", @@ -425,7 +426,7 @@ "deepseek-r1-fast-online.description": "DeepSeek R1 高速フルバージョンは、リアルタイムのウェブ検索を搭載し、671Bスケールの能力と高速応答を両立します。", "deepseek-r1-online.description": "DeepSeek R1 フルバージョンは、671Bパラメータとリアルタイムのウェブ検索を備え、より強力な理解と生成を提供します。", "deepseek-r1.description": "DeepSeek-R1は、強化学習前にコールドスタートデータを使用し、数学、コーディング、推論においてOpenAI-o1と同等の性能を発揮します。", - "deepseek-reasoner.description": "複雑な論理的推論タスクに特化したDeepSeek推論モデルです。", + "deepseek-reasoner.description": "DeepSeek V4 Flash思考モードの互換性エイリアス。廃止予定 — DeepSeek V4 Flashを使用してください。", "deepseek-v2.description": "DeepSeek V2は、コスト効率の高い処理を実現する効率的なMoEモデルです。", "deepseek-v2:236b.description": "DeepSeek V2 236Bは、コード生成に特化したDeepSeekのモデルで、強力なコード生成能力を持ちます。", "deepseek-v3-0324.description": "DeepSeek-V3-0324は、671BパラメータのMoEモデルで、プログラミングや技術的能力、文脈理解、長文処理において優れた性能を発揮します。", @@ -490,6 +491,8 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 は ByteDance Seed による画像生成モデルで、テキストと画像入力に対応し、高品質かつ制御性の高い画像生成を実現します。テキストプロンプトから画像を生成します。", "doubao-seedream-4-5-251128.description": "Seedream 4.5はByteDanceの最新マルチモーダル画像モデルで、テキストから画像生成、画像間変換、バッチ画像生成機能を統合し、常識と推論能力を組み込んでいます。前バージョン4.0と比較して、生成品質が大幅に向上し、編集の一貫性や複数画像の融合が改善されています。視覚的な詳細の制御がより正確になり、小さなテキストや顔を自然に生成し、レイアウトや色彩の調和が向上し、全体的な美観が強化されています。", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-liteはByteDanceの最新画像生成モデルです。初めてオンライン検索機能を統合し、リアルタイムのウェブ情報を取り入れることで生成画像のタイムリー性を向上させています。モデルの知能もアップグレードされ、複雑な指示や視覚コンテンツを正確に解釈できるようになりました。また、専門的なシナリオでのグローバルな知識カバレッジ、一貫性、生成品質が向上し、企業レベルの視覚制作ニーズにより適合しています。", + "dreamina-seedance-2-0-260128.description": "ByteDanceのSeedance 2.0は、マルチモーダル参照動画生成、動画編集、動画拡張、テキストから動画、画像から動画への同期音声付き生成をサポートする最も強力な動画生成モデルです。", + "dreamina-seedance-2-0-fast-260128.description": "ByteDanceのSeedance 2.0 Fastは、Seedance 2.0と同じ機能を提供しながら、より高速な生成速度と競争力のある価格を実現します。", "emohaa.description": "Emohaa は、専門的なカウンセリング能力を備えたメンタルヘルスモデルで、ユーザーが感情的な問題を理解するのを支援します。", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B は、ローカルおよびカスタマイズ展開に適した軽量なオープンソースモデルです。", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview は、ERNIE 4.5 の評価用に設計された 8K コンテキストプレビューモデルです。", @@ -514,7 +517,8 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K は、複雑な推論やマルチターン対話に対応した 32K コンテキストの高速思考モデルです。", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview は、評価およびテスト用の思考モデルプレビューです。", "ernie-x1.1.description": "ERNIE X1.1は評価とテスト用の思考モデルプレビューです。", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0は、ByteDance Seedによる画像生成モデルで、テキストと画像入力をサポートし、高度に制御可能で高品質な画像生成を実現します。テキストプロンプトから画像を生成します。", + "fal-ai/bytedance/seedream/v4.5.description": "ByteDance Seedチームが開発したSeedream 4.5は、マルチ画像編集と構成をサポートします。被写体の一貫性向上、正確な指示の追従、空間論理の理解、美的表現、ポスターのレイアウトやロゴデザイン、高精度なテキスト画像レンダリングを特徴としています。", + "fal-ai/bytedance/seedream/v4.description": "ByteDance Seedが開発したSeedream 4.0は、テキストと画像入力をサポートし、プロンプトからの高度に制御可能で高品質な画像生成を実現します。", "fal-ai/flux-kontext/dev.description": "FLUX.1 モデルは画像編集に特化しており、テキストと画像の入力に対応しています。", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] は、テキストと参照画像を入力として受け取り、局所的な編集や複雑なシーン全体の変換を可能にします。", "fal-ai/flux/krea.description": "Flux Krea [dev] は、よりリアルで自然な画像を生成する美的バイアスを持つ画像生成モデルです。", @@ -522,8 +526,8 @@ "fal-ai/hunyuan-image/v3.description": "強力なネイティブマルチモーダル画像生成モデルです。", "fal-ai/imagen4/preview.description": "Google による高品質な画像生成モデルです。", "fal-ai/nano-banana.description": "Nano Banana は Google による最新・最速・最も効率的なネイティブマルチモーダルモデルで、会話を通じた画像生成と編集が可能です。", - "fal-ai/qwen-image-edit.description": "Qwenチームによるプロフェッショナルな画像編集モデルで、意味や外観の編集をサポートし、中国語と英語のテキストを正確に編集できます。スタイル変換やオブジェクトの回転など、高品質な編集が可能です。", - "fal-ai/qwen-image.description": "Qwenチームによる強力な画像生成モデルで、中国語のテキストレンダリングや多様なビジュアルスタイルに優れています。", + "fal-ai/qwen-image-edit.description": "Qwenチームによるプロフェッショナルな画像編集モデルで、意味的および外観の編集、正確な中国語/英語テキスト編集、スタイル変換、回転などをサポートします。", + "fal-ai/qwen-image.description": "Qwenチームによる強力な画像生成モデルで、中国語テキストのレンダリング能力が高く、多様な視覚スタイルを提供します。", "flux-1-schnell.description": "Black Forest Labs による 120 億パラメータのテキストから画像への変換モデルで、潜在敵対的拡散蒸留を用いて 1~4 ステップで高品質な画像を生成します。クローズドな代替モデルに匹敵し、Apache-2.0 ライセンスのもと、個人・研究・商用利用が可能です。", "flux-dev.description": "非商用の研究開発向けに効率化されたオープンソース画像生成モデルです。", "flux-kontext-max.description": "最先端のコンテキスト画像生成・編集モデルで、テキストと画像を組み合わせて精密かつ一貫性のある結果を生成します。", @@ -562,10 +566,10 @@ "gemini-3-flash-preview.description": "Gemini 3 Flash は、最先端の知能と優れた検索基盤を融合し、スピードに特化した最もスマートなモデルです。", "gemini-3-flash.description": "Gemini 3 Flash by Google — 超高速モデルでマルチモーダル入力をサポートします。", "gemini-3-pro-image-preview.description": "Gemini 3 Pro Image(Nano Banana Pro)は、Googleの画像生成モデルで、マルチモーダル対話もサポートします。", - "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image(Nano Banana Pro)は、Googleの画像生成モデルで、マルチモーダルチャットもサポートしています。", + "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro)はGoogleの画像生成モデルで、マルチモーダルチャットもサポートします。", "gemini-3-pro-preview.description": "Gemini 3 Pro は、Google による最も強力なエージェントおよびバイブコーディングモデルで、最先端の推論に加え、より豊かなビジュアルと深い対話を実現します。", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image(Nano Banana 2)は、Googleの最速のネイティブ画像生成モデルで、思考サポート、対話型画像生成および編集を提供します。", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image(Nano Banana 2)は、Googleの最速のネイティブ画像生成モデルで、思考サポート、会話型画像生成、編集を提供します。", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2)は、プロレベルの画像品質をフラッシュ速度で提供し、マルチモーダルチャットをサポートします。", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite PreviewはGoogleの最もコスト効率の高いマルチモーダルモデルで、大量のエージェントタスク、翻訳、データ処理に最適化されています。", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-LiteはGoogleの最もコスト効率の高いマルチモーダルモデルで、大量のエージェントタスク、翻訳、データ処理に最適化されています。", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Previewは、Gemini 3 Proの推論能力を強化し、中程度の思考レベルサポートを追加しています。", @@ -730,6 +734,8 @@ "grok-4-fast-reasoning.description": "Grok 4 Fast をリリースしました。コスト効率の高い推論モデルにおける最新の進展です。", "grok-4.20-0309-non-reasoning.description": "単純なユースケース向けの非推論バリアント。", "grok-4.20-0309-reasoning.description": "応答前に推論する知的で超高速なモデル。", + "grok-4.20-beta-0309-non-reasoning.description": "シンプルなユースケース向けの非思考型バリアント", + "grok-4.20-beta-0309-reasoning.description": "応答前に推論する知的で超高速なモデル", "grok-4.20-multi-agent-0309.description": "4または16のエージェントチーム。研究ユースケースに優れています。現在、クライアントサイドツールはサポートしていません。xAIサーバーサイドツール(例:X Search、Web Searchツール)およびリモートMCPツールのみをサポートします。", "grok-4.3.description": "世界で最も真実を追求する大規模言語モデル", "grok-4.description": "最新のGrokフラッグシップモデルで、言語、数学、推論において比類のない性能を発揮する真のオールラウンダーです。現在はgrok-4-0709を指しており、リソースの制限により一時的に公式価格より10%高く設定されていますが、後に公式価格に戻る予定です。", @@ -1214,6 +1220,8 @@ "qwq.description": "QwQは、Qwenファミリーの推論モデルです。標準的な指示調整モデルと比較して、思考と推論能力に優れ、特に難解な問題において下流性能を大幅に向上させます。QwQ-32Bは、DeepSeek-R1やo1-miniと競合する中規模の推論モデルです。", "qwq_32b.description": "Qwenファミリーの中規模推論モデル。標準的な指示調整モデルと比較して、QwQの思考と推論能力は、特に難解な問題において下流性能を大幅に向上させます。", "r1-1776.description": "R1-1776は、DeepSeek R1のポストトレーニングバリアントで、検閲のない偏りのない事実情報を提供するよう設計されています。", + "seedance-1-5-pro-251215.description": "ByteDanceのSeedance 1.5 Proは、テキストから動画、画像から動画(最初のフレーム、最初+最後のフレーム)、視覚と同期した音声生成をサポートします。", + "seedream-5-0-260128.description": "BytePlusによるByteDance-Seedream-5.0-liteは、リアルタイム情報のためのウェブ検索補強生成、複雑なプロンプト解釈の強化、プロフェッショナルな視覚制作のための参照一貫性の向上を特徴としています。", "solar-mini-ja.description": "Solar Mini (Ja)は、Solar Miniを日本語に特化させたモデルで、英語と韓国語でも効率的かつ高性能な動作を維持します。", "solar-mini.description": "Solar Miniは、GPT-3.5を上回る性能を持つコンパクトなLLMで、英語と韓国語に対応した多言語機能を備え、効率的な小型ソリューションを提供します。", "solar-pro.description": "Solar Proは、Upstageが提供する高知能LLMで、単一GPU上での指示追従に特化し、IFEvalスコア80以上を記録しています。現在は英語に対応しており、2024年11月の正式リリースでは対応言語とコンテキスト長が拡張される予定です。", diff --git a/locales/ja-JP/plugin.json b/locales/ja-JP/plugin.json index d826bb4b1d..4aa39dee0d 100644 --- a/locales/ja-JP/plugin.json +++ b/locales/ja-JP/plugin.json @@ -237,6 +237,14 @@ "builtins.lobe-page-agent.apiName.updateNode": "ノードを更新", "builtins.lobe-page-agent.apiName.wrapNodes": "ノードをラップ", "builtins.lobe-page-agent.title": "ドキュメント", + "builtins.lobe-self-feedback-intent.apiName.declareSelfFeedbackIntent": "改善案を記録する", + "builtins.lobe-self-feedback-intent.inspector.gap.proposal": "改善を提案する", + "builtins.lobe-self-feedback-intent.inspector.memory.write": "好みを記録する", + "builtins.lobe-self-feedback-intent.inspector.rejected": "記録されませんでした", + "builtins.lobe-self-feedback-intent.inspector.skill.consolidate": "方法を整理する", + "builtins.lobe-self-feedback-intent.inspector.skill.create": "新しい方法を発見", + "builtins.lobe-self-feedback-intent.inspector.skill.refine": "方法を改善する", + "builtins.lobe-self-feedback-intent.title": "改善案", "builtins.lobe-skill-store.apiName.importFromMarket": "マーケットからインポート", "builtins.lobe-skill-store.apiName.importSkill": "スキルをインポート", "builtins.lobe-skill-store.apiName.searchSkill": "スキルを検索", diff --git a/locales/ja-JP/providers.json b/locales/ja-JP/providers.json index f09f806600..2c85d1b244 100644 --- a/locales/ja-JP/providers.json +++ b/locales/ja-JP/providers.json @@ -33,6 +33,7 @@ "jina.description": "Jina AIは2020年に設立された検索AIのリーディングカンパニーで、ベクトルモデル、リランカー、小型言語モデルを含む検索スタックにより、高品質な生成・マルチモーダル検索アプリを構築できます。", "kimicodingplan.description": "Moonshot AIのKimi Codeは、K2.5を含むKimiモデルへのアクセスを提供します。", "lmstudio.description": "LM Studioは、ローカルPC上でLLMの開発と実験ができるデスクトップアプリです。", + "lobehub.description": "LobeHub Cloudは公式APIを使用してAIモデルにアクセスし、モデルのトークンに紐づいたクレジットで使用量を測定します。", "longcat.description": "LongCatは、Meituanが独自に開発した生成AIの大型モデルシリーズです。効率的な計算アーキテクチャと強力なマルチモーダル機能を通じて、企業内部の生産性を向上させ、革新的なアプリケーションを可能にすることを目的としています。", "minimax.description": "MiniMaxは2021年に設立され、マルチモーダル基盤モデルを用いた汎用AIを開発しています。兆単位パラメータのMoEテキストモデル、音声モデル、ビジョンモデル、Hailuo AIなどのアプリを提供します。", "minimaxcodingplan.description": "MiniMaxトークンプランは、固定料金のサブスクリプションを通じてM2.7を含むMiniMaxモデルへのアクセスを提供します。", diff --git a/locales/ja-JP/subscription.json b/locales/ja-JP/subscription.json index 3fb96b69d1..11dd3d7185 100644 --- a/locales/ja-JP/subscription.json +++ b/locales/ja-JP/subscription.json @@ -41,7 +41,9 @@ "credits.autoTopUp.monthlyLimitDesc": "1か月あたりに自動課金される最大金額。制限なしの場合は空白のままにしてください", "credits.autoTopUp.monthlyLimitPlaceholder": "制限なし", "credits.autoTopUp.monthlyTopUpAmount": "月間チャージ金額", + "credits.autoTopUp.noCustomerHint": "自動チャージを有効にする前に、支払い方法を保存するために一度クレジットを購入してください。", "credits.autoTopUp.noPaymentMethodHint": "ファイルに支払い方法がありません。自動チャージには、自動的に請求するための保存されたカードが必要です。", + "credits.autoTopUp.purchaseCredits": "クレジットを購入", "credits.autoTopUp.saveError": "自動チャージ設定の保存に失敗しました", "credits.autoTopUp.saveSuccess": "自動チャージ設定が保存されました", "credits.autoTopUp.setupPaymentMethod": "支払い方法を追加", @@ -83,6 +85,7 @@ "credits.packages.title": "マイクレジットパッケージ", "credits.topUp.cancel": "キャンセル", "credits.topUp.custom": "カスタム", + "credits.topUp.freeFeeHint": "無料プランのチャージには、1Mクレジットごとに{{fee}}のサービス料が含まれます。", "credits.topUp.maxAmountError": "1回の購入金額は ${{max}} を超えることはできません", "credits.topUp.purchaseError": "購入に失敗しました。後でもう一度お試しください", "credits.topUp.purchaseNow": "今すぐ購入", @@ -120,6 +123,9 @@ "keyMissMatch.button": "使用を復元して会話を続ける", "keyMissMatch.description": "システムの一時的な障害により、現在のサブスクリプションの使用が一時的に無効になっています。下のボタンをクリックして使用を復元し、会話を続けてください。繰り返し発生する場合は、support@lobehub.com までご連絡ください。", "keyMissMatch.title": "今すぐサブスクリプション使用を復元", + "limitation.chat.budgetReady.action": "チャットを続ける", + "limitation.chat.budgetReady.desc": "現在の利用可能なクレジットでこのリクエストをカバーできます。", + "limitation.chat.budgetReady.title": "クレジット準備完了", "limitation.chat.success.action": "チャットを続ける", "limitation.chat.success.desc": "{{plan}} サブスクリプションが正常にアップグレードされました。AIチャットをお楽しみください。現在のプランには以下が含まれます:", "limitation.chat.success.title": "アップグレード成功", @@ -134,14 +140,23 @@ "limitation.hobby.docs": "設定ドキュメントを見る", "limitation.hobby.tip": "カスタムAPIキーを使用するモデルに切り替えるのをお忘れなく", "limitation.hobby.title": "モデルサービスAPIを設定してください", + "limitation.image.budgetReady.action": "生成を続ける", + "limitation.image.budgetReady.desc": "現在の利用可能なクレジットでこの生成をカバーできます。", + "limitation.image.budgetReady.title": "クレジット準備完了", "limitation.image.success.action": "生成を続ける", "limitation.image.success.desc": "{{plan}} サブスクリプションが正常にアップグレードされました。AI画像生成をお楽しみください。現在のプランには以下が含まれます:", "limitation.image.success.title": "アップグレード成功", "limitation.image.topupSuccess.action": "生成を続ける", "limitation.image.topupSuccess.desc": "チャージクレジットが有効になりました。AI画像生成をお楽しみください。現在のプランには以下が含まれます:", "limitation.image.topupSuccess.title": "チャージ成功", + "limitation.insufficientBudget.approximateDesc": "このリクエストには追加のクレジットが必要になる可能性があります。クレジットをチャージするか、プランをアップグレードしてください。", + "limitation.insufficientBudget.available": "利用可能なクレジット", "limitation.insufficientBudget.desc": "このモデルの推定コストに対して、残りのクレジットが不足しています。クレジットを追加するか、より安価なモデルに切り替えてください。", + "limitation.insufficientBudget.estimatedDesc": "このリクエストには追加のクレジットが必要と推定されます。クレジットをチャージするか、プランをアップグレードしてください。", + "limitation.insufficientBudget.exactDesc": "このリクエストには追加のクレジットが必要です。クレジットをチャージするか、プランをアップグレードしてください。", + "limitation.insufficientBudget.required": "必要なクレジット", "limitation.insufficientBudget.retry": "再試行", + "limitation.insufficientBudget.shortfall": "クレジット不足", "limitation.insufficientBudget.title": "このモデルに必要なクレジットが不足しています", "limitation.limited.action": "今すぐアップグレード", "limitation.limited.advanceFeature": "アップグレードしてプレミアム機能を利用:", @@ -151,6 +166,7 @@ "limitation.limited.title": "コンピューティングクレジットを使い切りました", "limitation.limited.topup": "クレジットをチャージ", "limitation.limited.upgrade": "上位プランにアップグレード", + "limitation.limited.upgradeToPlan": "{{plan}}にアップグレード", "limitation.providers.lock.addNew": "今すぐサブスクリプションに加入してカスタムAIプロバイダーを作成", "limitation.providers.lock.enableProvider": "今すぐサブスクリプションに加入してこのAIプロバイダーを有効化", "limitation.providers.lock.menuItem": "今すぐサブスクリプションに加入してカスタムAPIサービスを設定", @@ -161,6 +177,9 @@ "limitation.providers.prompter.subTitle": "カスタムAPIサービスは有料プランでのみ利用可能です。今すぐアップグレードして世界中の主流モデルサービスを利用しましょう", "limitation.providers.prompter.title": "今すぐサブスクリプションに加入してカスタムAPIサービスを利用", "limitation.providers.tooltip": "カスタムAPIサービスは有料プランでのみ利用可能です", + "limitation.video.budgetReady.action": "生成を続ける", + "limitation.video.budgetReady.desc": "現在の利用可能なクレジットでこの生成をカバーできます。", + "limitation.video.budgetReady.title": "クレジット準備完了", "limitation.video.success.action": "生成を続ける", "limitation.video.success.desc": "{{plan}}プランへのアップグレードが完了しました。AI動画生成をお楽しみください。現在のプラン内容:", "limitation.video.success.title": "アップグレード成功", diff --git a/locales/ko-KR/chat.json b/locales/ko-KR/chat.json index e69fdd8007..659366df86 100644 --- a/locales/ko-KR/chat.json +++ b/locales/ko-KR/chat.json @@ -196,6 +196,9 @@ "groupWizard.searchTemplates": "템플릿 검색…", "groupWizard.title": "그룹 만들기", "groupWizard.useTemplate": "템플릿 사용", + "heteroAgent.cloudNotConfigured.action": "구성", + "heteroAgent.cloudNotConfigured.desc": "에이전트 프로필에서 Claude Code 토큰을 구성하여 메시지 전송을 시작하세요.", + "heteroAgent.cloudNotConfigured.title": "클라우드 자격 증명 필요", "heteroAgent.cloudRepo.multiSelected": "{{count}}개의 저장소가 선택되었습니다", "heteroAgent.cloudRepo.noRepos": "구성된 저장소가 없습니다. 에이전트 설정에서 추가하세요.", "heteroAgent.cloudRepo.notSet": "선택된 저장소가 없습니다", @@ -467,6 +470,10 @@ "sharePage.error.unauthorized.subtitle": "공유된 주제를 보려면 로그인하세요.", "sharePage.error.unauthorized.title": "로그인이 필요합니다", "sharePageDisclaimer": "이 콘텐츠는 사용자가 공유한 것으로, LobeHub의 입장을 대변하지 않습니다. LobeHub는 이 공유 콘텐츠로 인해 발생하는 결과에 대해 책임지지 않습니다.", + "signalCallbacks.collapse": "세부 정보 숨기기", + "signalCallbacks.empty": "콜백 메시지가 없습니다", + "signalCallbacks.expand": "세부 정보 보기", + "signalCallbacks.title": "{{tool}} · {{count}} 콜백 업데이트", "stt.action": "음성 입력", "stt.loading": "인식 중…", "stt.prettifying": "다듬는 중…", @@ -689,7 +696,6 @@ "thread.closeSubagentThread": "하위 에이전트 대화 숨기기", "thread.divider": "하위 주제", "thread.openSubagentThread": "전체 하위 에이전트 대화 보기", - "thread.subagentBadge": "하위 에이전트", "thread.subagentReadOnlyHint": "SubAgent 대화는 읽기 전용입니다 — 실행은 부모 에이전트에 의해 진행됩니다.", "thread.threadMessageCount": "{{messageCount}}개의 메시지", "thread.title": "하위 주제", diff --git a/locales/ko-KR/models.json b/locales/ko-KR/models.json index b9a837d5fc..fa493f8f20 100644 --- a/locales/ko-KR/models.json +++ b/locales/ko-KR/models.json @@ -106,6 +106,7 @@ "MiniMax-Hailuo-2.3.description": "신규 비디오 생성 모델로 신체 움직임, 물리적 사실성, 지침 준수에서 전반적인 업그레이드 제공.", "MiniMax-M1.description": "80K 체인 오브 싱킹과 100만 입력을 지원하는 새로운 자체 개발 추론 모델로, 세계 최고 수준의 모델과 유사한 성능을 제공합니다.", "MiniMax-M2-Stable.description": "상업적 사용을 위한 높은 동시성을 제공하며, 효율적인 코딩 및 에이전트 워크플로우에 최적화되어 있습니다.", + "MiniMax-M2.1-Lightning.description": "강력한 다국어 프로그래밍 기능과 더 빠르고 효율적인 추론을 제공합니다.", "MiniMax-M2.1-highspeed.description": "강력한 다국어 프로그래밍 기능과 종합적으로 업그레이드된 프로그래밍 경험. 더 빠르고 효율적입니다.", "MiniMax-M2.1.description": "MiniMax-M2.1은 MiniMax에서 개발한 대표적인 오픈소스 대형 모델로, 복잡한 실제 과제를 해결하는 데 중점을 둡니다. 다국어 프로그래밍 능력과 에이전트로서 복잡한 작업을 수행하는 능력이 핵심 강점입니다.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: M2.5와 동일한 성능을 제공하며 추론 속도가 더 빠릅니다.", @@ -314,13 +315,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku는 Anthropic의 가장 빠르고 컴팩트한 모델로, 빠르고 정확한 성능으로 즉각적인 응답을 위해 설계되었습니다.", "claude-3-opus-20240229.description": "Claude 3 Opus는 Anthropic의 가장 강력한 모델로, 고난도 작업에서 뛰어난 성능, 지능, 유창성, 이해력을 자랑합니다.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet은 엔터프라이즈 워크로드를 위한 지능과 속도의 균형을 제공하며, 낮은 비용으로 높은 효용성과 안정적인 대규모 배포를 지원합니다.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5는 Anthropic의 가장 빠르고 스마트한 Haiku 모델로, 번개 같은 속도와 확장된 추론 능력을 제공합니다.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5는 Anthropic의 가장 빠르고 지능적인 Haiku 모델로, 번개 같은 속도와 확장된 사고 능력을 제공합니다.", "claude-haiku-4-5.description": "Anthropic의 Claude Haiku 4.5 — 향상된 추론 및 비전을 갖춘 차세대 Haiku.", "claude-haiku-4.5.description": "Claude Haiku 4.5는 Anthropic의 가장 빠르고 똑똑한 Haiku 모델로, 번개 같은 속도와 확장된 추론 능력을 자랑합니다.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking은 자신의 추론 과정을 드러낼 수 있는 고급 변형 모델입니다.", "claude-opus-4-1-20250805.description": "Claude Opus 4.1은 Anthropic의 최신 모델로, 매우 복잡한 작업에서 뛰어난 성능, 지능, 유창함, 이해력을 자랑합니다.", "claude-opus-4-1.description": "Anthropic의 Claude Opus 4.1 — 심층 분석 기능을 갖춘 프리미엄 추론 모델.", - "claude-opus-4-20250514.description": "Claude Opus 4는 Anthropic의 가장 강력한 모델로, 매우 복잡한 작업에서 뛰어난 성능, 지능, 유창함, 이해력을 제공합니다.", + "claude-opus-4-20250514.description": "Claude Opus 4는 Anthropic의 가장 강력한 모델로, 매우 복잡한 작업에서 뛰어난 성능, 지능, 유창함, 이해력을 자랑합니다.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5는 Anthropic의 플래그십 모델로, 탁월한 지능과 확장 가능한 성능을 결합하여 최고 품질의 응답과 추론이 필요한 복잡한 작업에 이상적입니다.", "claude-opus-4-5.description": "Anthropic의 Claude Opus 4.5 — 최고 수준의 추론 및 코딩을 갖춘 플래그십 모델.", "claude-opus-4-6.description": "Anthropic의 Claude Opus 4.6 — 고급 추론을 갖춘 1M 컨텍스트 윈도우 플래그십.", @@ -329,8 +330,8 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6은 에이전트 구축과 코딩을 위한 Anthropic의 가장 지능적인 모델입니다.", "claude-opus-4.6.description": "Claude Opus 4.6은 에이전트 구축과 코딩을 위한 Anthropic의 가장 지능적인 모델입니다.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking은 즉각적인 응답 또는 단계별 사고 과정을 시각적으로 보여주는 확장된 사고를 생성할 수 있습니다.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4는 거의 즉각적인 응답을 생성하거나 가시적인 과정을 통해 단계별 사고를 확장할 수 있습니다.", - "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5는 Anthropic의 현재까지 가장 지능적인 모델입니다.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4는 Anthropic의 가장 지능적인 모델로, API 사용자에게 세밀한 제어를 제공하며 즉각적인 응답 또는 단계별 사고를 지원합니다.", + "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5는 Anthropic의 가장 지능적인 모델입니다.", "claude-sonnet-4-5.description": "Anthropic의 Claude Sonnet 4.5 — 향상된 코딩 성능을 갖춘 개선된 Sonnet.", "claude-sonnet-4-6.description": "Anthropic의 Claude Sonnet 4.6 — 우수한 코딩 및 도구 사용을 갖춘 최신 Sonnet.", "claude-sonnet-4.5.description": "Claude Sonnet 4.5는 지금까지의 Anthropic 모델 중 가장 지능적인 모델입니다.", @@ -403,7 +404,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B)은 심층 언어 이해와 상호작용을 제공하는 혁신적인 모델입니다.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1은 복잡한 추론과 연쇄적 사고(chain-of-thought)에 강한 차세대 추론 모델로, 심층 분석 작업에 적합합니다.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2는 복잡한 추론과 연쇄 사고 능력이 강화된 차세대 추론 모델입니다.", - "deepseek-chat.description": "일반 대화 능력과 코드 능력을 결합한 새로운 오픈소스 모델입니다. 이 모델은 대화 모델의 일반적인 대화 능력과 코더 모델의 강력한 코딩 능력을 유지하며, 선호도 정렬을 개선했습니다. DeepSeek-V2.5는 글쓰기와 지침 준수 능력도 향상시켰습니다.", + "deepseek-chat.description": "DeepSeek V4 Flash 비사고 모드의 호환성 별칭입니다. 사용 중단 예정 — DeepSeek V4 Flash를 대신 사용하세요.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B는 2T 토큰(코드 87%, 중/영문 텍스트 13%)으로 학습된 코드 언어 모델입니다. 16K 문맥 창과 중간 채우기(fit-in-the-middle) 작업을 도입하여 프로젝트 수준의 코드 완성과 코드 조각 보완을 지원합니다.", "deepseek-coder-v2.description": "DeepSeek Coder V2는 GPT-4 Turbo에 필적하는 성능을 보이는 오픈소스 MoE 코드 모델입니다.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2는 GPT-4 Turbo에 필적하는 성능을 보이는 오픈소스 MoE 코드 모델입니다.", @@ -425,7 +426,7 @@ "deepseek-r1-fast-online.description": "DeepSeek R1의 빠른 전체 버전으로, 실시간 웹 검색을 지원하며 671B 규모의 성능과 빠른 응답을 결합합니다.", "deepseek-r1-online.description": "DeepSeek R1 전체 버전은 671B 파라미터와 실시간 웹 검색을 지원하여 더 강력한 이해 및 생성 능력을 제공합니다.", "deepseek-r1.description": "DeepSeek-R1은 강화 학습 전 콜드 스타트 데이터를 사용하며, 수학, 코딩, 추론에서 OpenAI-o1과 유사한 성능을 보입니다.", - "deepseek-reasoner.description": "복잡한 논리적 추론 작업에 초점을 맞춘 DeepSeek 추론 모델입니다.", + "deepseek-reasoner.description": "DeepSeek V4 Flash 사고 모드의 호환성 별칭입니다. 사용 중단 예정 — DeepSeek V4 Flash를 대신 사용하세요.", "deepseek-v2.description": "DeepSeek V2는 비용 효율적인 처리를 위한 고효율 MoE 모델입니다.", "deepseek-v2:236b.description": "DeepSeek V2 236B는 코드 생성에 강점을 가진 DeepSeek의 코드 특화 모델입니다.", "deepseek-v3-0324.description": "DeepSeek-V3-0324는 671B 파라미터의 MoE 모델로, 프로그래밍 및 기술 역량, 문맥 이해, 장문 처리에서 뛰어난 성능을 보입니다.", @@ -490,6 +491,8 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0은 ByteDance Seed의 이미지 생성 모델로, 텍스트 및 이미지 입력을 지원하며, 고품질의 이미지 생성과 높은 제어력을 제공합니다. 텍스트 프롬프트로부터 이미지를 생성합니다.", "doubao-seedream-4-5-251128.description": "Seedream 4.5는 ByteDance의 최신 멀티모달 이미지 모델로, 텍스트-이미지, 이미지-이미지, 배치 이미지 생성 기능을 통합하며 상식과 추론 능력을 포함합니다. 이전 4.0 버전에 비해 생성 품질이 크게 향상되었으며, 편집 일관성과 다중 이미지 융합이 개선되었습니다. 시각적 세부 사항에 대한 더 정밀한 제어를 제공하며, 작은 텍스트와 작은 얼굴을 더 자연스럽게 생성하고 레이아웃과 색상이 더 조화롭게 되어 전체적인 미학을 향상시킵니다.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite는 ByteDance의 최신 이미지 생성 모델로, 처음으로 온라인 검색 기능을 통합하여 실시간 웹 정보를 포함하고 생성된 이미지의 시의성을 향상시킵니다. 모델의 지능도 업그레이드되어 복잡한 지시와 시각적 콘텐츠를 정확히 해석할 수 있습니다. 또한 전문적인 시나리오에서 글로벌 지식 범위, 참조 일관성, 생성 품질이 개선되어 기업 수준의 시각적 창작 요구를 더 잘 충족시킵니다.", + "dreamina-seedance-2-0-260128.description": "ByteDance의 Seedance 2.0은 가장 강력한 비디오 생성 모델로, 다중 모달 참조 비디오 생성, 비디오 편집, 비디오 확장, 텍스트-비디오 및 이미지-비디오를 동기화된 오디오와 함께 지원합니다.", + "dreamina-seedance-2-0-fast-260128.description": "ByteDance의 Seedance 2.0 Fast는 Seedance 2.0과 동일한 기능을 제공하며, 더 빠른 생성 속도와 경쟁력 있는 가격을 자랑합니다.", "emohaa.description": "Emohaa는 전문 상담 능력을 갖춘 정신 건강 모델로, 사용자가 감정 문제를 이해하도록 돕습니다.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B는 로컬 및 맞춤형 배포를 위한 오픈소스 경량 모델입니다.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview는 ERNIE 4.5의 8K 컨텍스트 프리뷰 모델로, 평가용으로 사용됩니다.", @@ -514,7 +517,8 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K는 복잡한 추론 및 다중 턴 대화를 위한 32K 컨텍스트의 고속 사고 모델입니다.", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview는 평가 및 테스트를 위한 사고 모델 프리뷰입니다.", "ernie-x1.1.description": "ERNIE X1.1은 평가 및 테스트를 위한 사고 모델 프리뷰입니다.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0은 ByteDance Seed의 이미지 생성 모델로, 텍스트와 이미지 입력을 지원하며 매우 제어 가능한 고품질 이미지 생성을 제공합니다. 텍스트 프롬프트를 통해 이미지를 생성합니다.", + "fal-ai/bytedance/seedream/v4.5.description": "ByteDance Seed 팀이 개발한 Seedream 4.5는 다중 이미지 편집 및 합성을 지원하며, 향상된 주제 일관성, 정확한 지시 따르기, 공간 논리 이해, 미적 표현, 포스터 레이아웃 및 로고 디자인을 고정밀 텍스트-이미지 렌더링과 함께 제공합니다.", + "fal-ai/bytedance/seedream/v4.description": "ByteDance Seed가 개발한 Seedream 4.0은 텍스트 및 이미지 입력을 지원하며, 프롬프트를 기반으로 고품질 이미지를 생성할 수 있는 높은 제어력을 제공합니다.", "fal-ai/flux-kontext/dev.description": "FLUX.1 모델은 이미지 편집에 중점을 두며, 텍스트와 이미지 입력을 지원합니다.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro]는 텍스트와 참조 이미지를 입력으로 받아, 국소 편집과 복잡한 장면 변환을 정밀하게 수행할 수 있습니다.", "fal-ai/flux/krea.description": "Flux Krea [dev]는 보다 사실적이고 자연스러운 이미지 스타일에 중점을 둔 이미지 생성 모델입니다.", @@ -522,8 +526,8 @@ "fal-ai/hunyuan-image/v3.description": "강력한 네이티브 멀티모달 이미지 생성 모델입니다.", "fal-ai/imagen4/preview.description": "Google에서 개발한 고품질 이미지 생성 모델입니다.", "fal-ai/nano-banana.description": "Nano Banana는 Google의 최신, 가장 빠르고 효율적인 네이티브 멀티모달 모델로, 대화형 이미지 생성 및 편집을 지원합니다.", - "fal-ai/qwen-image-edit.description": "Qwen 팀의 전문 이미지 편집 모델로, 의미와 외형 편집을 지원하며, 중국어와 영어 텍스트를 정밀하게 편집하고 스타일 전환 및 객체 회전과 같은 고품질 편집을 가능하게 합니다.", - "fal-ai/qwen-image.description": "Qwen 팀의 강력한 이미지 생성 모델로, 중국어 텍스트 렌더링과 다양한 시각적 스타일에서 뛰어난 성능을 발휘합니다.", + "fal-ai/qwen-image-edit.description": "Qwen 팀의 전문 이미지 편집 모델로, 의미 및 외형 편집, 정확한 중국어/영어 텍스트 편집, 스타일 전환, 회전 등을 지원합니다.", + "fal-ai/qwen-image.description": "Qwen 팀의 강력한 이미지 생성 모델로, 뛰어난 중국어 텍스트 렌더링과 다양한 시각적 스타일을 제공합니다.", "flux-1-schnell.description": "Black Forest Labs에서 개발한 120억 파라미터 텍스트-이미지 모델로, 잠재 적대 확산 증류를 사용하여 1~4단계 내에 고품질 이미지를 생성합니다. Apache-2.0 라이선스로 개인, 연구, 상업적 사용이 가능합니다.", "flux-dev.description": "비상업적 혁신 연구를 위해 효율적으로 최적화된 오픈소스 R&D 이미지 생성 모델입니다.", "flux-kontext-max.description": "최첨단 문맥 기반 이미지 생성 및 편집 모델로, 텍스트와 이미지를 결합하여 정밀하고 일관된 결과를 생성합니다.", @@ -562,10 +566,10 @@ "gemini-3-flash-preview.description": "Gemini 3 Flash는 속도를 위해 설계된 가장 스마트한 모델로, 최첨단 지능과 뛰어난 검색 기반을 결합합니다.", "gemini-3-flash.description": "Google의 Gemini 3 Flash — 멀티모달 입력 지원을 갖춘 초고속 모델.", "gemini-3-pro-image-preview.description": "Gemini 3 Pro Image (Nano Banana Pro)는 구글의 이미지 생성 모델로, 멀티모달 대화도 지원합니다.", - "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro)는 Google의 이미지 생성 모델로, 멀티모달 대화를 지원합니다.", + "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro)는 Google의 이미지 생성 모델로, 다중 모달 채팅도 지원합니다.", "gemini-3-pro-preview.description": "Gemini 3 Pro는 Google의 가장 강력한 에이전트 및 바이브 코딩 모델로, 최첨단 추론 위에 풍부한 시각적 표현과 깊은 상호작용을 제공합니다.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2)는 구글의 가장 빠른 네이티브 이미지 생성 모델로, 사고 지원, 대화형 이미지 생성 및 편집을 제공합니다.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2)는 Google의 가장 빠른 네이티브 이미지 생성 모델로, 사고 지원, 대화형 이미지 생성 및 편집을 제공합니다.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2)는 Flash 속도로 Pro 수준의 이미지 품질을 제공하며, 다중 모달 채팅을 지원합니다.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview는 Google의 가장 비용 효율적인 다중 모드 모델로, 대량 에이전트 작업, 번역 및 데이터 처리에 최적화되어 있습니다.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite는 Google의 가장 비용 효율적인 멀티모달 모델로, 대량 에이전트 작업, 번역 및 데이터 처리에 최적화되어 있습니다.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview는 Gemini 3 Pro의 추론 능력을 강화하고 중간 사고 수준 지원을 추가합니다.", @@ -730,6 +734,8 @@ "grok-4-fast-reasoning.description": "Grok 4 Fast는 비용 효율적인 추론 모델 분야에서의 최신 성과로, 출시를 기쁘게 생각합니다.", "grok-4.20-0309-non-reasoning.description": "간단한 사용 사례를 위한 비추론 변형", "grok-4.20-0309-reasoning.description": "응답 전에 추론하는 지능적이고 매우 빠른 모델", + "grok-4.20-beta-0309-non-reasoning.description": "간단한 사용 사례를 위한 비사고 변형 모델입니다.", + "grok-4.20-beta-0309-reasoning.description": "응답 전에 사고하는 지능적이고 매우 빠른 모델입니다.", "grok-4.20-multi-agent-0309.description": "4명 또는 16명의 에이전트 팀, 연구 사용 사례에서 뛰어남, 현재 클라이언트 측 도구를 지원하지 않음. xAI 서버 측 도구(예: X Search, Web Search 도구) 및 원격 MCP 도구만 지원.", "grok-4.3.description": "세계에서 가장 진실을 추구하는 대규모 언어 모델", "grok-4.description": "언어, 수학, 추론에서 타의 추종을 불허하는 성능을 자랑하는 최신 Grok 플래그십 모델 — 진정한 만능 모델입니다. 현재 grok-4-0709를 가리키며, 제한된 자원으로 인해 공식 가격보다 임시로 10% 높게 책정되었으며, 이후 공식 가격으로 복귀할 예정입니다.", @@ -1214,6 +1220,8 @@ "qwq.description": "QwQ는 Qwen 계열의 추론 모델입니다. 일반적인 지시 조정 모델과 비교해 사고 및 추론 능력이 뛰어나며, 특히 어려운 문제에서 다운스트림 성능을 크게 향상시킵니다. QwQ-32B는 DeepSeek-R1 및 o1-mini와 경쟁할 수 있는 중형 추론 모델입니다.", "qwq_32b.description": "Qwen 계열의 중형 추론 모델입니다. 일반적인 지시 조정 모델과 비교해 QwQ의 사고 및 추론 능력은 특히 어려운 문제에서 다운스트림 성능을 크게 향상시킵니다.", "r1-1776.description": "R1-1776은 DeepSeek R1의 후속 학습 버전으로, 검열되지 않고 편향 없는 사실 정보를 제공합니다.", + "seedance-1-5-pro-251215.description": "ByteDance의 Seedance 1.5 Pro는 텍스트-비디오, 이미지-비디오(첫 프레임, 첫+마지막 프레임) 및 시각과 동기화된 오디오 생성을 지원합니다.", + "seedream-5-0-260128.description": "BytePlus의 ByteDance-Seedream-5.0-lite는 실시간 정보를 위한 웹 검색 기반 생성, 복잡한 프롬프트 해석 향상, 전문적인 시각적 창작을 위한 참조 일관성 개선을 제공합니다.", "solar-mini-ja.description": "Solar Mini (Ja)는 Solar Mini의 일본어 특화 버전으로, 영어와 한국어에서도 효율적이고 강력한 성능을 유지합니다.", "solar-mini.description": "Solar Mini는 GPT-3.5를 능가하는 성능을 가진 소형 LLM으로, 영어와 한국어를 지원하는 강력한 다국어 기능을 갖추고 있으며, 효율적인 경량 솔루션을 제공합니다.", "solar-pro.description": "Solar Pro는 Upstage의 고지능 LLM으로, 단일 GPU에서 지시 수행에 최적화되어 있으며, IFEval 점수 80 이상을 기록합니다. 현재는 영어를 지원하며, 2024년 11월 전체 릴리스 시 더 많은 언어와 긴 컨텍스트를 지원할 예정입니다.", diff --git a/locales/ko-KR/plugin.json b/locales/ko-KR/plugin.json index e0c1adb65a..c1759a1fb5 100644 --- a/locales/ko-KR/plugin.json +++ b/locales/ko-KR/plugin.json @@ -237,6 +237,14 @@ "builtins.lobe-page-agent.apiName.updateNode": "노드 업데이트", "builtins.lobe-page-agent.apiName.wrapNodes": "노드 감싸기", "builtins.lobe-page-agent.title": "문서", + "builtins.lobe-self-feedback-intent.apiName.declareSelfFeedbackIntent": "개선 아이디어 기록", + "builtins.lobe-self-feedback-intent.inspector.gap.proposal": "개선 제안", + "builtins.lobe-self-feedback-intent.inspector.memory.write": "선호도 기록", + "builtins.lobe-self-feedback-intent.inspector.rejected": "기록되지 않음", + "builtins.lobe-self-feedback-intent.inspector.skill.consolidate": "방법 정리", + "builtins.lobe-self-feedback-intent.inspector.skill.create": "새로운 방법 발견", + "builtins.lobe-self-feedback-intent.inspector.skill.refine": "방법 개선", + "builtins.lobe-self-feedback-intent.title": "개선 아이디어", "builtins.lobe-skill-store.apiName.importFromMarket": "마켓에서 가져오기", "builtins.lobe-skill-store.apiName.importSkill": "스킬 가져오기", "builtins.lobe-skill-store.apiName.searchSkill": "스킬 검색", diff --git a/locales/ko-KR/providers.json b/locales/ko-KR/providers.json index 08f8678375..1e05daa79e 100644 --- a/locales/ko-KR/providers.json +++ b/locales/ko-KR/providers.json @@ -33,6 +33,7 @@ "jina.description": "2020년에 설립된 Jina AI는 선도적인 검색 AI 기업으로, 벡터 모델, 재정렬기, 소형 언어 모델을 포함한 검색 스택을 통해 신뢰성 높고 고품질의 생성형 및 멀티모달 검색 앱을 구축합니다.", "kimicodingplan.description": "문샷 AI의 Kimi Code는 K2.5를 포함한 Kimi 모델에 접근하여 코딩 작업을 수행할 수 있습니다.", "lmstudio.description": "LM Studio는 데스크탑에서 LLM을 개발하고 실험할 수 있는 애플리케이션입니다.", + "lobehub.description": "LobeHub Cloud는 공식 API를 사용하여 AI 모델에 접근하며, 모델 토큰에 연동된 크레딧으로 사용량을 측정합니다.", "longcat.description": "LongCat은 Meituan에서 독자적으로 개발한 생성형 AI 대형 모델 시리즈입니다. 이는 효율적인 계산 아키텍처와 강력한 멀티모달 기능을 통해 내부 기업 생산성을 향상시키고 혁신적인 애플리케이션을 가능하게 하기 위해 설계되었습니다.", "minimax.description": "2021년에 설립된 MiniMax는 텍스트, 음성, 비전 등 멀티모달 기반의 범용 AI를 개발하며, 조 단위 파라미터의 MoE 텍스트 모델과 Hailuo AI와 같은 앱을 제공합니다.", "minimaxcodingplan.description": "MiniMax 토큰 플랜은 고정 요금 구독을 통해 M2.7을 포함한 MiniMax 모델에 접근하여 코딩 작업을 수행할 수 있습니다.", diff --git a/locales/ko-KR/subscription.json b/locales/ko-KR/subscription.json index 6f7e1c20f7..ecb9e075e8 100644 --- a/locales/ko-KR/subscription.json +++ b/locales/ko-KR/subscription.json @@ -41,7 +41,9 @@ "credits.autoTopUp.monthlyLimitDesc": "월별 자동 충전 가능한 최대 금액입니다. 제한이 없으려면 비워 두세요", "credits.autoTopUp.monthlyLimitPlaceholder": "제한 없음", "credits.autoTopUp.monthlyTopUpAmount": "월별 충전 금액", + "credits.autoTopUp.noCustomerHint": "자동 충전을 활성화하기 전에 결제를 저장하려면 크레딧을 한 번 구매하세요.", "credits.autoTopUp.noPaymentMethodHint": "등록된 결제 수단이 없습니다. 자동 충전을 위해서는 자동으로 청구할 수 있는 저장된 카드가 필요합니다.", + "credits.autoTopUp.purchaseCredits": "크레딧 구매", "credits.autoTopUp.saveError": "자동 충전 설정 저장 실패", "credits.autoTopUp.saveSuccess": "자동 충전 설정이 저장되었습니다", "credits.autoTopUp.setupPaymentMethod": "결제 수단 추가", @@ -83,6 +85,7 @@ "credits.packages.title": "내 크레딧 패키지", "credits.topUp.cancel": "취소", "credits.topUp.custom": "사용자 지정", + "credits.topUp.freeFeeHint": "무료 플랜 충전에는 1M 크레딧당 {{fee}} 서비스 수수료가 포함됩니다.", "credits.topUp.maxAmountError": "1회 구매 금액은 ${{max}}를 초과할 수 없습니다", "credits.topUp.purchaseError": "구매에 실패했습니다. 나중에 다시 시도해 주세요", "credits.topUp.purchaseNow": "지금 구매하기", @@ -120,6 +123,9 @@ "keyMissMatch.button": "사용량 복원 및 대화 계속하기", "keyMissMatch.description": "일시적인 시스템 오류로 인해 현재 구독 사용량이 비활성화되었습니다. 아래 버튼을 클릭하여 사용량을 복원하고 대화를 계속하세요. 이 문제가 반복되면 이메일(support@lobehub.com)로 문의해 주세요.", "keyMissMatch.title": "지금 구독 사용량 복원하기", + "limitation.chat.budgetReady.action": "채팅 계속하기", + "limitation.chat.budgetReady.desc": "사용 가능한 크레딧이 이제 이 요청을 충족합니다.", + "limitation.chat.budgetReady.title": "크레딧 준비 완료", "limitation.chat.success.action": "대화 계속하기", "limitation.chat.success.desc": "{{plan}} 구독이 성공적으로 업그레이드되었습니다. AI 대화를 즐기세요. 현재 플랜에는 다음이 포함됩니다:", "limitation.chat.success.title": "업그레이드 완료", @@ -134,14 +140,23 @@ "limitation.hobby.docs": "설정 문서 보기", "limitation.hobby.tip": "사용자 지정 API 키가 있는 모델로 전환하는 것을 잊지 마세요", "limitation.hobby.title": "모델 서비스 API 설정 필요", + "limitation.image.budgetReady.action": "생성 계속하기", + "limitation.image.budgetReady.desc": "사용 가능한 크레딧이 이제 이 생성 요청을 충족합니다.", + "limitation.image.budgetReady.title": "크레딧 준비 완료", "limitation.image.success.action": "생성 계속하기", "limitation.image.success.desc": "{{plan}} 구독이 성공적으로 업그레이드되었습니다. AI 이미지 생성을 즐기세요. 현재 플랜에는 다음이 포함됩니다:", "limitation.image.success.title": "업그레이드 완료", "limitation.image.topupSuccess.action": "생성 계속하기", "limitation.image.topupSuccess.desc": "충전 크레딧이 활성화되었습니다. AI 이미지 생성을 즐기세요. 현재 플랜에는 다음이 포함됩니다:", "limitation.image.topupSuccess.title": "충전 완료", + "limitation.insufficientBudget.approximateDesc": "이 요청에는 더 많은 크레딧이 필요할 수 있습니다. 크레딧을 충전하거나 플랜을 업그레이드하세요.", + "limitation.insufficientBudget.available": "사용 가능한 크레딧", "limitation.insufficientBudget.desc": "남은 크레딧이 이 모델의 예상 비용을 충당하기에 부족합니다. 크레딧을 충전하거나 더 저렴한 모델로 전환하세요.", + "limitation.insufficientBudget.estimatedDesc": "이 요청에는 더 많은 크레딧이 필요할 것으로 예상됩니다. 크레딧을 충전하거나 플랜을 업그레이드하세요.", + "limitation.insufficientBudget.exactDesc": "이 요청에는 더 많은 크레딧이 필요합니다. 크레딧을 충전하거나 플랜을 업그레이드하세요.", + "limitation.insufficientBudget.required": "필요한 크레딧", "limitation.insufficientBudget.retry": "다시 시도", + "limitation.insufficientBudget.shortfall": "크레딧 부족", "limitation.insufficientBudget.title": "이 모델에 대한 크레딧 부족", "limitation.limited.action": "지금 업그레이드", "limitation.limited.advanceFeature": "프리미엄 기능을 사용하려면 업그레이드하세요:", @@ -151,6 +166,7 @@ "limitation.limited.title": "컴퓨팅 크레딧 소진됨", "limitation.limited.topup": "크레딧 충전", "limitation.limited.upgrade": "상위 플랜으로 업그레이드", + "limitation.limited.upgradeToPlan": "{{plan}} 플랜으로 업그레이드", "limitation.providers.lock.addNew": "지금 구독하여 사용자 지정 AI 제공자 생성", "limitation.providers.lock.enableProvider": "지금 구독하여 이 AI 제공자 사용", "limitation.providers.lock.menuItem": "지금 구독하여 사용자 지정 API 서비스 설정", @@ -161,6 +177,9 @@ "limitation.providers.prompter.subTitle": "사용자 지정 API 서비스는 유료 플랜에서만 제공됩니다. 업그레이드하여 글로벌 주요 모델 서비스를 이용하세요", "limitation.providers.prompter.title": "지금 구독하여 사용자 지정 API 서비스 사용", "limitation.providers.tooltip": "사용자 지정 API 서비스는 유료 플랜에서만 제공됩니다", + "limitation.video.budgetReady.action": "생성 계속하기", + "limitation.video.budgetReady.desc": "사용 가능한 크레딧이 이제 이 생성 요청을 충족합니다.", + "limitation.video.budgetReady.title": "크레딧 준비 완료", "limitation.video.success.action": "생성 계속하기", "limitation.video.success.desc": "{{plan}} 구독이 성공적으로 업그레이드되었습니다. AI 영상 생성을 즐겨보세요. 현재 사용 중인 플랜은 다음과 같습니다:", "limitation.video.success.title": "업그레이드 완료", diff --git a/locales/nl-NL/chat.json b/locales/nl-NL/chat.json index 83de24af47..d333feaff7 100644 --- a/locales/nl-NL/chat.json +++ b/locales/nl-NL/chat.json @@ -196,6 +196,9 @@ "groupWizard.searchTemplates": "Sjablonen zoeken...", "groupWizard.title": "Groep aanmaken", "groupWizard.useTemplate": "Sjabloon gebruiken", + "heteroAgent.cloudNotConfigured.action": "Configureren", + "heteroAgent.cloudNotConfigured.desc": "Configureer je Claude Code-token in het agentprofiel om berichten te verzenden.", + "heteroAgent.cloudNotConfigured.title": "Cloudreferenties vereist", "heteroAgent.cloudRepo.multiSelected": "{{count}} repositories geselecteerd", "heteroAgent.cloudRepo.noRepos": "Geen repositories geconfigureerd. Voeg ze toe in de agentinstellingen.", "heteroAgent.cloudRepo.notSet": "Geen repository geselecteerd", @@ -467,6 +470,10 @@ "sharePage.error.unauthorized.subtitle": "Log in om dit gedeelde onderwerp te bekijken.", "sharePage.error.unauthorized.title": "Inloggen vereist", "sharePageDisclaimer": "Deze inhoud is gedeeld door een gebruiker en vertegenwoordigt niet de standpunten van LobeHub. LobeHub is niet verantwoordelijk voor eventuele gevolgen van deze gedeelde inhoud.", + "signalCallbacks.collapse": "Details verbergen", + "signalCallbacks.empty": "Geen callbackberichten", + "signalCallbacks.expand": "Details weergeven", + "signalCallbacks.title": "{{tool}} · {{count}} callback-updates", "stt.action": "Spraakopname", "stt.loading": "Herkennen...", "stt.prettifying": "Verfraaien...", @@ -689,7 +696,6 @@ "thread.closeSubagentThread": "Subagentgesprek samenvouwen", "thread.divider": "Subonderwerp", "thread.openSubagentThread": "Volledig subagentgesprek bekijken", - "thread.subagentBadge": "Subagent", "thread.subagentReadOnlyHint": "SubAgent-gesprekken zijn alleen-lezen — uitvoering wordt aangestuurd door de hoofdagent.", "thread.threadMessageCount": "{{messageCount}} berichten", "thread.title": "Subonderwerp", diff --git a/locales/nl-NL/models.json b/locales/nl-NL/models.json index 5da43008e6..d669a1018d 100644 --- a/locales/nl-NL/models.json +++ b/locales/nl-NL/models.json @@ -106,6 +106,7 @@ "MiniMax-Hailuo-2.3.description": "Gloednieuw videogenereermodel met uitgebreide verbeteringen in lichaamsbeweging, fysieke realisme en instructienaleving.", "MiniMax-M1.description": "Een nieuw intern redeneermodel met 80K chain-of-thought en 1M input, met prestaties vergelijkbaar met toonaangevende wereldwijde modellen.", "MiniMax-M2-Stable.description": "Ontworpen voor efficiënte codeer- en agentworkflows, met hogere gelijktijdigheid voor commercieel gebruik.", + "MiniMax-M2.1-Lightning.description": "Krachtige meertalige programmeermogelijkheden met snellere en efficiëntere inferentie.", "MiniMax-M2.1-highspeed.description": "Krachtige meertalige programmeermogelijkheden, een volledig verbeterde programmeerervaring. Sneller en efficiënter.", "MiniMax-M2.1.description": "MiniMax-M2.1 is het vlaggenschip open-source grote model van MiniMax, gericht op het oplossen van complexe, realistische taken. De kernkwaliteiten zijn meertalige programmeermogelijkheden en het vermogen om complexe taken als een Agent op te lossen.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: Zelfde prestaties als M2.5 met snellere inferentie.", @@ -314,7 +315,7 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku is het snelste en meest compacte model van Anthropic, ontworpen voor vrijwel directe reacties met snelle en nauwkeurige prestaties.", "claude-3-opus-20240229.description": "Claude 3 Opus is het krachtigste model van Anthropic voor zeer complexe taken, met uitmuntende prestaties, intelligentie, vloeiendheid en begrip.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet biedt een balans tussen intelligentie en snelheid voor zakelijke toepassingen, met hoge bruikbaarheid tegen lagere kosten en betrouwbare grootschalige inzet.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 is het snelste en slimste Haiku-model van Anthropic, met bliksemsnelle snelheid en uitgebreide redeneervermogen.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 is het snelste en meest intelligente Haiku-model van Anthropic, met bliksemsnelle prestaties en uitgebreide denkcapaciteit.", "claude-haiku-4-5.description": "Claude Haiku 4.5 door Anthropic — next-gen Haiku met verbeterde redenering en visie.", "claude-haiku-4.5.description": "Claude Haiku 4.5 is Anthropic's snelste en slimste Haiku-model, met bliksemsnelle snelheid en uitgebreide redeneervermogen.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking is een geavanceerde variant die zijn redeneerproces kan onthullen.", @@ -329,7 +330,7 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 is Anthropic's meest intelligente model voor het bouwen van agents en coderen.", "claude-opus-4.6.description": "Claude Opus 4.6 is Anthropic's meest intelligente model voor het bouwen van agents en coderen.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking kan vrijwel directe antwoorden geven of uitgebreide stapsgewijze redenering tonen met zichtbaar proces.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 kan bijna onmiddellijke reacties genereren of uitgebreide stapsgewijze redeneringen met een zichtbaar proces.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 is het meest intelligente model van Anthropic tot nu toe, met bijna directe reacties of uitgebreide stapsgewijze denkprocessen en fijne controle voor API-gebruikers.", "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 is het meest intelligente model van Anthropic tot nu toe.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 door Anthropic — verbeterde Sonnet met verbeterde codeerprestaties.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 door Anthropic — nieuwste Sonnet met superieure codering en hulpmiddelengebruik.", @@ -403,7 +404,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) is een innovatief model dat diep taalbegrip en interactie biedt.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 is een next-gen redeneermodel met sterkere complexe redenering en chain-of-thought voor diepgaande analysetaken.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 is een next-gen redeneermodel met sterkere complexe redeneer- en keten-van-denken-capaciteiten.", - "deepseek-chat.description": "Een nieuw open-source model dat algemene en codeermogelijkheden combineert. Het behoudt de algemene dialoog van het chatmodel en de sterke codeermogelijkheden van het coderingsmodel, met betere voorkeurafstemming. DeepSeek-V2.5 verbetert ook schrijven en het opvolgen van instructies.", + "deepseek-chat.description": "Compatibiliteitsalias voor DeepSeek V4 Flash niet-denkmodus. Gepland voor afschaffing — gebruik DeepSeek V4 Flash in plaats daarvan.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B is een codeertaalmodel getraind op 2 biljoen tokens (87% code, 13% Chinees/Engels tekst). Het introduceert een contextvenster van 16K en 'fill-in-the-middle'-taken, wat projectniveau codeaanvulling en fragmentinvoeging mogelijk maakt.", "deepseek-coder-v2.description": "DeepSeek Coder V2 is een open-source MoE-codeermodel dat sterk presteert bij programmeertaken, vergelijkbaar met GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 is een open-source MoE-codeermodel dat sterk presteert bij programmeertaken, vergelijkbaar met GPT-4 Turbo.", @@ -425,7 +426,7 @@ "deepseek-r1-fast-online.description": "DeepSeek R1 snelle volledige versie met realtime webzoekfunctie, combineert 671B-capaciteit met snellere reacties.", "deepseek-r1-online.description": "DeepSeek R1 volledige versie met 671B parameters en realtime webzoekfunctie, biedt sterkere begrip- en generatiecapaciteiten.", "deepseek-r1.description": "DeepSeek-R1 gebruikt cold-start data vóór versterkingsleren en presteert vergelijkbaar met OpenAI-o1 op wiskunde, programmeren en redenering.", - "deepseek-reasoner.description": "Een DeepSeek-redeneermodel gericht op complexe logische redeneertaken.", + "deepseek-reasoner.description": "Compatibiliteitsalias voor DeepSeek V4 Flash denkmodus. Gepland voor afschaffing — gebruik DeepSeek V4 Flash in plaats daarvan.", "deepseek-v2.description": "DeepSeek V2 is een efficiënt MoE-model voor kosteneffectieve verwerking.", "deepseek-v2:236b.description": "DeepSeek V2 236B is DeepSeek’s codegerichte model met sterke codegeneratie.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 is een MoE-model met 671B parameters en uitmuntende prestaties in programmeren, technische vaardigheden, contextbegrip en verwerking van lange teksten.", @@ -490,6 +491,8 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 is een beeldgeneratiemodel van ByteDance Seed dat tekst- en afbeeldingsinvoer ondersteunt voor zeer controleerbare, hoogwaardige beeldgeneratie. Het genereert beelden op basis van tekstprompts.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 is het nieuwste multimodale beeldmodel van ByteDance, dat tekst-naar-beeld, beeld-naar-beeld en batchbeeldgeneratiecapaciteiten integreert, terwijl het gezond verstand en redeneervermogen bevat. Vergeleken met de vorige 4.0-versie levert het aanzienlijk verbeterde generatiekwaliteit, met betere bewerkingsconsistentie en multi-beeldfusie. Het biedt meer precieze controle over visuele details, produceert kleine teksten en gezichten natuurlijker, en bereikt een harmonieuzere lay-out en kleur, wat de algehele esthetiek verbetert.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite is het nieuwste beeldgeneratiemodel van ByteDance. Voor het eerst integreert het online zoekmogelijkheden, waardoor het real-time webinformatie kan opnemen en de actualiteit van gegenereerde beelden kan verbeteren. De intelligentie van het model is ook geüpgraded, waardoor het complexe instructies en visuele inhoud nauwkeurig kan interpreteren. Bovendien biedt het verbeterde wereldwijde kennisdekking, referentieconsistentie en generatiekwaliteit in professionele scenario's, waardoor het beter voldoet aan visuele creatiebehoeften op ondernemingsniveau.", + "dreamina-seedance-2-0-260128.description": "Seedance 2.0 van ByteDance is het krachtigste videogeneratiemodel, met ondersteuning voor multimodale referentievideogeneratie, videobewerking, video-uitbreiding, tekst-naar-video en afbeelding-naar-video met gesynchroniseerde audio.", + "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast van ByteDance biedt dezelfde mogelijkheden als Seedance 2.0 met snellere generatiesnelheden tegen een concurrerende prijs.", "emohaa.description": "Emohaa is een mentaal gezondheidsmodel met professionele begeleidingsvaardigheden om gebruikers te helpen emotionele problemen te begrijpen.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B is een lichtgewicht open-source model voor lokale en aangepaste implementatie.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview is een previewmodel met 8K context voor het evalueren van ERNIE 4.5.", @@ -514,7 +517,8 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K is een snel denkend model met 32K context voor complexe redenatie en meerstapsgesprekken.", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview is een preview van een denkmodel voor evaluatie en testen.", "ernie-x1.1.description": "ERNIE X1.1 is een preview van een denkmodel voor evaluatie en testen.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 is een beeldgeneratiemodel van ByteDance Seed, dat tekst- en beeldinvoer ondersteunt met zeer controleerbare, hoogwaardige beeldgeneratie. Het genereert beelden op basis van tekstprompts.", + "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5, ontwikkeld door het ByteDance Seed-team, ondersteunt multi-afbeeldingbewerking en compositie. Kenmerken zijn verbeterde onderwerpconsistentie, nauwkeurige instructievolging, ruimtelijk logisch begrip, esthetische expressie, posterlay-out en logo-ontwerp met hoogprecisie tekst-afbeelding rendering.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, ontwikkeld door ByteDance Seed, ondersteunt tekst- en afbeeldingsinvoer voor zeer controleerbare, hoogwaardige afbeeldingsgeneratie vanuit prompts.", "fal-ai/flux-kontext/dev.description": "FLUX.1-model gericht op beeldbewerking, met ondersteuning voor tekst- en afbeeldingsinvoer.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] accepteert tekst en referentieafbeeldingen als invoer, waardoor gerichte lokale bewerkingen en complexe wereldwijde scèneaanpassingen mogelijk zijn.", "fal-ai/flux/krea.description": "Flux Krea [dev] is een afbeeldingsgeneratiemodel met een esthetische voorkeur voor realistische, natuurlijke beelden.", @@ -522,8 +526,8 @@ "fal-ai/hunyuan-image/v3.description": "Een krachtig, native multimodaal afbeeldingsgeneratiemodel.", "fal-ai/imagen4/preview.description": "Hoogwaardig afbeeldingsgeneratiemodel van Google.", "fal-ai/nano-banana.description": "Nano Banana is het nieuwste, snelste en meest efficiënte native multimodale model van Google, waarmee beeldgeneratie en -bewerking via conversatie mogelijk is.", - "fal-ai/qwen-image-edit.description": "Een professioneel beeldbewerkingsmodel van het Qwen-team dat semantische en visuele bewerkingen ondersteunt, Chinese en Engelse tekst nauwkeurig bewerkt en hoogwaardige bewerkingen mogelijk maakt, zoals stijltransfer en objectrotatie.", - "fal-ai/qwen-image.description": "Een krachtig beeldgeneratiemodel van het Qwen-team met indrukwekkende Chinese tekstrendering en diverse visuele stijlen.", + "fal-ai/qwen-image-edit.description": "Een professioneel afbeeldingsbewerkingsmodel van het Qwen-team, met ondersteuning voor semantische en uiterlijke bewerkingen, nauwkeurige Chinese/Engelse tekstbewerking, stijltransfer, rotatie en meer.", + "fal-ai/qwen-image.description": "Een krachtig afbeeldingsgeneratiemodel van het Qwen-team met sterke Chinese tekstweergave en diverse visuele stijlen.", "flux-1-schnell.description": "Een tekst-naar-beeldmodel met 12 miljard parameters van Black Forest Labs, dat gebruikmaakt van latente adversariële diffusiedistillatie om hoogwaardige beelden te genereren in 1–4 stappen. Het evenaart gesloten alternatieven en is uitgebracht onder de Apache-2.0-licentie voor persoonlijk, onderzoeks- en commercieel gebruik.", "flux-dev.description": "Open-source R&D-beeldgeneratiemodel, efficiënt geoptimaliseerd voor niet-commercieel innovatief onderzoek.", "flux-kontext-max.description": "State-of-the-art contextuele beeldgeneratie en -bewerking, waarbij tekst en afbeeldingen worden gecombineerd voor nauwkeurige, samenhangende resultaten.", @@ -562,10 +566,10 @@ "gemini-3-flash-preview.description": "Gemini 3 Flash is het slimste model dat is gebouwd voor snelheid, met geavanceerde intelligentie en uitstekende zoekverankering.", "gemini-3-flash.description": "Gemini 3 Flash door Google — ultrafast model met ondersteuning voor multimodale invoer.", "gemini-3-pro-image-preview.description": "Gemini 3 Pro Image (Nano Banana Pro) is het beeldgeneratiemodel van Google dat ook multimodale dialogen ondersteunt.", - "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) is het beeldgeneratiemodel van Google en ondersteunt ook multimodale chat.", + "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) is het afbeeldingsgeneratiemodel van Google en ondersteunt ook multimodale chat.", "gemini-3-pro-preview.description": "Gemini 3 Pro is het krachtigste agent- en vibe-codingmodel van Google, met rijkere visuele output en diepere interactie bovenop geavanceerde redeneercapaciteiten.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) is het snelste native beeldgeneratiemodel van Google met denksupport, conversatiebeeldgeneratie en bewerking.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) is het snelste native beeldgeneratiemodel van Google met ondersteuning voor denken, conversatiegerichte beeldgeneratie en bewerking.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) levert Pro-niveau afbeeldingskwaliteit met Flash-snelheid en ondersteuning voor multimodale chat.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview is het meest kostenefficiënte multimodale model van Google, geoptimaliseerd voor grootschalige agenttaken, vertaling en gegevensverwerking.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite is Google's meest kostenefficiënte multimodale model, geoptimaliseerd voor grootschalige agenttaken, vertaling en gegevensverwerking.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview verbetert Gemini 3 Pro met verbeterde redeneercapaciteiten en voegt ondersteuning toe voor een gemiddeld denkniveau.", @@ -730,6 +734,8 @@ "grok-4-fast-reasoning.description": "We zijn verheugd Grok 4 Fast uit te brengen, onze nieuwste vooruitgang in kosteneffectieve redeneermodellen.", "grok-4.20-0309-non-reasoning.description": "Een niet-redenerende variant voor eenvoudige gebruiksscenario's.", "grok-4.20-0309-reasoning.description": "Intelligent, razendsnel model dat redeneert voordat het reageert.", + "grok-4.20-beta-0309-non-reasoning.description": "Een niet-denkende variant voor eenvoudige gebruiksscenario's.", + "grok-4.20-beta-0309-reasoning.description": "Intelligent, razendsnel model dat redeneert voordat het reageert.", "grok-4.20-multi-agent-0309.description": "Een team van 4 of 16 agents, uitblinkend in onderzoeksgebruiksscenario's. Ondersteunt momenteel geen client-side tools. Ondersteunt alleen xAI server-side tools (bijv. X Search, Web Search tools) en remote MCP tools.", "grok-4.3.description": "Het meest waarheidsgetrouwe grote taalmodel ter wereld", "grok-4.description": "Het nieuwste vlaggenschip van Grok met ongeëvenaarde prestaties in taal, wiskunde en redeneervermogen — een echte alleskunner. Momenteel verwijst het naar grok-4-0709; vanwege beperkte middelen is de prijs tijdelijk 10% hoger dan de officiële prijs en wordt verwacht later terug te keren naar de officiële prijs.", @@ -1214,6 +1220,8 @@ "qwq.description": "QwQ is een redeneermodel binnen de Qwen-familie. In vergelijking met standaard instructie-getrainde modellen biedt het denk- en redeneervermogen dat de prestaties op complexe problemen aanzienlijk verbetert. QwQ-32B is een middelgroot redeneermodel dat zich kan meten met topmodellen zoals DeepSeek-R1 en o1-mini.", "qwq_32b.description": "Middelgroot redeneermodel binnen de Qwen-familie. In vergelijking met standaard instructie-getrainde modellen verbeteren QwQ’s denk- en redeneervermogen de prestaties op complexe problemen aanzienlijk.", "r1-1776.description": "R1-1776 is een na-getrainde variant van DeepSeek R1, ontworpen om ongecensureerde, onbevooroordeelde feitelijke informatie te bieden.", + "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro van ByteDance ondersteunt tekst-naar-video, afbeelding-naar-video (eerste frame, eerste+laatste frame) en audiogeneratie gesynchroniseerd met visuals.", + "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite van BytePlus biedt web-ophaal-geaugmenteerde generatie voor realtime informatie, verbeterde interpretatie van complexe prompts en verbeterde referentieconsistentie voor professionele visuele creatie.", "solar-mini-ja.description": "Solar Mini (Ja) breidt Solar Mini uit met focus op Japans, terwijl het efficiënte, sterke prestaties in Engels en Koreaans behoudt.", "solar-mini.description": "Solar Mini is een compact LLM dat beter presteert dan GPT-3.5, met sterke meertalige ondersteuning voor Engels en Koreaans, en biedt een efficiënte oplossing met een kleine voetafdruk.", "solar-pro.description": "Solar Pro is een intelligent LLM van Upstage, gericht op instructieopvolging op een enkele GPU, met IFEval-scores boven de 80. Momenteel ondersteunt het Engels; de volledige release stond gepland voor november 2024 met uitgebreidere taalondersteuning en langere context.", diff --git a/locales/nl-NL/plugin.json b/locales/nl-NL/plugin.json index 4dbd40a23e..feeee684e1 100644 --- a/locales/nl-NL/plugin.json +++ b/locales/nl-NL/plugin.json @@ -237,6 +237,14 @@ "builtins.lobe-page-agent.apiName.updateNode": "Knooppunt bijwerken", "builtins.lobe-page-agent.apiName.wrapNodes": "Knooppunten inpakken", "builtins.lobe-page-agent.title": "Pagina", + "builtins.lobe-self-feedback-intent.apiName.declareSelfFeedbackIntent": "Verbeteringsidee vastleggen", + "builtins.lobe-self-feedback-intent.inspector.gap.proposal": "Verbetering voorstellen", + "builtins.lobe-self-feedback-intent.inspector.memory.write": "Voorkeur vastleggen", + "builtins.lobe-self-feedback-intent.inspector.rejected": "Niet vastgelegd", + "builtins.lobe-self-feedback-intent.inspector.skill.consolidate": "Methoden organiseren", + "builtins.lobe-self-feedback-intent.inspector.skill.create": "Nieuwe methode gevonden", + "builtins.lobe-self-feedback-intent.inspector.skill.refine": "Methode verbeteren", + "builtins.lobe-self-feedback-intent.title": "Verbeteringsideeën", "builtins.lobe-skill-store.apiName.importFromMarket": "Importeren uit de Markt", "builtins.lobe-skill-store.apiName.importSkill": "Vaardigheid Importeren", "builtins.lobe-skill-store.apiName.searchSkill": "Vaardigheden Zoeken", diff --git a/locales/nl-NL/providers.json b/locales/nl-NL/providers.json index 05ef593c8b..d24aaec7f3 100644 --- a/locales/nl-NL/providers.json +++ b/locales/nl-NL/providers.json @@ -33,6 +33,7 @@ "jina.description": "Opgericht in 2020, is Jina AI een toonaangevend zoek-AI-bedrijf. De zoekstack omvat vectormodellen, herordenaars en kleine taalmodellen om betrouwbare, hoogwaardige generatieve en multimodale zoekapps te bouwen.", "kimicodingplan.description": "Kimi Code van Moonshot AI biedt toegang tot Kimi-modellen, waaronder K2.5, voor coderingstaken.", "lmstudio.description": "LM Studio is een desktopapplicatie voor het ontwikkelen en experimenteren met LLM’s op je eigen computer.", + "lobehub.description": "LobeHub Cloud gebruikt officiële API's om toegang te krijgen tot AI-modellen en meet het gebruik met Credits die gekoppeld zijn aan modeltokens.", "longcat.description": "LongCat is een reeks generatieve AI-grote modellen die onafhankelijk zijn ontwikkeld door Meituan. Het is ontworpen om de productiviteit binnen ondernemingen te verbeteren en innovatieve toepassingen mogelijk te maken door middel van een efficiënte computationele architectuur en sterke multimodale mogelijkheden.", "minimax.description": "Opgericht in 2021, bouwt MiniMax algemene AI met multimodale fundamentele modellen, waaronder tekstmodellen met biljoenen parameters, spraakmodellen en visiemodellen, evenals apps zoals Hailuo AI.", "minimaxcodingplan.description": "MiniMax Token Plan biedt toegang tot MiniMax-modellen, waaronder M2.7, voor coderingstaken via een abonnement met vaste kosten.", diff --git a/locales/nl-NL/subscription.json b/locales/nl-NL/subscription.json index 064589d277..0944240df4 100644 --- a/locales/nl-NL/subscription.json +++ b/locales/nl-NL/subscription.json @@ -41,7 +41,9 @@ "credits.autoTopUp.monthlyLimitDesc": "Maximaal bedrag dat per maand automatisch kan worden afgeschreven. Laat leeg voor geen limiet", "credits.autoTopUp.monthlyLimitPlaceholder": "Geen limiet", "credits.autoTopUp.monthlyTopUpAmount": "Maandelijkse Oplaadbedrag", + "credits.autoTopUp.noCustomerHint": "Koop eenmalig credits om een betaalmethode op te slaan voordat u automatisch opwaarderen inschakelt.", "credits.autoTopUp.noPaymentMethodHint": "Geen betaalmethode opgeslagen. Automatisch opwaarderen vereist een opgeslagen kaart om automatisch kosten in rekening te brengen.", + "credits.autoTopUp.purchaseCredits": "Koop Credits", "credits.autoTopUp.saveError": "Opslaan van automatische oplaadinstellingen mislukt", "credits.autoTopUp.saveSuccess": "Automatische oplaadinstellingen opgeslagen", "credits.autoTopUp.setupPaymentMethod": "Betaalmethode toevoegen", @@ -83,6 +85,7 @@ "credits.packages.title": "Mijn Tegoedpakketten", "credits.topUp.cancel": "Annuleren", "credits.topUp.custom": "Aangepast", + "credits.topUp.freeFeeHint": "Gratis plan-opwaarderingen omvatten een servicekosten van {{fee}} per 1M credits.", "credits.topUp.maxAmountError": "Het aankoopbedrag mag niet hoger zijn dan ${{max}}", "credits.topUp.purchaseError": "Aankoop mislukt, probeer het later opnieuw", "credits.topUp.purchaseNow": "Nu Kopen", @@ -120,6 +123,9 @@ "keyMissMatch.button": "Herstel gebruik en ga verder met het gesprek", "keyMissMatch.description": "Door een tijdelijke systeemfout is je huidige abonnement tijdelijk inactief. Klik op de knop hieronder om het gebruik te herstellen en verder te gaan met het gesprek. Als dit vaker gebeurt, neem dan contact met ons op via e-mail (support@lobehub.com)", "keyMissMatch.title": "Herstel Abonnementsgebruik Nu", + "limitation.chat.budgetReady.action": "Doorgaan met Chatten", + "limitation.chat.budgetReady.desc": "Uw beschikbare credits dekken nu dit verzoek.", + "limitation.chat.budgetReady.title": "Credits Beschikbaar", "limitation.chat.success.action": "Ga Verder met Chatten", "limitation.chat.success.desc": "Je {{plan}} abonnement is succesvol geüpgraded. Veel plezier met AI-chatten. Je huidige abonnement bevat:", "limitation.chat.success.title": "Upgrade Geslaagd", @@ -134,14 +140,23 @@ "limitation.hobby.docs": "Bekijk configuratiedocumentatie", "limitation.hobby.tip": "Vergeet niet over te schakelen naar een model met aangepaste API-sleutel", "limitation.hobby.title": "Configureer Modelservice-API", + "limitation.image.budgetReady.action": "Doorgaan met Genereren", + "limitation.image.budgetReady.desc": "Uw beschikbare credits dekken nu deze generatie.", + "limitation.image.budgetReady.title": "Credits Beschikbaar", "limitation.image.success.action": "Ga Verder met Genereren", "limitation.image.success.desc": "Je {{plan}} abonnement is succesvol geüpgraded. Veel plezier met AI-afbeeldingen genereren. Je huidige abonnement bevat:", "limitation.image.success.title": "Upgrade Geslaagd", "limitation.image.topupSuccess.action": "Ga Verder met Genereren", "limitation.image.topupSuccess.desc": "Je opwaardeertegoed is nu actief. Veel plezier met AI-afbeeldingen genereren. Je huidige abonnement bevat:", "limitation.image.topupSuccess.title": "Opwaardering Geslaagd", + "limitation.insufficientBudget.approximateDesc": "Dit verzoek kan meer credits nodig hebben. Voeg credits toe of upgrade uw plan.", + "limitation.insufficientBudget.available": "Beschikbare Credits", "limitation.insufficientBudget.desc": "Uw resterende tegoed is niet voldoende voor de geschatte kosten van dit model. Vul uw tegoed aan of schakel over naar een minder duur model.", + "limitation.insufficientBudget.estimatedDesc": "Dit verzoek heeft naar schatting meer credits nodig. Voeg credits toe of upgrade uw plan.", + "limitation.insufficientBudget.exactDesc": "Dit verzoek heeft meer credits nodig. Voeg credits toe of upgrade uw plan.", + "limitation.insufficientBudget.required": "Vereiste Credits", "limitation.insufficientBudget.retry": "Opnieuw proberen", + "limitation.insufficientBudget.shortfall": "Tekort aan Credits", "limitation.insufficientBudget.title": "Onvoldoende tegoed voor dit model", "limitation.limited.action": "Nu Upgraden", "limitation.limited.advanceFeature": "Upgrade om premiumfuncties te gebruiken:", @@ -151,6 +166,7 @@ "limitation.limited.title": "Rekentegoed Opgebruikt", "limitation.limited.topup": "Tegoed Opwaarderen", "limitation.limited.upgrade": "Upgrade naar Hoger Abonnement", + "limitation.limited.upgradeToPlan": "Upgrade naar {{plan}}", "limitation.providers.lock.addNew": "Abonneer nu om aangepaste AI-providers te maken", "limitation.providers.lock.enableProvider": "Abonneer nu om deze AI-provider in te schakelen", "limitation.providers.lock.menuItem": "Abonneer nu om aangepaste API-service te configureren", @@ -161,6 +177,9 @@ "limitation.providers.prompter.subTitle": "Aangepaste API-service is alleen beschikbaar voor betaalde abonnementen. Upgrade nu om gebruik te maken van wereldwijde modelservices", "limitation.providers.prompter.title": "Abonneer nu om aangepaste API-service te gebruiken", "limitation.providers.tooltip": "Aangepaste API-service is alleen beschikbaar voor betaalde abonnementen", + "limitation.video.budgetReady.action": "Doorgaan met Genereren", + "limitation.video.budgetReady.desc": "Uw beschikbare credits dekken nu deze generatie.", + "limitation.video.budgetReady.title": "Credits Beschikbaar", "limitation.video.success.action": "Doorgaan met genereren", "limitation.video.success.desc": "Je {{plan}}-abonnement is succesvol geüpgraded. Geniet van het genereren van AI-video's. Je huidige abonnement bevat:", "limitation.video.success.title": "Upgrade geslaagd", diff --git a/locales/pl-PL/chat.json b/locales/pl-PL/chat.json index 4d542df068..71cbd20e9d 100644 --- a/locales/pl-PL/chat.json +++ b/locales/pl-PL/chat.json @@ -196,6 +196,9 @@ "groupWizard.searchTemplates": "Szukaj szablonów...", "groupWizard.title": "Utwórz grupę", "groupWizard.useTemplate": "Użyj szablonu", + "heteroAgent.cloudNotConfigured.action": "Skonfiguruj", + "heteroAgent.cloudNotConfigured.desc": "Skonfiguruj swój token Claude Code w profilu agenta, aby rozpocząć wysyłanie wiadomości.", + "heteroAgent.cloudNotConfigured.title": "Wymagane dane uwierzytelniające w chmurze", "heteroAgent.cloudRepo.multiSelected": "{{count}} repozytoriów wybranych", "heteroAgent.cloudRepo.noRepos": "Brak skonfigurowanych repozytoriów. Dodaj je w ustawieniach agenta.", "heteroAgent.cloudRepo.notSet": "Nie wybrano repozytorium", @@ -467,6 +470,10 @@ "sharePage.error.unauthorized.subtitle": "Zaloguj się, aby zobaczyć ten udostępniony temat.", "sharePage.error.unauthorized.title": "Wymagane logowanie", "sharePageDisclaimer": "Ta treść została udostępniona przez użytkownika i nie odzwierciedla poglądów LobeHub. LobeHub nie ponosi odpowiedzialności za jakiekolwiek konsekwencje wynikające z udostępnionej treści.", + "signalCallbacks.collapse": "Ukryj szczegóły", + "signalCallbacks.empty": "Brak wiadomości zwrotnych", + "signalCallbacks.expand": "Pokaż szczegóły", + "signalCallbacks.title": "{{tool}} · {{count}} aktualizacje zwrotne", "stt.action": "Wprowadzanie głosowe", "stt.loading": "Rozpoznawanie...", "stt.prettifying": "Dopracowywanie...", @@ -689,7 +696,6 @@ "thread.closeSubagentThread": "Zwiń rozmowę subagenta", "thread.divider": "Podtemat", "thread.openSubagentThread": "Pokaż pełną rozmowę subagenta", - "thread.subagentBadge": "Subagent", "thread.subagentReadOnlyHint": "Rozmowy z SubAgentem są tylko do odczytu — wykonanie jest sterowane przez agenta nadrzędnego.", "thread.threadMessageCount": "{{messageCount}} wiadomości", "thread.title": "Podtemat", diff --git a/locales/pl-PL/models.json b/locales/pl-PL/models.json index e02b10499f..53b563b533 100644 --- a/locales/pl-PL/models.json +++ b/locales/pl-PL/models.json @@ -106,6 +106,7 @@ "MiniMax-Hailuo-2.3.description": "Nowy model generowania wideo z kompleksowymi ulepszeniami w zakresie ruchu ciała, realizmu fizycznego i przestrzegania instrukcji.", "MiniMax-M1.description": "Nowy wewnętrzny model rozumowania z 80 tys. łańcuchów myślowych i 1 mln tokenów wejściowych, oferujący wydajność porównywalną z czołowymi modelami światowymi.", "MiniMax-M2-Stable.description": "Zaprojektowany z myślą o wydajnym kodowaniu i przepływach pracy agentów, z większą równoległością dla zastosowań komercyjnych.", + "MiniMax-M2.1-Lightning.description": "Potężne wielojęzyczne możliwości programowania z szybszym i bardziej wydajnym wnioskowaniem.", "MiniMax-M2.1-highspeed.description": "Potężne wielojęzyczne możliwości programistyczne, kompleksowo ulepszone doświadczenie programowania. Szybszy i bardziej wydajny.", "MiniMax-M2.1.description": "MiniMax-M2.1 to flagowy, otwartoźródłowy model dużej skali od MiniMax, zaprojektowany do rozwiązywania złożonych zadań rzeczywistych. Jego główne atuty to wielojęzyczne możliwości programistyczne oraz zdolność działania jako Agent do rozwiązywania skomplikowanych problemów.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: Ta sama wydajność co M2.5, ale z szybszym wnioskowaniem.", @@ -314,13 +315,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku to najszybszy i najbardziej kompaktowy model firmy Anthropic, zaprojektowany do natychmiastowych odpowiedzi z szybką i dokładną wydajnością.", "claude-3-opus-20240229.description": "Claude 3 Opus to najpotężniejszy model firmy Anthropic do bardzo złożonych zadań, wyróżniający się wydajnością, inteligencją, płynnością i zrozumieniem.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet łączy inteligencję i szybkość dla obciążeń korporacyjnych, oferując wysoką użyteczność przy niższych kosztach i niezawodnym wdrażaniu na dużą skalę.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 to najszybszy i najinteligentniejszy model Haiku firmy Anthropic, oferujący błyskawiczną prędkość i rozszerzone możliwości rozumowania.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 to najszybszy i najbardziej inteligentny model Haiku od Anthropic, oferujący błyskawiczną prędkość i rozszerzone myślenie.", "claude-haiku-4-5.description": "Claude Haiku 4.5 by Anthropic — model nowej generacji Haiku z ulepszonym rozumowaniem i wizją.", "claude-haiku-4.5.description": "Claude Haiku 4.5 to najszybszy i najinteligentniejszy model Haiku firmy Anthropic, charakteryzujący się błyskawiczną szybkością i rozszerzonym rozumowaniem.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking to zaawansowany wariant, który może ujawniać swój proces rozumowania.", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1 to najnowszy i najbardziej zaawansowany model firmy Anthropic do wysoce złożonych zadań, wyróżniający się wydajnością, inteligencją, płynnością i zrozumieniem.", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1 to najnowszy i najbardziej zaawansowany model Anthropic do wysoce złożonych zadań, wyróżniający się wydajnością, inteligencją, płynnością i zrozumieniem.", "claude-opus-4-1.description": "Claude Opus 4.1 by Anthropic — model premium do rozumowania z głębokimi możliwościami analizy.", - "claude-opus-4-20250514.description": "Claude Opus 4 to najpotężniejszy model firmy Anthropic do wysoce złożonych zadań, wyróżniający się wydajnością, inteligencją, płynnością i zrozumieniem.", + "claude-opus-4-20250514.description": "Claude Opus 4 to najpotężniejszy model Anthropic do wysoce złożonych zadań, wyróżniający się wydajnością, inteligencją, płynnością i zrozumieniem.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 to flagowy model firmy Anthropic, łączący wyjątkową inteligencję z wydajnością na dużą skalę, idealny do złożonych zadań wymagających najwyższej jakości odpowiedzi i rozumowania.", "claude-opus-4-5.description": "Claude Opus 4.5 by Anthropic — flagowy model z najwyższej klasy rozumowaniem i kodowaniem.", "claude-opus-4-6.description": "Claude Opus 4.6 by Anthropic — flagowy model z oknem kontekstowym 1M i zaawansowanym rozumowaniem.", @@ -329,8 +330,8 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 to najbardziej inteligentny model firmy Anthropic do tworzenia agentów i kodowania.", "claude-opus-4.6.description": "Claude Opus 4.6 to najbardziej inteligentny model firmy Anthropic do tworzenia agentów i kodowania.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking może generować natychmiastowe odpowiedzi lub rozszerzone rozumowanie krok po kroku z widocznym procesem.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 potrafi generować niemal natychmiastowe odpowiedzi lub rozbudowane, krok po kroku przemyślenia z widocznym procesem.", - "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 to najbardziej inteligentny model firmy Anthropic do tej pory.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 to najbardziej inteligentny model Anthropic do tej pory, oferujący niemal natychmiastowe odpowiedzi lub rozszerzone, krok po kroku myślenie z precyzyjną kontrolą dla użytkowników API.", + "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 to najbardziej inteligentny model Anthropic do tej pory.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 by Anthropic — ulepszony Sonnet z lepszą wydajnością kodowania.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 by Anthropic — najnowszy Sonnet z lepszym kodowaniem i obsługą narzędzi.", "claude-sonnet-4.5.description": "Claude Sonnet 4.5 to najbardziej inteligentny model firmy Anthropic do tej pory.", @@ -403,7 +404,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) to innowacyjny model oferujący głębokie zrozumienie języka i interakcję.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 to model nowej generacji do rozumowania z silniejszym rozumowaniem złożonym i łańcuchem myśli do zadań wymagających głębokiej analizy.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 to model rozumowania nowej generacji z ulepszonymi zdolnościami do rozwiązywania złożonych problemów i myślenia łańcuchowego.", - "deepseek-chat.description": "Nowy model open-source łączący ogólne zdolności dialogowe i programistyczne. Zachowuje ogólny charakter dialogu modelu czatu oraz silne zdolności kodowania modelu programistycznego, z lepszym dopasowaniem do preferencji. DeepSeek-V2.5 poprawia również pisanie i wykonywanie instrukcji.", + "deepseek-chat.description": "Alias kompatybilności dla trybu bezmyślowego DeepSeek V4 Flash. Przeznaczony do wycofania — użyj zamiast tego DeepSeek V4 Flash.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B to model języka kodu wytrenowany na 2T tokenach (87% kod, 13% tekst chiński/angielski). Wprowadza okno kontekstu 16K i zadania uzupełniania w środku, oferując uzupełnianie kodu na poziomie projektu i wypełnianie fragmentów.", "deepseek-coder-v2.description": "DeepSeek Coder V2 to open-source’owy model kodu MoE, który osiąga wysokie wyniki w zadaniach programistycznych, porównywalne z GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 to open-source’owy model kodu MoE, który osiąga wysokie wyniki w zadaniach programistycznych, porównywalne z GPT-4 Turbo.", @@ -425,7 +426,7 @@ "deepseek-r1-fast-online.description": "Szybka pełna wersja DeepSeek R1 z wyszukiwaniem w czasie rzeczywistym, łącząca możliwości modelu 671B z szybszymi odpowiedziami.", "deepseek-r1-online.description": "Pełna wersja DeepSeek R1 z 671 miliardami parametrów i wyszukiwaniem w czasie rzeczywistym, oferująca lepsze rozumienie i generowanie.", "deepseek-r1.description": "DeepSeek-R1 wykorzystuje dane startowe przed RL i osiąga wyniki porównywalne z OpenAI-o1 w zadaniach matematycznych, programistycznych i logicznych.", - "deepseek-reasoner.description": "Model rozumowania DeepSeek skoncentrowany na złożonych zadaniach logicznego rozumowania.", + "deepseek-reasoner.description": "Alias kompatybilności dla trybu myślowego DeepSeek V4 Flash. Przeznaczony do wycofania — użyj zamiast tego DeepSeek V4 Flash.", "deepseek-v2.description": "DeepSeek V2 to wydajny model MoE zoptymalizowany pod kątem efektywności kosztowej.", "deepseek-v2:236b.description": "DeepSeek V2 236B to model skoncentrowany na kodzie, oferujący zaawansowane generowanie kodu.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 to model MoE z 671 miliardami parametrów, wyróżniający się w programowaniu, rozumieniu kontekstu i obsłudze długich tekstów.", @@ -490,6 +491,8 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 to model generowania obrazów od ByteDance Seed, obsługujący wejścia tekstowe i obrazowe z wysoką kontrolą i jakością. Generuje obrazy na podstawie tekstowych promptów.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 to najnowszy multimodalny model obrazu ByteDance, integrujący funkcje tekst-do-obrazu, obraz-do-obrazu i generowanie obrazów w partiach, jednocześnie uwzględniając zdrowy rozsądek i zdolności rozumowania. W porównaniu do poprzedniej wersji 4.0 oferuje znacznie lepszą jakość generowania, lepszą spójność edycji i fuzję wielu obrazów. Zapewnia bardziej precyzyjną kontrolę nad szczegółami wizualnymi, naturalnie generując małe teksty i twarze, a także osiąga bardziej harmonijny układ i kolorystykę, poprawiając estetykę ogólną.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite to najnowszy model generowania obrazów ByteDance. Po raz pierwszy integruje funkcje wyszukiwania online, co pozwala na uwzględnienie informacji w czasie rzeczywistym i poprawę aktualności generowanych obrazów. Inteligencja modelu została również ulepszona, umożliwiając precyzyjną interpretację złożonych instrukcji i treści wizualnych. Dodatkowo oferuje lepsze pokrycie globalnej wiedzy, spójność odniesień i jakość generowania w profesjonalnych scenariuszach, lepiej spełniając potrzeby wizualnej kreacji na poziomie przedsiębiorstw.", + "dreamina-seedance-2-0-260128.description": "Seedance 2.0 od ByteDance to najpotężniejszy model generowania wideo, obsługujący multimodalne generowanie wideo referencyjnego, edycję wideo, rozszerzanie wideo, tekst na wideo oraz obraz na wideo z zsynchronizowanym dźwiękiem.", + "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast od ByteDance oferuje te same możliwości co Seedance 2.0, ale z szybszymi prędkościami generowania i bardziej konkurencyjną ceną.", "emohaa.description": "Emohaa to model zdrowia psychicznego z profesjonalnymi umiejętnościami doradczymi, pomagający użytkownikom zrozumieć problemy emocjonalne.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B to lekki model open-source przeznaczony do lokalnego i dostosowanego wdrażania.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview to model podglądowy z kontekstem 8K, służący do oceny możliwości ERNIE 4.5.", @@ -514,7 +517,8 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K to szybki model rozumowania z kontekstem 32K do złożonego rozumowania i dialogów wieloetapowych.", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview to podgląd modelu rozumowania do oceny i testów.", "ernie-x1.1.description": "ERNIE X1.1 to model rozumowania w wersji podglądowej do oceny i testowania.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 to model generowania obrazów firmy ByteDance Seed, obsługujący wejścia tekstowe i obrazowe, z możliwością wysoce kontrolowanego, wysokiej jakości generowania obrazów. Generuje obrazy na podstawie tekstowych wskazówek.", + "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5, stworzony przez zespół ByteDance Seed, obsługuje edycję i kompozycję wielu obrazów. Funkcje obejmują ulepszoną spójność obiektów, precyzyjne wykonywanie instrukcji, zrozumienie logiki przestrzennej, ekspresję estetyczną, układ plakatów i projektowanie logo z wysoką precyzją renderowania tekstu i obrazu.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, stworzony przez ByteDance Seed, obsługuje wejścia tekstowe i obrazowe do wysoce kontrolowanego, wysokiej jakości generowania obrazów na podstawie podpowiedzi.", "fal-ai/flux-kontext/dev.description": "Model FLUX.1 skoncentrowany na edycji obrazów, obsługujący wejścia tekstowe i obrazowe.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] przyjmuje tekst i obrazy referencyjne jako dane wejściowe, umożliwiając lokalne edycje i złożone transformacje sceny.", "fal-ai/flux/krea.description": "Flux Krea [dev] to model generowania obrazów z estetycznym ukierunkowaniem na bardziej realistyczne, naturalne obrazy.", @@ -522,8 +526,8 @@ "fal-ai/hunyuan-image/v3.description": "Potężny natywny model multimodalny do generowania obrazów.", "fal-ai/imagen4/preview.description": "Model generowania obrazów wysokiej jakości od Google.", "fal-ai/nano-banana.description": "Nano Banana to najnowszy, najszybszy i najbardziej wydajny natywny model multimodalny Google, umożliwiający generowanie i edycję obrazów w rozmowie.", - "fal-ai/qwen-image-edit.description": "Profesjonalny model edycji obrazów zespołu Qwen, który obsługuje edycję semantyczną i wyglądu, precyzyjnie edytuje tekst w języku chińskim i angielskim oraz umożliwia wysokiej jakości edycje, takie jak transfer stylu i obrót obiektów.", - "fal-ai/qwen-image.description": "Potężny model generowania obrazów zespołu Qwen z imponującym renderowaniem tekstu w języku chińskim i różnorodnymi stylami wizualnymi.", + "fal-ai/qwen-image-edit.description": "Profesjonalny model edycji obrazów od zespołu Qwen, obsługujący edycje semantyczne i wyglądu, precyzyjną edycję tekstu w języku chińskim/angielskim, transfer stylu, obrót i inne.", + "fal-ai/qwen-image.description": "Potężny model generowania obrazów od zespołu Qwen z silnym renderowaniem tekstu w języku chińskim i różnorodnymi stylami wizualnymi.", "flux-1-schnell.description": "Model tekst-na-obraz z 12 miliardami parametrów od Black Forest Labs, wykorzystujący latent adversarial diffusion distillation do generowania wysokiej jakości obrazów w 1–4 krokach. Dorównuje zamkniętym alternatywom i jest dostępny na licencji Apache-2.0 do użytku osobistego, badawczego i komercyjnego.", "flux-dev.description": "Model generowania obrazów do badań i rozwoju o otwartym kodzie źródłowym, zoptymalizowany pod kątem niekomercyjnych badań innowacyjnych.", "flux-kontext-max.description": "Najnowocześniejsze generowanie i edycja obrazów kontekstowych, łączące tekst i obrazy dla precyzyjnych, spójnych wyników.", @@ -562,10 +566,10 @@ "gemini-3-flash-preview.description": "Gemini 3 Flash to najszybszy i najinteligentniejszy model, łączący najnowsze osiągnięcia AI z doskonałym osadzeniem w wynikach wyszukiwania.", "gemini-3-flash.description": "Gemini 3 Flash by Google — ultraszybki model z obsługą wejść multimodalnych.", "gemini-3-pro-image-preview.description": "Gemini 3 Pro Image (Nano Banana Pro) to model generowania obrazów od Google, który obsługuje również dialogi multimodalne.", - "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) to model generowania obrazów firmy Google, który obsługuje również multimodalny czat.", + "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) to model generowania obrazów od Google, który obsługuje również czat multimodalny.", "gemini-3-pro-preview.description": "Gemini 3 Pro to najpotężniejszy model agenta i kodowania nastrojów od Google, oferujący bogatsze wizualizacje i głębszą interakcję przy zaawansowanym rozumowaniu.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) to najszybszy natywny model generowania obrazów od Google z obsługą myślenia, generowaniem obrazów w rozmowach i edycją.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) to najszybszy natywny model generowania obrazów firmy Google, wspierający myślenie, generowanie obrazów w rozmowie oraz ich edycję.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) oferuje jakość obrazu na poziomie Pro z prędkością Flash i obsługą czatu multimodalnego.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview to najbardziej ekonomiczny model multimodalny Google, zoptymalizowany do zadań agentowych o dużej skali, tłumaczeń i przetwarzania danych.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite to najbardziej opłacalny model multimodalny Google, zoptymalizowany do zadań agentowych o dużej skali, tłumaczeń i przetwarzania danych.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview ulepsza Gemini 3 Pro, oferując lepsze zdolności rozumowania i wsparcie dla średniego poziomu myślenia.", @@ -730,6 +734,8 @@ "grok-4-fast-reasoning.description": "Z radością prezentujemy Grok 4 Fast — nasz najnowszy postęp w dziedzinie modeli rozumowania o wysokiej opłacalności.", "grok-4.20-0309-non-reasoning.description": "Wariant bez rozumowania do prostych przypadków użycia.", "grok-4.20-0309-reasoning.description": "Inteligentny, błyskawiczny model, który rozumuje przed odpowiedzią.", + "grok-4.20-beta-0309-non-reasoning.description": "Wariant bezmyślowy do prostych przypadków użycia.", + "grok-4.20-beta-0309-reasoning.description": "Inteligentny, błyskawiczny model, który rozważa przed udzieleniem odpowiedzi.", "grok-4.20-multi-agent-0309.description": "Zespół 4 lub 16 agentów, doskonały w przypadkach badawczych. Obecnie nie obsługuje narzędzi po stronie klienta. Obsługuje tylko narzędzia po stronie serwera xAI (np. X Search, Web Search tools) i zdalne narzędzia MCP.", "grok-4.3.description": "Najbardziej poszukujący prawdy duży model językowy na świecie", "grok-4.description": "Najnowszy flagowy model Grok z niezrównaną wydajnością w języku, matematyce i rozumowaniu — prawdziwy wszechstronny model. Obecnie wskazuje na grok-4-0709; z powodu ograniczonych zasobów jego cena jest tymczasowo o 10% wyższa od oficjalnej i oczekuje się, że powróci do oficjalnej ceny później.", @@ -1214,6 +1220,8 @@ "qwq.description": "QwQ to model rozumowania z rodziny Qwen. W porównaniu do standardowych modeli dostrojonych instrukcyjnie, oferuje zaawansowane myślenie i rozumowanie, co znacząco poprawia wydajność w zadaniach trudnych. QwQ-32B to model średniej wielkości, konkurujący z czołowymi modelami rozumowania, takimi jak DeepSeek-R1 i o1-mini.", "qwq_32b.description": "Model rozumowania średniej wielkości z rodziny Qwen. W porównaniu do standardowych modeli dostrojonych instrukcyjnie, zdolności myślenia i rozumowania QwQ znacząco poprawiają wydajność w trudnych zadaniach.", "r1-1776.description": "R1-1776 to wariant modelu DeepSeek R1 po dodatkowym treningu, zaprojektowany do dostarczania nieocenzurowanych, bezstronnych informacji faktograficznych.", + "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro od ByteDance obsługuje tekst na wideo, obraz na wideo (pierwsza klatka, pierwsza + ostatnia klatka) oraz generowanie dźwięku zsynchronizowanego z wizualizacjami.", + "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite od BytePlus oferuje generowanie wspomagane wyszukiwaniem w sieci w czasie rzeczywistym, ulepszoną interpretację złożonych podpowiedzi oraz poprawioną spójność odniesień do profesjonalnego tworzenia wizualnego.", "solar-mini-ja.description": "Solar Mini (Ja) rozszerza Solar Mini o nacisk na język japoński, zachowując jednocześnie wydajność w języku angielskim i koreańskim.", "solar-mini.description": "Solar Mini to kompaktowy model LLM, który przewyższa GPT-3.5, oferując silne możliwości wielojęzyczne w języku angielskim i koreańskim oraz efektywne działanie przy małych zasobach.", "solar-pro.description": "Solar Pro to inteligentny model LLM od Upstage, skoncentrowany na wykonywaniu instrukcji na pojedynczym GPU, z wynikami IFEval powyżej 80. Obecnie obsługuje język angielski; pełna wersja z rozszerzonym wsparciem językowym i dłuższym kontekstem planowana jest na listopad 2024.", diff --git a/locales/pl-PL/plugin.json b/locales/pl-PL/plugin.json index 9324ac8db2..9539ebd2b2 100644 --- a/locales/pl-PL/plugin.json +++ b/locales/pl-PL/plugin.json @@ -237,6 +237,14 @@ "builtins.lobe-page-agent.apiName.updateNode": "Zaktualizuj węzeł", "builtins.lobe-page-agent.apiName.wrapNodes": "Zawiń węzły", "builtins.lobe-page-agent.title": "Strona", + "builtins.lobe-self-feedback-intent.apiName.declareSelfFeedbackIntent": "Zapisz pomysł na ulepszenie", + "builtins.lobe-self-feedback-intent.inspector.gap.proposal": "Zaproponuj ulepszenie", + "builtins.lobe-self-feedback-intent.inspector.memory.write": "Zapisz preferencję", + "builtins.lobe-self-feedback-intent.inspector.rejected": "Nie zapisano", + "builtins.lobe-self-feedback-intent.inspector.skill.consolidate": "Zorganizuj metody", + "builtins.lobe-self-feedback-intent.inspector.skill.create": "Znaleziono nową metodę", + "builtins.lobe-self-feedback-intent.inspector.skill.refine": "Udoskonal metodę", + "builtins.lobe-self-feedback-intent.title": "Pomysły na ulepszenia", "builtins.lobe-skill-store.apiName.importFromMarket": "Importuj z Rynku", "builtins.lobe-skill-store.apiName.importSkill": "Importuj Umiejętność", "builtins.lobe-skill-store.apiName.searchSkill": "Wyszukaj Umiejętności", diff --git a/locales/pl-PL/providers.json b/locales/pl-PL/providers.json index c61dde8836..87cb4c8a9c 100644 --- a/locales/pl-PL/providers.json +++ b/locales/pl-PL/providers.json @@ -33,6 +33,7 @@ "jina.description": "Założona w 2020 roku, Jina AI to wiodąca firma zajmująca się wyszukiwaniem AI. Jej stos wyszukiwania obejmuje modele wektorowe, rerankery i małe modele językowe do tworzenia niezawodnych, wysokiej jakości aplikacji generatywnych i multimodalnych.", "kimicodingplan.description": "Kimi Code od Moonshot AI zapewnia dostęp do modeli Kimi, w tym K2.5, do zadań związanych z kodowaniem.", "lmstudio.description": "LM Studio to aplikacja desktopowa do tworzenia i testowania LLM-ów na własnym komputerze.", + "lobehub.description": "LobeHub Cloud korzysta z oficjalnych interfejsów API do uzyskiwania dostępu do modeli AI i mierzy zużycie za pomocą Kredytów powiązanych z tokenami modeli.", "longcat.description": "LongCat to seria dużych modeli generatywnej sztucznej inteligencji, niezależnie opracowanych przez Meituan. Został zaprojektowany, aby zwiększyć produktywność wewnętrzną przedsiębiorstwa i umożliwić innowacyjne zastosowania dzięki wydajnej architekturze obliczeniowej i silnym możliwościom multimodalnym.", "minimax.description": "Założona w 2021 roku, MiniMax tworzy AI ogólnego przeznaczenia z multimodalnymi modelami bazowymi, w tym tekstowymi modelami MoE z bilionami parametrów, modelami mowy i wizji oraz aplikacjami takimi jak Hailuo AI.", "minimaxcodingplan.description": "MiniMax Token Plan zapewnia dostęp do modeli MiniMax, w tym M2.7, do zadań związanych z kodowaniem w ramach subskrypcji o stałej opłacie.", diff --git a/locales/pl-PL/subscription.json b/locales/pl-PL/subscription.json index 0c524987c2..8b1fbbf245 100644 --- a/locales/pl-PL/subscription.json +++ b/locales/pl-PL/subscription.json @@ -41,7 +41,9 @@ "credits.autoTopUp.monthlyLimitDesc": "Maksymalna kwota, która może być automatycznie pobrana w ciągu miesiąca. Pozostaw puste, aby nie ustawiać limitu", "credits.autoTopUp.monthlyLimitPlaceholder": "Brak limitu", "credits.autoTopUp.monthlyTopUpAmount": "Miesięczna kwota doładowania", + "credits.autoTopUp.noCustomerHint": "Kup kredyty raz, aby zapisać metodę płatności przed włączeniem automatycznego doładowania.", "credits.autoTopUp.noPaymentMethodHint": "Brak zapisanej metody płatności. Automatyczne doładowanie wymaga zapisanej karty do automatycznego obciążenia.", + "credits.autoTopUp.purchaseCredits": "Kup Kredyty", "credits.autoTopUp.saveError": "Nie udało się zapisać ustawień automatycznego doładowania", "credits.autoTopUp.saveSuccess": "Ustawienia automatycznego doładowania zapisane", "credits.autoTopUp.setupPaymentMethod": "Dodaj metodę płatności", @@ -83,6 +85,7 @@ "credits.packages.title": "Moje pakiety kredytowe", "credits.topUp.cancel": "Anuluj", "credits.topUp.custom": "Niestandardowe", + "credits.topUp.freeFeeHint": "Doładowania w darmowym planie obejmują opłatę serwisową w wysokości {{fee}} za każde 1M kredytów.", "credits.topUp.maxAmountError": "Jednorazowa kwota zakupu nie może przekroczyć ${{max}}", "credits.topUp.purchaseError": "Zakup nieudany, spróbuj ponownie później", "credits.topUp.purchaseNow": "Kup teraz", @@ -120,6 +123,9 @@ "keyMissMatch.button": "Przywróć użycie i kontynuuj rozmowę", "keyMissMatch.description": "Z powodu sporadycznej awarii systemu, Twoje bieżące użycie subskrypcji jest tymczasowo nieaktywne. Kliknij przycisk poniżej, aby przywrócić użycie i kontynuować rozmowę. Jeśli problem się powtarza, skontaktuj się z nami przez e-mail (support@lobehub.com)", "keyMissMatch.title": "Przywróć użycie subskrypcji teraz", + "limitation.chat.budgetReady.action": "Kontynuuj Rozmowę", + "limitation.chat.budgetReady.desc": "Twoje dostępne kredyty pokrywają teraz to żądanie.", + "limitation.chat.budgetReady.title": "Kredyty Gotowe", "limitation.chat.success.action": "Kontynuuj rozmowę", "limitation.chat.success.desc": "Twoja subskrypcja {{plan}} została pomyślnie zaktualizowana. Ciesz się rozmowami z AI. Twój obecny plan zawiera:", "limitation.chat.success.title": "Ulepszenie zakończone sukcesem", @@ -134,14 +140,23 @@ "limitation.hobby.docs": "Zobacz dokumentację konfiguracji", "limitation.hobby.tip": "Pamiętaj, aby przełączyć się na model z własnym kluczem API", "limitation.hobby.title": "Skonfiguruj API usługi modelu", + "limitation.image.budgetReady.action": "Kontynuuj Generowanie", + "limitation.image.budgetReady.desc": "Twoje dostępne kredyty pokrywają teraz tę generację.", + "limitation.image.budgetReady.title": "Kredyty Gotowe", "limitation.image.success.action": "Kontynuuj generowanie", "limitation.image.success.desc": "Twoja subskrypcja {{plan}} została pomyślnie zaktualizowana. Ciesz się generowaniem obrazów AI. Twój obecny plan zawiera:", "limitation.image.success.title": "Ulepszenie zakończone sukcesem", "limitation.image.topupSuccess.action": "Kontynuuj generowanie", "limitation.image.topupSuccess.desc": "Twoje kredyty doładowujące są teraz aktywne. Ciesz się generowaniem obrazów AI. Twój obecny plan zawiera:", "limitation.image.topupSuccess.title": "Doładowanie zakończone sukcesem", + "limitation.insufficientBudget.approximateDesc": "To żądanie może wymagać więcej kredytów. Doładuj kredyty lub zaktualizuj swój plan.", + "limitation.insufficientBudget.available": "Dostępne Kredyty", "limitation.insufficientBudget.desc": "Twoje pozostałe kredyty nie wystarczają na szacowany koszt tego modelu. Proszę doładować kredyty lub wybrać tańszy model.", + "limitation.insufficientBudget.estimatedDesc": "Szacuje się, że to żądanie wymaga więcej kredytów. Doładuj kredyty lub zaktualizuj swój plan.", + "limitation.insufficientBudget.exactDesc": "To żądanie wymaga więcej kredytów. Doładuj kredyty lub zaktualizuj swój plan.", + "limitation.insufficientBudget.required": "Wymagane Kredyty", "limitation.insufficientBudget.retry": "Spróbuj ponownie", + "limitation.insufficientBudget.shortfall": "Brak Kredytów", "limitation.insufficientBudget.title": "Niewystarczające kredyty dla tego modelu", "limitation.limited.action": "Ulepsz teraz", "limitation.limited.advanceFeature": "Ulepsz, aby korzystać z funkcji premium:", @@ -151,6 +166,7 @@ "limitation.limited.title": "Kredyty obliczeniowe wyczerpane", "limitation.limited.topup": "Doładuj kredyty", "limitation.limited.upgrade": "Ulepsz do wyższego planu", + "limitation.limited.upgradeToPlan": "Zaktualizuj do {{plan}}", "limitation.providers.lock.addNew": "Zasubskrybuj, aby tworzyć własnych dostawców AI", "limitation.providers.lock.enableProvider": "Zasubskrybuj, aby włączyć tego dostawcę AI", "limitation.providers.lock.menuItem": "Zasubskrybuj, aby skonfigurować własną usługę API", @@ -161,6 +177,9 @@ "limitation.providers.prompter.subTitle": "Własna usługa API dostępna jest tylko w płatnych planach. Ulepsz teraz, aby korzystać z globalnych modeli AI", "limitation.providers.prompter.title": "Zasubskrybuj, aby korzystać z własnej usługi API", "limitation.providers.tooltip": "Własna usługa API dostępna tylko w płatnych planach", + "limitation.video.budgetReady.action": "Kontynuuj Generowanie", + "limitation.video.budgetReady.desc": "Twoje dostępne kredyty pokrywają teraz tę generację.", + "limitation.video.budgetReady.title": "Kredyty Gotowe", "limitation.video.success.action": "Kontynuuj generowanie", "limitation.video.success.desc": "Twoja subskrypcja {{plan}} została pomyślnie zaktualizowana. Ciesz się generowaniem wideo AI. Twój aktualny plan obejmuje:", "limitation.video.success.title": "Aktualizacja zakończona sukcesem", diff --git a/locales/pt-BR/chat.json b/locales/pt-BR/chat.json index 9b07c1d67b..bc218fa9e4 100644 --- a/locales/pt-BR/chat.json +++ b/locales/pt-BR/chat.json @@ -196,6 +196,9 @@ "groupWizard.searchTemplates": "Buscar modelos...", "groupWizard.title": "Criar Grupo", "groupWizard.useTemplate": "Usar Modelo", + "heteroAgent.cloudNotConfigured.action": "Configurar", + "heteroAgent.cloudNotConfigured.desc": "Configure seu token Claude Code no perfil do agente para começar a enviar mensagens.", + "heteroAgent.cloudNotConfigured.title": "Credenciais de nuvem necessárias", "heteroAgent.cloudRepo.multiSelected": "{{count}} repositórios selecionados", "heteroAgent.cloudRepo.noRepos": "Nenhum repositório configurado. Adicione-os nas configurações do agente.", "heteroAgent.cloudRepo.notSet": "Nenhum repositório selecionado", @@ -467,6 +470,10 @@ "sharePage.error.unauthorized.subtitle": "Faça login para visualizar este tópico compartilhado.", "sharePage.error.unauthorized.title": "Login Necessário", "sharePageDisclaimer": "Este conteúdo foi compartilhado por um usuário e não representa a opinião da LobeHub. A LobeHub não se responsabiliza por quaisquer consequências decorrentes deste conteúdo compartilhado.", + "signalCallbacks.collapse": "Ocultar detalhes", + "signalCallbacks.empty": "Nenhuma mensagem de callback", + "signalCallbacks.expand": "Mostrar detalhes", + "signalCallbacks.title": "{{tool}} · {{count}} atualizações de callback", "stt.action": "Entrada por Voz", "stt.loading": "Reconhecendo...", "stt.prettifying": "Ajustando...", @@ -689,7 +696,6 @@ "thread.closeSubagentThread": "Recolher conversa do subagente", "thread.divider": "Subtópico", "thread.openSubagentThread": "Ver conversa completa do subagente", - "thread.subagentBadge": "Subagente", "thread.subagentReadOnlyHint": "Conversas de SubAgente são somente leitura — a execução é conduzida pelo agente principal.", "thread.threadMessageCount": "{{messageCount}} mensagens", "thread.title": "Subtópico", diff --git a/locales/pt-BR/models.json b/locales/pt-BR/models.json index 9acde70b09..e6833f6073 100644 --- a/locales/pt-BR/models.json +++ b/locales/pt-BR/models.json @@ -106,6 +106,7 @@ "MiniMax-Hailuo-2.3.description": "Novo modelo de geração de vídeo com melhorias abrangentes em movimento corporal, realismo físico e seguimento de instruções.", "MiniMax-M1.description": "Um novo modelo de raciocínio interno com 80 mil cadeias de pensamento e 1 milhão de tokens de entrada, oferecendo desempenho comparável aos principais modelos globais.", "MiniMax-M2-Stable.description": "Projetado para fluxos de trabalho de codificação e agentes eficientes, com maior concorrência para uso comercial.", + "MiniMax-M2.1-Lightning.description": "Capacidades poderosas de programação multilíngue com inferência mais rápida e eficiente.", "MiniMax-M2.1-highspeed.description": "Poderosas capacidades de programação multilíngue, experiência de programação amplamente aprimorada. Mais rápido e eficiente.", "MiniMax-M2.1.description": "MiniMax-M2.1 é o principal modelo open-source da MiniMax, focado em resolver tarefas complexas do mundo real. Seus principais pontos fortes são as capacidades de programação multilíngue e a habilidade de atuar como um Agente para resolver tarefas complexas.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: Mesmo desempenho do M2.5 com inferência mais rápida.", @@ -314,7 +315,7 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku é o modelo mais rápido e compacto da Anthropic, projetado para respostas quase instantâneas com desempenho rápido e preciso.", "claude-3-opus-20240229.description": "Claude 3 Opus é o modelo mais poderoso da Anthropic para tarefas altamente complexas, com excelência em desempenho, inteligência, fluência e compreensão.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet equilibra inteligência e velocidade para cargas de trabalho empresariais, oferecendo alta utilidade com menor custo e implantação confiável em larga escala.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 é o modelo Haiku mais rápido e inteligente da Anthropic, com velocidade relâmpago e raciocínio ampliado.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 é o modelo Haiku mais rápido e inteligente da Anthropic, com velocidade relâmpago e pensamento ampliado.", "claude-haiku-4-5.description": "Claude Haiku 4.5 da Anthropic — nova geração do Haiku, com raciocínio e visão aprimorados.", "claude-haiku-4.5.description": "Claude Haiku 4.5 é o modelo Haiku mais rápido e inteligente da Anthropic, com velocidade relâmpago e raciocínio ampliado.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking é uma variante avançada que pode revelar seu processo de raciocínio.", @@ -329,7 +330,7 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 é o modelo mais inteligente da Anthropic para criação de agentes e codificação.", "claude-opus-4.6.description": "Claude Opus 4.6 é o modelo mais inteligente da Anthropic para criação de agentes e codificação.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking pode produzir respostas quase instantâneas ou pensamento passo a passo estendido com processo visível.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 pode produzir respostas quase instantâneas ou raciocínio passo a passo detalhado com processo visível.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 é o modelo mais inteligente da Anthropic até o momento, oferecendo respostas quase instantâneas ou pensamento detalhado passo a passo com controle refinado para usuários de API.", "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 é o modelo mais inteligente da Anthropic até o momento.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 da Anthropic — versão aprimorada do Sonnet com desempenho superior em programação.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 da Anthropic — última geração do Sonnet, com alta qualidade em programação e uso de ferramentas.", @@ -403,7 +404,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "O DeepSeek LLM Chat (67B) é um modelo inovador que oferece compreensão profunda da linguagem e interação.", "deepseek-ai/deepseek-v3.1-terminus.description": "O DeepSeek V3.1 é um modelo de raciocínio de nova geração com raciocínio complexo mais forte e cadeia de pensamento para tarefas de análise profunda.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 é um modelo de raciocínio de próxima geração com capacidades mais fortes de raciocínio complexo e cadeia de pensamento.", - "deepseek-chat.description": "Um novo modelo de código aberto que combina habilidades gerais e de codificação. Ele preserva o diálogo geral do modelo de chat e a forte capacidade de codificação do modelo de programador, com melhor alinhamento de preferências. O DeepSeek-V2.5 também melhora a escrita e o seguimento de instruções.", + "deepseek-chat.description": "Alias de compatibilidade para o modo sem pensamento do DeepSeek V4 Flash. Programado para descontinuação — use o DeepSeek V4 Flash.", "deepseek-coder-33B-instruct.description": "O DeepSeek Coder 33B é um modelo de linguagem para código treinado com 2 trilhões de tokens (87% código, 13% texto em chinês/inglês). Introduz uma janela de contexto de 16K e tarefas de preenchimento intermediário, oferecendo preenchimento de código em nível de projeto e inserção de trechos.", "deepseek-coder-v2.description": "O DeepSeek Coder V2 é um modelo de código MoE open-source com forte desempenho em tarefas de programação, comparável ao GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "O DeepSeek Coder V2 é um modelo de código MoE open-source com forte desempenho em tarefas de programação, comparável ao GPT-4 Turbo.", @@ -425,7 +426,7 @@ "deepseek-r1-fast-online.description": "Versão completa e rápida do DeepSeek R1 com busca em tempo real na web, combinando capacidade de 671B com respostas mais ágeis.", "deepseek-r1-online.description": "Versão completa do DeepSeek R1 com 671B de parâmetros e busca em tempo real na web, oferecendo compreensão e geração mais robustas.", "deepseek-r1.description": "O DeepSeek-R1 usa dados de inicialização a frio antes do RL e apresenta desempenho comparável ao OpenAI-o1 em matemática, programação e raciocínio.", - "deepseek-reasoner.description": "Um modelo de raciocínio DeepSeek focado em tarefas complexas de raciocínio lógico.", + "deepseek-reasoner.description": "Alias de compatibilidade para o modo de pensamento do DeepSeek V4 Flash. Programado para descontinuação — use o DeepSeek V4 Flash.", "deepseek-v2.description": "O DeepSeek V2 é um modelo MoE eficiente para processamento econômico.", "deepseek-v2:236b.description": "O DeepSeek V2 236B é o modelo da DeepSeek focado em código com forte geração de código.", "deepseek-v3-0324.description": "O DeepSeek-V3-0324 é um modelo MoE com 671B de parâmetros, com destaque em programação, capacidade técnica, compreensão de contexto e manipulação de textos longos.", @@ -490,6 +491,8 @@ "doubao-seedream-4-0-250828.description": "O Seedream 4.0 é um modelo de geração de imagem da ByteDance Seed, que suporta entradas de texto e imagem com geração de imagem altamente controlável e de alta qualidade. Gera imagens a partir de comandos de texto.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 é o mais recente modelo multimodal de imagem da ByteDance, integrando capacidades de texto-para-imagem, imagem-para-imagem e geração de imagens em lote, enquanto incorpora senso comum e habilidades de raciocínio. Comparado à versão anterior 4.0, oferece qualidade de geração significativamente melhorada, com maior consistência de edição e fusão de múltiplas imagens. Oferece controle mais preciso sobre detalhes visuais, produzindo texto pequeno e rostos pequenos de forma mais natural, além de alcançar layouts e cores mais harmoniosos, melhorando a estética geral.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite é o mais recente modelo de geração de imagens da ByteDance. Pela primeira vez, integra capacidades de recuperação online, permitindo incorporar informações da web em tempo real e melhorar a atualidade das imagens geradas. A inteligência do modelo também foi aprimorada, permitindo interpretação precisa de instruções complexas e conteúdo visual. Além disso, oferece melhor cobertura de conhecimento global, consistência de referência e qualidade de geração em cenários profissionais, atendendo melhor às necessidades de criação visual em nível empresarial.", + "dreamina-seedance-2-0-260128.description": "Seedance 2.0 da ByteDance é o modelo de geração de vídeo mais poderoso, suportando geração de vídeo multimodal com referência, edição de vídeo, extensão de vídeo, texto para vídeo e imagem para vídeo com áudio sincronizado.", + "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast da ByteDance oferece as mesmas capacidades do Seedance 2.0 com velocidades de geração mais rápidas a um preço mais competitivo.", "emohaa.description": "O Emohaa é um modelo voltado para saúde mental com habilidades profissionais de aconselhamento para ajudar os usuários a compreender questões emocionais.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B é um modelo leve de código aberto para implantação local e personalizada.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview é um modelo de pré-visualização com contexto de 8K para avaliação do ERNIE 4.5.", @@ -514,7 +517,8 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K é um modelo de raciocínio rápido com contexto de 32K para raciocínio complexo e bate-papo de múltiplas interações.", "ernie-x1.1-preview.description": "Pré-visualização do modelo de raciocínio ERNIE X1.1 para avaliação e testes.", "ernie-x1.1.description": "ERNIE X1.1 é um modelo de pensamento em pré-visualização para avaliação e testes.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 é um modelo de geração de imagens da ByteDance Seed, que suporta entradas de texto e imagem com geração de imagens altamente controlável e de alta qualidade. Ele gera imagens a partir de prompts de texto.", + "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5, desenvolvido pela equipe Seed da ByteDance, suporta edição e composição de múltiplas imagens. Apresenta consistência aprimorada de objetos, seguimento preciso de instruções, compreensão de lógica espacial, expressão estética, layout de pôster e design de logotipo com renderização de texto-imagem de alta precisão.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, desenvolvido pela ByteDance Seed, suporta entradas de texto e imagem para geração altamente controlável e de alta qualidade a partir de prompts.", "fal-ai/flux-kontext/dev.description": "Modelo FLUX.1 focado em edição de imagens, com suporte a entradas de texto e imagem.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] aceita texto e imagens de referência como entrada, permitindo edições locais direcionadas e transformações complexas de cena.", "fal-ai/flux/krea.description": "Flux Krea [dev] é um modelo de geração de imagens com viés estético para imagens mais realistas e naturais.", @@ -522,8 +526,8 @@ "fal-ai/hunyuan-image/v3.description": "Um poderoso modelo multimodal nativo de geração de imagens.", "fal-ai/imagen4/preview.description": "Modelo de geração de imagens de alta qualidade do Google.", "fal-ai/nano-banana.description": "Nano Banana é o modelo multimodal nativo mais novo, rápido e eficiente do Google, permitindo geração e edição de imagens por meio de conversas.", - "fal-ai/qwen-image-edit.description": "Um modelo profissional de edição de imagens da equipe Qwen que suporta edições semânticas e de aparência, edita texto em chinês e inglês com precisão e permite edições de alta qualidade, como transferência de estilo e rotação de objetos.", - "fal-ai/qwen-image.description": "Um modelo poderoso de geração de imagens da equipe Qwen com renderização impressionante de texto em chinês e estilos visuais diversificados.", + "fal-ai/qwen-image-edit.description": "Um modelo profissional de edição de imagens da equipe Qwen, suportando edições semânticas e de aparência, edição precisa de texto em chinês/inglês, transferência de estilo, rotação e mais.", + "fal-ai/qwen-image.description": "Um modelo poderoso de geração de imagens da equipe Qwen com forte renderização de texto em chinês e estilos visuais diversificados.", "flux-1-schnell.description": "Modelo de texto para imagem com 12 bilhões de parâmetros da Black Forest Labs, usando difusão adversarial latente para gerar imagens de alta qualidade em 1 a 4 etapas. Rivaliza com alternativas fechadas e é lançado sob licença Apache-2.0 para uso pessoal, acadêmico e comercial.", "flux-dev.description": "Modelo open-source de geração de imagens para P&D, otimizado de forma eficiente para pesquisa inovadora não comercial.", "flux-kontext-max.description": "Geração e edição de imagens contextuais de última geração, combinando texto e imagens para resultados precisos e coerentes.", @@ -565,7 +569,7 @@ "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) é o modelo de geração de imagens do Google e também suporta chat multimodal.", "gemini-3-pro-preview.description": "Gemini 3 Pro é o agente mais poderoso do Google, com capacidades de codificação emocional e visuais aprimoradas, além de raciocínio de última geração.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) é o modelo de geração de imagens nativo mais rápido do Google, com suporte a raciocínio, geração e edição de imagens conversacionais.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) é o modelo nativo de geração de imagens mais rápido do Google, com suporte a raciocínio, geração e edição de imagens conversacionais.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) oferece qualidade de imagem em nível Pro com velocidade Flash e suporte a chat multimodal.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview é o modelo multimodal mais econômico do Google, otimizado para tarefas agentivas de alto volume, tradução e processamento de dados.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite é o modelo multimodal mais econômico do Google, otimizado para tarefas agentivas de alto volume, tradução e processamento de dados.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview melhora o Gemini 3 Pro com capacidades de raciocínio aprimoradas e adiciona suporte a nível médio de pensamento.", @@ -730,6 +734,8 @@ "grok-4-fast-reasoning.description": "Estamos entusiasmados em lançar o Grok 4 Fast, nosso mais recente avanço em modelos de raciocínio com ótimo custo-benefício.", "grok-4.20-0309-non-reasoning.description": "Variante sem raciocínio para casos de uso simples.", "grok-4.20-0309-reasoning.description": "Modelo inteligente e extremamente rápido que raciocina antes de responder.", + "grok-4.20-beta-0309-non-reasoning.description": "Uma variante sem raciocínio para casos de uso simples.", + "grok-4.20-beta-0309-reasoning.description": "Modelo inteligente e extremamente rápido que raciocina antes de responder.", "grok-4.20-multi-agent-0309.description": "Equipe de 4 ou 16 agentes. Excelente para pesquisas, sem suporte atual a ferramentas do lado do cliente. Suporta apenas ferramentas do servidor xAI (como X Search e Web Search) e ferramentas MCP remotas.", "grok-4.3.description": "O modelo de linguagem de grande porte mais comprometido com a verdade no mundo.", "grok-4.description": "O mais recente modelo Grok de ponta com desempenho incomparável em linguagem, matemática e raciocínio — um verdadeiro polivalente. Atualmente aponta para o grok-4-0709; devido a recursos limitados, está temporariamente 10% acima do preço oficial e espera-se que retorne ao preço oficial posteriormente.", @@ -1214,6 +1220,8 @@ "qwq.description": "QwQ é um modelo de raciocínio da família Qwen. Em comparação com modelos ajustados por instruções padrão, oferece habilidades de pensamento e raciocínio que melhoram significativamente o desempenho em tarefas difíceis. O QwQ-32B é um modelo de porte médio que compete com os principais modelos como DeepSeek-R1 e o1-mini.", "qwq_32b.description": "Modelo de raciocínio de porte médio da família Qwen. Em comparação com modelos ajustados por instruções padrão, as habilidades de pensamento e raciocínio do QwQ aumentam significativamente o desempenho em tarefas difíceis.", "r1-1776.description": "R1-1776 é uma variante pós-treinada do DeepSeek R1 projetada para fornecer informações factuais sem censura e imparciais.", + "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro da ByteDance suporta texto para vídeo, imagem para vídeo (primeiro quadro, primeiro+último quadro) e geração de áudio sincronizado com visuais.", + "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite da BytePlus apresenta geração aumentada por recuperação na web para informações em tempo real, interpretação aprimorada de prompts complexos e consistência melhorada de referências para criação visual profissional.", "solar-mini-ja.description": "Solar Mini (Ja) estende o Solar Mini com foco no japonês, mantendo desempenho eficiente e forte em inglês e coreano.", "solar-mini.description": "Solar Mini é um LLM compacto que supera o GPT-3.5, com forte capacidade multilíngue suportando inglês e coreano, oferecendo uma solução eficiente e de baixo custo.", "solar-pro.description": "Solar Pro é um LLM de alta inteligência da Upstage, focado em seguir instruções em uma única GPU, com pontuações IFEval acima de 80. Atualmente suporta inglês; o lançamento completo está previsto para novembro de 2024 com suporte expandido a idiomas e contexto mais longo.", diff --git a/locales/pt-BR/plugin.json b/locales/pt-BR/plugin.json index 3d82d3c1cc..a47ef38b6a 100644 --- a/locales/pt-BR/plugin.json +++ b/locales/pt-BR/plugin.json @@ -237,6 +237,14 @@ "builtins.lobe-page-agent.apiName.updateNode": "Atualizar nó", "builtins.lobe-page-agent.apiName.wrapNodes": "Agrupar nós", "builtins.lobe-page-agent.title": "Página", + "builtins.lobe-self-feedback-intent.apiName.declareSelfFeedbackIntent": "Registrar ideia de melhoria", + "builtins.lobe-self-feedback-intent.inspector.gap.proposal": "Sugerir melhoria", + "builtins.lobe-self-feedback-intent.inspector.memory.write": "Registrar preferência", + "builtins.lobe-self-feedback-intent.inspector.rejected": "Não registrado", + "builtins.lobe-self-feedback-intent.inspector.skill.consolidate": "Organizar métodos", + "builtins.lobe-self-feedback-intent.inspector.skill.create": "Novo método encontrado", + "builtins.lobe-self-feedback-intent.inspector.skill.refine": "Aprimorar método", + "builtins.lobe-self-feedback-intent.title": "Ideias de Melhoria", "builtins.lobe-skill-store.apiName.importFromMarket": "Importar do Mercado", "builtins.lobe-skill-store.apiName.importSkill": "Importar Habilidade", "builtins.lobe-skill-store.apiName.searchSkill": "Buscar Habilidades", diff --git a/locales/pt-BR/providers.json b/locales/pt-BR/providers.json index e45fb62c70..3df739cec1 100644 --- a/locales/pt-BR/providers.json +++ b/locales/pt-BR/providers.json @@ -33,6 +33,7 @@ "jina.description": "Fundada em 2020, a Jina AI é uma empresa líder em busca com IA. Sua pilha de busca inclui modelos vetoriais, reranqueadores e pequenos modelos de linguagem para construir aplicativos generativos e multimodais confiáveis e de alta qualidade.", "kimicodingplan.description": "O Kimi Code da Moonshot AI oferece acesso aos modelos Kimi, incluindo o K2.5, para tarefas de codificação.", "lmstudio.description": "O LM Studio é um aplicativo de desktop para desenvolver e experimentar com LLMs no seu computador.", + "lobehub.description": "O LobeHub Cloud utiliza APIs oficiais para acessar modelos de IA e mede o uso com Créditos vinculados aos tokens dos modelos.", "longcat.description": "LongCat é uma série de grandes modelos de IA generativa desenvolvidos de forma independente pela Meituan. Ele foi projetado para aumentar a produtividade interna da empresa e possibilitar aplicações inovadoras por meio de uma arquitetura computacional eficiente e fortes capacidades multimodais.", "minimax.description": "Fundada em 2021, a MiniMax desenvolve IA de uso geral com modelos fundamentais multimodais, incluindo modelos de texto com trilhões de parâmetros, modelos de fala e visão, além de aplicativos como o Hailuo AI.", "minimaxcodingplan.description": "O Plano de Tokens MiniMax oferece acesso aos modelos MiniMax, incluindo o M2.7, para tarefas de codificação por meio de uma assinatura de taxa fixa.", diff --git a/locales/pt-BR/subscription.json b/locales/pt-BR/subscription.json index d986c6ee0b..6ede84ffce 100644 --- a/locales/pt-BR/subscription.json +++ b/locales/pt-BR/subscription.json @@ -41,7 +41,9 @@ "credits.autoTopUp.monthlyLimitDesc": "Valor máximo que pode ser cobrado automaticamente por mês. Deixe vazio para sem limite", "credits.autoTopUp.monthlyLimitPlaceholder": "Sem limite", "credits.autoTopUp.monthlyTopUpAmount": "Valor de Recarga Mensal", + "credits.autoTopUp.noCustomerHint": "Compre créditos uma vez para salvar um método de pagamento antes de ativar a recarga automática.", "credits.autoTopUp.noPaymentMethodHint": "Nenhum método de pagamento registrado. A recarga automática precisa de um cartão salvo para cobrar automaticamente.", + "credits.autoTopUp.purchaseCredits": "Comprar Créditos", "credits.autoTopUp.saveError": "Falha ao salvar as configurações de recarga automática", "credits.autoTopUp.saveSuccess": "Configurações de recarga automática salvas", "credits.autoTopUp.setupPaymentMethod": "Adicionar Método de Pagamento", @@ -83,6 +85,7 @@ "credits.packages.title": "Meus Pacotes de Créditos", "credits.topUp.cancel": "Cancelar", "credits.topUp.custom": "Personalizado", + "credits.topUp.freeFeeHint": "Recargas do plano gratuito incluem uma taxa de serviço de {{fee}} por 1M de créditos.", "credits.topUp.maxAmountError": "O valor de compra única não pode exceder ${{max}}", "credits.topUp.purchaseError": "Falha na compra, tente novamente mais tarde", "credits.topUp.purchaseNow": "Comprar Agora", @@ -120,6 +123,9 @@ "keyMissMatch.button": "Restaurar uso e continuar conversa", "keyMissMatch.description": "Devido a uma falha ocasional do sistema, o uso da sua assinatura atual está temporariamente inativo. Clique no botão abaixo para restaurar o uso e continuar a conversa. Se isso acontecer repetidamente, entre em contato conosco por e-mail (support@lobehub.com)", "keyMissMatch.title": "Restaurar Uso da Assinatura Agora", + "limitation.chat.budgetReady.action": "Continuar Conversando", + "limitation.chat.budgetReady.desc": "Seus créditos disponíveis agora cobrem esta solicitação.", + "limitation.chat.budgetReady.title": "Créditos Disponíveis", "limitation.chat.success.action": "Continuar Conversando", "limitation.chat.success.desc": "Sua assinatura {{plan}} foi atualizada com sucesso. Aproveite as conversas com IA. Seu plano atual inclui:", "limitation.chat.success.title": "Atualização Bem-sucedida", @@ -134,14 +140,23 @@ "limitation.hobby.docs": "Ver documentação de configuração", "limitation.hobby.tip": "Lembre-se de mudar para um modelo com chave de API personalizada", "limitation.hobby.title": "Configure a API do Serviço de Modelo", + "limitation.image.budgetReady.action": "Continuar Gerando", + "limitation.image.budgetReady.desc": "Seus créditos disponíveis agora cobrem esta geração.", + "limitation.image.budgetReady.title": "Créditos Disponíveis", "limitation.image.success.action": "Continuar Gerando", "limitation.image.success.desc": "Sua assinatura {{plan}} foi atualizada com sucesso. Aproveite a geração de imagens com IA. Seu plano atual inclui:", "limitation.image.success.title": "Atualização Bem-sucedida", "limitation.image.topupSuccess.action": "Continuar Gerando", "limitation.image.topupSuccess.desc": "Seus créditos adicionais estão ativos. Aproveite a geração de imagens com IA. Seu plano atual inclui:", "limitation.image.topupSuccess.title": "Créditos Adicionados com Sucesso", + "limitation.insufficientBudget.approximateDesc": "Esta solicitação pode precisar de mais créditos. Recarregue créditos ou atualize seu plano.", + "limitation.insufficientBudget.available": "Créditos Disponíveis", "limitation.insufficientBudget.desc": "Seus créditos restantes não são suficientes para o custo estimado deste modelo. Por favor, adicione mais créditos ou mude para um modelo menos caro.", + "limitation.insufficientBudget.estimatedDesc": "Estima-se que esta solicitação precise de mais créditos. Recarregue créditos ou atualize seu plano.", + "limitation.insufficientBudget.exactDesc": "Esta solicitação precisa de mais créditos. Recarregue créditos ou atualize seu plano.", + "limitation.insufficientBudget.required": "Créditos Necessários", "limitation.insufficientBudget.retry": "Tentar novamente", + "limitation.insufficientBudget.shortfall": "Déficit de Créditos", "limitation.insufficientBudget.title": "Créditos Insuficientes para Este Modelo", "limitation.limited.action": "Atualizar Agora", "limitation.limited.advanceFeature": "Atualize para aproveitar recursos premium:", @@ -151,6 +166,7 @@ "limitation.limited.title": "Créditos de Computação Esgotados", "limitation.limited.topup": "Adicionar Créditos", "limitation.limited.upgrade": "Atualizar para Plano Superior", + "limitation.limited.upgradeToPlan": "Atualize para {{plan}}", "limitation.providers.lock.addNew": "Assine agora para criar provedores de IA personalizados", "limitation.providers.lock.enableProvider": "Assine agora para ativar este provedor de IA", "limitation.providers.lock.menuItem": "Assine agora para configurar serviço de API personalizado", @@ -161,6 +177,9 @@ "limitation.providers.prompter.subTitle": "O serviço de API personalizado está disponível apenas para planos pagos. Atualize agora para aproveitar os principais serviços de modelo do mundo", "limitation.providers.prompter.title": "Assine agora para usar serviço de API personalizado", "limitation.providers.tooltip": "O serviço de API personalizado está disponível apenas para planos pagos", + "limitation.video.budgetReady.action": "Continuar Gerando", + "limitation.video.budgetReady.desc": "Seus créditos disponíveis agora cobrem esta geração.", + "limitation.video.budgetReady.title": "Créditos Disponíveis", "limitation.video.success.action": "Continuar Gerando", "limitation.video.success.desc": "Sua assinatura {{plan}} foi atualizada com sucesso. Aproveite a geração de vídeos com IA. Seu plano atual inclui:", "limitation.video.success.title": "Atualização Bem-sucedida", diff --git a/locales/ru-RU/chat.json b/locales/ru-RU/chat.json index fefb9a38cf..b1dfb31f2a 100644 --- a/locales/ru-RU/chat.json +++ b/locales/ru-RU/chat.json @@ -196,6 +196,9 @@ "groupWizard.searchTemplates": "Поиск шаблонов...", "groupWizard.title": "Создать группу", "groupWizard.useTemplate": "Использовать шаблон", + "heteroAgent.cloudNotConfigured.action": "Настроить", + "heteroAgent.cloudNotConfigured.desc": "Настройте токен Claude Code в профиле агента, чтобы начать отправлять сообщения.", + "heteroAgent.cloudNotConfigured.title": "Требуются учетные данные облака", "heteroAgent.cloudRepo.multiSelected": "Выбрано {{count}} репозиториев", "heteroAgent.cloudRepo.noRepos": "Репозитории не настроены. Добавьте их в настройках агента.", "heteroAgent.cloudRepo.notSet": "Репозиторий не выбран", @@ -467,6 +470,10 @@ "sharePage.error.unauthorized.subtitle": "Пожалуйста, войдите в систему, чтобы просмотреть эту тему.", "sharePage.error.unauthorized.title": "Требуется вход", "sharePageDisclaimer": "Этот контент был опубликован пользователем и не отражает точку зрения LobeHub. LobeHub не несёт ответственности за последствия, вызванные этим контентом.", + "signalCallbacks.collapse": "Скрыть детали", + "signalCallbacks.empty": "Нет сообщений обратного вызова", + "signalCallbacks.expand": "Показать детали", + "signalCallbacks.title": "{{tool}} · {{count}} обновлений обратного вызова", "stt.action": "Голосовой ввод", "stt.loading": "Распознавание...", "stt.prettifying": "Обработка...", @@ -689,7 +696,6 @@ "thread.closeSubagentThread": "Свернуть диалог с субагентом", "thread.divider": "Подтема", "thread.openSubagentThread": "Показать весь диалог с субагентом", - "thread.subagentBadge": "Субагент", "thread.subagentReadOnlyHint": "Разговоры с субагентом доступны только для чтения — выполнение управляется основным агентом.", "thread.threadMessageCount": "{{messageCount}} сообщений", "thread.title": "Подтема", diff --git a/locales/ru-RU/models.json b/locales/ru-RU/models.json index 38c1fe0a56..101e45b4b2 100644 --- a/locales/ru-RU/models.json +++ b/locales/ru-RU/models.json @@ -106,6 +106,7 @@ "MiniMax-Hailuo-2.3.description": "Совершенно новая модель генерации видео с комплексными улучшениями в движении тела, физическом реализме и следовании инструкциям.", "MiniMax-M1.description": "Новая внутренняя модель рассуждений с поддержкой 80K цепочек размышлений и 1M входных токенов, обеспечивающая производительность на уровне ведущих мировых моделей.", "MiniMax-M2-Stable.description": "Создана для эффективного программирования и работы агентов, с повышенной параллельностью для коммерческого использования.", + "MiniMax-M2.1-Lightning.description": "Мощные многоязычные возможности программирования с более быстрым и эффективным выводом.", "MiniMax-M2.1-highspeed.description": "Мощные многоязычные программные возможности, всесторонне улучшенный опыт программирования. Быстрее и эффективнее.", "MiniMax-M2.1.description": "MiniMax-M2.1 — это флагманская модель с открытым исходным кодом от MiniMax, ориентированная на решение сложных задач из реального мира. Её ключевые преимущества — поддержка многозадачного программирования и способность выступать в роли интеллектуального агента.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: Та же производительность, что и у M2.5, но с ускоренным выводом.", @@ -314,13 +315,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku — самая быстрая и компактная модель от Anthropic, предназначенная для мгновенных ответов с высокой точностью и скоростью.", "claude-3-opus-20240229.description": "Claude 3 Opus — самая мощная модель от Anthropic для высокосложных задач, превосходящая по производительности, интеллекту, беглости и пониманию.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet сочетает интеллект и скорость для корпоративных задач, обеспечивая высокую полезность при низкой стоимости и надежное масштабируемое развертывание.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 — самая быстрая и умная модель Haiku от Anthropic, с молниеносной скоростью и расширенными возможностями рассуждения.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 — самая быстрая и интеллектуальная модель Haiku от Anthropic, с молниеносной скоростью и расширенным мышлением.", "claude-haiku-4-5.description": "Claude Haiku 4.5 от Anthropic — модель нового поколения с улучшенными возможностями рассуждения и работы с изображениями.", "claude-haiku-4.5.description": "Claude Haiku 4.5 — это самая быстрая и умная модель Haiku от Anthropic, с молниеносной скоростью и расширенными возможностями рассуждения.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking — продвинутая версия, способная демонстрировать процесс рассуждения.", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1 — новейшая и самая мощная модель от Anthropic для выполнения сложных задач, превосходящая в производительности, интеллекте, беглости и понимании.", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1 — последняя и самая мощная модель Anthropic для выполнения сложных задач, превосходящая в производительности, интеллекте, беглости и понимании.", "claude-opus-4-1.description": "Claude Opus 4.1 от Anthropic — премиальная модель рассуждения с глубокими аналитическими возможностями.", - "claude-opus-4-20250514.description": "Claude Opus 4 — самая мощная модель от Anthropic для выполнения сложных задач, превосходящая в производительности, интеллекте, беглости и понимании.", + "claude-opus-4-20250514.description": "Claude Opus 4 — самая мощная модель Anthropic для выполнения сложных задач, превосходящая в производительности, интеллекте, беглости и понимании.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 — флагманская модель от Anthropic, сочетающая выдающийся интеллект с масштабируемой производительностью, идеально подходящая для сложных задач, требующих высококачественных ответов и рассуждений.", "claude-opus-4-5.description": "Claude Opus 4.5 от Anthropic — флагманская модель с передовыми возможностями рассуждения и программирования.", "claude-opus-4-6.description": "Claude Opus 4.6 от Anthropic — флагманская модель с контекстом 1M и усовершенствованными возможностями рассуждения.", @@ -329,8 +330,8 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 — это самая интеллектуальная модель Anthropic для создания агентов и программирования.", "claude-opus-4.6.description": "Claude Opus 4.6 — это самая интеллектуальная модель Anthropic для создания агентов и программирования.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking может выдавать как мгновенные ответы, так и пошаговое рассуждение с видимым процессом.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 может выдавать почти мгновенные ответы или пошаговые рассуждения с видимым процессом.", - "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 — самая интеллектуальная модель от Anthropic на сегодняшний день.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 — самая интеллектуальная модель Anthropic на сегодняшний день, предлагающая мгновенные ответы или пошаговое мышление с тонкой настройкой для пользователей API.", + "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 — самая интеллектуальная модель Anthropic на сегодняшний день.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 от Anthropic — улучшенная модель Sonnet с повышенной производительностью в программировании.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 от Anthropic — последняя версия Sonnet с превосходными возможностями в программировании и использовании инструментов.", "claude-sonnet-4.5.description": "Claude Sonnet 4.5 — это самая интеллектуальная модель Anthropic на сегодняшний день.", @@ -403,7 +404,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) — инновационная модель с глубоким пониманием языка и возможностью взаимодействия.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 — модель нового поколения для рассуждений, обладающая улучшенными возможностями для сложных рассуждений и цепочек размышлений, подходящая для задач глубокого анализа.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 — это модель рассуждений следующего поколения с улучшенными возможностями сложных рассуждений и цепочки размышлений.", - "deepseek-chat.description": "Новая модель с открытым исходным кодом, объединяющая общие и кодовые возможности. Она сохраняет общий диалоговый стиль модели чата и сильные навыки кодирования, с улучшенным выравниванием предпочтений. DeepSeek-V2.5 также улучшает навыки письма и следование инструкциям.", + "deepseek-chat.description": "Алиас совместимости для режима без мышления DeepSeek V4 Flash. Планируется к устареванию — используйте DeepSeek V4 Flash.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B — языковая модель для программирования, обученная на 2 триллионах токенов (87% кода, 13% китайского/английского текста). Поддерживает контекстное окно 16K и задачи заполнения в середине, обеспечивая автодополнение на уровне проекта и вставку фрагментов кода.", "deepseek-coder-v2.description": "DeepSeek Coder V2 — модель кода с открытым исходным кодом, демонстрирующая высокую производительность в задачах программирования, сопоставимую с GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 — модель кода с открытым исходным кодом, демонстрирующая высокую производительность в задачах программирования, сопоставимую с GPT-4 Turbo.", @@ -425,7 +426,7 @@ "deepseek-r1-fast-online.description": "Быстрая полная версия DeepSeek R1 с поиском в интернете в реальном времени, объединяющая возможности масштаба 671B и ускоренный отклик.", "deepseek-r1-online.description": "Полная версия DeepSeek R1 с 671B параметрами и поиском в интернете в реальном времени, обеспечивающая улучшенное понимание и генерацию.", "deepseek-r1.description": "DeepSeek-R1 использует данные холодного старта до этапа RL и демонстрирует сопоставимую с OpenAI-o1 производительность в математике, программировании и логическом мышлении.", - "deepseek-reasoner.description": "Модель DeepSeek, ориентированная на выполнение сложных логических задач.", + "deepseek-reasoner.description": "Алиас совместимости для режима мышления DeepSeek V4 Flash. Планируется к устареванию — используйте DeepSeek V4 Flash.", "deepseek-v2.description": "DeepSeek V2 — эффективная модель MoE для экономичной обработки.", "deepseek-v2:236b.description": "DeepSeek V2 236B — модель DeepSeek, ориентированная на программирование, с высокой способностью к генерации кода.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 — модель MoE с 671B параметрами, выделяющаяся в программировании, технических задачах, понимании контекста и работе с длинными текстами.", @@ -490,6 +491,8 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 — модель генерации изображений от ByteDance Seed, поддерживающая ввод текста и изображений с высококачественной и управляемой генерацией. Генерирует изображения по текстовым подсказкам.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 — это последняя мультимодальная модель изображений от ByteDance, объединяющая возможности преобразования текста в изображение, изображения в изображение и пакетной генерации изображений, а также включающая здравый смысл и способности к рассуждению. По сравнению с предыдущей версией 4.0, она обеспечивает значительно улучшенное качество генерации, лучшую согласованность редактирования и слияние нескольких изображений. Модель предлагает более точный контроль над визуальными деталями, естественно воспроизводя мелкий текст и лица, а также достигает более гармоничного макета и цветовой палитры, улучшая общую эстетику.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite — это последняя модель генерации изображений от ByteDance. Впервые она интегрирует возможности онлайн-поиска, что позволяет использовать информацию в реальном времени и улучшать актуальность создаваемых изображений. Интеллект модели также был обновлен, что позволяет точно интерпретировать сложные инструкции и визуальный контент. Кроме того, она предлагает улучшенное покрытие глобальных знаний, согласованность ссылок и качество генерации в профессиональных сценариях, лучше удовлетворяя потребности корпоративного визуального творчества.", + "dreamina-seedance-2-0-260128.description": "Seedance 2.0 от ByteDance — самая мощная модель генерации видео, поддерживающая мультимодальную генерацию видео по ссылке, редактирование видео, расширение видео, текст-видео и изображение-видео с синхронизированным аудио.", + "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast от ByteDance предлагает те же возможности, что и Seedance 2.0, с более высокой скоростью генерации и конкурентной ценой.", "emohaa.description": "Emohaa — модель для поддержки психического здоровья с профессиональными навыками консультирования, помогающая пользователям разобраться в эмоциональных проблемах.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B — легковесная модель с открытым исходным кодом для локального и кастомизированного развертывания.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview — модель с контекстом 8K для предварительной оценки возможностей ERNIE 4.5.", @@ -514,7 +517,8 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K — быстрая модель мышления с контекстом 32K для сложного рассуждения и многотурового общения.", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview — предварительная версия модели мышления для оценки и тестирования.", "ernie-x1.1.description": "ERNIE X1.1 — это предварительная версия модели мышления для оценки и тестирования.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 — модель генерации изображений от ByteDance Seed, поддерживающая текстовые и визуальные входные данные с высокой степенью управляемости и качеством генерации. Она создает изображения на основе текстовых подсказок.", + "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5, созданная командой ByteDance Seed, поддерживает редактирование и композицию нескольких изображений. Обладает улучшенной согласованностью объектов, точным следованием инструкциям, пониманием пространственной логики, эстетическим выражением, дизайном постеров и логотипов с высокоточной генерацией текст-изображений.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, созданная ByteDance Seed, поддерживает текстовые и визуальные входные данные для высококонтролируемой генерации изображений высокого качества на основе подсказок.", "fal-ai/flux-kontext/dev.description": "Модель FLUX.1, ориентированная на редактирование изображений, поддерживает ввод текста и изображений.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] принимает текст и эталонные изображения, позволяя выполнять локальные правки и сложные глобальные трансформации сцены.", "fal-ai/flux/krea.description": "Flux Krea [dev] — модель генерации изображений с эстетическим уклоном в сторону более реалистичных и естественных изображений.", @@ -522,8 +526,8 @@ "fal-ai/hunyuan-image/v3.description": "Мощная нативная мультимодальная модель генерации изображений.", "fal-ai/imagen4/preview.description": "Модель генерации изображений высокого качества от Google.", "fal-ai/nano-banana.description": "Nano Banana — новейшая, самая быстрая и эффективная нативная мультимодальная модель от Google, поддерживающая генерацию и редактирование изображений в диалоговом режиме.", - "fal-ai/qwen-image-edit.description": "Профессиональная модель редактирования изображений от команды Qwen, поддерживающая семантические и визуальные правки, точное редактирование текста на китайском и английском языках, а также высококачественные изменения, такие как перенос стиля и вращение объектов.", - "fal-ai/qwen-image.description": "Мощная модель генерации изображений от команды Qwen с впечатляющим рендерингом китайского текста и разнообразными визуальными стилями.", + "fal-ai/qwen-image-edit.description": "Профессиональная модель редактирования изображений от команды Qwen, поддерживающая семантические и визуальные изменения, точное редактирование текста на китайском/английском языках, перенос стиля, вращение и многое другое.", + "fal-ai/qwen-image.description": "Мощная модель генерации изображений от команды Qwen с сильной поддержкой китайского текста и разнообразными визуальными стилями.", "flux-1-schnell.description": "Модель преобразования текста в изображение с 12 миллиардами параметров от Black Forest Labs, использующая латентную диффузию с дистилляцией для генерации качественных изображений за 1–4 шага. Конкурирует с закрытыми аналогами и распространяется по лицензии Apache-2.0 для личного, исследовательского и коммерческого использования.", "flux-dev.description": "Открытая исследовательская модель генерации изображений, оптимизированная для некоммерческих инновационных исследований.", "flux-kontext-max.description": "Передовая генерация и редактирование изображений с учётом контекста, объединяющая текст и изображения для точных и согласованных результатов.", @@ -565,7 +569,7 @@ "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) — модель генерации изображений от Google, также поддерживающая мультимодальный чат.", "gemini-3-pro-preview.description": "Gemini 3 Pro — самая мощная агентная модель от Google с поддержкой визуализации и глубокой интерактивности, основанная на передовых возможностях рассуждения.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) — это самая быстрая нативная модель генерации изображений от Google с поддержкой мышления, генерации и редактирования изображений в диалоговом режиме.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) — самая быстрая нативная модель генерации изображений от Google с поддержкой размышлений, генерации и редактирования изображений в диалоговом формате.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) обеспечивает качество изображений уровня Pro с высокой скоростью генерации и поддержкой мультимодального чата.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview — самая экономичная мультимодальная модель от Google, оптимизированная для задач с высоким объемом, перевода и обработки данных.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite — самая экономичная мультимодальная модель от Google, оптимизированная для задач с высоким объемом, перевода и обработки данных.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview улучшает Gemini 3 Pro с расширенными возможностями рассуждений и добавляет поддержку среднего уровня мышления.", @@ -730,6 +734,8 @@ "grok-4-fast-reasoning.description": "Мы рады представить Grok 4 Fast — наш последний прогресс в области экономичных моделей рассуждения.", "grok-4.20-0309-non-reasoning.description": "Вариант без рассуждений для простых задач.", "grok-4.20-0309-reasoning.description": "Интеллектуальная, сверхбыстрая модель, которая размышляет перед тем, как ответить.", + "grok-4.20-beta-0309-non-reasoning.description": "Вариант без мышления для простых случаев использования.", + "grok-4.20-beta-0309-reasoning.description": "Интеллектуальная, сверхбыстрая модель, которая размышляет перед ответом.", "grok-4.20-multi-agent-0309.description": "Команда из 4 или 16 агентов. Превосходна для исследовательских задач. Пока не поддерживает клиентские инструменты. Поддерживает только серверные инструменты xAI (например, X Search, Web Search) и удалённые MCP-инструменты.", "grok-4.3.description": "Самая правдолюбивая крупная языковая модель в мире.", "grok-4.description": "Последняя флагманская модель Grok с непревзойденной производительностью в языке, математике и рассуждениях — настоящий универсал. В настоящее время указывает на grok-4-0709; из-за ограниченных ресурсов временно цена на 10% выше официальной и ожидается возвращение к официальной цене позже.", @@ -1214,6 +1220,8 @@ "qwq.description": "QwQ — модель логического вывода из семейства Qwen. По сравнению со стандартными моделями, обученными на инструкциях, она обладает способностями к мышлению и логике, которые значительно улучшают производительность на сложных задачах. QwQ-32B — среднеразмерная модель, успешно конкурирующая с ведущими моделями, такими как DeepSeek-R1 и o1-mini.", "qwq_32b.description": "Среднеразмерная модель логического вывода из семейства Qwen. По сравнению со стандартными моделями, обученными на инструкциях, способности QwQ к мышлению и логике значительно повышают производительность на сложных задачах.", "r1-1776.description": "R1-1776 — дообученный вариант DeepSeek R1, предназначенный для предоставления нецензурированной, объективной и достоверной информации.", + "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro от ByteDance поддерживает текст-видео, изображение-видео (первый кадр, первый+последний кадр) и генерацию аудио, синхронизированного с визуальными эффектами.", + "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite от BytePlus предлагает генерацию с дополнением веб-поиска для получения актуальной информации, улучшенную интерпретацию сложных подсказок и повышенную согласованность ссылок для профессионального визуального творчества.", "solar-mini-ja.description": "Solar Mini (Ja) расширяет возможности Solar Mini с акцентом на японский язык, сохраняя при этом высокую эффективность и производительность на английском и корейском.", "solar-mini.description": "Solar Mini — компактная LLM-модель, превосходящая GPT-3.5, с мощной многоязычной поддержкой английского и корейского языков, предлагающая эффективное решение с малым объемом.", "solar-pro.description": "Solar Pro — интеллектуальная LLM-модель от Upstage, ориентированная на следование инструкциям на одном GPU, с результатами IFEval выше 80. В настоящее время поддерживает английский язык; полный релиз с расширенной языковой поддержкой и увеличенным контекстом запланирован на ноябрь 2024 года.", diff --git a/locales/ru-RU/plugin.json b/locales/ru-RU/plugin.json index fe2a44083a..233db06c6b 100644 --- a/locales/ru-RU/plugin.json +++ b/locales/ru-RU/plugin.json @@ -237,6 +237,14 @@ "builtins.lobe-page-agent.apiName.updateNode": "Обновить узел", "builtins.lobe-page-agent.apiName.wrapNodes": "Обернуть узлы", "builtins.lobe-page-agent.title": "Страница", + "builtins.lobe-self-feedback-intent.apiName.declareSelfFeedbackIntent": "Записать идею улучшения", + "builtins.lobe-self-feedback-intent.inspector.gap.proposal": "Предложить улучшение", + "builtins.lobe-self-feedback-intent.inspector.memory.write": "Записать предпочтение", + "builtins.lobe-self-feedback-intent.inspector.rejected": "Не записано", + "builtins.lobe-self-feedback-intent.inspector.skill.consolidate": "Организовать методы", + "builtins.lobe-self-feedback-intent.inspector.skill.create": "Найден новый метод", + "builtins.lobe-self-feedback-intent.inspector.skill.refine": "Улучшить метод", + "builtins.lobe-self-feedback-intent.title": "Идеи улучшений", "builtins.lobe-skill-store.apiName.importFromMarket": "Импортировать с рынка", "builtins.lobe-skill-store.apiName.importSkill": "Импортировать навык", "builtins.lobe-skill-store.apiName.searchSkill": "Поиск навыков", diff --git a/locales/ru-RU/providers.json b/locales/ru-RU/providers.json index 14c784b315..d61fd0b5d4 100644 --- a/locales/ru-RU/providers.json +++ b/locales/ru-RU/providers.json @@ -33,6 +33,7 @@ "jina.description": "Основанная в 2020 году, Jina AI — ведущая компания в области поискового ИИ. Её стек включает векторные модели, переоценщики и малые языковые модели для создания надежных генеративных и мультимодальных поисковых приложений.", "kimicodingplan.description": "Kimi Code от Moonshot AI предоставляет доступ к моделям Kimi, включая K2.5, для выполнения задач кодирования.", "lmstudio.description": "LM Studio — это настольное приложение для разработки и экспериментов с LLM на вашем компьютере.", + "lobehub.description": "LobeHub Cloud использует официальные API для доступа к моделям ИИ и измеряет использование с помощью Кредитов, связанных с токенами моделей.", "longcat.description": "LongCat — это серия больших моделей генеративного ИИ, разработанных Meituan. Она предназначена для повышения внутренней производительности предприятия и создания инновационных приложений благодаря эффективной вычислительной архитектуре и мощным мультимодальным возможностям.", "minimax.description": "Основанная в 2021 году, MiniMax разрабатывает универсальные ИИ-модели на базе мультимодальных основ, включая текстовые модели с триллионами параметров, речевые и визуальные модели, а также приложения, такие как Hailuo AI.", "minimaxcodingplan.description": "План токенов MiniMax предоставляет доступ к моделям MiniMax, включая M2.7, для выполнения задач кодирования по подписке с фиксированной оплатой.", diff --git a/locales/ru-RU/subscription.json b/locales/ru-RU/subscription.json index ea15868bf2..86a8054040 100644 --- a/locales/ru-RU/subscription.json +++ b/locales/ru-RU/subscription.json @@ -41,7 +41,9 @@ "credits.autoTopUp.monthlyLimitDesc": "Максимальная сумма, которая может быть автоматически списана в месяц. Оставьте пустым для отсутствия лимита", "credits.autoTopUp.monthlyLimitPlaceholder": "Без лимита", "credits.autoTopUp.monthlyTopUpAmount": "Сумма ежемесячного пополнения", + "credits.autoTopUp.noCustomerHint": "Совершите покупку кредитов один раз, чтобы сохранить способ оплаты перед включением автоматического пополнения.", "credits.autoTopUp.noPaymentMethodHint": "Нет сохраненного способа оплаты. Для автоматического пополнения требуется сохраненная карта для автоматического списания.", + "credits.autoTopUp.purchaseCredits": "Купить кредиты", "credits.autoTopUp.saveError": "Не удалось сохранить настройки автоматического пополнения", "credits.autoTopUp.saveSuccess": "Настройки автоматического пополнения сохранены", "credits.autoTopUp.setupPaymentMethod": "Добавить способ оплаты", @@ -83,6 +85,7 @@ "credits.packages.title": "Мои пакеты кредитов", "credits.topUp.cancel": "Отмена", "credits.topUp.custom": "Свой вариант", + "credits.topUp.freeFeeHint": "Пополнения для бесплатного плана включают комиссию за обслуживание в размере {{fee}} за 1M кредитов.", "credits.topUp.maxAmountError": "Сумма одной покупки не может превышать ${{max}}", "credits.topUp.purchaseError": "Не удалось совершить покупку, попробуйте позже", "credits.topUp.purchaseNow": "Купить сейчас", @@ -120,6 +123,9 @@ "keyMissMatch.button": "Восстановить использование и продолжить", "keyMissMatch.description": "Из-за временного сбоя системы использование вашей подписки временно приостановлено. Пожалуйста, нажмите кнопку ниже, чтобы восстановить использование и продолжить. Если это повторяется, свяжитесь с нами по email (support@lobehub.com)", "keyMissMatch.title": "Восстановить использование подписки", + "limitation.chat.budgetReady.action": "Продолжить общение", + "limitation.chat.budgetReady.desc": "Ваши доступные кредиты теперь покрывают этот запрос.", + "limitation.chat.budgetReady.title": "Кредиты готовы", "limitation.chat.success.action": "Продолжить общение", "limitation.chat.success.desc": "Ваша подписка {{plan}} успешно обновлена. Наслаждайтесь общением с ИИ. Ваш текущий план включает:", "limitation.chat.success.title": "Обновление успешно", @@ -134,14 +140,23 @@ "limitation.hobby.docs": "Посмотреть документацию по настройке", "limitation.hobby.tip": "Не забудьте переключиться на модель с собственным API-ключом", "limitation.hobby.title": "Пожалуйста, настройте API сервиса модели", + "limitation.image.budgetReady.action": "Продолжить генерацию", + "limitation.image.budgetReady.desc": "Ваши доступные кредиты теперь покрывают эту генерацию.", + "limitation.image.budgetReady.title": "Кредиты готовы", "limitation.image.success.action": "Продолжить генерацию", "limitation.image.success.desc": "Ваша подписка {{plan}} успешно обновлена. Наслаждайтесь генерацией изображений ИИ. Ваш текущий план включает:", "limitation.image.success.title": "Обновление успешно", "limitation.image.topupSuccess.action": "Продолжить генерацию", "limitation.image.topupSuccess.desc": "Ваши пополненные кредиты активированы. Наслаждайтесь генерацией изображений ИИ. Ваш текущий план включает:", "limitation.image.topupSuccess.title": "Пополнение успешно", + "limitation.insufficientBudget.approximateDesc": "Для этого запроса может потребоваться больше кредитов. Пополните кредиты или обновите свой план.", + "limitation.insufficientBudget.available": "Доступные кредиты", "limitation.insufficientBudget.desc": "Ваших оставшихся кредитов недостаточно для предполагаемой стоимости этой модели. Пожалуйста, пополните кредиты или переключитесь на менее дорогую модель.", + "limitation.insufficientBudget.estimatedDesc": "Для этого запроса, по оценкам, потребуется больше кредитов. Пополните кредиты или обновите свой план.", + "limitation.insufficientBudget.exactDesc": "Для этого запроса требуется больше кредитов. Пополните кредиты или обновите свой план.", + "limitation.insufficientBudget.required": "Требуемые кредиты", "limitation.insufficientBudget.retry": "Повторить", + "limitation.insufficientBudget.shortfall": "Недостаток кредитов", "limitation.insufficientBudget.title": "Недостаточно кредитов для этой модели", "limitation.limited.action": "Обновить сейчас", "limitation.limited.advanceFeature": "Обновите план, чтобы получить доступ к премиум-функциям:", @@ -151,6 +166,7 @@ "limitation.limited.title": "Вычислительные кредиты исчерпаны", "limitation.limited.topup": "Пополнить кредиты", "limitation.limited.upgrade": "Обновить до более высокого плана", + "limitation.limited.upgradeToPlan": "Обновите до {{plan}}", "limitation.providers.lock.addNew": "Оформите подписку, чтобы создать собственных AI-провайдеров", "limitation.providers.lock.enableProvider": "Оформите подписку, чтобы активировать этого AI-провайдера", "limitation.providers.lock.menuItem": "Оформите подписку, чтобы настроить собственный API-сервис", @@ -161,6 +177,9 @@ "limitation.providers.prompter.subTitle": "Собственный API-сервис доступен только в платных планах. Обновите план, чтобы использовать глобальные модели", "limitation.providers.prompter.title": "Оформите подписку для использования собственного API-сервиса", "limitation.providers.tooltip": "Собственный API-сервис доступен только в платных планах", + "limitation.video.budgetReady.action": "Продолжить генерацию", + "limitation.video.budgetReady.desc": "Ваши доступные кредиты теперь покрывают эту генерацию.", + "limitation.video.budgetReady.title": "Кредиты готовы", "limitation.video.success.action": "Продолжить генерацию", "limitation.video.success.desc": "Ваша подписка {{plan}} успешно обновлена. Наслаждайтесь генерацией видео с помощью ИИ. Ваш текущий план включает:", "limitation.video.success.title": "Обновление прошло успешно", diff --git a/locales/tr-TR/chat.json b/locales/tr-TR/chat.json index 8d9d4377db..7595afb7bb 100644 --- a/locales/tr-TR/chat.json +++ b/locales/tr-TR/chat.json @@ -196,6 +196,9 @@ "groupWizard.searchTemplates": "Şablonlarda ara...", "groupWizard.title": "Grup Oluştur", "groupWizard.useTemplate": "Şablon Kullan", + "heteroAgent.cloudNotConfigured.action": "Yapılandır", + "heteroAgent.cloudNotConfigured.desc": "Mesaj göndermeye başlamak için Claude Code jetonunuzu ajan profilinde yapılandırın.", + "heteroAgent.cloudNotConfigured.title": "Bulut kimlik bilgileri gerekli", "heteroAgent.cloudRepo.multiSelected": "{{count}} depo seçildi", "heteroAgent.cloudRepo.noRepos": "Hiçbir depo yapılandırılmamış. Bunları ajan ayarlarında ekleyin.", "heteroAgent.cloudRepo.notSet": "Hiçbir depo seçilmedi", @@ -467,6 +470,10 @@ "sharePage.error.unauthorized.subtitle": "Paylaşılan bu konuyu görüntülemek için lütfen giriş yapın.", "sharePage.error.unauthorized.title": "Giriş Gerekli", "sharePageDisclaimer": "Bu içerik bir kullanıcı tarafından paylaşılmıştır ve LobeHub'un görüşlerini yansıtmaz. LobeHub, bu paylaşılan içerikten doğabilecek sonuçlardan sorumlu değildir.", + "signalCallbacks.collapse": "Ayrıntıları gizle", + "signalCallbacks.empty": "Çağrı mesajı yok", + "signalCallbacks.expand": "Ayrıntıları göster", + "signalCallbacks.title": "{{tool}} · {{count}} çağrı güncellemeleri", "stt.action": "Sesli Giriş", "stt.loading": "Tanınıyor...", "stt.prettifying": "Düzenleniyor...", @@ -689,7 +696,6 @@ "thread.closeSubagentThread": "Alt ajan konuşmasını daralt", "thread.divider": "Alt Konu", "thread.openSubagentThread": "Alt ajan konuşmasının tamamını görüntüle", - "thread.subagentBadge": "Alt ajan", "thread.subagentReadOnlyHint": "Alt Ajan konuşmaları yalnızca okunabilir — yürütme, ana ajan tarafından yönlendirilir.", "thread.threadMessageCount": "{{messageCount}} mesaj", "thread.title": "Alt Konu", diff --git a/locales/tr-TR/models.json b/locales/tr-TR/models.json index e3e583b157..e763c6a197 100644 --- a/locales/tr-TR/models.json +++ b/locales/tr-TR/models.json @@ -106,6 +106,7 @@ "MiniMax-Hailuo-2.3.description": "Vücut hareketi, fiziksel gerçekçilik ve talimat takibinde kapsamlı yükseltmelerle yepyeni bir video üretim modeli.", "MiniMax-M1.description": "80K düşünce zinciri ve 1M giriş desteğiyle üst düzey modellerle karşılaştırılabilir performans sunan yeni bir yerli akıl yürütme modeli.", "MiniMax-M2-Stable.description": "Ticari kullanım için daha yüksek eşzamanlılık sunan, verimli kodlama ve ajan iş akışları için tasarlanmıştır.", + "MiniMax-M2.1-Lightning.description": "Güçlü çok dilli programlama yetenekleriyle daha hızlı ve daha verimli çıkarım.", "MiniMax-M2.1-highspeed.description": "Güçlü çok dilli programlama yetenekleri, kapsamlı olarak geliştirilmiş bir programlama deneyimi. Daha hızlı ve daha verimli.", "MiniMax-M2.1.description": "MiniMax-M2.1, MiniMax tarafından geliştirilen amiral gemisi açık kaynak büyük modeldir ve karmaşık gerçek dünya görevlerini çözmeye odaklanır. Temel güçlü yönleri çok dilli programlama yetenekleri ve bir Ajan olarak karmaşık görevleri çözme becerisidir.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: M2.5 ile aynı performans, ancak daha hızlı çıkarım.", @@ -314,13 +315,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku, Anthropic’in en hızlı ve en kompakt modelidir; anında yanıtlar için hızlı ve doğru performans sunar.", "claude-3-opus-20240229.description": "Claude 3 Opus, karmaşık görevler için Anthropic’in en güçlü modelidir; performans, zeka, akıcılık ve anlama konularında üstündür.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet, kurumsal iş yükleri için zeka ve hızı dengeler; düşük maliyetle yüksek fayda ve güvenilir büyük ölçekli dağıtım sunar.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5, Anthropic'in en hızlı ve en akıllı Haiku modeli olup, yıldırım hızında ve gelişmiş akıl yürütme yeteneklerine sahiptir.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5, Anthropic'in en hızlı ve en akıllı Haiku modeli olup, yıldırım hızında ve genişletilmiş düşünme yeteneğine sahiptir.", "claude-haiku-4-5.description": "Anthropic’in Claude Haiku 4.5 modeli — gelişmiş akıl yürütme ve görsel yeteneklere sahip yeni nesil Haiku.", "claude-haiku-4.5.description": "Claude Haiku 4.5, Anthropic'in en hızlı ve en akıllı Haiku modeli olup, yıldırım hızında ve gelişmiş akıl yürütme yeteneklerine sahiptir.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking, akıl yürütme sürecini görünür şekilde ortaya koyabilen gelişmiş bir varyanttır.", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1, Anthropic'in en yeni ve en yetenekli modeli olup, karmaşık görevlerde performans, zeka, akıcılık ve anlayış açısından üstünlük sağlar.", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1, Anthropic'in en yeni ve en yetenekli modeli olup, performans, zeka, akıcılık ve anlama konularında üstün başarı sağlar.", "claude-opus-4-1.description": "Anthropic’in Claude Opus 4.1 modeli — derin analiz yeteneklerine sahip üst seviye akıl yürütme modeli.", - "claude-opus-4-20250514.description": "Claude Opus 4, Anthropic'in karmaşık görevler için en güçlü modeli olup, performans, zeka, akıcılık ve kavrama açısından üstünlük sağlar.", + "claude-opus-4-20250514.description": "Claude Opus 4, Anthropic'in en güçlü modeli olup, performans, zeka, akıcılık ve anlama konularında üstün başarı sağlar.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5, Anthropic’in amiral gemisi modelidir; olağanüstü zeka ile ölçeklenebilir performansı birleştirir. En yüksek kaliteli yanıtlar ve akıl yürütme gerektiren karmaşık görevler için idealdir.", "claude-opus-4-5.description": "Anthropic’in Claude Opus 4.5 modeli — üst düzey akıl yürütme ve kodlama yeteneklerine sahip amiral gemisi model.", "claude-opus-4-6.description": "Anthropic’in Claude Opus 4.6 modeli — 1M bağlam penceresi ve gelişmiş akıl yürütme yetenekleriyle amiral gemisi sürümü.", @@ -329,7 +330,7 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6, Anthropic'in ajanlar oluşturma ve kodlama için en akıllı modelidir.", "claude-opus-4.6.description": "Claude Opus 4.6, Anthropic'in ajanlar oluşturma ve kodlama için en akıllı modelidir.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking, anında yanıtlar veya adım adım düşünme süreçleri üretebilir.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4, anında yanıtlar veya görünür bir süreçle adım adım düşünme yeteneği sunabilir.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4, Anthropic'in bugüne kadarki en akıllı modeli olup, API kullanıcıları için ince ayarlı kontrol ile anında yanıtlar veya adım adım genişletilmiş düşünme sunar.", "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5, Anthropic'in bugüne kadarki en akıllı modelidir.", "claude-sonnet-4-5.description": "Anthropic’in Claude Sonnet 4.5 modeli — geliştirilmiş kodlama performansıyla güçlendirilmiş Sonnet sürümü.", "claude-sonnet-4-6.description": "Anthropic’in Claude Sonnet 4.6 modeli — üstün kodlama ve araç kullanımıyla en yeni Sonnet.", @@ -403,7 +404,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B), derin dil anlama ve etkileşim sunan yenilikçi bir modeldir.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1, karmaşık akıl yürütme ve düşünce zinciriyle derin analiz görevleri için geliştirilmiş yeni nesil bir modeldir.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2, daha güçlü karmaşık akıl yürütme ve düşünce zinciri yeteneklerine sahip yeni nesil bir akıl yürütme modelidir.", - "deepseek-chat.description": "Genel ve kod yeteneklerini birleştiren yeni bir açık kaynak modeli. Sohbet modelinin genel diyaloğunu ve kodlayıcı modelinin güçlü kodlama yeteneklerini korur, daha iyi tercih uyumu sağlar. DeepSeek-V2.5 ayrıca yazma ve talimat takip etme yeteneklerini geliştirir.", + "deepseek-chat.description": "DeepSeek V4 Flash düşünme modu için uyumluluk takma adı. Kullanımdan kaldırılması planlanıyor — bunun yerine DeepSeek V4 Flash kullanın.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B, 2T token (%%87 kod, %%13 Çince/İngilizce metin) ile eğitilmiş bir kodlama dil modelidir. 16K bağlam penceresi ve ortadan doldurma görevleri sunar; proje düzeyinde kod tamamlama ve kod parçacığı doldurma sağlar.", "deepseek-coder-v2.description": "DeepSeek Coder V2, GPT-4 Turbo ile karşılaştırılabilir güçlü performansa sahip açık kaynaklı bir MoE kod modeli.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2, GPT-4 Turbo ile karşılaştırılabilir güçlü performansa sahip açık kaynaklı bir MoE kod modeli.", @@ -425,7 +426,7 @@ "deepseek-r1-fast-online.description": "Gerçek zamanlı web aramasıyla 671B ölçekli yetenek ve hızlı yanıtları birleştiren DeepSeek R1 hızlı tam sürüm.", "deepseek-r1-online.description": "671B parametreli ve gerçek zamanlı web aramasına sahip DeepSeek R1 tam sürüm; güçlü anlama ve üretim sunar.", "deepseek-r1.description": "DeepSeek-R1, RL öncesi soğuk başlangıç verileri kullanır ve matematik, kodlama ve akıl yürütmede OpenAI-o1 ile karşılaştırılabilir performans sunar.", - "deepseek-reasoner.description": "Karmaşık mantıksal akıl yürütme görevlerine odaklanan bir DeepSeek akıl yürütme modeli.", + "deepseek-reasoner.description": "DeepSeek V4 Flash düşünme modu için uyumluluk takma adı. Kullanımdan kaldırılması planlanıyor — bunun yerine DeepSeek V4 Flash kullanın.", "deepseek-v2.description": "DeepSeek V2, maliyet etkin işlem için verimli bir MoE modelidir.", "deepseek-v2:236b.description": "DeepSeek V2 236B, güçlü kod üretimi sunan DeepSeek’in kod odaklı modelidir.", "deepseek-v3-0324.description": "DeepSeek-V3-0324, programlama ve teknik yetenek, bağlam anlama ve uzun metin işleme konularında öne çıkan 671B parametreli bir MoE modelidir.", @@ -490,6 +491,8 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0, ByteDance Seed tarafından geliştirilen bir görsel üretim modelidir; metin ve görsel girişlerini destekler, yüksek kaliteli ve kontrol edilebilir görseller üretir. Metin istemlerinden görseller oluşturur.", "doubao-seedream-4-5-251128.description": "Seedream 4.5, ByteDance'in en son çok modlu görüntü modelidir. Metinden görüntüye, görüntüden görüntüye ve toplu görüntü oluşturma yeteneklerini entegre ederken, sağduyu ve akıl yürütme yeteneklerini içerir. Önceki 4.0 sürümüne kıyasla, daha iyi düzenleme tutarlılığı ve çoklu görüntü birleştirme ile önemli ölçüde geliştirilmiş üretim kalitesi sunar. Görsel detaylar üzerinde daha hassas kontrol sağlar, küçük metin ve küçük yüzleri daha doğal bir şekilde üretir ve daha uyumlu düzen ve renk elde ederek genel estetiği artırır.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite, ByteDance'in en son görüntü oluşturma modelidir. İlk kez çevrimiçi bilgi alma yeteneklerini entegre ederek gerçek zamanlı web bilgilerini dahil etmesine ve oluşturulan görüntülerin zamanlamasını artırmasına olanak tanır. Modelin zekası da yükseltilmiş, karmaşık talimatları ve görsel içeriği hassas bir şekilde yorumlama yeteneği kazandırılmıştır. Ayrıca, profesyonel senaryolarda daha iyi küresel bilgi kapsamı, referans tutarlılığı ve üretim kalitesi sunarak kurumsal düzeyde görsel yaratım ihtiyaçlarını daha iyi karşılar.", + "dreamina-seedance-2-0-260128.description": "ByteDance tarafından geliştirilen Seedance 2.0, en güçlü video üretim modeli olup, çok modlu referans video üretimi, video düzenleme, video genişletme, metinden videoya ve görüntüden videoya senkronize ses ile destek sağlar.", + "dreamina-seedance-2-0-fast-260128.description": "ByteDance tarafından geliştirilen Seedance 2.0 Fast, Seedance 2.0 ile aynı yetenekleri daha hızlı üretim hızları ve daha rekabetçi fiyatlarla sunar.", "emohaa.description": "Emohaa, kullanıcıların duygusal sorunları anlamalarına yardımcı olmak için profesyonel danışmanlık yeteneklerine sahip bir ruh sağlığı modelidir.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B, yerel ve özelleştirilmiş dağıtım için açık kaynaklı hafif bir modeldir.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Önizleme, ERNIE 4.5'in değerlendirilmesi için 8K bağlam önizleme modelidir.", @@ -514,7 +517,8 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K, karmaşık akıl yürütme ve çoklu dönüşlü sohbetler için 32K bağlamlı hızlı düşünme modelidir.", "ernie-x1.1-preview.description": "ERNIE X1.1 Önizleme, değerlendirme ve test için bir düşünme modeli önizlemesidir.", "ernie-x1.1.description": "ERNIE X1.1, değerlendirme ve test için bir düşünme-modeli önizlemesidir.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, ByteDance Seed tarafından geliştirilen bir görüntü oluşturma modelidir. Metin ve görüntü girdilerini destekler, yüksek kaliteli ve kontrol edilebilir görüntü oluşturma sağlar. Metin istemlerinden görüntüler oluşturur.", + "fal-ai/bytedance/seedream/v4.5.description": "ByteDance Seed ekibi tarafından geliştirilen Seedream 4.5, çoklu görüntü düzenleme ve kompozisyonu destekler. Geliştirilmiş konu tutarlılığı, hassas talimat takibi, mekansal mantık anlayışı, estetik ifade, poster düzeni ve logo tasarımı ile yüksek hassasiyetli metin-görüntü oluşturma özelliklerine sahiptir.", + "fal-ai/bytedance/seedream/v4.description": "ByteDance Seed tarafından geliştirilen Seedream 4.0, metin ve görüntü girdilerini destekleyerek, istemlerden yüksek kaliteli ve kontrol edilebilir görüntü üretimi sağlar.", "fal-ai/flux-kontext/dev.description": "FLUX.1 modeli, metin ve görsel girdileri destekleyen görsel düzenleme odaklı bir modeldir.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro], metin ve referans görselleri girdi olarak alarak hedefe yönelik yerel düzenlemeler ve karmaşık sahne dönüşümleri sağlar.", "fal-ai/flux/krea.description": "Flux Krea [dev], daha gerçekçi ve doğal görseller üretmeye eğilimli estetik önyargıya sahip bir görsel üretim modelidir.", @@ -522,8 +526,8 @@ "fal-ai/hunyuan-image/v3.description": "Güçlü bir yerel çok modlu görsel üretim modelidir.", "fal-ai/imagen4/preview.description": "Google tarafından geliştirilen yüksek kaliteli görsel üretim modeli.", "fal-ai/nano-banana.description": "Nano Banana, Google’ın en yeni, en hızlı ve en verimli yerel çok modlu modelidir. Konuşma yoluyla görsel üretim ve düzenleme sağlar.", - "fal-ai/qwen-image-edit.description": "Qwen ekibinden profesyonel bir görüntü düzenleme modeli. Anlam ve görünüm düzenlemelerini destekler, Çince ve İngilizce metni hassas bir şekilde düzenler ve stil transferi veya nesne döndürme gibi yüksek kaliteli düzenlemeler yapar.", - "fal-ai/qwen-image.description": "Qwen ekibinden güçlü bir görüntü oluşturma modeli. Çince metin işleme ve çeşitli görsel stiller konusunda etkileyicidir.", + "fal-ai/qwen-image-edit.description": "Qwen ekibinden profesyonel bir görüntü düzenleme modeli olup, semantik ve görünüm düzenlemeleri, hassas Çince/İngilizce metin düzenleme, stil transferi, döndürme ve daha fazlasını destekler.", + "fal-ai/qwen-image.description": "Qwen ekibinden güçlü bir görüntü üretim modeli olup, güçlü Çince metin işleme ve çeşitli görsel stiller sunar.", "flux-1-schnell.description": "Black Forest Labs tarafından geliştirilen 12 milyar parametreli metinden görsele model. Latent adversarial diffusion distillation yöntemiyle 1-4 adımda yüksek kaliteli görseller üretir. Kapalı kaynaklı alternatiflerle rekabet eder ve kişisel, araştırma ve ticari kullanım için Apache-2.0 lisansı ile sunulur.", "flux-dev.description": "Açık kaynaklı Ar-Ge görsel üretim modelidir; ticari olmayan yenilikçi araştırmalar için verimli şekilde optimize edilmiştir.", "flux-kontext-max.description": "Metin ve görselleri birleştirerek hassas ve tutarlı sonuçlar sunan son teknoloji bağlamsal görsel üretim ve düzenleme.", @@ -562,10 +566,10 @@ "gemini-3-flash-preview.description": "Gemini 3 Flash, hız için tasarlanmış en akıllı modeldir. En son yapay zeka zekasını mükemmel arama temellendirmesiyle birleştirir.", "gemini-3-flash.description": "Google’ın Gemini 3 Flash modeli — çok hızlı ve çok modlu giriş desteğine sahiptir.", "gemini-3-pro-image-preview.description": "Gemini 3 Pro Image (Nano Banana Pro), Google'ın çok modlu diyaloğu da destekleyen görüntü oluşturma modelidir.", - "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro), Google'ın görüntü oluşturma modelidir ve aynı zamanda çok modlu sohbeti destekler.", + "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro), Google'ın görüntü üretim modeli olup, çok modlu sohbeti de destekler.", "gemini-3-pro-preview.description": "Gemini 3 Pro, Google’ın en güçlü ajan ve vibe-coding modelidir. En yeni akıl yürütme yeteneklerinin üzerine zengin görseller ve derin etkileşim sunar.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2), Google'ın düşünme desteği, konuşmalı görüntü oluşturma ve düzenleme özelliklerine sahip en hızlı yerel görüntü oluşturma modelidir.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2), Google'ın en hızlı yerel görüntü oluşturma modelidir. Düşünme desteği, sohbet tabanlı görüntü oluşturma ve düzenleme özelliklerini içerir.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2), Flash hızında Pro seviyesinde görüntü kalitesi sunar ve çok modlu sohbeti destekler.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview, Google'ın en maliyet etkin çok modlu modeli olup, yüksek hacimli ajan görevleri, çeviri ve veri işleme için optimize edilmiştir.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite, Google'ın en maliyet etkin çok modlu modelidir ve yüksek hacimli ajan görevleri, çeviri ve veri işleme için optimize edilmiştir.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview, Gemini 3 Pro'ya kıyasla geliştirilmiş akıl yürütme yetenekleri sunar ve orta düzey düşünme desteği ekler.", @@ -730,6 +734,8 @@ "grok-4-fast-reasoning.description": "Grok 4 Fast modelimizi sunmaktan heyecan duyuyoruz; uygun maliyetli akıl yürütme modellerinde en son gelişmemizdir.", "grok-4.20-0309-non-reasoning.description": "Basit kullanım senaryoları için akıl yürütme içermeyen varyant.", "grok-4.20-0309-reasoning.description": "Yanıt vermeden önce akıl yürüten, son derece hızlı ve akıllı model.", + "grok-4.20-beta-0309-non-reasoning.description": "Basit kullanım durumları için düşünme gerektirmeyen bir varyant.", + "grok-4.20-beta-0309-reasoning.description": "Yanıt vermeden önce düşünen, son derece hızlı ve akıllı model.", "grok-4.20-multi-agent-0309.description": "4 veya 16 ajanlık bir ekip; araştırma senaryolarında üstündür. Şu anda istemci tarafı araçları desteklemez. Yalnızca xAI sunucu tarafı araçlarını (örn. X Search, Web Search araçları) ve uzak MCP araçlarını destekler.", "grok-4.3.description": "Dünyanın en doğruyu arayan büyük dil modeli", "grok-4.description": "Dil, matematik ve akıl yürütme konularında rakipsiz performansa sahip en son Grok amiral gemisi — gerçek bir çok yönlü model. Şu anda grok-4-0709'a işaret ediyor; sınırlı kaynaklar nedeniyle geçici olarak resmi fiyatlandırmadan %10 daha yüksek ve daha sonra resmi fiyatına dönmesi bekleniyor.", @@ -1214,6 +1220,8 @@ "qwq.description": "QwQ, Qwen ailesine ait bir akıl yürütme modelidir. Standart talimatla eğitilmiş modellere kıyasla düşünme ve akıl yürütme yetenekleriyle özellikle zor problemler üzerinde performansı önemli ölçüde artırır. QwQ-32B, DeepSeek-R1 ve o1-mini gibi üst düzey modellerle rekabet eden orta boyutlu bir modeldir.", "qwq_32b.description": "Qwen ailesine ait orta boyutlu bir akıl yürütme modelidir. Standart talimatla eğitilmiş modellere kıyasla QwQ’nun düşünme ve akıl yürütme yetenekleri, özellikle zor problemler üzerinde performansı önemli ölçüde artırır.", "r1-1776.description": "R1-1776, sansürsüz ve tarafsız gerçek bilgi sunmak üzere DeepSeek R1 üzerine eğitilmiş bir varyanttır.", + "seedance-1-5-pro-251215.description": "ByteDance tarafından geliştirilen Seedance 1.5 Pro, metinden videoya, görüntüden videoya (ilk kare, ilk+son kare) ve görsellerle senkronize ses üretimini destekler.", + "seedream-5-0-260128.description": "BytePlus tarafından geliştirilen ByteDance-Seedream-5.0-lite, gerçek zamanlı bilgi için web arama ile zenginleştirilmiş üretim, karmaşık istem yorumlama ve profesyonel görsel oluşturma için geliştirilmiş referans tutarlılığı sağlar.", "solar-mini-ja.description": "Solar Mini (Ja), Japonca odaklı geliştirilmiş Solar Mini modelidir; İngilizce ve Korece'de de verimli ve güçlü performans sunar.", "solar-mini.description": "Solar Mini, GPT-3.5'i geride bırakan kompakt bir LLM'dir. İngilizce ve Korece destekli çok dilli yetenekleriyle verimli ve küçük boyutlu bir çözüm sunar.", "solar-pro.description": "Solar Pro, Upstage tarafından geliştirilen yüksek zekaya sahip bir LLM'dir. Tek bir GPU üzerinde talimat izleme odaklıdır ve IFEval skorları 80'in üzerindedir. Şu anda İngilizce desteklidir; tam sürüm Kasım 2024'te daha fazla dil desteği ve uzun bağlamla planlanmıştır.", diff --git a/locales/tr-TR/plugin.json b/locales/tr-TR/plugin.json index ed8761288f..498dc98c7d 100644 --- a/locales/tr-TR/plugin.json +++ b/locales/tr-TR/plugin.json @@ -237,6 +237,14 @@ "builtins.lobe-page-agent.apiName.updateNode": "Düğümü güncelle", "builtins.lobe-page-agent.apiName.wrapNodes": "Düğümleri sar", "builtins.lobe-page-agent.title": "Sayfa", + "builtins.lobe-self-feedback-intent.apiName.declareSelfFeedbackIntent": "Geliştirme fikrini kaydet", + "builtins.lobe-self-feedback-intent.inspector.gap.proposal": "Geliştirme önerisi", + "builtins.lobe-self-feedback-intent.inspector.memory.write": "Tercihi kaydet", + "builtins.lobe-self-feedback-intent.inspector.rejected": "Kaydedilmedi", + "builtins.lobe-self-feedback-intent.inspector.skill.consolidate": "Yöntemleri düzenle", + "builtins.lobe-self-feedback-intent.inspector.skill.create": "Yeni yöntem bulundu", + "builtins.lobe-self-feedback-intent.inspector.skill.refine": "Yöntemi geliştir", + "builtins.lobe-self-feedback-intent.title": "Geliştirme Fikirleri", "builtins.lobe-skill-store.apiName.importFromMarket": "Pazardan İçe Aktar", "builtins.lobe-skill-store.apiName.importSkill": "Beceri İçe Aktar", "builtins.lobe-skill-store.apiName.searchSkill": "Becerileri Ara", diff --git a/locales/tr-TR/providers.json b/locales/tr-TR/providers.json index 02005f3045..7548148d43 100644 --- a/locales/tr-TR/providers.json +++ b/locales/tr-TR/providers.json @@ -33,6 +33,7 @@ "jina.description": "2020 yılında kurulan Jina AI, önde gelen bir arama yapay zekası şirketidir. Vektör modelleri, yeniden sıralayıcılar ve küçük dil modelleri içeren arama yığını ile güvenilir ve yüksek kaliteli üretken ve çok modlu arama uygulamaları geliştirir.", "kimicodingplan.description": "Moonshot AI'den Kimi Code, kodlama görevleri için K2.5 dahil olmak üzere Kimi modellerine erişim sağlar.", "lmstudio.description": "LM Studio, bilgisayarınızda büyük dil modelleriyle geliştirme ve denemeler yapmanızı sağlayan bir masaüstü uygulamasıdır.", + "lobehub.description": "LobeHub Cloud, resmi API'leri kullanarak yapay zeka modellerine erişir ve kullanımını model jetonlarına bağlı Kredilerle ölçer.", "longcat.description": "LongCat, Meituan tarafından bağımsız olarak geliştirilen bir dizi üretken yapay zeka büyük modelidir. Verimli bir hesaplama mimarisi ve güçlü çok modlu yetenekler aracılığıyla kurumsal iç verimliliği artırmak ve yenilikçi uygulamaları mümkün kılmak için tasarlanmıştır.", "minimax.description": "2021 yılında kurulan MiniMax, çok modlu temel modellerle genel amaçlı yapay zeka geliştirir. Trilyon parametreli MoE metin modelleri, ses ve görsel modellerin yanı sıra Hailuo AI gibi uygulamalar sunar.", "minimaxcodingplan.description": "MiniMax Token Planı, sabit ücretli bir abonelik aracılığıyla kodlama görevleri için M2.7 dahil olmak üzere MiniMax modellerine erişim sağlar.", diff --git a/locales/tr-TR/subscription.json b/locales/tr-TR/subscription.json index f41d9e5540..82ed2c5573 100644 --- a/locales/tr-TR/subscription.json +++ b/locales/tr-TR/subscription.json @@ -41,7 +41,9 @@ "credits.autoTopUp.monthlyLimitDesc": "Aylık olarak otomatik olarak çekilebilecek maksimum tutar. Sınırsız için boş bırakın", "credits.autoTopUp.monthlyLimitPlaceholder": "Sınırsız", "credits.autoTopUp.monthlyTopUpAmount": "Aylık Yükleme Tutarı", + "credits.autoTopUp.noCustomerHint": "Otomatik kredi yükleme özelliğini etkinleştirmeden önce bir ödeme yöntemi kaydetmek için bir kez kredi satın alın.", "credits.autoTopUp.noPaymentMethodHint": "Kayıtlı bir ödeme yöntemi yok. Otomatik bakiye yükleme, otomatik olarak ücretlendirmek için kayıtlı bir karta ihtiyaç duyar.", + "credits.autoTopUp.purchaseCredits": "Kredi Satın Al", "credits.autoTopUp.saveError": "Otomatik yükleme ayarları kaydedilemedi", "credits.autoTopUp.saveSuccess": "Otomatik yükleme ayarları kaydedildi", "credits.autoTopUp.setupPaymentMethod": "Ödeme Yöntemi Ekle", @@ -83,6 +85,7 @@ "credits.packages.title": "Kredi Paketlerim", "credits.topUp.cancel": "İptal Et", "credits.topUp.custom": "Özel", + "credits.topUp.freeFeeHint": "Ücretsiz plan yüklemelerinde her 1M kredi için {{fee}} hizmet ücreti uygulanır.", "credits.topUp.maxAmountError": "Tek seferlik satın alma tutarı ${{max}}'i geçemez", "credits.topUp.purchaseError": "Satın alma başarısız oldu, lütfen daha sonra tekrar deneyin", "credits.topUp.purchaseNow": "Şimdi Satın Al", @@ -120,6 +123,9 @@ "keyMissMatch.button": "Kullanımı geri yükle ve konuşmaya devam et", "keyMissMatch.description": "Zaman zaman yaşanan sistem hatası nedeniyle mevcut abonelik kullanımınız geçici olarak devre dışı. Lütfen aşağıdaki butona tıklayarak kullanımı geri yükleyin ve konuşmaya devam edin. Bu durum tekrar ederse, lütfen bizimle e-posta yoluyla iletişime geçin (support@lobehub.com)", "keyMissMatch.title": "Abonelik Kullanımını Şimdi Geri Yükle", + "limitation.chat.budgetReady.action": "Sohbete Devam Et", + "limitation.chat.budgetReady.desc": "Mevcut kredileriniz bu isteği karşılıyor.", + "limitation.chat.budgetReady.title": "Krediler Hazır", "limitation.chat.success.action": "Sohbete Devam Et", "limitation.chat.success.desc": "{{plan}} aboneliğiniz başarıyla yükseltildi. Yapay zeka sohbetinin keyfini çıkarın. Mevcut planınız şunları içerir:", "limitation.chat.success.title": "Yükseltme Başarılı", @@ -134,14 +140,23 @@ "limitation.hobby.docs": "Yapılandırma belgelerini görüntüle", "limitation.hobby.tip": "Özel API Anahtarı olan bir modele geçmeyi unutmayın", "limitation.hobby.title": "Model Servis API'sini Yapılandırın", + "limitation.image.budgetReady.action": "Üretime Devam Et", + "limitation.image.budgetReady.desc": "Mevcut kredileriniz bu üretimi karşılıyor.", + "limitation.image.budgetReady.title": "Krediler Hazır", "limitation.image.success.action": "Oluşturmaya Devam Et", "limitation.image.success.desc": "{{plan}} aboneliğiniz başarıyla yükseltildi. Yapay zeka görsel üretiminin keyfini çıkarın. Mevcut planınız şunları içerir:", "limitation.image.success.title": "Yükseltme Başarılı", "limitation.image.topupSuccess.action": "Oluşturmaya Devam Et", "limitation.image.topupSuccess.desc": "Yüklediğiniz krediler artık aktif. Yapay zeka görsel üretiminin keyfini çıkarın. Mevcut planınız şunları içerir:", "limitation.image.topupSuccess.title": "Yükleme Başarılı", + "limitation.insufficientBudget.approximateDesc": "Bu istek için daha fazla kredi gerekebilir. Kredileri yükleyin veya planınızı yükseltin.", + "limitation.insufficientBudget.available": "Mevcut Krediler", "limitation.insufficientBudget.desc": "Kalan kredileriniz bu modelin tahmini maliyeti için yeterli değil. Lütfen kredi yükleyin veya daha az maliyetli bir modele geçin.", + "limitation.insufficientBudget.estimatedDesc": "Bu isteğin daha fazla kredi gerektirdiği tahmin ediliyor. Kredileri yükleyin veya planınızı yükseltin.", + "limitation.insufficientBudget.exactDesc": "Bu istek için daha fazla kredi gerekiyor. Kredileri yükleyin veya planınızı yükseltin.", + "limitation.insufficientBudget.required": "Gerekli Krediler", "limitation.insufficientBudget.retry": "Tekrar Dene", + "limitation.insufficientBudget.shortfall": "Kredi Eksikliği", "limitation.insufficientBudget.title": "Bu Model İçin Yetersiz Kredi", "limitation.limited.action": "Şimdi Yükselt", "limitation.limited.advanceFeature": "Premium özelliklerin keyfini çıkarmak için yükseltin:", @@ -151,6 +166,7 @@ "limitation.limited.title": "Hesaplama Kredileri Tükendi", "limitation.limited.topup": "Kredi Yükle", "limitation.limited.upgrade": "Daha Yüksek Plana Yükselt", + "limitation.limited.upgradeToPlan": "{{plan}} planına yükseltin", "limitation.providers.lock.addNew": "Özel yapay zeka sağlayıcıları oluşturmak için şimdi abone olun", "limitation.providers.lock.enableProvider": "Bu yapay zeka sağlayıcısını etkinleştirmek için şimdi abone olun", "limitation.providers.lock.menuItem": "Özel API servisini yapılandırmak için şimdi abone olun", @@ -161,6 +177,9 @@ "limitation.providers.prompter.subTitle": "Özel API servisi yalnızca ücretli planlarda mevcuttur. Küresel ana model servislerinin keyfini çıkarmak için şimdi yükseltin", "limitation.providers.prompter.title": "Özel API servisini kullanmak için şimdi abone olun", "limitation.providers.tooltip": "Özel API servisi yalnızca ücretli planlarda mevcuttur", + "limitation.video.budgetReady.action": "Üretime Devam Et", + "limitation.video.budgetReady.desc": "Mevcut kredileriniz bu üretimi karşılıyor.", + "limitation.video.budgetReady.title": "Krediler Hazır", "limitation.video.success.action": "Oluşturmaya Devam Et", "limitation.video.success.desc": "{{plan}} aboneliğiniz başarıyla yükseltildi. Yapay zeka ile video oluşturmanın keyfini çıkarın. Mevcut planınız şunları içeriyor:", "limitation.video.success.title": "Yükseltme Başarılı", diff --git a/locales/vi-VN/chat.json b/locales/vi-VN/chat.json index c568c20aa6..86798dc8fa 100644 --- a/locales/vi-VN/chat.json +++ b/locales/vi-VN/chat.json @@ -196,6 +196,9 @@ "groupWizard.searchTemplates": "Tìm kiếm mẫu...", "groupWizard.title": "Tạo nhóm", "groupWizard.useTemplate": "Sử dụng mẫu", + "heteroAgent.cloudNotConfigured.action": "Cấu hình", + "heteroAgent.cloudNotConfigured.desc": "Cấu hình mã thông báo Claude Code của bạn trong hồ sơ tác nhân để bắt đầu gửi tin nhắn.", + "heteroAgent.cloudNotConfigured.title": "Yêu cầu thông tin đăng nhập đám mây", "heteroAgent.cloudRepo.multiSelected": "{{count}} kho lưu trữ đã được chọn", "heteroAgent.cloudRepo.noRepos": "Chưa cấu hình kho lưu trữ nào. Thêm chúng trong cài đặt tác nhân.", "heteroAgent.cloudRepo.notSet": "Chưa chọn kho lưu trữ nào", @@ -467,6 +470,10 @@ "sharePage.error.unauthorized.subtitle": "Vui lòng đăng nhập để xem chủ đề được chia sẻ.", "sharePage.error.unauthorized.title": "Yêu cầu đăng nhập", "sharePageDisclaimer": "Nội dung này được chia sẻ bởi người dùng và không đại diện cho quan điểm của LobeHub. LobeHub không chịu trách nhiệm cho bất kỳ hậu quả nào phát sinh từ nội dung được chia sẻ này.", + "signalCallbacks.collapse": "Ẩn chi tiết", + "signalCallbacks.empty": "Không có tin nhắn phản hồi", + "signalCallbacks.expand": "Hiển thị chi tiết", + "signalCallbacks.title": "{{tool}} · {{count}} cập nhật phản hồi", "stt.action": "Nhập bằng giọng nói", "stt.loading": "Đang nhận dạng...", "stt.prettifying": "Đang chỉnh sửa...", @@ -689,7 +696,6 @@ "thread.closeSubagentThread": "Thu gọn hội thoại tác tử phụ", "thread.divider": "Chủ đề phụ", "thread.openSubagentThread": "Xem toàn bộ hội thoại tác tử phụ", - "thread.subagentBadge": "Tác tử phụ", "thread.subagentReadOnlyHint": "Các cuộc trò chuyện của SubAgent chỉ có thể đọc — việc thực thi được điều khiển bởi tác nhân chính.", "thread.threadMessageCount": "{{messageCount}} tin nhắn", "thread.title": "Chủ đề phụ", diff --git a/locales/vi-VN/models.json b/locales/vi-VN/models.json index e9c1384e26..7669a8cae8 100644 --- a/locales/vi-VN/models.json +++ b/locales/vi-VN/models.json @@ -106,6 +106,7 @@ "MiniMax-Hailuo-2.3.description": "Mô hình tạo video hoàn toàn mới với các nâng cấp toàn diện về chuyển động cơ thể, tính hiện thực vật lý và tuân thủ hướng dẫn.", "MiniMax-M1.description": "Mô hình suy luận nội bộ mới với 80K chuỗi suy nghĩ và đầu vào 1M, đạt hiệu suất tương đương các mô hình hàng đầu toàn cầu.", "MiniMax-M2-Stable.description": "Được xây dựng cho lập trình hiệu quả và quy trình tác tử, với khả năng đồng thời cao hơn cho mục đích thương mại.", + "MiniMax-M2.1-Lightning.description": "Khả năng lập trình đa ngôn ngữ mạnh mẽ với suy luận nhanh hơn và hiệu quả hơn.", "MiniMax-M2.1-highspeed.description": "Khả năng lập trình đa ngôn ngữ mạnh mẽ, trải nghiệm lập trình được nâng cấp toàn diện. Nhanh hơn và hiệu quả hơn.", "MiniMax-M2.1.description": "Khả năng lập trình đa ngôn ngữ mạnh mẽ, trải nghiệm lập trình được nâng cấp toàn diện", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: Hiệu suất tương tự như M2.5 với suy luận nhanh hơn.", @@ -314,13 +315,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku là mô hình nhanh nhất và nhỏ gọn nhất của Anthropic, được thiết kế cho phản hồi gần như tức thì với hiệu suất nhanh và chính xác.", "claude-3-opus-20240229.description": "Claude 3 Opus là mô hình mạnh mẽ nhất của Anthropic cho các tác vụ phức tạp, xuất sắc về hiệu suất, trí tuệ, lưu loát và hiểu biết.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet cân bằng giữa trí tuệ và tốc độ cho khối lượng công việc doanh nghiệp, mang lại giá trị cao với chi phí thấp hơn và triển khai quy mô lớn đáng tin cậy.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 là mô hình Haiku nhanh nhất và thông minh nhất của Anthropic, với tốc độ cực nhanh và khả năng suy luận mở rộng.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 là mô hình Haiku nhanh nhất và thông minh nhất của Anthropic, với tốc độ vượt trội và khả năng tư duy mở rộng.", "claude-haiku-4-5.description": "Claude Haiku 4.5 của Anthropic — Haiku thế hệ mới với khả năng lý luận và thị giác nâng cao.", "claude-haiku-4.5.description": "Claude Haiku 4.5 là mô hình Haiku nhanh nhất và thông minh nhất của Anthropic, với tốc độ vượt trội và khả năng suy luận mở rộng.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking là biến thể nâng cao có thể hiển thị quá trình suy luận của nó.", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1 là mô hình mới nhất và mạnh mẽ nhất của Anthropic dành cho các nhiệm vụ phức tạp cao, vượt trội về hiệu suất, trí tuệ, sự lưu loát và khả năng hiểu biết.", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1 là mô hình mới nhất và mạnh mẽ nhất của Anthropic dành cho các nhiệm vụ phức tạp, vượt trội về hiệu suất, trí tuệ, sự lưu loát và khả năng hiểu biết.", "claude-opus-4-1.description": "Claude Opus 4.1 của Anthropic — mô hình lý luận cao cấp với khả năng phân tích sâu.", - "claude-opus-4-20250514.description": "Claude Opus 4 là mô hình mạnh mẽ nhất của Anthropic dành cho các nhiệm vụ phức tạp cao, vượt trội về hiệu suất, trí tuệ, sự lưu loát và khả năng lĩnh hội.", + "claude-opus-4-20250514.description": "Claude Opus 4 là mô hình mạnh mẽ nhất của Anthropic dành cho các nhiệm vụ phức tạp, vượt trội về hiệu suất, trí tuệ, sự lưu loát và khả năng hiểu biết.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 là mô hình hàng đầu của Anthropic, kết hợp trí tuệ vượt trội với hiệu suất có thể mở rộng, lý tưởng cho các tác vụ phức tạp đòi hỏi phản hồi và suy luận chất lượng cao nhất.", "claude-opus-4-5.description": "Claude Opus 4.5 của Anthropic — mô hình hàng đầu với khả năng lý luận và mã hóa đỉnh cao.", "claude-opus-4-6.description": "Claude Opus 4.6 của Anthropic — mô hình hàng đầu với cửa sổ ngữ cảnh 1 triệu và khả năng lý luận nâng cao.", @@ -329,7 +330,7 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 là mô hình thông minh nhất của Anthropic dành cho việc xây dựng các tác nhân và lập trình.", "claude-opus-4.6.description": "Claude Opus 4.6 là mô hình thông minh nhất của Anthropic dành cho việc xây dựng các tác nhân và lập trình.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking có thể tạo phản hồi gần như tức thì hoặc suy luận từng bước mở rộng với quy trình hiển thị.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 có thể tạo ra phản hồi gần như tức thì hoặc suy nghĩ từng bước một với quy trình rõ ràng.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 là mô hình thông minh nhất của Anthropic cho đến nay, cung cấp phản hồi gần như tức thì hoặc tư duy từng bước mở rộng với khả năng kiểm soát chi tiết cho người dùng API.", "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 là mô hình thông minh nhất của Anthropic cho đến nay.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 của Anthropic — Sonnet được cải tiến với hiệu suất mã hóa nâng cao.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 của Anthropic — Sonnet mới nhất với khả năng mã hóa và sử dụng công cụ vượt trội.", @@ -403,7 +404,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) là một mô hình sáng tạo cung cấp khả năng hiểu và tương tác ngôn ngữ sâu sắc.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 là mô hình suy luận thế hệ mới với khả năng suy luận phức tạp mạnh mẽ và chuỗi suy nghĩ cho các tác vụ phân tích chuyên sâu.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 là mô hình suy luận thế hệ mới với khả năng suy luận phức tạp mạnh mẽ và tư duy chuỗi.", - "deepseek-chat.description": "Một mô hình mã nguồn mở mới kết hợp khả năng tổng quát và mã hóa. Nó duy trì đối thoại chung của mô hình trò chuyện và khả năng mã hóa mạnh mẽ của mô hình coder, với sự điều chỉnh ưu tiên tốt hơn. DeepSeek-V2.5 cũng cải thiện khả năng viết và tuân theo hướng dẫn.", + "deepseek-chat.description": "Tên thay thế tương thích cho chế độ không tư duy của DeepSeek V4 Flash. Dự kiến sẽ bị loại bỏ — hãy sử dụng DeepSeek V4 Flash thay thế.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B là mô hình ngôn ngữ lập trình được huấn luyện trên 2 nghìn tỷ token (87% mã nguồn, 13% văn bản tiếng Trung/Anh). Mô hình này hỗ trợ cửa sổ ngữ cảnh 16K và nhiệm vụ điền vào giữa đoạn mã, cung cấp khả năng hoàn thành mã ở cấp độ dự án và chèn đoạn mã chính xác.", "deepseek-coder-v2.description": "DeepSeek Coder V2 là mô hình mã nguồn MoE mã nguồn mở với hiệu suất mạnh mẽ trong các tác vụ lập trình, có thể so sánh với GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 là mô hình mã nguồn MoE mã nguồn mở với hiệu suất mạnh mẽ trong các tác vụ lập trình, có thể so sánh với GPT-4 Turbo.", @@ -425,7 +426,7 @@ "deepseek-r1-fast-online.description": "Phiên bản đầy đủ DeepSeek R1 nhanh với tìm kiếm web thời gian thực, kết hợp khả năng 671B và phản hồi nhanh hơn.", "deepseek-r1-online.description": "Phiên bản đầy đủ DeepSeek R1 với 671B tham số và tìm kiếm web thời gian thực, mang lại khả năng hiểu và tạo nội dung mạnh mẽ hơn.", "deepseek-r1.description": "DeepSeek-R1 sử dụng dữ liệu khởi động lạnh trước khi áp dụng học tăng cường và đạt hiệu suất tương đương OpenAI-o1 trong các tác vụ toán học, lập trình và suy luận.", - "deepseek-reasoner.description": "Một mô hình suy luận DeepSeek tập trung vào các nhiệm vụ suy luận logic phức tạp.", + "deepseek-reasoner.description": "Tên thay thế tương thích cho chế độ tư duy của DeepSeek V4 Flash. Dự kiến sẽ bị loại bỏ — hãy sử dụng DeepSeek V4 Flash thay thế.", "deepseek-v2.description": "DeepSeek V2 là mô hình MoE hiệu quả cho xử lý tiết kiệm chi phí.", "deepseek-v2:236b.description": "DeepSeek V2 236B là mô hình tập trung vào mã nguồn của DeepSeek với khả năng tạo mã mạnh mẽ.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 là mô hình MoE với 671B tham số, nổi bật về lập trình, khả năng kỹ thuật, hiểu ngữ cảnh và xử lý văn bản dài.", @@ -490,6 +491,8 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 là mô hình tạo hình ảnh từ ByteDance Seed, hỗ trợ đầu vào văn bản và hình ảnh với khả năng tạo hình ảnh chất lượng cao, dễ kiểm soát. Mô hình tạo hình ảnh từ văn bản gợi ý.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 là mô hình hình ảnh đa phương thức mới nhất của ByteDance, tích hợp khả năng tạo hình ảnh từ văn bản, chỉnh sửa hình ảnh và tạo hình ảnh hàng loạt, đồng thời kết hợp khả năng suy luận và kiến thức thông thường. So với phiên bản 4.0 trước đó, nó mang lại chất lượng tạo hình ảnh được cải thiện đáng kể, với sự nhất quán chỉnh sửa tốt hơn và khả năng hợp nhất nhiều hình ảnh. Nó cung cấp khả năng kiểm soát chi tiết hình ảnh chính xác hơn, tạo ra văn bản nhỏ và khuôn mặt nhỏ một cách tự nhiên hơn, và đạt được bố cục và màu sắc hài hòa hơn, nâng cao tính thẩm mỹ tổng thể.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite là mô hình tạo hình ảnh mới nhất của ByteDance. Lần đầu tiên, nó tích hợp khả năng truy xuất trực tuyến, cho phép kết hợp thông tin web theo thời gian thực và nâng cao tính kịp thời của hình ảnh được tạo. Trí tuệ của mô hình cũng được nâng cấp, cho phép diễn giải chính xác các hướng dẫn phức tạp và nội dung hình ảnh. Ngoài ra, nó cung cấp phạm vi kiến thức toàn cầu được cải thiện, tính nhất quán tham chiếu và chất lượng tạo hình ảnh trong các tình huống chuyên nghiệp, đáp ứng tốt hơn nhu cầu sáng tạo hình ảnh ở cấp độ doanh nghiệp.", + "dreamina-seedance-2-0-260128.description": "Seedance 2.0 của ByteDance là mô hình tạo video mạnh mẽ nhất, hỗ trợ tạo video tham chiếu đa phương thức, chỉnh sửa video, mở rộng video, chuyển văn bản thành video và chuyển hình ảnh thành video với âm thanh đồng bộ.", + "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast của ByteDance cung cấp các khả năng tương tự như Seedance 2.0 với tốc độ tạo nhanh hơn và giá cả cạnh tranh hơn.", "emohaa.description": "Emohaa là mô hình hỗ trợ sức khỏe tinh thần với khả năng tư vấn chuyên nghiệp, giúp người dùng hiểu rõ các vấn đề cảm xúc.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B là mô hình nhẹ mã nguồn mở, phù hợp để triển khai cục bộ và tùy chỉnh.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview là mô hình xem trước với ngữ cảnh 8K để đánh giá ERNIE 4.5.", @@ -514,7 +517,8 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K là mô hình tư duy nhanh với ngữ cảnh 32K dành cho lý luận phức tạp và trò chuyện nhiều lượt.", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview là bản xem trước mô hình tư duy để đánh giá và thử nghiệm.", "ernie-x1.1.description": "ERNIE X1.1 là mô hình suy nghĩ thử nghiệm dành cho đánh giá và kiểm tra.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 là mô hình tạo hình ảnh từ ByteDance Seed, hỗ trợ đầu vào văn bản và hình ảnh với khả năng tạo hình ảnh chất lượng cao, có thể kiểm soát cao. Nó tạo hình ảnh từ các gợi ý văn bản.", + "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5, được phát triển bởi đội ngũ Seed của ByteDance, hỗ trợ chỉnh sửa và ghép nhiều hình ảnh. Tính năng bao gồm cải thiện tính nhất quán của chủ thể, tuân thủ hướng dẫn chính xác, hiểu biết logic không gian, biểu đạt thẩm mỹ, bố cục poster và thiết kế logo với khả năng kết xuất văn bản-hình ảnh chính xác cao.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, được phát triển bởi ByteDance Seed, hỗ trợ đầu vào văn bản và hình ảnh để tạo hình ảnh chất lượng cao, có khả năng kiểm soát cao từ các gợi ý.", "fal-ai/flux-kontext/dev.description": "Mô hình FLUX.1 tập trung vào chỉnh sửa hình ảnh, hỗ trợ đầu vào văn bản và hình ảnh.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] chấp nhận đầu vào là văn bản và hình ảnh tham chiếu, cho phép chỉnh sửa cục bộ chính xác và biến đổi toàn cảnh phức tạp.", "fal-ai/flux/krea.description": "Flux Krea [dev] là mô hình tạo hình ảnh với thiên hướng thẩm mỹ hướng đến hình ảnh chân thực và tự nhiên hơn.", @@ -522,8 +526,8 @@ "fal-ai/hunyuan-image/v3.description": "Mô hình tạo hình ảnh đa phương thức mạnh mẽ bản địa.", "fal-ai/imagen4/preview.description": "Mô hình tạo hình ảnh chất lượng cao từ Google.", "fal-ai/nano-banana.description": "Nano Banana là mô hình đa phương thức bản địa mới nhất, nhanh nhất và hiệu quả nhất của Google, cho phép tạo và chỉnh sửa hình ảnh thông qua hội thoại.", - "fal-ai/qwen-image-edit.description": "Một mô hình chỉnh sửa hình ảnh chuyên nghiệp từ đội ngũ Qwen, hỗ trợ chỉnh sửa ngữ nghĩa và hình thức, chỉnh sửa chính xác văn bản tiếng Trung và tiếng Anh, và cho phép các chỉnh sửa chất lượng cao như chuyển đổi phong cách và xoay vật thể.", - "fal-ai/qwen-image.description": "Một mô hình tạo hình ảnh mạnh mẽ từ đội ngũ Qwen với khả năng hiển thị văn bản tiếng Trung ấn tượng và các phong cách hình ảnh đa dạng.", + "fal-ai/qwen-image-edit.description": "Mô hình chỉnh sửa hình ảnh chuyên nghiệp từ đội ngũ Qwen, hỗ trợ chỉnh sửa ngữ nghĩa và ngoại hình, chỉnh sửa văn bản tiếng Trung/Anh chính xác, chuyển đổi phong cách, xoay hình và nhiều hơn nữa.", + "fal-ai/qwen-image.description": "Mô hình tạo hình ảnh mạnh mẽ từ đội ngũ Qwen với khả năng kết xuất văn bản tiếng Trung mạnh mẽ và phong cách hình ảnh đa dạng.", "flux-1-schnell.description": "Mô hình chuyển văn bản thành hình ảnh với 12 tỷ tham số từ Black Forest Labs, sử dụng phương pháp khuếch tán đối kháng tiềm ẩn để tạo hình ảnh chất lượng cao chỉ trong 1–4 bước. Mô hình cạnh tranh với các lựa chọn đóng và được phát hành theo giấy phép Apache-2.0 cho mục đích cá nhân, nghiên cứu và thương mại.", "flux-dev.description": "Mô hình tạo ảnh R&D mã nguồn mở, tối ưu hiệu quả cho nghiên cứu sáng tạo phi thương mại.", "flux-kontext-max.description": "Tạo và chỉnh sửa hình ảnh theo ngữ cảnh tiên tiến, kết hợp văn bản và hình ảnh để tạo ra kết quả chính xác và mạch lạc.", @@ -565,7 +569,7 @@ "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) là mô hình tạo hình ảnh của Google và cũng hỗ trợ trò chuyện đa phương thức.", "gemini-3-pro-preview.description": "Gemini 3 Pro là mô hình mạnh mẽ nhất của Google, kết hợp khả năng mã hóa cảm xúc và suy luận tiên tiến, mang đến hình ảnh phong phú và tương tác sâu sắc.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) là mô hình tạo hình ảnh bản địa nhanh nhất của Google với hỗ trợ suy nghĩ, tạo hình ảnh đối thoại và chỉnh sửa.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) là mô hình tạo hình ảnh nhanh nhất của Google với hỗ trợ tư duy, tạo và chỉnh sửa hình ảnh trong cuộc trò chuyện.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) cung cấp chất lượng hình ảnh cấp độ Pro với tốc độ Flash và hỗ trợ trò chuyện đa phương thức.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview là mô hình đa phương thức tiết kiệm chi phí nhất của Google, được tối ưu hóa cho các nhiệm vụ tác nhân khối lượng lớn, dịch thuật và xử lý dữ liệu.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite là mô hình đa phương thức tiết kiệm chi phí nhất của Google, được tối ưu hóa cho các nhiệm vụ đại lý khối lượng lớn, dịch thuật và xử lý dữ liệu.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview cải tiến Gemini 3 Pro với khả năng suy luận nâng cao và bổ sung hỗ trợ mức suy nghĩ trung bình.", @@ -730,6 +734,8 @@ "grok-4-fast-reasoning.description": "Chúng tôi rất vui mừng ra mắt Grok 4 Fast, bước tiến mới nhất trong các mô hình suy luận tiết kiệm chi phí.", "grok-4.20-0309-non-reasoning.description": "Biến thể không lý luận dành cho các trường hợp sử dụng đơn giản.", "grok-4.20-0309-reasoning.description": "Mô hình thông minh, siêu nhanh, lý luận trước khi phản hồi.", + "grok-4.20-beta-0309-non-reasoning.description": "Biến thể không tư duy dành cho các trường hợp sử dụng đơn giản.", + "grok-4.20-beta-0309-reasoning.description": "Mô hình thông minh, cực nhanh, có khả năng tư duy trước khi phản hồi.", "grok-4.20-multi-agent-0309.description": "Một nhóm gồm 4 hoặc 16 tác nhân, xuất sắc trong các trường hợp nghiên cứu. Hiện không hỗ trợ công cụ phía khách hàng. Chỉ hỗ trợ công cụ phía máy chủ xAI (ví dụ: X Search, công cụ tìm kiếm web) và công cụ MCP từ xa.", "grok-4.3.description": "Mô hình ngôn ngữ lớn tìm kiếm sự thật nhất trên thế giới", "grok-4.description": "Mô hình Grok hàng đầu mới nhất với hiệu suất vượt trội trong ngôn ngữ, toán học và suy luận — một mô hình toàn diện thực sự. Hiện tại đang trỏ đến grok-4-0709; do nguồn lực hạn chế, giá tạm thời cao hơn 10% so với giá chính thức và dự kiến sẽ trở lại giá chính thức sau.", @@ -1214,6 +1220,8 @@ "qwq.description": "QwQ là mô hình lập luận trong họ Qwen. So với các mô hình điều chỉnh theo hướng dẫn tiêu chuẩn, nó mang lại khả năng tư duy và lập luận giúp cải thiện đáng kể hiệu suất các tác vụ phía sau, đặc biệt là các vấn đề khó. QwQ-32B là mô hình lập luận tầm trung có thể cạnh tranh với các mô hình hàng đầu như DeepSeek-R1 và o1-mini.", "qwq_32b.description": "Mô hình lập luận tầm trung trong họ Qwen. So với các mô hình điều chỉnh theo hướng dẫn tiêu chuẩn, khả năng tư duy và lập luận của QwQ giúp cải thiện đáng kể hiệu suất các tác vụ phía sau, đặc biệt là các vấn đề khó.", "r1-1776.description": "R1-1776 là biến thể hậu huấn luyện của DeepSeek R1 được thiết kế để cung cấp thông tin thực tế không kiểm duyệt, không thiên lệch.", + "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro của ByteDance hỗ trợ chuyển văn bản thành video, chuyển hình ảnh thành video (khung đầu tiên, khung đầu tiên + khung cuối cùng) và tạo âm thanh đồng bộ với hình ảnh.", + "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite của BytePlus có tính năng tạo nội dung tăng cường truy xuất web để cung cấp thông tin theo thời gian thực, cải thiện diễn giải gợi ý phức tạp và tăng cường tính nhất quán tham chiếu cho sáng tạo hình ảnh chuyên nghiệp.", "solar-mini-ja.description": "Solar Mini (Ja) mở rộng Solar Mini với trọng tâm vào tiếng Nhật trong khi vẫn duy trì hiệu suất mạnh mẽ và hiệu quả với tiếng Anh và tiếng Hàn.", "solar-mini.description": "Solar Mini là mô hình ngôn ngữ nhỏ gọn vượt trội hơn GPT-3.5, với khả năng đa ngôn ngữ mạnh mẽ hỗ trợ tiếng Anh và tiếng Hàn, mang lại giải pháp hiệu quả với dung lượng nhỏ.", "solar-pro.description": "Solar Pro là mô hình ngôn ngữ thông minh cao từ Upstage, tập trung vào tuân thủ hướng dẫn trên một GPU duy nhất, với điểm IFEval trên 80. Hiện hỗ trợ tiếng Anh; bản phát hành đầy đủ dự kiến vào tháng 11 năm 2024 với hỗ trợ ngôn ngữ mở rộng và ngữ cảnh dài hơn.", diff --git a/locales/vi-VN/plugin.json b/locales/vi-VN/plugin.json index 15ef49cc24..c63cd6c7ea 100644 --- a/locales/vi-VN/plugin.json +++ b/locales/vi-VN/plugin.json @@ -237,6 +237,14 @@ "builtins.lobe-page-agent.apiName.updateNode": "Cập nhật nút", "builtins.lobe-page-agent.apiName.wrapNodes": "Bao nút", "builtins.lobe-page-agent.title": "Trang", + "builtins.lobe-self-feedback-intent.apiName.declareSelfFeedbackIntent": "Ghi lại ý tưởng cải tiến", + "builtins.lobe-self-feedback-intent.inspector.gap.proposal": "Đề xuất cải tiến", + "builtins.lobe-self-feedback-intent.inspector.memory.write": "Ghi lại sở thích", + "builtins.lobe-self-feedback-intent.inspector.rejected": "Không được ghi lại", + "builtins.lobe-self-feedback-intent.inspector.skill.consolidate": "Sắp xếp phương pháp", + "builtins.lobe-self-feedback-intent.inspector.skill.create": "Phương pháp mới được tìm thấy", + "builtins.lobe-self-feedback-intent.inspector.skill.refine": "Cải thiện phương pháp", + "builtins.lobe-self-feedback-intent.title": "Ý tưởng cải tiến", "builtins.lobe-skill-store.apiName.importFromMarket": "Nhập từ Thị trường", "builtins.lobe-skill-store.apiName.importSkill": "Nhập Kỹ năng", "builtins.lobe-skill-store.apiName.searchSkill": "Tìm kiếm Kỹ năng", diff --git a/locales/vi-VN/providers.json b/locales/vi-VN/providers.json index f02a27c541..26790e7dda 100644 --- a/locales/vi-VN/providers.json +++ b/locales/vi-VN/providers.json @@ -33,6 +33,7 @@ "jina.description": "Thành lập năm 2020, Jina AI là công ty hàng đầu về AI tìm kiếm. Bộ công cụ tìm kiếm của họ bao gồm mô hình vector, bộ xếp hạng lại và mô hình ngôn ngữ nhỏ để xây dựng ứng dụng tìm kiếm sinh và đa phương thức chất lượng cao.", "kimicodingplan.description": "Kimi Code từ Moonshot AI cung cấp quyền truy cập vào các mô hình Kimi bao gồm K2.5 cho các nhiệm vụ lập trình.", "lmstudio.description": "LM Studio là ứng dụng máy tính để phát triển và thử nghiệm LLM ngay trên máy của bạn.", + "lobehub.description": "LobeHub Cloud sử dụng các API chính thức để truy cập các mô hình AI và đo lường việc sử dụng bằng Tín dụng gắn liền với các token của mô hình.", "longcat.description": "LongCat là một loạt các mô hình AI tạo sinh lớn được phát triển độc lập bởi Meituan. Nó được thiết kế để nâng cao năng suất nội bộ của doanh nghiệp và thúc đẩy các ứng dụng sáng tạo thông qua kiến trúc tính toán hiệu quả và khả năng đa phương thức mạnh mẽ.", "minimax.description": "Thành lập năm 2021, MiniMax xây dựng AI đa năng với các mô hình nền tảng đa phương thức, bao gồm mô hình văn bản MoE hàng nghìn tỷ tham số, mô hình giọng nói và thị giác, cùng các ứng dụng như Hailuo AI.", "minimaxcodingplan.description": "MiniMax Token Plan cung cấp quyền truy cập vào các mô hình MiniMax bao gồm M2.7 cho các nhiệm vụ lập trình thông qua gói đăng ký cố định.", diff --git a/locales/vi-VN/subscription.json b/locales/vi-VN/subscription.json index 9e5e9abb60..8285dc7f70 100644 --- a/locales/vi-VN/subscription.json +++ b/locales/vi-VN/subscription.json @@ -41,7 +41,9 @@ "credits.autoTopUp.monthlyLimitDesc": "Số tiền tối đa có thể tự động nạp mỗi tháng. Để trống nếu không giới hạn", "credits.autoTopUp.monthlyLimitPlaceholder": "Không giới hạn", "credits.autoTopUp.monthlyTopUpAmount": "Số tiền nạp hàng tháng", + "credits.autoTopUp.noCustomerHint": "Mua tín dụng một lần để lưu phương thức thanh toán trước khi bật tự động nạp tiền.", "credits.autoTopUp.noPaymentMethodHint": "Không có phương thức thanh toán nào được lưu. Tự động nạp tiền cần một thẻ đã lưu để tự động trừ tiền.", + "credits.autoTopUp.purchaseCredits": "Mua Tín Dụng", "credits.autoTopUp.saveError": "Không thể lưu cài đặt tự động nạp tiền", "credits.autoTopUp.saveSuccess": "Cài đặt tự động nạp tiền đã được lưu", "credits.autoTopUp.setupPaymentMethod": "Thêm Phương Thức Thanh Toán", @@ -83,6 +85,7 @@ "credits.packages.title": "Gói tín dụng của tôi", "credits.topUp.cancel": "Hủy", "credits.topUp.custom": "Tùy chỉnh", + "credits.topUp.freeFeeHint": "Nạp tiền gói miễn phí bao gồm phí dịch vụ {{fee}} cho mỗi 1M tín dụng.", "credits.topUp.maxAmountError": "Số tiền mua một lần không được vượt quá ${{max}}", "credits.topUp.purchaseError": "Mua thất bại, vui lòng thử lại sau", "credits.topUp.purchaseNow": "Mua ngay", @@ -120,6 +123,9 @@ "keyMissMatch.button": "Khôi phục sử dụng và tiếp tục trò chuyện", "keyMissMatch.description": "Do lỗi hệ thống tạm thời, việc sử dụng đăng ký hiện tại của bạn đang bị gián đoạn. Vui lòng nhấn nút bên dưới để khôi phục và tiếp tục trò chuyện. Nếu lỗi này xảy ra nhiều lần, vui lòng liên hệ chúng tôi qua email (support@lobehub.com)", "keyMissMatch.title": "Khôi phục sử dụng đăng ký ngay", + "limitation.chat.budgetReady.action": "Tiếp Tục Trò Chuyện", + "limitation.chat.budgetReady.desc": "Số tín dụng khả dụng của bạn hiện đã đủ cho yêu cầu này.", + "limitation.chat.budgetReady.title": "Tín Dụng Sẵn Sàng", "limitation.chat.success.action": "Tiếp tục trò chuyện", "limitation.chat.success.desc": "Gói đăng ký {{plan}} của bạn đã được nâng cấp thành công. Hãy tận hưởng trò chuyện AI. Gói hiện tại của bạn bao gồm:", "limitation.chat.success.title": "Nâng cấp thành công", @@ -134,14 +140,23 @@ "limitation.hobby.docs": "Xem tài liệu cấu hình", "limitation.hobby.tip": "Hãy nhớ chuyển sang mô hình sử dụng API Key tùy chỉnh", "limitation.hobby.title": "Vui lòng cấu hình API dịch vụ mô hình", + "limitation.image.budgetReady.action": "Tiếp Tục Tạo", + "limitation.image.budgetReady.desc": "Số tín dụng khả dụng của bạn hiện đã đủ cho lần tạo này.", + "limitation.image.budgetReady.title": "Tín Dụng Sẵn Sàng", "limitation.image.success.action": "Tiếp tục tạo ảnh", "limitation.image.success.desc": "Gói đăng ký {{plan}} của bạn đã được nâng cấp thành công. Hãy tận hưởng tạo ảnh AI. Gói hiện tại của bạn bao gồm:", "limitation.image.success.title": "Nâng cấp thành công", "limitation.image.topupSuccess.action": "Tiếp tục tạo ảnh", "limitation.image.topupSuccess.desc": "Tín dụng nạp thêm của bạn đã được kích hoạt. Hãy tận hưởng tạo ảnh AI. Gói hiện tại của bạn bao gồm:", "limitation.image.topupSuccess.title": "Nạp tín dụng thành công", + "limitation.insufficientBudget.approximateDesc": "Yêu cầu này có thể cần thêm tín dụng. Nạp thêm tín dụng hoặc nâng cấp gói của bạn.", + "limitation.insufficientBudget.available": "Tín Dụng Khả Dụng", "limitation.insufficientBudget.desc": "Số dư tín dụng của bạn không đủ để chi trả cho chi phí ước tính của mô hình này. Vui lòng nạp thêm tín dụng hoặc chuyển sang mô hình ít tốn kém hơn.", + "limitation.insufficientBudget.estimatedDesc": "Yêu cầu này được ước tính cần thêm tín dụng. Nạp thêm tín dụng hoặc nâng cấp gói của bạn.", + "limitation.insufficientBudget.exactDesc": "Yêu cầu này cần thêm tín dụng. Nạp thêm tín dụng hoặc nâng cấp gói của bạn.", + "limitation.insufficientBudget.required": "Tín Dụng Cần Thiết", "limitation.insufficientBudget.retry": "Thử lại", + "limitation.insufficientBudget.shortfall": "Thiếu Hụt Tín Dụng", "limitation.insufficientBudget.title": "Không Đủ Tín Dụng Cho Mô Hình Này", "limitation.limited.action": "Nâng cấp ngay", "limitation.limited.advanceFeature": "Nâng cấp để sử dụng tính năng cao cấp:", @@ -151,6 +166,7 @@ "limitation.limited.title": "Tín dụng tính toán đã hết", "limitation.limited.topup": "Nạp thêm tín dụng", "limitation.limited.upgrade": "Nâng cấp lên gói cao hơn", + "limitation.limited.upgradeToPlan": "Nâng cấp lên {{plan}}", "limitation.providers.lock.addNew": "Đăng ký ngay để tạo nhà cung cấp AI tùy chỉnh", "limitation.providers.lock.enableProvider": "Đăng ký ngay để kích hoạt nhà cung cấp AI này", "limitation.providers.lock.menuItem": "Đăng ký ngay để cấu hình dịch vụ API tùy chỉnh", @@ -161,6 +177,9 @@ "limitation.providers.prompter.subTitle": "Dịch vụ API tùy chỉnh chỉ khả dụng cho các gói trả phí. Nâng cấp ngay để sử dụng các mô hình phổ biến toàn cầu", "limitation.providers.prompter.title": "Đăng ký ngay để sử dụng dịch vụ API tùy chỉnh", "limitation.providers.tooltip": "Dịch vụ API tùy chỉnh chỉ khả dụng cho các gói trả phí", + "limitation.video.budgetReady.action": "Tiếp Tục Tạo", + "limitation.video.budgetReady.desc": "Số tín dụng khả dụng của bạn hiện đã đủ cho lần tạo này.", + "limitation.video.budgetReady.title": "Tín Dụng Sẵn Sàng", "limitation.video.success.action": "Tiếp tục tạo video", "limitation.video.success.desc": "Gói đăng ký {{plan}} của bạn đã được nâng cấp thành công. Hãy tận hưởng tính năng tạo video bằng AI. Gói hiện tại của bạn bao gồm:", "limitation.video.success.title": "Nâng cấp thành công", diff --git a/locales/zh-CN/chat.json b/locales/zh-CN/chat.json index afd4c1f2ca..5b1c706545 100644 --- a/locales/zh-CN/chat.json +++ b/locales/zh-CN/chat.json @@ -470,6 +470,10 @@ "sharePage.error.unauthorized.subtitle": "请登录后查看此分享话题。", "sharePage.error.unauthorized.title": "需要登录", "sharePageDisclaimer": "此内容由用户分享,不代表 LobeHub 观点。LobeHub 不对该分享内容产生的任何后果承担责任。", + "signalCallbacks.collapse": "隐藏详情", + "signalCallbacks.empty": "没有回调消息", + "signalCallbacks.expand": "显示详情", + "signalCallbacks.title": "{{tool}} · {{count}} 条回调更新", "stt.action": "语音输入", "stt.loading": "识别中…", "stt.prettifying": "润色中…", @@ -692,7 +696,6 @@ "thread.closeSubagentThread": "收起 SubAgent 对话", "thread.divider": "子话题", "thread.openSubagentThread": "查看完整 SubAgent 对话", - "thread.subagentBadge": "SubAgent", "thread.subagentReadOnlyHint": "SubAgent 对话仅可查看,由父智能体驱动执行", "thread.threadMessageCount": "{{messageCount}} 条消息", "thread.title": "子话题", diff --git a/locales/zh-CN/models.json b/locales/zh-CN/models.json index 4a578e368c..8319380d37 100644 --- a/locales/zh-CN/models.json +++ b/locales/zh-CN/models.json @@ -106,6 +106,7 @@ "MiniMax-Hailuo-2.3.description": "全新视频生成模型,在身体动作、物理真实感和指令遵循性方面全面升级。", "MiniMax-M1.description": "一款全新自研推理模型,支持 80K 思维链和 100 万输入,性能媲美全球顶尖模型。", "MiniMax-M2-Stable.description": "专为高效编程与智能体工作流打造,具备更高并发能力,适用于商业场景。", + "MiniMax-M2.1-Lightning.description": "强大的多语言编程能力,推理速度更快、更高效。", "MiniMax-M2.1-highspeed.description": "强大的多语言编程能力,全面升级的编程体验。更快、更高效。", "MiniMax-M2.1.description": "MiniMax-M2.1 是 MiniMax 推出的旗舰开源大模型,专注于解决复杂的现实世界任务。其核心优势在于多语言编程能力以及作为智能体解决复杂任务的能力。", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed:与M2.5性能相同,但推理速度更快。", @@ -314,13 +315,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku 是 Anthropic 推出的最快、最紧凑的模型,专为近乎即时响应而设计,具备快速且准确的性能。", "claude-3-opus-20240229.description": "Claude 3 Opus 是 Anthropic 最强大的模型,适用于高度复杂的任务,在性能、智能、流畅性和理解力方面表现卓越。", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet 在智能与速度之间取得平衡,适用于企业级工作负载,提供高效能与低成本的可靠部署。", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 是 Anthropic 最快速、最智能的 Haiku 模型,具有闪电般的速度和扩展的推理能力。", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 是 Anthropic 最快、最智能的 Haiku 模型,具有闪电般的速度和扩展的思维能力。", "claude-haiku-4-5.description": "Anthropic 的 Claude Haiku 4.5 —— 新一代 Haiku,具备更强推理与视觉能力。", "claude-haiku-4.5.description": "Claude Haiku 4.5 是 Anthropic 最快速、最智能的 Haiku 模型,具有闪电般的速度和扩展的推理能力。", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking 是一款高级变体,能够展示其推理过程。", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1 是 Anthropic 最新且最强大的模型,专为高度复杂的任务设计,表现出色,具备卓越的智能、流畅性和理解能力。", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1 是 Anthropic 最新且最强大的模型,专为处理高度复杂的任务而设计,表现卓越,智能、流畅性和理解力均出类拔萃。", "claude-opus-4-1.description": "Anthropic 的 Claude Opus 4.1 —— 高端推理模型,具备深度分析能力。", - "claude-opus-4-20250514.description": "Claude Opus 4 是 Anthropic 最强大的模型,专为高度复杂的任务设计,表现出色,具备卓越的智能、流畅性和理解能力。", + "claude-opus-4-20250514.description": "Claude Opus 4 是 Anthropic 最强大的模型,专为处理高度复杂的任务而设计,表现卓越,智能、流畅性和理解力均出类拔萃。", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 是 Anthropic 的旗舰模型,结合卓越智能与可扩展性能,适用于需要最高质量响应与推理的复杂任务。", "claude-opus-4-5.description": "Anthropic 的 Claude Opus 4.5 —— 旗舰模型,具备顶级推理与编码能力。", "claude-opus-4-6.description": "Anthropic 的 Claude Opus 4.6 —— 具备 100 万上下文窗口的旗舰模型,拥有先进推理能力。", @@ -329,7 +330,7 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 是 Anthropic 最智能的模型,用于构建代理和编程。", "claude-opus-4.6.description": "Claude Opus 4.6 是 Anthropic 最智能的模型,用于构建代理和编程。", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking 可生成近乎即时的响应或可视化的逐步推理过程。", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 能够生成近乎即时的响应,或通过可见的过程进行逐步推理。", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 是迄今为止 Anthropic 最智能的模型,提供近乎即时的响应或扩展的逐步思考,并为 API 用户提供精细化控制。", "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 是迄今为止 Anthropic 最智能的模型。", "claude-sonnet-4-5.description": "Anthropic 的 Claude Sonnet 4.5 —— 性能增强的新一代 Sonnet,具备更强编码能力。", "claude-sonnet-4-6.description": "Anthropic 的 Claude Sonnet 4.6 —— 最新 Sonnet 模型,在编码与工具使用方面表现更强。", @@ -403,7 +404,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat(67B)是一款创新模型,具备深度语言理解与交互能力。", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 是下一代推理模型,具备更强的复杂推理与链式思维能力,适用于深度分析任务。", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2是下一代推理模型,具备更强的复杂推理和链式思维能力。", - "deepseek-chat.description": "一个结合了通用能力和代码能力的开源新模型。它保留了聊天模型的通用对话能力和代码模型的强大编程能力,并具有更好的偏好对齐。DeepSeek-V2.5 还改进了写作和指令遵循能力。", + "deepseek-chat.description": "DeepSeek V4 Flash 非思考模式的兼容别名。计划弃用——请改用 DeepSeek V4 Flash。", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B 是一款代码语言模型,训练于 2T 数据(87% 代码,13% 中英文文本)。支持 16K 上下文窗口与中间填充任务,提供项目级代码补全与片段填充。", "deepseek-coder-v2.description": "DeepSeek Coder V2 是一款开源 MoE 编程模型,在编程任务中表现强劲,可媲美 GPT-4 Turbo。", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 是一款开源 MoE 编程模型,在编程任务中表现强劲,可媲美 GPT-4 Turbo。", @@ -425,7 +426,7 @@ "deepseek-r1-fast-online.description": "DeepSeek R1 快速全量版本,支持实时网页搜索,结合 671B 规模能力与更快响应。", "deepseek-r1-online.description": "DeepSeek R1 全量版本,具备 671B 参数与实时网页搜索,提供更强理解与生成能力。", "deepseek-r1.description": "DeepSeek-R1 在强化学习前使用冷启动数据,在数学、编程和推理任务中表现可与 OpenAI-o1 相媲美。", - "deepseek-reasoner.description": "一个专注于复杂逻辑推理任务的 DeepSeek 推理模型。", + "deepseek-reasoner.description": "DeepSeek V4 Flash 思考模式的兼容别名。计划弃用——请改用 DeepSeek V4 Flash。", "deepseek-v2.description": "DeepSeek V2 是一款高效的 MoE 模型,适用于成本敏感型处理任务。", "deepseek-v2:236b.description": "DeepSeek V2 236B 是 DeepSeek 推出的代码专用模型,具备强大代码生成能力。", "deepseek-v3-0324.description": "DeepSeek-V3-0324 是一款拥有 671B 参数的 MoE 模型,在编程与技术能力、上下文理解和长文本处理方面表现突出。", @@ -490,6 +491,8 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 是字节跳动 Seed 推出的图像生成模型,支持文本与图像输入,具备高度可控的高质量图像生成能力。可根据文本提示生成图像。", "doubao-seedream-4-5-251128.description": "Seedream 4.5是字节跳动最新的多模态图像模型,集成了文本生成图像、图像生成图像和批量图像生成功能,同时具备常识和推理能力。与之前的4.0版本相比,生成质量显著提升,编辑一致性和多图融合效果更好。它对视觉细节的控制更加精确,能够更自然地生成小文字和小面部,并实现更和谐的布局和色彩,提升整体美感。", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite是字节跳动最新的图像生成模型。首次集成在线检索功能,能够结合实时网络信息,提升生成图像的时效性。模型智能性也得到升级,能够精确解析复杂指令和视觉内容。此外,它在专业场景中的全球知识覆盖、参考一致性和生成质量方面均有提升,更好地满足企业级视觉创作需求。", + "dreamina-seedance-2-0-260128.description": "字节跳动推出的 Seedance 2.0 是最强大的视频生成模型,支持多模态参考视频生成、视频编辑、视频扩展、文本生成视频以及图像生成视频,并同步音频。", + "dreamina-seedance-2-0-fast-260128.description": "字节跳动推出的 Seedance 2.0 Fast 提供与 Seedance 2.0 相同的功能,但生成速度更快,价格更具竞争力。", "emohaa.description": "Emohaa 是一款心理健康模型,具备专业咨询能力,帮助用户理解情绪问题。", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B 是一款开源轻量级模型,适用于本地和定制化部署。", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview 是一款用于评估 ERNIE 4.5 的 8K 上下文预览模型。", @@ -514,7 +517,8 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K 是一款快速思考模型,具备 32K 上下文能力,适合复杂推理与多轮对话。", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview 是一款用于评估与测试的思考模型预览版。", "ernie-x1.1.description": "ERNIE X1.1是一个用于评估和测试的思维模型预览版。", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 是字节跳动 Seed 的图像生成模型,支持文本和图像输入,能够高质量且高度可控地生成图像。它可以根据文本提示生成图像。", + "fal-ai/bytedance/seedream/v4.5.description": "字节跳动 Seed 团队打造的 Seedream 4.5 支持多图编辑与合成,具备增强的主体一致性、精准的指令执行、空间逻辑理解、美学表达、海报布局和标志设计,并提供高精度的图文渲染。", + "fal-ai/bytedance/seedream/v4.description": "字节跳动 Seed 团队打造的 Seedream 4.0 支持文本和图像输入,可根据提示生成高质量、可控性强的图像。", "fal-ai/flux-kontext/dev.description": "FLUX.1 模型专注于图像编辑,支持文本与图像输入。", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] 接受文本与参考图像输入,支持局部精准编辑与复杂全局场景变换。", "fal-ai/flux/krea.description": "Flux Krea [dev] 是一款图像生成模型,偏好更真实自然的美学风格。", @@ -522,8 +526,8 @@ "fal-ai/hunyuan-image/v3.description": "一款强大的原生多模态图像生成模型。", "fal-ai/imagen4/preview.description": "来自 Google 的高质量图像生成模型。", "fal-ai/nano-banana.description": "Nano Banana 是 Google 最新、最快、最高效的原生多模态模型,支持通过对话生成与编辑图像。", - "fal-ai/qwen-image-edit.description": "Qwen 团队推出的专业图像编辑模型,支持语义和外观编辑,精准编辑中英文文本,并实现高质量的编辑效果,如风格迁移和物体旋转。", - "fal-ai/qwen-image.description": "Qwen 团队推出的强大图像生成模型,具有令人印象深刻的中文文本渲染能力和多样化的视觉风格。", + "fal-ai/qwen-image-edit.description": "Qwen 团队推出的专业图像编辑模型,支持语义和外观编辑、精准的中英文文本编辑、风格转换、旋转等功能。", + "fal-ai/qwen-image.description": "Qwen 团队推出的强大图像生成模型,具备优秀的中文文本渲染能力和多样化的视觉风格。", "flux-1-schnell.description": "来自 Black Forest Labs 的 120 亿参数文本转图像模型,采用潜在对抗扩散蒸馏技术,可在 1-4 步内生成高质量图像。性能媲美闭源模型,采用 Apache-2.0 许可,适用于个人、研究与商业用途。", "flux-dev.description": "开源研发图像生成模型,专为非商业创新研究进行高效优化。", "flux-kontext-max.description": "最先进的上下文图像生成与编辑模型,结合文本与图像输入,实现精准一致的结果。", @@ -562,10 +566,10 @@ "gemini-3-flash-preview.description": "Gemini 3 Flash 是一款以速度为核心的智能模型,融合前沿智能与卓越的搜索能力。", "gemini-3-flash.description": "Google 的 Gemini 3 Flash —— 超高速模型,支持多模态输入。", "gemini-3-pro-image-preview.description": "Gemini 3 Pro Image(Nano Banana Pro)是 Google 的图像生成模型,同时支持多模态对话。", - "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image(Nano Banana Pro)是 Google 的图像生成模型,同时支持多模态聊天。", + "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image(Nano Banana Pro)是谷歌的图像生成模型,同时支持多模态聊天。", "gemini-3-pro-preview.description": "Gemini 3 Pro 是 Google 最强大的智能体与编程模型,在最先进推理基础上提供更丰富的视觉效果与更深入的交互体验。", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image(Nano Banana 2)是 Google 最快的原生图像生成模型,支持思考、对话式图像生成和编辑。", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image(Nano Banana 2)是 Google 最快速的原生图像生成模型,支持思维能力、对话式图像生成和编辑。", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image(Nano Banana 2)以闪电速度提供专业级图像质量,并支持多模态聊天。", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview是谷歌最具成本效益的多模态模型,专为高容量代理任务、翻译和数据处理优化。", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite 是谷歌最具成本效益的多模态模型,优化用于高容量代理任务、翻译和数据处理。", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview在Gemini 3 Pro的基础上增强了推理能力,并增加了中等思维水平支持。", @@ -730,6 +734,8 @@ "grok-4-fast-reasoning.description": "我们很高兴发布 Grok 4 Fast,这是我们在高性价比推理模型方面的最新进展。", "grok-4.20-0309-non-reasoning.description": "无推理模式版本,适用于简单场景。", "grok-4.20-0309-reasoning.description": "智能、高速的推理模型,先思考后回答。", + "grok-4.20-beta-0309-non-reasoning.description": "适用于简单用例的非推理版本。", + "grok-4.20-beta-0309-reasoning.description": "智能且极快的模型,在响应前进行推理。", "grok-4.20-multi-agent-0309.description": "由 4 至 16 个 Agent 组成的团队,擅长科研类任务。目前不支持客户端工具,只支持 xAI 服务器端工具(如 X Search、Web Search)及远程 MCP 工具。", "grok-4.3.description": "世界上最追求真理的大型语言模型", "grok-4.description": "最新的 Grok 旗舰模型,在语言、数学和推理方面表现无与伦比——真正的全能选手。目前指向 grok-4-0709;由于资源有限,价格暂时比官方定价高 10%,预计稍后会恢复到官方价格。", @@ -1214,6 +1220,8 @@ "qwq.description": "QwQ 是 Qwen 系列中的推理模型。相比标准指令微调模型,具备更强的思维与推理能力,显著提升下游复杂任务表现。QwQ-32B 是一款中型推理模型,性能可媲美 DeepSeek-R1 和 o1-mini 等顶级模型。", "qwq_32b.description": "Qwen 系列中的中型推理模型。相比标准指令微调模型,QwQ 的思维与推理能力显著提升下游复杂任务表现。", "r1-1776.description": "R1-1776 是 DeepSeek R1 的后训练版本,旨在提供无审查、无偏见的真实信息。", + "seedance-1-5-pro-251215.description": "字节跳动推出的 Seedance 1.5 Pro 支持文本生成视频、图像生成视频(首帧、首帧+尾帧)以及与视觉同步的音频生成。", + "seedream-5-0-260128.description": "字节跳动 BytePlus 推出的 Seedream 5.0 Lite,支持基于网页检索增强的实时信息生成,复杂提示解释能力提升,并改进参考一致性,适用于专业视觉创作。", "solar-mini-ja.description": "Solar Mini (Ja) 是 Solar Mini 的日语增强版本,同时保持在英语和韩语中的高效强性能。", "solar-mini.description": "Solar Mini 是一款紧凑型大语言模型,性能超越 GPT-3.5,具备强大的多语言能力,支持英语和韩语,提供高效的小体积解决方案。", "solar-pro.description": "Solar Pro 是 Upstage 推出的高智能大语言模型,专注于单 GPU 上的指令跟随任务,IFEval 得分超过 80。目前支持英语,完整版本计划于 2024 年 11 月发布,届时将扩展语言支持并提升上下文长度。", diff --git a/locales/zh-CN/providers.json b/locales/zh-CN/providers.json index 52bcd46109..3d00f55a6e 100644 --- a/locales/zh-CN/providers.json +++ b/locales/zh-CN/providers.json @@ -33,6 +33,7 @@ "jina.description": "Jina AI 成立于 2020 年,是领先的搜索 AI 公司,其搜索技术栈包括向量模型、重排序器与小型语言模型,支持构建高质量的生成式与多模态搜索应用。", "kimicodingplan.description": "Kimi Code Plan 由 Moonshot AI 提供,通过固定月费订阅方式访问 Kimi 模型(包括 K2.5)用于编程任务。", "lmstudio.description": "LM Studio 是一款桌面应用,支持在本地开发与实验大语言模型。", + "lobehub.description": "LobeHub Cloud 使用官方 API 访问 AI 模型,并通过与模型令牌相关的积分来衡量使用情况。", "longcat.description": "LongCat 是由美团自主研发的生成式 AI 大模型系列,旨在通过高效的计算架构和强大的多模态能力,提升企业内部工作效率并推动创新应用的发展。", "minimax.description": "MiniMax 成立于 2021 年,致力于构建通用 AI,拥有多模态基础模型,包括万亿参数的 MoE 文本模型、语音模型与视觉模型,并推出海螺 AI 等应用。", "minimaxcodingplan.description": "MiniMax Token Plan 提供对 MiniMax 模型(包括 M2.7)的访问,适用于编程任务,采用固定月费订阅模式。", diff --git a/locales/zh-CN/subscription.json b/locales/zh-CN/subscription.json index 30d5efa61a..667a31c20d 100644 --- a/locales/zh-CN/subscription.json +++ b/locales/zh-CN/subscription.json @@ -123,6 +123,9 @@ "keyMissMatch.button": "恢复使用并继续对话", "keyMissMatch.description": "由于系统偶发故障,您的订阅使用暂时失效。请点击下方按钮恢复使用并继续对话。如多次出现,请通过邮箱(support@lobehub.com)联系我们。", "keyMissMatch.title": "立即恢复订阅使用", + "limitation.chat.budgetReady.action": "继续聊天", + "limitation.chat.budgetReady.desc": "当前可用积分已足够支付本次请求。", + "limitation.chat.budgetReady.title": "积分已就绪", "limitation.chat.success.action": "继续聊天", "limitation.chat.success.desc": "您的 {{plan}} 订阅已成功升级,畅享 AI 聊天。当前套餐包含:", "limitation.chat.success.title": "升级成功", @@ -137,15 +140,24 @@ "limitation.hobby.docs": "查看配置文档", "limitation.hobby.tip": "请记得切换至使用自定义 API Key 的模型", "limitation.hobby.title": "请配置模型服务 API", + "limitation.image.budgetReady.action": "继续生成", + "limitation.image.budgetReady.desc": "当前可用积分已足够支付本次生成。", + "limitation.image.budgetReady.title": "积分已就绪", "limitation.image.success.action": "继续生成", "limitation.image.success.desc": "您的 {{plan}} 订阅已成功升级,畅享 AI 图像生成。当前套餐包含:", "limitation.image.success.title": "升级成功", "limitation.image.topupSuccess.action": "继续生成", "limitation.image.topupSuccess.desc": "您的充值积分已激活,畅享 AI 图像生成。当前套餐包含:", "limitation.image.topupSuccess.title": "充值成功", - "limitation.insufficientBudget.desc": "剩余额度不足以支付此模型的预估费用。请充值积分或切换到费用更低的模型。", + "limitation.insufficientBudget.approximateDesc": "本次请求预计需要更多积分,请充值积分或升级套餐。", + "limitation.insufficientBudget.available": "可用积分", + "limitation.insufficientBudget.desc": "当前可用积分不足以继续,请充值积分或升级套餐。", + "limitation.insufficientBudget.estimatedDesc": "本次请求预计需要更多积分,请充值积分或升级套餐。", + "limitation.insufficientBudget.exactDesc": "本次请求需要更多积分,请充值积分或升级套餐。", + "limitation.insufficientBudget.required": "所需积分", "limitation.insufficientBudget.retry": "重试", - "limitation.insufficientBudget.title": "额度不足以使用此模型", + "limitation.insufficientBudget.shortfall": "缺口积分", + "limitation.insufficientBudget.title": "积分不足", "limitation.limited.action": "立即升级", "limitation.limited.advanceFeature": "升级以解锁高级功能:", "limitation.limited.desc": "您的 {{plan}} 计算积分已用尽。请立即升级以获取更多积分。", @@ -154,6 +166,7 @@ "limitation.limited.title": "计算积分已用尽", "limitation.limited.topup": "充值积分", "limitation.limited.upgrade": "升级至更高套餐", + "limitation.limited.upgradeToPlan": "升级至 {{plan}}", "limitation.providers.lock.addNew": "立即订阅以创建自定义 AI 提供商", "limitation.providers.lock.enableProvider": "立即订阅以启用此 AI 提供商", "limitation.providers.lock.menuItem": "立即订阅以配置自定义 API 服务", @@ -164,6 +177,9 @@ "limitation.providers.prompter.subTitle": "自定义 API 服务仅对付费用户开放,升级后可使用全球主流模型服务", "limitation.providers.prompter.title": "立即订阅以使用自定义 API 服务", "limitation.providers.tooltip": "自定义 API 服务仅对付费用户开放", + "limitation.video.budgetReady.action": "继续生成", + "limitation.video.budgetReady.desc": "当前可用积分已足够支付本次生成。", + "limitation.video.budgetReady.title": "积分已就绪", "limitation.video.success.action": "继续生成", "limitation.video.success.desc": "您的 {{plan}} 订阅已成功升级。尽情享受 AI 视频生成服务。您当前的套餐包括:", "limitation.video.success.title": "升级成功", diff --git a/locales/zh-TW/chat.json b/locales/zh-TW/chat.json index 9c03a0329a..90a0f4d4e9 100644 --- a/locales/zh-TW/chat.json +++ b/locales/zh-TW/chat.json @@ -196,6 +196,9 @@ "groupWizard.searchTemplates": "搜尋範本...", "groupWizard.title": "建立群組", "groupWizard.useTemplate": "使用模板", + "heteroAgent.cloudNotConfigured.action": "設定", + "heteroAgent.cloudNotConfigured.desc": "在代理設定中配置您的 Claude Code 令牌以開始發送消息。", + "heteroAgent.cloudNotConfigured.title": "需要雲端憑證", "heteroAgent.cloudRepo.multiSelected": "已選擇 {{count}} 個儲存庫", "heteroAgent.cloudRepo.noRepos": "尚未配置任何儲存庫。請在代理設置中新增。", "heteroAgent.cloudRepo.notSet": "未選擇儲存庫", @@ -467,6 +470,10 @@ "sharePage.error.unauthorized.subtitle": "請登入以查看此分享主題。", "sharePage.error.unauthorized.title": "需要登入", "sharePageDisclaimer": "此內容由使用者分享,並不代表 LobeHub 的立場。LobeHub 對此分享內容所引發的任何後果不負責任。", + "signalCallbacks.collapse": "隱藏詳情", + "signalCallbacks.empty": "沒有回調消息", + "signalCallbacks.expand": "顯示詳情", + "signalCallbacks.title": "{{tool}} · {{count}} 回調更新", "stt.action": "語音輸入", "stt.loading": "識別中...", "stt.prettifying": "潤色中...", @@ -689,7 +696,6 @@ "thread.closeSubagentThread": "收合子代理對話", "thread.divider": "子話題", "thread.openSubagentThread": "查看完整子代理對話", - "thread.subagentBadge": "子代理", "thread.subagentReadOnlyHint": "子代理對話為唯讀——執行由主代理驅動。", "thread.threadMessageCount": "{{messageCount}} 條消息", "thread.title": "子話題", diff --git a/locales/zh-TW/models.json b/locales/zh-TW/models.json index 8857df7d2f..3da71550e0 100644 --- a/locales/zh-TW/models.json +++ b/locales/zh-TW/models.json @@ -106,6 +106,7 @@ "MiniMax-Hailuo-2.3.description": "全新影像生成模型,全面升級身體動作、物理真實性及指令遵循性。", "MiniMax-M1.description": "一款內部開發的推理模型,具備 80K 思路鏈與 100 萬輸入,效能媲美全球頂尖模型。", "MiniMax-M2-Stable.description": "專為高效編碼與代理流程設計,具備更高併發能力,適用於商業應用。", + "MiniMax-M2.1-Lightning.description": "強大的多語言編程能力,具有更快、更高效的推理性能。", "MiniMax-M2.1-highspeed.description": "強大的多語言編程能力,全面升級的編程體驗。速度更快,效率更高。", "MiniMax-M2.1.description": "MiniMax-M2.1 是 MiniMax 推出的旗艦開源大型模型,專注於解決複雜的真實世界任務。其核心優勢在於多語言程式設計能力與作為智能代理執行複雜任務的能力。", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 高速版:與 M2.5 性能相同,但推理速度更快。", @@ -314,13 +315,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku 是 Anthropic 推出的最快速且最精簡的模型,設計用於即時回應,具備快速且準確的表現。", "claude-3-opus-20240229.description": "Claude 3 Opus 是 Anthropic 最強大的模型,適用於高度複雜任務,具備卓越的效能、智慧、流暢度與理解力。", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet 在智慧與速度之間取得平衡,適合企業工作負載,提供高效能與低成本的大規模部署。", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 是 Anthropic 最快速且最智能的 Haiku 模型,具備閃電般的速度和延展的推理能力。", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 是 Anthropic 最快且最智能的 Haiku 模型,擁有閃電般的速度和延展性思維。", "claude-haiku-4-5.description": "Anthropic 的 Claude Haiku 4.5 —— 新一代 Haiku,具備更強推理與視覺能力。", "claude-haiku-4.5.description": "Claude Haiku 4.5 是 Anthropic 最快速且最聰明的 Haiku 模型,具備閃電般的速度和延展的推理能力。", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking 是一個進階版本,能夠揭示其推理過程。", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1 是 Anthropic 最新且最強大的模型,專為高度複雜的任務設計,表現出色,具備卓越的智能、流暢性和理解力。", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1 是 Anthropic 最新且最強大的模型,專為高度複雜的任務設計,表現卓越,智能、流暢性和理解力均出色。", "claude-opus-4-1.description": "Anthropic 的 Claude Opus 4.1 —— 旗艦級推理模型,擅長深度分析。", - "claude-opus-4-20250514.description": "Claude Opus 4 是 Anthropic 最強大的模型,專為高度複雜的任務設計,表現出色,具備卓越的智能、流暢性和理解力。", + "claude-opus-4-20250514.description": "Claude Opus 4 是 Anthropic 最強大的模型,專為高度複雜的任務設計,表現卓越,智能、流暢性和理解力均出色。", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 是 Anthropic 的旗艦模型,結合卓越智慧與可擴展效能,適合需要最高品質回應與推理的複雜任務。", "claude-opus-4-5.description": "Anthropic 的 Claude Opus 4.5 —— 旗艦模型,具備頂級推理與程式能力。", "claude-opus-4-6.description": "Anthropic 的 Claude Opus 4.6 —— 支援 100 萬上下文的旗艦模型,擁有先進推理能力。", @@ -329,8 +330,8 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 是 Anthropic 最智能的模型,用於構建代理和編碼。", "claude-opus-4.6.description": "Claude Opus 4.6 是 Anthropic 最智能的模型,用於構建代理和編碼。", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking 可產生即時回應或延伸的逐步思考,並顯示其推理過程。", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 能夠生成近乎即時的回應,或提供帶有可見過程的逐步推理。", - "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 是迄今為止 Anthropic 最智能的模型。", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 是 Anthropic 至今最智能的模型,提供近乎即時的回應或延展的逐步思考,為 API 使用者提供精細的控制。", + "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 是 Anthropic 至今最智能的模型。", "claude-sonnet-4-5.description": "Anthropic 的 Claude Sonnet 4.5 —— 強化版 Sonnet,提升程式能力。", "claude-sonnet-4-6.description": "Anthropic 的 Claude Sonnet 4.6 —— 最新 Sonnet,具備更卓越的程式與工具使用能力。", "claude-sonnet-4.5.description": "Claude Sonnet 4.5 是 Anthropic 迄今為止最智能的模型。", @@ -403,7 +404,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat(67B)是一款創新模型,具備深層語言理解與互動能力。", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 是新一代推理模型,具備更強的複雜推理與思維鏈能力,適用於深度分析任務。", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 是下一代推理模型,具備更強的複雜推理和連鎖思維能力。", - "deepseek-chat.description": "一個結合通用能力與編碼能力的新開源模型。它保留了聊天模型的通用對話能力和編碼模型的強大編碼能力,並具有更好的偏好對齊。DeepSeek-V2.5 還改進了寫作和指令遵循能力。", + "deepseek-chat.description": "DeepSeek V4 Flash 非思考模式的兼容別名。計劃淘汰 — 請改用 DeepSeek V4 Flash。", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B 是一款程式語言模型,訓練於 2T token(87% 程式碼,13% 中英文文本),支援 16K 上下文視窗與中間填充任務,提供專案級程式補全與片段填充功能。", "deepseek-coder-v2.description": "DeepSeek Coder V2 是一款開源 MoE 程式模型,在程式任務中表現強勁,媲美 GPT-4 Turbo。", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 是一款開源 MoE 程式模型,在程式任務中表現強勁,媲美 GPT-4 Turbo。", @@ -425,7 +426,7 @@ "deepseek-r1-fast-online.description": "DeepSeek R1 快速全量版,支援即時網頁搜尋,結合 671B 規模能力與快速回應。", "deepseek-r1-online.description": "DeepSeek R1 全量版擁有 671B 參數與即時網頁搜尋功能,提供更強的理解與生成能力。", "deepseek-r1.description": "DeepSeek-R1 在強化學習前使用冷啟動資料,於數學、程式碼與推理任務中表現可媲美 OpenAI-o1。", - "deepseek-reasoner.description": "一個專注於複雜邏輯推理任務的 DeepSeek 推理模型。", + "deepseek-reasoner.description": "DeepSeek V4 Flash 思考模式的兼容別名。計劃淘汰 — 請改用 DeepSeek V4 Flash。", "deepseek-v2.description": "DeepSeek V2 是一款高效的 MoE 模型,適用於具成本效益的處理任務。", "deepseek-v2:236b.description": "DeepSeek V2 236B 是 DeepSeek 專注於程式碼生成的模型,具備強大能力。", "deepseek-v3-0324.description": "DeepSeek-V3-0324 是一款擁有 671B 參數的 MoE 模型,在程式設計、技術能力、語境理解與長文本處理方面表現出色。", @@ -490,6 +491,8 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 是字節跳動 Seed 團隊推出的圖像生成模型,支援文字與圖像輸入,實現高度可控、高品質的圖像生成。可根據文字提示生成圖像。", "doubao-seedream-4-5-251128.description": "Seedream 4.5 是字節跳動最新的多模態圖像模型,整合了文本生成圖像、圖像生成圖像和批量圖像生成功能,同時融入了常識和推理能力。與之前的 4.0 版本相比,生成質量顯著提升,編輯一致性和多圖融合效果更好。它對視覺細節的控制更加精確,能更自然地生成小字和小臉,並實現更和諧的佈局和色彩,提升整體美感。", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite 是字節跳動最新的圖像生成模型。首次整合了在線檢索功能,能夠結合實時網絡信息,提升生成圖像的時效性。模型的智能性也得到了升級,能夠精確解讀複雜指令和視覺內容。此外,它在專業場景中的全球知識覆蓋、參考一致性和生成質量方面均有改進,更好地滿足企業級視覺創作需求。", + "dreamina-seedance-2-0-260128.description": "字節跳動的 Seedance 2.0 是最強大的視頻生成模型,支持多模態參考視頻生成、視頻編輯、視頻擴展、文本生成視頻以及圖像生成視頻,並同步音頻。", + "dreamina-seedance-2-0-fast-260128.description": "字節跳動的 Seedance 2.0 Fast 提供與 Seedance 2.0 相同的功能,但生成速度更快,價格更具競爭力。", "emohaa.description": "Emohaa 是一款心理健康模型,具備專業諮詢能力,協助使用者理解情緒問題。", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B 是一款開源輕量級模型,適合本地與客製化部署。", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview 是一款 8K 上下文預覽模型,用於評估 ERNIE 4.5。", @@ -514,7 +517,8 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K 是一款快速思考模型,具備 32K 上下文,適合複雜推理與多輪對話。", "ernie-x1.1-preview.description": "ERNIE X1.1 預覽版是一款思考模型預覽,用於評估與測試。", "ernie-x1.1.description": "ERNIE X1.1 是一個用於評估和測試的思考模型預覽版。", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 是來自字節跳動 Seed 的圖像生成模型,支持文本和圖像輸入,能夠生成高度可控、高品質的圖像,並可根據文本提示生成圖像。", + "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5,由字節跳動 Seed 團隊打造,支持多圖像編輯和合成。特點包括增強的主題一致性、精確的指令執行、空間邏輯理解、美學表達、海報佈局和標誌設計,以及高精度的文字圖像渲染。", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0,由字節跳動 Seed 團隊打造,支持文本和圖像輸入,能從提示中生成高度可控、高質量的圖像。", "fal-ai/flux-kontext/dev.description": "FLUX.1 模型專注於圖像編輯,支援文字與圖像輸入。", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] 接受文字與參考圖像輸入,實現目標區域編輯與複雜場景轉換。", "fal-ai/flux/krea.description": "Flux Krea [dev] 是一款圖像生成模型,偏好更真實自然的美學風格。", @@ -522,8 +526,8 @@ "fal-ai/hunyuan-image/v3.description": "一款強大的原生多模態圖像生成模型。", "fal-ai/imagen4/preview.description": "來自 Google 的高品質圖像生成模型。", "fal-ai/nano-banana.description": "Nano Banana 是 Google 最新、最快且最高效的原生多模態模型,支援透過對話進行圖像生成與編輯。", - "fal-ai/qwen-image-edit.description": "來自 Qwen 團隊的專業圖像編輯模型,支持語義和外觀編輯,精確編輯中英文文本,並實現高品質的編輯效果,如風格轉換和物體旋轉。", - "fal-ai/qwen-image.description": "來自 Qwen 團隊的強大圖像生成模型,具有令人印象深刻的中文文本渲染能力和多樣化的視覺風格。", + "fal-ai/qwen-image-edit.description": "Qwen 團隊的專業圖像編輯模型,支持語義和外觀編輯、精確的中英文文字編輯、風格轉換、旋轉等功能。", + "fal-ai/qwen-image.description": "Qwen 團隊的強大圖像生成模型,具有強大的中文文字渲染能力和多樣化的視覺風格。", "flux-1-schnell.description": "來自黑森林實驗室的 12B 參數文字轉圖像模型,透過潛在對抗擴散蒸餾技術,在 1 至 4 步內生成高品質圖像。其表現媲美封閉式替代方案,並以 Apache-2.0 授權釋出,供個人、研究與商業用途。", "flux-dev.description": "開源研發導向的影像生成模型,針對非商業創新研究進行高效優化。", "flux-kontext-max.description": "最先進的語境圖像生成與編輯技術,結合文字與圖像輸入,實現精準且一致的結果。", @@ -565,7 +569,7 @@ "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image(Nano Banana Pro)是 Google 的圖像生成模型,並支持多模態聊天。", "gemini-3-pro-preview.description": "Gemini 3 Pro 是 Google 最強大的智能代理與情境編碼模型,具備頂尖推理能力、豐富視覺表現與深度互動。", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) 是 Google 最快的原生影像生成模型,支持思考、對話式影像生成與編輯。", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image(Nano Banana 2)是 Google 最快速的原生圖像生成模型,支持思維輔助、對話式圖像生成和編輯。", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image(Nano Banana 2)以閃電速度提供專業級圖像質量,並支持多模態聊天。", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview 是 Google 最具成本效益的多模態模型,專為高容量代理任務、翻譯和數據處理而優化。", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite 是 Google 最具成本效益的多模態模型,專為高容量代理任務、翻譯和數據處理而優化。", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview 在 Gemini 3 Pro 的基礎上增強了推理能力,並新增了中等思考層級支持。", @@ -730,6 +734,8 @@ "grok-4-fast-reasoning.description": "我們很高興推出 Grok 4 Fast,這是我們在高性價比推理模型上的最新進展。", "grok-4.20-0309-non-reasoning.description": "不具推理功能的版本,適用於簡單使用情境。", "grok-4.20-0309-reasoning.description": "智慧、極速的模型,會先思考再作答。", + "grok-4.20-beta-0309-non-reasoning.description": "適用於簡單使用場景的非推理變體。", + "grok-4.20-beta-0309-reasoning.description": "智能且極速的模型,在回應前進行推理。", "grok-4.20-multi-agent-0309.description": "由 4 或 16 個代理組成的團隊,擅長研究類任務。目前不支援客戶端工具,只支援 xAI 伺服器端工具(如 X Search、Web Search)及遠端 MCP 工具。", "grok-4.3.description": "世界上最追求真理的大型語言模型", "grok-4.description": "最新的 Grok 旗艦模型,擁有無與倫比的語言、數學和推理能力 — 真正的全能型模型。目前指向 grok-4-0709;由於資源有限,暫時比官方定價高 10%,預計稍後會恢復至官方價格。", @@ -1214,6 +1220,8 @@ "qwq.description": "QwQ 是 Qwen 系列中的推理模型。相較於標準指令微調模型,它具備更強的思考與推理能力,顯著提升下游任務表現,特別是在處理困難問題時。QwQ-32B 是中型推理模型,表現可媲美 DeepSeek-R1 與 o1-mini 等頂尖模型。", "qwq_32b.description": "Qwen 系列中的中型推理模型。相較於標準指令微調模型,QwQ 的思考與推理能力顯著提升下游任務表現,特別是在處理困難問題時。", "r1-1776.description": "R1-1776 是 DeepSeek R1 的後訓練版本,旨在提供未經審查、無偏見的事實資訊。", + "seedance-1-5-pro-251215.description": "字節跳動的 Seedance 1.5 Pro 支持文本生成視頻、圖像生成視頻(首幀、首幀+尾幀)以及與視覺同步的音頻生成。", + "seedream-5-0-260128.description": "字節跳動 BytePlus 的 Seedream 5.0 Lite,支持基於網絡檢索的增強生成,提供即時信息、複雜提示解釋增強,以及改進的參考一致性,用於專業視覺創作。", "solar-mini-ja.description": "Solar Mini (Ja) 是 Solar Mini 的日文強化版本,同時維持在英文與韓文上的高效能表現。", "solar-mini.description": "Solar Mini 是一款緊湊型大型語言模型,效能超越 GPT-3.5,具備強大的多語言能力,支援英文與韓文,提供高效能且佔用資源小的解決方案。", "solar-pro.description": "Solar Pro 是 Upstage 推出的高智慧大型語言模型,專注於單 GPU 上的指令遵循任務,IFEval 分數超過 80。目前支援英文,完整版本預計於 2024 年 11 月推出,將擴展語言支援與上下文長度。", diff --git a/locales/zh-TW/plugin.json b/locales/zh-TW/plugin.json index ddba5948d9..c2fedd38b5 100644 --- a/locales/zh-TW/plugin.json +++ b/locales/zh-TW/plugin.json @@ -237,6 +237,14 @@ "builtins.lobe-page-agent.apiName.updateNode": "更新節點", "builtins.lobe-page-agent.apiName.wrapNodes": "包裝節點", "builtins.lobe-page-agent.title": "文件", + "builtins.lobe-self-feedback-intent.apiName.declareSelfFeedbackIntent": "記錄改進想法", + "builtins.lobe-self-feedback-intent.inspector.gap.proposal": "建議改進", + "builtins.lobe-self-feedback-intent.inspector.memory.write": "記錄偏好", + "builtins.lobe-self-feedback-intent.inspector.rejected": "未記錄", + "builtins.lobe-self-feedback-intent.inspector.skill.consolidate": "整理方法", + "builtins.lobe-self-feedback-intent.inspector.skill.create": "發現新方法", + "builtins.lobe-self-feedback-intent.inspector.skill.refine": "改進方法", + "builtins.lobe-self-feedback-intent.title": "改進想法", "builtins.lobe-skill-store.apiName.importFromMarket": "從市場匯入", "builtins.lobe-skill-store.apiName.importSkill": "匯入技能", "builtins.lobe-skill-store.apiName.searchSkill": "搜尋技能", diff --git a/locales/zh-TW/providers.json b/locales/zh-TW/providers.json index 41054f2aa6..68348ea3ce 100644 --- a/locales/zh-TW/providers.json +++ b/locales/zh-TW/providers.json @@ -33,6 +33,7 @@ "jina.description": "Jina AI 成立於 2020 年,是領先的搜尋 AI 公司。其搜尋技術堆疊包含向量模型、重排序器與小型語言模型,打造可靠且高品質的生成式與多模態搜尋應用。", "kimicodingplan.description": "來自 Moonshot AI 的 Kimi Code 提供對 Kimi 模型(包括 K2.5)的訪問,用於編碼任務。", "lmstudio.description": "LM Studio 是一款桌面應用程式,可在本機開發與實驗大型語言模型。", + "lobehub.description": "LobeHub Cloud 使用官方 API 存取 AI 模型,並以與模型代幣相關的點數來計算使用量。", "longcat.description": "LongCat 是美團自主研發的一系列生成式 AI 大模型。其設計旨在通過高效的計算架構和強大的多模態能力,提升企業內部生產力並實現創新應用。", "minimax.description": "MiniMax 成立於 2021 年,致力於打造通用 AI,擁有多模態基礎模型,包括兆級參數的 MoE 文本模型、語音模型與視覺模型,並推出如海螺 AI 等應用。", "minimaxcodingplan.description": "MiniMax 代幣計劃通過固定費用訂閱提供對 MiniMax 模型(包括 M2.7)的訪問,用於編碼任務。", diff --git a/locales/zh-TW/subscription.json b/locales/zh-TW/subscription.json index c3aea62d1c..687ba12858 100644 --- a/locales/zh-TW/subscription.json +++ b/locales/zh-TW/subscription.json @@ -41,7 +41,9 @@ "credits.autoTopUp.monthlyLimitDesc": "每月自動扣款的最高金額。留空表示無上限", "credits.autoTopUp.monthlyLimitPlaceholder": "無上限", "credits.autoTopUp.monthlyTopUpAmount": "每月加值金額", + "credits.autoTopUp.noCustomerHint": "首次購買點數以保存付款方式,然後啟用自動充值。", "credits.autoTopUp.noPaymentMethodHint": "沒有已存檔的付款方式。自動充值需要一張已保存的信用卡以自動扣款。", + "credits.autoTopUp.purchaseCredits": "購買點數", "credits.autoTopUp.saveError": "儲存自動加值設定失敗", "credits.autoTopUp.saveSuccess": "自動加值設定已儲存", "credits.autoTopUp.setupPaymentMethod": "新增付款方式", @@ -83,6 +85,7 @@ "credits.packages.title": "我的點數方案", "credits.topUp.cancel": "取消", "credits.topUp.custom": "自訂", + "credits.topUp.freeFeeHint": "免費方案充值每 1M 點數需支付 {{fee}} 手續費。", "credits.topUp.maxAmountError": "單次購買金額不得超過 ${{max}}", "credits.topUp.purchaseError": "購買失敗,請稍後再試", "credits.topUp.purchaseNow": "立即購買", @@ -120,6 +123,9 @@ "keyMissMatch.button": "恢復使用並繼續對話", "keyMissMatch.description": "由於系統偶發錯誤,您的訂閱使用暫時失效。請點擊下方按鈕恢復使用並繼續對話。如多次發生,請透過電子郵件聯繫我們(support@lobehub.com)", "keyMissMatch.title": "立即恢復訂閱使用", + "limitation.chat.budgetReady.action": "繼續聊天", + "limitation.chat.budgetReady.desc": "您的可用點數現在足以覆蓋此請求。", + "limitation.chat.budgetReady.title": "點數已準備好", "limitation.chat.success.action": "繼續對話", "limitation.chat.success.desc": "您的 {{plan}} 訂閱已成功升級,享受 AI 對話體驗。您目前的方案包含:", "limitation.chat.success.title": "升級成功", @@ -134,14 +140,23 @@ "limitation.hobby.docs": "查看設定文件", "limitation.hobby.tip": "請記得切換至使用自訂 API 金鑰的模型", "limitation.hobby.title": "請設定模型服務 API", + "limitation.image.budgetReady.action": "繼續生成", + "limitation.image.budgetReady.desc": "您的可用點數現在足以覆蓋此生成。", + "limitation.image.budgetReady.title": "點數已準備好", "limitation.image.success.action": "繼續生成", "limitation.image.success.desc": "您的 {{plan}} 訂閱已成功升級,享受 AI 圖像生成功能。您目前的方案包含:", "limitation.image.success.title": "升級成功", "limitation.image.topupSuccess.action": "繼續生成", "limitation.image.topupSuccess.desc": "您的儲值點數已啟用,享受 AI 圖像生成功能。您目前的方案包含:", "limitation.image.topupSuccess.title": "儲值成功", + "limitation.insufficientBudget.approximateDesc": "此請求可能需要更多點數。請充值或升級您的方案。", + "limitation.insufficientBudget.available": "可用點數", "limitation.insufficientBudget.desc": "您的剩餘點數不足以支付此模型的預估費用。請充值點數或切換到較便宜的模型。", + "limitation.insufficientBudget.estimatedDesc": "此請求估計需要更多點數。請充值或升級您的方案。", + "limitation.insufficientBudget.exactDesc": "此請求需要更多點數。請充值或升級您的方案。", + "limitation.insufficientBudget.required": "所需點數", "limitation.insufficientBudget.retry": "重試", + "limitation.insufficientBudget.shortfall": "點數不足", "limitation.insufficientBudget.title": "此模型點數不足", "limitation.limited.action": "立即升級", "limitation.limited.advanceFeature": "升級以享受進階功能:", @@ -151,6 +166,7 @@ "limitation.limited.title": "運算點數已用完", "limitation.limited.topup": "儲值點數", "limitation.limited.upgrade": "升級至更高方案", + "limitation.limited.upgradeToPlan": "升級至 {{plan}}", "limitation.providers.lock.addNew": "立即訂閱以建立自訂 AI 提供者", "limitation.providers.lock.enableProvider": "立即訂閱以啟用此 AI 提供者", "limitation.providers.lock.menuItem": "立即訂閱以設定自訂 API 服務", @@ -161,6 +177,9 @@ "limitation.providers.prompter.subTitle": "自訂 API 服務僅限付費方案使用。立即升級以享受全球主流模型服務", "limitation.providers.prompter.title": "立即訂閱以使用自訂 API 服務", "limitation.providers.tooltip": "自訂 API 服務僅限付費方案使用", + "limitation.video.budgetReady.action": "繼續生成", + "limitation.video.budgetReady.desc": "您的可用點數現在足以覆蓋此生成。", + "limitation.video.budgetReady.title": "點數已準備好", "limitation.video.success.action": "繼續生成", "limitation.video.success.desc": "您的 {{plan}} 訂閱已成功升級。盡情享受 AI 影片生成功能。您目前的方案包含:", "limitation.video.success.title": "升級成功", diff --git a/packages/model-runtime/src/types/type.ts b/packages/model-runtime/src/types/type.ts index 3ddcdc9fa0..eaeb40963a 100644 --- a/packages/model-runtime/src/types/type.ts +++ b/packages/model-runtime/src/types/type.ts @@ -1,3 +1,4 @@ +import type { ErrorType } from '@lobechat/types'; import type { ModelProvider } from 'model-bank'; import type OpenAI from 'openai'; @@ -13,7 +14,7 @@ export interface ChatCompletionErrorPayload { [key: string]: any; endpoint?: string; error: object; - errorType: ILobeAgentRuntimeErrorType; + errorType: ErrorType | ILobeAgentRuntimeErrorType; message?: string; provider: string; } diff --git a/src/locales/default/subscription.ts b/src/locales/default/subscription.ts index 01486377cb..ef23429bbd 100644 --- a/src/locales/default/subscription.ts +++ b/src/locales/default/subscription.ts @@ -138,6 +138,9 @@ export default { 'keyMissMatch.description': 'Due to an occasional system failure, your current subscription usage is temporarily inactive. Please click the button below to restore usage and continue the conversation. If this happens repeatedly, please contact us via email (support@lobehub.com)', 'keyMissMatch.title': 'Restore Subscription Usage Now', + 'limitation.chat.budgetReady.action': 'Continue Chatting', + 'limitation.chat.budgetReady.desc': 'Your available credits now cover this request.', + 'limitation.chat.budgetReady.title': 'Credits Ready', 'limitation.chat.success.action': 'Continue Chatting', 'limitation.chat.success.desc': 'Your {{plan}} subscription has been upgraded successfully. Enjoy AI chatting. Your current plan includes:', @@ -149,10 +152,19 @@ export default { 'limitation.expired.desc': 'Your {{plan}} computing credits expired on {{expiredAt}}. Upgrade your plan now to get computing credits.', 'limitation.expired.title': 'Computing Credits Expired', + 'limitation.insufficientBudget.approximateDesc': + 'This request may need more credits. Top up credits or upgrade your plan.', + 'limitation.insufficientBudget.available': 'Available Credits', 'limitation.insufficientBudget.desc': - 'Your remaining credits are not enough for the estimated cost of this model. Please top up credits or switch to a less expensive model.', + 'Your credits are not enough to continue. Top up credits or upgrade your plan.', + 'limitation.insufficientBudget.estimatedDesc': + 'This request is estimated to need more credits. Top up credits or upgrade your plan.', + 'limitation.insufficientBudget.exactDesc': + 'This request needs more credits. Top up credits or upgrade your plan.', + 'limitation.insufficientBudget.required': 'Required Credits', 'limitation.insufficientBudget.retry': 'Retry', - 'limitation.insufficientBudget.title': 'Insufficient Credits for This Model', + 'limitation.insufficientBudget.shortfall': 'Credit Shortfall', + 'limitation.insufficientBudget.title': 'Insufficient Credits', 'limitation.hobby.action': 'Configured, continue chatting', 'limitation.hobby.configAPI': 'Configure API', 'limitation.hobby.desc': @@ -160,6 +172,9 @@ export default { 'limitation.hobby.docs': 'View configuration docs', 'limitation.hobby.tip': 'Remember to switch to a model with custom API Key', 'limitation.hobby.title': 'Please Configure Model Service API', + 'limitation.image.budgetReady.action': 'Continue Generating', + 'limitation.image.budgetReady.desc': 'Your available credits now cover this generation.', + 'limitation.image.budgetReady.title': 'Credits Ready', 'limitation.image.success.action': 'Continue Generating', 'limitation.image.success.desc': 'Your {{plan}} subscription has been upgraded successfully. Enjoy AI image generation. Your current plan includes:', @@ -168,6 +183,9 @@ export default { 'limitation.image.topupSuccess.desc': 'Your top-up credits are now active. Enjoy AI image generation. Your current plan includes:', 'limitation.image.topupSuccess.title': 'Top-up Successful', + 'limitation.video.budgetReady.action': 'Continue Generating', + 'limitation.video.budgetReady.desc': 'Your available credits now cover this generation.', + 'limitation.video.budgetReady.title': 'Credits Ready', 'limitation.video.success.action': 'Continue Generating', 'limitation.video.success.desc': 'Your {{plan}} subscription has been upgraded successfully. Enjoy AI video generation. Your current plan includes:', @@ -186,6 +204,7 @@ export default { 'limitation.limited.title': 'Computing Credits Exhausted', 'limitation.limited.topup': 'Top-Up Credits', 'limitation.limited.upgrade': 'Upgrade to Higher Plan', + 'limitation.limited.upgradeToPlan': 'Upgrade to {{plan}}', 'limitation.providers.lock.addNew': 'Subscribe now to create custom AI providers', 'limitation.providers.lock.enableProvider': 'Subscribe now to enable this AI provider', 'limitation.providers.lock.menuItem': 'Subscribe now to configure custom API service', From 97ea30e48baf5ddc5f96ead62bc611d3535a394a Mon Sep 17 00:00:00 2001 From: AmAzing- <115673583+AmAzing129@users.noreply.github.com> Date: Tue, 19 May 2026 12:40:11 +0800 Subject: [PATCH 019/224] =?UTF-8?q?=F0=9F=92=AC=20fix(messenger):=20standa?= =?UTF-8?q?rdize=20platform=20preposition=20copy=20(#14959)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/usage/channels/discord.mdx | 2 +- docs/usage/channels/feishu.mdx | 6 +++--- docs/usage/channels/lark.mdx | 4 ++-- docs/usage/channels/line.mdx | 8 ++++---- docs/usage/channels/qq.mdx | 2 +- docs/usage/channels/slack.mdx | 6 +++--- docs/usage/channels/telegram.mdx | 2 +- docs/usage/channels/wechat.mdx | 4 ++-- docs/usage/messenger/discord.mdx | 18 +++++++++--------- docs/usage/messenger/slack.mdx | 10 +++++----- docs/usage/messenger/telegram.mdx | 6 +++--- locales/en-US/agent.json | 4 ++-- locales/en-US/messenger.json | 18 +++++++++--------- src/locales/default/agent.ts | 4 ++-- src/locales/default/messenger.ts | 18 +++++++++--------- .../services/messenger/MessengerRouter.ts | 2 +- .../messenger/platforms/discord/binder.ts | 2 +- .../messenger/platforms/slack/binder.ts | 4 ++-- 18 files changed, 60 insertions(+), 60 deletions(-) diff --git a/docs/usage/channels/discord.mdx b/docs/usage/channels/discord.mdx index 25b7b8e18f..a8f8f8fd94 100644 --- a/docs/usage/channels/discord.mdx +++ b/docs/usage/channels/discord.mdx @@ -121,7 +121,7 @@ By connecting a Discord channel to your LobeHub agent, users can interact with t ## Step 4: Test the Connection -Back in LobeHub's channel settings for Discord, click **Test Connection** to verify everything is configured correctly. Then send a message to your bot in Discord to confirm it responds. +Back in LobeHub's channel settings for Discord, click **Test Connection** to verify everything is configured correctly. Then send a message to your bot on Discord to confirm it responds. ## Step 5: Set Your Platform Identity (Recommended) diff --git a/docs/usage/channels/feishu.mdx b/docs/usage/channels/feishu.mdx index 5b9add8826..2753322a3c 100644 --- a/docs/usage/channels/feishu.mdx +++ b/docs/usage/channels/feishu.mdx @@ -3,7 +3,7 @@ title: Connect LobeHub to Feishu (飞书) description: >- Learn how to create a Feishu custom app and connect it to your LobeHub agent as a message channel, enabling your AI assistant to interact with team members - in Feishu chats. + on Feishu. tags: - Feishu - 飞书 @@ -14,7 +14,7 @@ tags: # Connect LobeHub to Feishu (飞书) -By connecting a Feishu channel to your LobeHub agent, team members can interact with the AI assistant directly in Feishu private chats and group conversations. +By connecting a Feishu channel to your LobeHub agent, team members can interact with the AI assistant directly on Feishu through private chats and group conversations. > If you are using the international version (Lark), please refer to the [Lark setup guide](/docs/usage/channels/lark). @@ -172,7 +172,7 @@ By connecting a Feishu channel to your LobeHub agent, team members can interact ## Step 6: Test the Connection -Back in LobeHub's channel settings, click **Test Connection** to verify the credentials. Then find your bot in Feishu by searching its name and send it a message to confirm it responds. +Back in LobeHub's channel settings, click **Test Connection** to verify the credentials. Then find your bot on Feishu by searching its name and send it a message to confirm it responds. ## Step 7: Set Your Platform Identity (Recommended) diff --git a/docs/usage/channels/lark.mdx b/docs/usage/channels/lark.mdx index 460743721d..cf6e46a6d8 100644 --- a/docs/usage/channels/lark.mdx +++ b/docs/usage/channels/lark.mdx @@ -13,7 +13,7 @@ tags: # Connect LobeHub to Lark -By connecting a Lark channel to your LobeHub agent, team members can interact with the AI assistant directly in Lark private chats and group conversations. +By connecting a Lark channel to your LobeHub agent, team members can interact with the AI assistant directly on Lark through private chats and group conversations. > If you are using the Chinese version (飞书), please refer to the [Feishu setup guide](/docs/usage/channels/feishu). @@ -163,7 +163,7 @@ By connecting a Lark channel to your LobeHub agent, team members can interact wi ## Step 6: Test the Connection -Back in LobeHub's channel settings, click **Test Connection** to verify the credentials. Then find your bot in Lark by searching its name and send it a message to confirm it responds. +Back in LobeHub's channel settings, click **Test Connection** to verify the credentials. Then find your bot on Lark by searching its name and send it a message to confirm it responds. ## Step 7: Set Your Platform Identity (Recommended) diff --git a/docs/usage/channels/line.mdx b/docs/usage/channels/line.mdx index 230d6802af..05eaa19075 100644 --- a/docs/usage/channels/line.mdx +++ b/docs/usage/channels/line.mdx @@ -2,7 +2,7 @@ title: Connect LobeHub to LINE description: >- Learn how to connect a LINE Messaging API bot to your LobeHub agent, - enabling your AI assistant to chat with users in LINE direct messages and + enabling your AI assistant to chat with users on LINE through direct messages and group conversations. tags: - LINE @@ -145,11 +145,11 @@ By default the LINE Official Account Manager auto-replies to user messages and s ### Add the bot as a friend - Open the **Messaging API** tab in the LINE Developers Console and scan the bot's **QR code** with your phone, or search for the **Bot basic ID** (e.g. `@abc1234x`) in LINE. + Open the **Messaging API** tab in the LINE Developers Console and scan the bot's **QR code** with your phone, or search for the **Bot basic ID** (e.g. `@abc1234x`) on LINE. ### Send a real message - Send any message to the bot in LINE. Within a few seconds your LobeHub agent should reply. + Send any message to the bot on LINE. Within a few seconds your LobeHub agent should reply. ### Run Test Connection (optional) @@ -190,7 +190,7 @@ LINE's Messaging API has a few specifics that LobeHub maps as follows: - **"Verify" fails in the LINE Developers Console.** The Channel Secret in LobeHub must match the value shown on the LINE Developers Console **Basic settings** tab exactly. Re-paste it, save, and try again. - **`Authentication failed.` on Save / Test Connection.** Your Channel Access Token is invalid or expired. Re-issue the long-lived token in the Messaging API tab and paste the new value into LobeHub. - **`Channel access token belongs to bot Uxxx, not Uyyy`.** The Destination User ID does not match the token. Easiest fix: clear the field and click **Fetch from LINE** to re-pull the correct `userId`. The `Uxxx` shown in the error is also the userId the token actually belongs to — you can paste it in directly. (Manual check: `curl -H "Authorization: Bearer " https://api.line.me/v2/bot/info`.) -- **Webhook delivery is rejected with `401 Invalid signature`.** The Channel Secret in LobeHub doesn't match the one in LINE. Update LobeHub with the correct Channel Secret. +- **Webhook delivery is rejected with `401 Invalid signature`.** The Channel Secret in LobeHub doesn't match the one shown in the LINE Developers Console. Update LobeHub with the correct Channel Secret. - **Bot doesn't respond.** Check that: 1. **Use webhook** is toggled **ON** in the Messaging API tab. 2. **Auto-response messages** and **Greeting message** are disabled in the LINE Official Account Manager. diff --git a/docs/usage/channels/qq.mdx b/docs/usage/channels/qq.mdx index 6a2cd5a4ad..0fef1be322 100644 --- a/docs/usage/channels/qq.mdx +++ b/docs/usage/channels/qq.mdx @@ -2,7 +2,7 @@ title: Connect LobeHub to QQ description: >- Learn how to create a QQ bot and connect it to your LobeHub agent as a message - channel, enabling your AI assistant to chat with users in QQ group chats and + channel, enabling your AI assistant to chat with users on QQ through group chats and direct messages. tags: - QQ diff --git a/docs/usage/channels/slack.mdx b/docs/usage/channels/slack.mdx index c79b57e49a..83649f446d 100644 --- a/docs/usage/channels/slack.mdx +++ b/docs/usage/channels/slack.mdx @@ -2,8 +2,8 @@ title: Connect LobeHub to Slack description: >- Learn how to create a Slack app and connect it to your LobeHub agent as a - message channel, enabling your AI assistant to interact with users in Slack - channels and direct messages. + message channel, enabling your AI assistant to interact with users on Slack + through channels and direct messages. tags: - Slack - Message Channels @@ -165,7 +165,7 @@ LobeHub supports two connection modes for Slack: Click **Test Connection** in LobeHub, then go to Slack, invite the bot to a channel, and mention it with `@LobeHub Assistant` to confirm it responds. -> **Slash Commands:** If you used the manifest template above, the `/new` and `/stop` commands are automatically configured. Type `/new` in Slack to reset the conversation, or `/stop` to stop the current execution. You can also use these commands via `@bot /new`. +> **Slash Commands:** If you used the manifest template above, the `/new` and `/stop` commands are automatically configured. Type `/new` on Slack to reset the conversation, or `/stop` to stop the current execution. You can also use these commands via `@bot /new`. --- diff --git a/docs/usage/channels/telegram.mdx b/docs/usage/channels/telegram.mdx index 2daac27807..0b346afce0 100644 --- a/docs/usage/channels/telegram.mdx +++ b/docs/usage/channels/telegram.mdx @@ -2,7 +2,7 @@ title: Connect LobeHub to Telegram description: >- Learn how to create a Telegram bot and connect it to your LobeHub agent as a - message channel, enabling your AI assistant to chat with users in Telegram + message channel, enabling your AI assistant to chat with users on Telegram private and group conversations. tags: - Telegram diff --git a/docs/usage/channels/wechat.mdx b/docs/usage/channels/wechat.mdx index 01c95bc7cd..80da2a98b6 100644 --- a/docs/usage/channels/wechat.mdx +++ b/docs/usage/channels/wechat.mdx @@ -2,7 +2,7 @@ title: Connect LobeHub to WeChat description: >- Learn how to connect a WeChat bot to your LobeHub agent via the iLink Bot API, - enabling your AI assistant to chat with users in WeChat private and group + enabling your AI assistant to chat with users on WeChat through private and group conversations. tags: - WeChat @@ -37,7 +37,7 @@ In LobeHub, navigate to your agent's settings, then select the **Channels** tab. ### Confirm Login - After scanning, a confirmation prompt will appear in WeChat. Tap **Confirm** to authorize the connection. + After scanning, a confirmation prompt will appear on WeChat. Tap **Confirm** to authorize the connection. ![](/blog/assets8a08815733e06500b6552019d6dfbe7b.webp) diff --git a/docs/usage/messenger/discord.mdx b/docs/usage/messenger/discord.mdx index 1164ab1119..21255ebf28 100644 --- a/docs/usage/messenger/discord.mdx +++ b/docs/usage/messenger/discord.mdx @@ -2,8 +2,8 @@ title: Use LobeHub on Discord description: >- Add the official LobeHub bot to a Discord server, then link your Discord - account to LobeHub. Pick a default agent and chat with your AI assistants in - Discord DMs — no bot setup required. + account to LobeHub. Pick a default agent and chat with your AI assistants on + Discord through DMs — no bot setup required. tags: - Messenger - Discord @@ -33,7 +33,7 @@ Discord works in two phases: a **server admin adds** the official LobeHub bot to {/* TODO: screenshot — Discord detail page with empty Connections list and the Connect button */} - ### Authorise in Discord + ### Authorise on Discord You'll be redirected to Discord's bot-add screen. Pick the server you want to add the bot to and click **Authorise**. @@ -45,15 +45,15 @@ Discord works in two phases: a **server admin adds** the official LobeHub bot to {/* TODO: screenshot — Discord detail page showing one connected server row */} - > **Server already connected by someone else?** LobeHub shows a "Server already connected" notice. You don't need to add the bot again — just DM the LobeHub bot in Discord to link your personal account. + > **Server already connected by someone else?** LobeHub shows a "Server already connected" notice. You don't need to add the bot again — just DM the LobeHub bot on Discord to link your personal account. ## Phase B — Link your personal account (each member) - ### Open the LobeHub bot in Discord + ### Open the LobeHub bot on Discord - Open the LobeHub bot in Discord — the **Open in Discord** button on the Discord detail page (or the pending-link row) takes you straight there. + Open the LobeHub bot on Discord — the **Open in Discord** button on the Discord detail page (or the pending-link row) takes you straight there. {/* TODO: screenshot — Discord detail page with the pending user row + Open in Discord button */} @@ -65,7 +65,7 @@ Discord works in two phases: a **server admin adds** the official LobeHub bot to ### Confirm the link in your browser - Tap the link, sign in to LobeHub if asked, and choose a **default agent**. Every message you DM the LobeHub bot in Discord (across all servers) will route to this agent. + Tap the link, sign in to LobeHub if asked, and choose a **default agent**. Every message you DM the LobeHub bot on Discord (across all servers) will route to this agent. {/* TODO: screenshot — confirm-link page in LobeHub with the agent picker */} @@ -78,7 +78,7 @@ Discord works in two phases: a **server admin adds** the official LobeHub bot to Two equivalent ways: -- **In Discord** — DM the bot `/agents` and pick a different agent. +- **On Discord** — DM the bot `/agents` and pick a different agent. - **In LobeHub** — open **Settings → Messenger → Discord** and use the agent picker on your link row. The change takes effect on the next message you send. @@ -96,7 +96,7 @@ You can re-add the bot to a server (or re-link personally) at any time by repeat ## Troubleshooting -- **"Server already connected"** — Another LobeHub user already added the bot to this server. DM the LobeHub bot in Discord to link your personal account; you do not need to add the bot again. +- **"Server already connected"** — Another LobeHub user already added the bot to this server. DM the LobeHub bot on Discord to link your personal account; you do not need to add the bot again. - **Discord install failed (``)** — Common reasons: authorisation cancelled, install session expired (re-open the modal and try again), Discord returned incomplete data (retry; if persistent, contact support). - **Bot is in the server but doesn't reply** — Check that you have a personal link under **Settings → Messenger → Discord**. The bot only answers users with a confirmed personal link. - **Removed the audit row but the bot is still in my server** — That's expected. Disconnecting in LobeHub only removes the audit entry; a Discord server admin must kick the bot from Discord itself. diff --git a/docs/usage/messenger/slack.mdx b/docs/usage/messenger/slack.mdx index 23f6ec2798..a6f4337530 100644 --- a/docs/usage/messenger/slack.mdx +++ b/docs/usage/messenger/slack.mdx @@ -31,7 +31,7 @@ Slack works in two phases: a **workspace admin installs** the official LobeHub S {/* TODO: screenshot — Slack detail page with empty Connections list and the Connect button */} - ### Authorise in Slack + ### Authorise on Slack You'll be redirected to Slack's authorisation screen. Pick the workspace you want to install into and click **Allow**. @@ -43,15 +43,15 @@ Slack works in two phases: a **workspace admin installs** the official LobeHub S {/* TODO: screenshot — Slack detail page showing one connected workspace row */} - > **Workspace already connected by someone else?** LobeHub blocks the install and shows a "Workspace already connected" notice. You don't need to install again — just DM **@LobeHub** in Slack to link your personal account. If you want to take over ownership, ask the original installer to disconnect the workspace first. + > **Workspace already connected by someone else?** LobeHub blocks the install and shows a "Workspace already connected" notice. You don't need to install again — just DM **@LobeHub** on Slack to link your personal account. If you want to take over ownership, ask the original installer to disconnect the workspace first. ## Phase B — Link your personal account (each member) - ### Open the LobeHub bot in Slack + ### Open the LobeHub bot on Slack - In Slack, open the **Apps** sidebar and find **LobeHub**, or search for `@LobeHub`. Open a DM with the bot. + On Slack, open the **Apps** sidebar and find **LobeHub**, or search for `@LobeHub`. Open a DM with the bot. {/* TODO: screenshot — Slack apps sidebar with LobeHub highlighted */} @@ -76,7 +76,7 @@ Slack works in two phases: a **workspace admin installs** the official LobeHub S Two equivalent ways: -- **In Slack** — DM the bot `/agents` and pick a different agent. +- **On Slack** — DM the bot `/agents` and pick a different agent. - **In LobeHub** — open **Settings → Messenger → Slack** and use the agent picker on your link row. The change takes effect on the next message you send. diff --git a/docs/usage/messenger/telegram.mdx b/docs/usage/messenger/telegram.mdx index d2a69011e7..1eb42ee768 100644 --- a/docs/usage/messenger/telegram.mdx +++ b/docs/usage/messenger/telegram.mdx @@ -32,7 +32,7 @@ On the Telegram detail page, click **Connect** in the top-right corner. A modal {/* TODO: screenshot — Telegram detail page with empty Connections list and the Connect button */} -## Step 3: Open the bot in Telegram +## Step 3: Open the bot on Telegram Either tap **Open in Telegram** in the modal, or scan the QR code with your phone. Telegram opens the official LobeHub bot. @@ -58,7 +58,7 @@ After confirming, the Telegram detail page shows your link as a "user" row with Two equivalent ways: -- **In Telegram** — send `/agents` to the bot and pick a different agent. +- **On Telegram** — send `/agents` to the bot and pick a different agent. - **In LobeHub** — open **Settings → Messenger → Telegram** and use the agent picker on your link row. The change takes effect on the next message you send. @@ -67,7 +67,7 @@ The change takes effect on the next message you send. In **Settings → Messenger → Telegram**, click **Disconnect** on the link row. The bot will stop accepting your messages until you re-link by sending `/start` again. -> Disconnecting from LobeHub does not remove the bot from your Telegram chat list — you can manually delete the chat in Telegram if you no longer want to see it. +> Disconnecting from LobeHub does not remove the bot from your Telegram chat list — you can manually delete the chat on Telegram if you no longer want to see it. ## Troubleshooting diff --git a/locales/en-US/agent.json b/locales/en-US/agent.json index dd2aa2864c..3650daca8d 100644 --- a/locales/en-US/agent.json +++ b/locales/en-US/agent.json @@ -179,7 +179,7 @@ "channel.userIdHint.line": "Open the LINE Developers Console → your channel → Basic settings tab, and copy \"Your user ID\" (starts with U, 33 chars).", "channel.userIdHint.qq": "Your QQ number, shown on your QQ profile page.", "channel.userIdHint.slack": "Open your Slack profile → ⋮ More → Copy member ID (starts with U).", - "channel.userIdHint.telegram": "Send any message to @userinfobot in Telegram — it replies with your numeric User ID.", + "channel.userIdHint.telegram": "Send any message to @userinfobot on Telegram — it replies with your numeric User ID.", "channel.userIdMissingDesc": "Without it, AI tools can't reach you with reminders, and pairing approvals will fail. Fill it in under Advanced Settings.", "channel.userIdMissingTitle": "Add your platform User ID", "channel.validationError": "Please fill in Application ID and Token", @@ -202,7 +202,7 @@ "channel.wechatManagedCredentials": "This channel is already connected through QR code authorization. Credentials are managed automatically.", "channel.wechatQrExpired": "QR code expired. Please refresh to get a new one.", "channel.wechatQrRefresh": "Refresh QR Code", - "channel.wechatQrScaned": "QR code scanned. Please confirm the login in WeChat.", + "channel.wechatQrScaned": "QR code scanned. Please confirm the login on WeChat.", "channel.wechatQrWait": "Open WeChat and scan the QR code to connect.", "channel.wechatRebind": "Rebind via QR Code", "channel.wechatScanTitle": "Connect WeChat Bot", diff --git a/locales/en-US/messenger.json b/locales/en-US/messenger.json index 47bd784d13..b4a5af2ec4 100644 --- a/locales/en-US/messenger.json +++ b/locales/en-US/messenger.json @@ -21,7 +21,7 @@ "messenger.discord.connections.disconnectSuccess": "Server removed.", "messenger.discord.connections.disconnectTitle": "Remove server", "messenger.discord.installBlocked.dismiss": "Got it", - "messenger.discord.installBlocked.suggestion": "DM the LobeHub bot in Discord to link your personal account — you don't need to add the bot again. Or ask the original installer to remove this server in LobeHub Settings → Messenger before re-adding it.", + "messenger.discord.installBlocked.suggestion": "DM the LobeHub bot on Discord to link your personal account — you don't need to add the bot again. Or ask the original installer to remove this server in LobeHub Settings → Messenger before re-adding it.", "messenger.discord.installBlocked.title": "Server already connected", "messenger.discord.installBlocked.withName": "\"{{workspace}}\" is already connected to LobeHub by another user.", "messenger.discord.installBlocked.withoutName": "This Discord server is already connected to LobeHub by another user.", @@ -37,7 +37,7 @@ "messenger.discord.installResult.reasons.persistFailed": "the server connection could not be saved", "messenger.discord.installResult.success": "Discord server connected.", "messenger.discord.userPending.cta": "Open in Discord", - "messenger.discord.userPending.hint": "Open the bot in Discord and send any message to finish linking your account.", + "messenger.discord.userPending.hint": "Open the bot on Discord and send any message to finish linking your account.", "messenger.discord.userPending.name": "Not linked yet", "messenger.error.agentNotFound": "Agent not found.", "messenger.error.disconnectNotAllowed": "You can only disconnect installations you started.", @@ -46,27 +46,27 @@ "messenger.error.pickDefaultAgent": "Select a default agent before confirming.", "messenger.error.platformNotConfigured": "This messenger platform isn't available right now. Please try again later.", "messenger.linkCta": "Connect", - "messenger.linkModal.continueIn": "Continue setup in {{platform}}", + "messenger.linkModal.continueIn": "Continue setup on {{platform}}", "messenger.linkModal.instructions": "Open the bot, send /start, then tap \"Link Account\" to connect your LobeHub account.", "messenger.linkModal.notConfigured": "This connection isn't available right now. Please try again later.", "messenger.linkModal.openCta": "Open in {{platform}}", "messenger.linkModal.scanHint": "Or scan with your phone to open {{platform}}.", - "messenger.list.discord.description": "Chat with your LobeHub agents from any Discord server via DM with the LobeHub bot.", - "messenger.list.slack.description": "Chat with your LobeHub agents from any Slack workspace via DM or @LobeHub.", - "messenger.list.telegram.description": "Chat with your LobeHub agents in Telegram and pick which one answers from anywhere.", + "messenger.list.discord.description": "Chat with your LobeHub agents on Discord by DMing the LobeHub bot from any server.", + "messenger.list.slack.description": "Chat with your LobeHub agents on Slack by DMing or mentioning @LobeHub in any workspace.", + "messenger.list.telegram.description": "Chat with your LobeHub agents on Telegram, and choose which agent replies.", "messenger.noPlatformsConfigured": "No platforms are available yet. Check back soon.", "messenger.setActiveFailed": "Failed to set as active.", "messenger.setActiveSuccess": "Active agent updated.", - "messenger.slack.connectModal.continueButton": "Continue in Slack", + "messenger.slack.connectModal.continueButton": "Continue on Slack", "messenger.slack.connectModal.description": "You will be redirected to Slack to authorize the LobeHub workspace install.", "messenger.slack.connectModal.notConfigured": "Slack isn't available right now. Please try again later.", - "messenger.slack.connectModal.title": "Continue setup in Slack", + "messenger.slack.connectModal.title": "Continue setup on Slack", "messenger.slack.connections.disconnectConfirm": "Disconnect the LobeHub bot from this Slack workspace? Existing user links will pause until you re-install.", "messenger.slack.connections.disconnectFailed": "Failed to disconnect.", "messenger.slack.connections.disconnectSuccess": "Workspace disconnected.", "messenger.slack.connections.disconnectTitle": "Disconnect workspace", "messenger.slack.installBlocked.dismiss": "Got it", - "messenger.slack.installBlocked.suggestion": "DM @LobeHub in Slack to link your personal account — you don't need to install again. Or ask the original installer to disconnect this workspace first if you want to take over ownership.", + "messenger.slack.installBlocked.suggestion": "DM @LobeHub on Slack to link your personal account — you don't need to install again. Or ask the original installer to disconnect this workspace first if you want to take over ownership.", "messenger.slack.installBlocked.title": "Workspace already connected", "messenger.slack.installBlocked.withName": "\"{{workspace}}\" is already connected to LobeHub by another user.", "messenger.slack.installBlocked.withoutName": "This Slack workspace is already connected to LobeHub by another user.", diff --git a/src/locales/default/agent.ts b/src/locales/default/agent.ts index 60f9e63613..5430e557af 100644 --- a/src/locales/default/agent.ts +++ b/src/locales/default/agent.ts @@ -91,7 +91,7 @@ export default { 'Connect this assistant to WeChat via iLink Bot for private and group chats.', 'channel.wechatQrExpired': 'QR code expired. Please refresh to get a new one.', 'channel.wechatQrRefresh': 'Refresh QR Code', - 'channel.wechatQrScaned': 'QR code scanned. Please confirm the login in WeChat.', + 'channel.wechatQrScaned': 'QR code scanned. Please confirm the login on WeChat.', 'channel.wechatQrWait': 'Open WeChat and scan the QR code to connect.', 'channel.wechatBotId': 'Bot ID', 'channel.wechatConnectedInfo': 'Connected WeChat Account', @@ -239,7 +239,7 @@ export default { 'channel.userIdHint.qq': 'Your QQ number, shown on your QQ profile page.', 'channel.userIdHint.slack': 'Open your Slack profile → ⋮ More → Copy member ID (starts with U).', 'channel.userIdHint.telegram': - 'Send any message to @userinfobot in Telegram — it replies with your numeric User ID.', + 'Send any message to @userinfobot on Telegram — it replies with your numeric User ID.', 'channel.refreshStatus': 'Refresh status', 'channel.runtimeDisconnected': 'Bot disconnected', 'channel.statusConnected': 'Connected', diff --git a/src/locales/default/messenger.ts b/src/locales/default/messenger.ts index 9a463c24c6..c7dc664e50 100644 --- a/src/locales/default/messenger.ts +++ b/src/locales/default/messenger.ts @@ -21,7 +21,7 @@ export default { 'messenger.error.platformNotConfigured': "This messenger platform isn't available right now. Please try again later.", 'messenger.linkCta': 'Connect', - 'messenger.linkModal.continueIn': 'Continue setup in {{platform}}', + 'messenger.linkModal.continueIn': 'Continue setup on {{platform}}', 'messenger.linkModal.instructions': 'Open the bot, send /start, then tap "Link Account" to connect your LobeHub account.', 'messenger.linkModal.notConfigured': @@ -29,12 +29,12 @@ export default { 'messenger.linkModal.openCta': 'Open in {{platform}}', 'messenger.linkModal.scanHint': 'Or scan with your phone to open {{platform}}.', 'messenger.noPlatformsConfigured': 'No platforms are available yet. Check back soon.', - 'messenger.slack.connectModal.continueButton': 'Continue in Slack', + 'messenger.slack.connectModal.continueButton': 'Continue on Slack', 'messenger.slack.connectModal.description': 'You will be redirected to Slack to authorize the LobeHub workspace install.', 'messenger.slack.connectModal.notConfigured': "Slack isn't available right now. Please try again later.", - 'messenger.slack.connectModal.title': 'Continue setup in Slack', + 'messenger.slack.connectModal.title': 'Continue setup on Slack', 'messenger.slack.connections.disconnectConfirm': 'Disconnect the LobeHub bot from this Slack workspace? Existing user links will pause until you re-install.', 'messenger.slack.connections.disconnectFailed': 'Failed to disconnect.', @@ -42,7 +42,7 @@ export default { 'messenger.slack.connections.disconnectTitle': 'Disconnect workspace', 'messenger.slack.installBlocked.dismiss': 'Got it', 'messenger.slack.installBlocked.suggestion': - "DM @LobeHub in Slack to link your personal account — you don't need to install again. Or ask the original installer to disconnect this workspace first if you want to take over ownership.", + "DM @LobeHub on Slack to link your personal account — you don't need to install again. Or ask the original installer to disconnect this workspace first if you want to take over ownership.", 'messenger.slack.installBlocked.title': 'Workspace already connected', 'messenger.slack.installBlocked.withName': '"{{workspace}}" is already connected to LobeHub by another user.', @@ -76,7 +76,7 @@ export default { 'messenger.discord.connections.disconnectTitle': 'Remove server', 'messenger.discord.installBlocked.dismiss': 'Got it', 'messenger.discord.installBlocked.suggestion': - "DM the LobeHub bot in Discord to link your personal account — you don't need to add the bot again. Or ask the original installer to remove this server in LobeHub Settings → Messenger before re-adding it.", + "DM the LobeHub bot on Discord to link your personal account — you don't need to add the bot again. Or ask the original installer to remove this server in LobeHub Settings → Messenger before re-adding it.", 'messenger.discord.installBlocked.title': 'Server already connected', 'messenger.discord.installBlocked.withName': '"{{workspace}}" is already connected to LobeHub by another user.', @@ -100,14 +100,14 @@ export default { 'messenger.discord.installResult.success': 'Discord server connected.', 'messenger.discord.userPending.cta': 'Open in Discord', 'messenger.discord.userPending.hint': - 'Open the bot in Discord and send any message to finish linking your account.', + 'Open the bot on Discord and send any message to finish linking your account.', 'messenger.discord.userPending.name': 'Not linked yet', 'messenger.list.discord.description': - 'Chat with your LobeHub agents from any Discord server via DM with the LobeHub bot.', + 'Chat with your LobeHub agents on Discord by DMing the LobeHub bot from any server.', 'messenger.list.slack.description': - 'Chat with your LobeHub agents from any Slack workspace via DM or @LobeHub.', + 'Chat with your LobeHub agents on Slack by DMing or mentioning @LobeHub in any workspace.', 'messenger.list.telegram.description': - 'Chat with your LobeHub agents in Telegram and pick which one answers from anywhere.', + 'Chat with your LobeHub agents on Telegram, and choose which agent replies.', 'messenger.setActiveFailed': 'Failed to set as active.', 'messenger.setActiveSuccess': 'Active agent updated.', 'messenger.subtitle': diff --git a/src/server/services/messenger/MessengerRouter.ts b/src/server/services/messenger/MessengerRouter.ts index 06ee75ab73..9f6b62f765 100644 --- a/src/server/services/messenger/MessengerRouter.ts +++ b/src/server/services/messenger/MessengerRouter.ts @@ -1107,7 +1107,7 @@ export class MessengerRouter { } const text = [ - ":wave: Hi, I'm *LobeHub* — your AI agent in Slack.", + ":wave: Hi, I'm *LobeHub* — your AI agent on Slack.", '', '• Mention me with `@LobeHub ` to chat in this channel.', '• First time? Send me a *direct message* to link your LobeHub account.', diff --git a/src/server/services/messenger/platforms/discord/binder.ts b/src/server/services/messenger/platforms/discord/binder.ts index 0a2a8521c7..9831de2b35 100644 --- a/src/server/services/messenger/platforms/discord/binder.ts +++ b/src/server/services/messenger/platforms/discord/binder.ts @@ -159,7 +159,7 @@ export class MessengerDiscordBinder implements MessengerPlatformBinder { // where the unlinked message handler runs after the chat-sdk has already // dispatched the message — so we stick to a markdown link for v1. const text = [ - "Hi, I'm LobeHub — your AI agent in Discord.", + "Hi, I'm LobeHub — your AI agent on Discord.", 'To start, link your LobeHub account.', '', `🔗 [Link Account](${verifyUrl})`, diff --git a/src/server/services/messenger/platforms/slack/binder.ts b/src/server/services/messenger/platforms/slack/binder.ts index 061e7a7438..a8315f6abe 100644 --- a/src/server/services/messenger/platforms/slack/binder.ts +++ b/src/server/services/messenger/platforms/slack/binder.ts @@ -175,7 +175,7 @@ export class MessengerSlackBinder implements MessengerPlatformBinder { if (ctx.channelMentionThreadId) { const [, channelId, threadTs] = ctx.channelMentionThreadId.split(':'); const text = - "Hi, I'm LobeHub — your AI agent in Slack.\n" + + "Hi, I'm LobeHub — your AI agent on Slack.\n" + `Link your LobeHub account to start chatting: <${verifyUrl}|click here>`; await this.replyEphemeral({ channelId, @@ -187,7 +187,7 @@ export class MessengerSlackBinder implements MessengerPlatformBinder { } const intro = - "Hi, I'm LobeHub — your AI agent in Slack.\n" + 'To start, link your LobeHub account.'; + "Hi, I'm LobeHub — your AI agent on Slack.\n" + 'To start, link your LobeHub account.'; const linkLabel = `Or copy this link: <${verifyUrl}|${verifyUrl}>`; const api = new SlackApi(this.creds.botToken); From 391b16e08272d13d4c29e046467bedf09d56027e Mon Sep 17 00:00:00 2001 From: YuTengjing Date: Tue, 19 May 2026 12:53:32 +0800 Subject: [PATCH 020/224] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20perf:=20optimize?= =?UTF-8?q?=20chat=20bootstrap=20persistence=20(#14934)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/messages/message.create.test.ts | 121 +- .../__tests__/topics/topic.create.test.ts | 7 +- packages/database/src/models/message.ts | 1283 ++++++++++++----- packages/database/src/models/topic.ts | 292 ++-- .../model-runtime/src/core/ModelRuntime.ts | 114 +- .../src/core/RouterRuntime/createRuntime.ts | 201 ++- packages/types/src/aiChat.test.ts | 21 + packages/types/src/aiChat.ts | 5 + packages/utils/src/index.ts | 1 + packages/utils/src/timing.ts | 173 +++ .../routers/lambda/__tests__/aiChat.test.ts | 174 ++- src/server/routers/lambda/aiChat.ts | 264 +++- src/server/routers/lambda/message.ts | 35 +- src/server/services/aiChat/index.test.ts | 15 +- src/server/services/aiChat/index.ts | 90 +- src/server/services/message/index.ts | 43 +- .../__tests__/conversationLifecycle.test.ts | 8 +- .../aiChat/actions/conversationLifecycle.ts | 19 +- 18 files changed, 2239 insertions(+), 627 deletions(-) create mode 100644 packages/types/src/aiChat.test.ts create mode 100644 packages/utils/src/timing.ts diff --git a/packages/database/src/models/__tests__/messages/message.create.test.ts b/packages/database/src/models/__tests__/messages/message.create.test.ts index 42ad9ed236..6176c17ac5 100644 --- a/packages/database/src/models/__tests__/messages/message.create.test.ts +++ b/packages/database/src/models/__tests__/messages/message.create.test.ts @@ -1,5 +1,5 @@ import type { DBMessageItem } from '@lobechat/types'; -import { eq } from 'drizzle-orm'; +import { asc, eq } from 'drizzle-orm'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { uuid } from '@/utils/uuid'; @@ -16,6 +16,7 @@ import { messages, messagesFiles, sessions, + topics, users, } from '../../../schemas'; import type { LobeChatDatabase } from '../../../type'; @@ -248,6 +249,124 @@ describe('MessageModel Create Tests', () => { expect(pluginResult[0].arguments).not.toContain('\u0000'); }); + it('should create user and assistant messages with one topic touch', async () => { + await serverDB.insert(topics).values({ + id: 'topic-pair', + sessionId: '1', + title: 'Topic pair', + userId, + }); + + const timingEvents: string[] = []; + const result = await messageModel.createUserAndAssistantMessages( + { + assistantMessage: { + content: '', + model: 'gpt-4o', + provider: 'openai', + role: 'assistant', + sessionId: '1', + topicId: 'topic-pair', + }, + userMessage: { + content: 'hello', + files: ['f1'], + role: 'user', + sessionId: '1', + topicId: 'topic-pair', + }, + }, + { + timing: { + log: (event) => timingEvents.push(event), + }, + }, + ); + + expect(result.userMessage.id).toBeDefined(); + expect(result.assistantMessage.id).toBeDefined(); + expect(result.assistantMessage.parentId).toBe(result.userMessage.id); + expect(result.userMessage.createdAt.getTime()).toBeLessThan( + result.assistantMessage.createdAt.getTime(), + ); + + const dbMessages = await serverDB + .select() + .from(messages) + .where(eq(messages.userId, userId)) + .orderBy(asc(messages.createdAt)); + + expect(dbMessages.map((message) => message.id)).toEqual([ + result.userMessage.id, + result.assistantMessage.id, + ]); + + const messageFiles = await serverDB + .select() + .from(messagesFiles) + .where(eq(messagesFiles.messageId, result.userMessage.id)); + + expect(messageFiles).toHaveLength(1); + expect( + timingEvents.filter( + (event) => event === 'db.message.createUserAndAssistant.messages.insert:start', + ), + ).toHaveLength(1); + expect( + timingEvents.filter( + (event) => event === 'db.message.createUserAndAssistant.topic.touchUpdatedAt:start', + ), + ).toHaveLength(1); + }); + + it('should skip topic touch when creating a pair for an already-created topic', async () => { + await serverDB.insert(topics).values({ + id: 'topic-pair-no-touch', + sessionId: '1', + title: 'Topic pair no touch', + userId, + }); + + const timingEvents: string[] = []; + const result = await messageModel.createUserAndAssistantMessages( + { + assistantMessage: { + content: '', + model: 'gpt-4o', + provider: 'openai', + role: 'assistant', + sessionId: '1', + topicId: 'topic-pair-no-touch', + }, + userMessage: { + content: 'hello', + role: 'user', + sessionId: '1', + topicId: 'topic-pair-no-touch', + }, + }, + { + timing: { + log: (event) => timingEvents.push(event), + }, + touchTopicUpdatedAt: false, + }, + ); + + expect(result.userMessage.id).toBeDefined(); + expect(result.assistantMessage.parentId).toBe(result.userMessage.id); + expect( + timingEvents.filter( + (event) => event === 'db.message.createUserAndAssistant.messages.insert:start', + ), + ).toHaveLength(1); + expect( + timingEvents.filter( + (event) => event === 'db.message.createUserAndAssistant.topic.touchUpdatedAt:start', + ), + ).toHaveLength(0); + }); + describe('create with advanced parameters', () => { it('should create a message with custom ID', async () => { const customId = 'custom-msg-id'; diff --git a/packages/database/src/models/__tests__/topics/topic.create.test.ts b/packages/database/src/models/__tests__/topics/topic.create.test.ts index 451d79d953..fdef1d23ad 100644 --- a/packages/database/src/models/__tests__/topics/topic.create.test.ts +++ b/packages/database/src/models/__tests__/topics/topic.create.test.ts @@ -95,7 +95,10 @@ describe('TopicModel - Create', () => { const topicId = 'new-topic'; - const createdTopic = await topicModel.create(topicData, topicId); + const timingEvents: string[] = []; + const createdTopic = await topicModel.create(topicData, topicId, { + log: (event) => timingEvents.push(event), + }); expect(createdTopic).toEqual({ id: topicId, @@ -123,6 +126,8 @@ describe('TopicModel - Create', () => { const dbTopic = await serverDB.select().from(topics).where(eq(topics.id, topicId)); expect(dbTopic).toHaveLength(1); expect(dbTopic[0]).toEqual(createdTopic); + expect(timingEvents).toContain('db.topic.create.topics.insert:start'); + expect(timingEvents).not.toContain('db.topic.create.transaction:start'); }); it('should create a new topic with agentId', async () => { diff --git a/packages/database/src/models/message.ts b/packages/database/src/models/message.ts index 9945566f25..2b5bd87182 100644 --- a/packages/database/src/models/message.ts +++ b/packages/database/src/models/message.ts @@ -21,6 +21,12 @@ import type { UpdateMessageRAGParams, } from '@lobechat/types'; import { MessageGroupType, ThreadType } from '@lobechat/types'; +import type { TimingSink } from '@lobechat/utils'; +import { + getDurationMs, + logTimingSink as logTiming, + runTimedSinkStage as runTimedStage, +} from '@lobechat/utils'; import type { HeatmapsProps } from '@lobehub/charts'; import dayjs from 'dayjs'; import type { SQL } from 'drizzle-orm'; @@ -84,6 +90,7 @@ export interface QueryMessagesOptions { * Post-process function for file URLs */ postProcessUrl?: (path: string | null, file: { fileType: string }) => Promise; + timing?: ModelTimingContext; /** * Topic ID for MessageGroup aggregation queries */ @@ -94,6 +101,92 @@ export interface QueryMessagesOptions { where?: SQL; } +export interface ModelTimingContext extends TimingSink {} + +interface MessageRelatedFile { + fileType: string | null; + id: string; + messageId: string; + name: string | null; + size: number | null; + url: string; +} + +interface MessageChunkRelation { + fileId: string; + filename: string | null; + fileType: string | null; + fileUrl: string | null; + id: string | null; + messageId: string | null; + similarity: string | null; + text: string | null; +} + +interface MessageQueryRelation { + id: string; + messageId: string; + rewriteQuery: string | null; + userQuery: string | null; +} + +interface MessageThreadRelation { + metadata: unknown; + sourceMessageId: string | null; + status: string | null; + threadId: string; + title: string | null; +} + +interface MessageFileRelations { + documentsMap: Record; + relatedFileList: MessageRelatedFile[]; +} + +interface CreateUserAndAssistantMessagesParams { + assistantMessage: CreateMessageParams; + userMessage: CreateMessageParams; +} + +interface CreateUserAndAssistantMessagesOptions { + timing?: ModelTimingContext; + touchTopicUpdatedAt?: boolean; +} + +interface CreateMessageInsertParams { + createdAt?: CreateMessageParams['createdAt']; + fromModel?: CreateMessageParams['model']; + fromProvider?: CreateMessageParams['provider']; + message: Omit< + CreateMessageParams, + | 'createdAt' + | 'fileChunks' + | 'files' + | 'model' + | 'plugin' + | 'pluginIntervention' + | 'pluginState' + | 'provider' + | 'ragQueryId' + | 'updatedAt' + >; + updatedAt?: CreateMessageParams['updatedAt']; +} + +interface CreateMessageRelationParams { + fileChunks?: CreateMessageParams['fileChunks']; + files?: CreateMessageParams['files']; + plugin?: CreateMessageParams['plugin']; + pluginIntervention?: CreateMessageParams['pluginIntervention']; + pluginState?: CreateMessageParams['pluginState']; + ragQueryId?: CreateMessageParams['ragQueryId']; +} + +interface SplitCreateMessageParams { + insert: CreateMessageInsertParams; + relations: CreateMessageRelationParams; +} + export class MessageModel { private userId: string; private db: LobeChatDatabase; @@ -134,26 +227,55 @@ export class MessageModel { }: QueryMessageParams = {}, options: { postProcessUrl?: (path: string | null, file: { fileType: string }) => Promise; + timing?: ModelTimingContext; } = {}, ) => { + const queryStartedAt = Date.now(); + const timing = options.timing; + logTiming(timing, 'db.message.query:start', { + current, + hasAgentId: !!agentId, + hasGroupId: !!groupId, + hasSessionId: !!sessionId, + hasThreadId: !!threadId, + hasTopicId: !!topicId, + pageSize, + }); + // Build agent condition (handles legacy sessionId lookup) let agentCondition: SQL | undefined; if (agentId) { - agentCondition = await this.buildAgentCondition(agentId); + agentCondition = await runTimedStage( + timing, + 'db.message.query.buildAgentCondition', + () => this.buildAgentCondition(agentId), + { hasAgentId: true }, + ); } else if (sessionId) { agentCondition = this.matchSession(sessionId); } // For thread queries, we need to fetch complete thread data (parent + thread messages) if (threadId) { - const threadCondition = await this.buildThreadQueryCondition(threadId); - return this.queryWithWhere({ + const threadCondition = await runTimedStage( + timing, + 'db.message.query.buildThreadCondition', + () => this.buildThreadQueryCondition(threadId), + { hasThreadId: true }, + ); + const messageItems = await this.queryWithWhere({ current, pageSize, postProcessUrl: options.postProcessUrl, + timing, // Thread queries optionally add agent/session scope if provided where: agentCondition ? and(agentCondition, threadCondition) : threadCondition, }); + logTiming(timing, 'db.message.query:done', { + messageCount: messageItems.length, + stageMs: getDurationMs(queryStartedAt), + }); + return messageItems; } // For Group Chat queries: filter by groupId only (not agentId) @@ -166,13 +288,19 @@ export class MessageModel { this.matchThread(threadId), ); - return this.queryWithWhere({ + const messageItems = await this.queryWithWhere({ current, pageSize, postProcessUrl: options.postProcessUrl, + timing, topicId: topicId ?? undefined, where: whereCondition, }); + logTiming(timing, 'db.message.query:done', { + messageCount: messageItems.length, + stageMs: getDurationMs(queryStartedAt), + }); + return messageItems; } // Standard query with session/topic/group filters @@ -183,13 +311,19 @@ export class MessageModel { this.matchThread(threadId), ); - return this.queryWithWhere({ + const messageItems = await this.queryWithWhere({ current, pageSize, postProcessUrl: options.postProcessUrl, + timing, topicId: topicId ?? undefined, where: whereCondition, }); + logTiming(timing, 'db.message.query:done', { + messageCount: messageItems.length, + stageMs: getDurationMs(queryStartedAt), + }); + return messageItems; }; /** @@ -208,162 +342,122 @@ export class MessageModel { * @returns Messages with all related data, including MessageGroup nodes */ queryWithWhere = async (options: QueryMessagesOptions = {}): Promise => { - const { where, current = 0, pageSize = 1000, postProcessUrl, topicId } = options; + const { where, current = 0, pageSize = 1000, postProcessUrl, topicId, timing } = options; + const totalStartedAt = Date.now(); const offset = current * pageSize; // 1. get basic messages with joins, excluding messages that belong to MessageGroups - const result = await this.db - .select({ - id: messages.id, - role: messages.role, - content: messages.content, - editorData: messages.editorData, - reasoning: messages.reasoning, - search: messages.search, - metadata: messages.metadata, - error: messages.error, + const result = await runTimedStage( + timing, + 'db.message.queryWithWhere.baseSelect', + () => + this.db + .select({ + id: messages.id, + role: messages.role, + content: messages.content, + editorData: messages.editorData, + reasoning: messages.reasoning, + search: messages.search, + metadata: messages.metadata, + error: messages.error, - model: messages.model, - provider: messages.provider, + model: messages.model, + provider: messages.provider, - createdAt: messages.createdAt, - updatedAt: messages.updatedAt, + createdAt: messages.createdAt, + updatedAt: messages.updatedAt, - sessionId: messages.sessionId, - topicId: messages.topicId, - parentId: messages.parentId, - threadId: messages.threadId, + sessionId: messages.sessionId, + topicId: messages.topicId, + parentId: messages.parentId, + threadId: messages.threadId, - // Group chat fields - groupId: messages.groupId, - agentId: messages.agentId, - targetId: messages.targetId, + // Group chat fields + groupId: messages.groupId, + agentId: messages.agentId, + targetId: messages.targetId, - tools: messages.tools, - tool_call_id: messagePlugins.toolCallId, + tools: messages.tools, + tool_call_id: messagePlugins.toolCallId, - plugin: { - apiName: messagePlugins.apiName, - arguments: messagePlugins.arguments, - identifier: messagePlugins.identifier, - type: messagePlugins.type, - }, - pluginError: messagePlugins.error, - pluginIntervention: messagePlugins.intervention, - pluginState: messagePlugins.state, + plugin: { + apiName: messagePlugins.apiName, + arguments: messagePlugins.arguments, + identifier: messagePlugins.identifier, + type: messagePlugins.type, + }, + pluginError: messagePlugins.error, + pluginIntervention: messagePlugins.intervention, + pluginState: messagePlugins.state, - translate: { - content: messageTranslates.content, - from: messageTranslates.from, - to: messageTranslates.to, - }, + translate: { + content: messageTranslates.content, + from: messageTranslates.from, + to: messageTranslates.to, + }, - ttsId: messageTTS.id, - ttsContentMd5: messageTTS.contentMd5, - ttsFile: messageTTS.fileId, - ttsVoice: messageTTS.voice, - }) - .from(messages) - .where( - and( - eq(messages.userId, this.userId), - // Filter out messages that belong to MessageGroups - isNull(messages.messageGroupId), - where, - ), - ) - .leftJoin(messagePlugins, eq(messagePlugins.id, messages.id)) - .leftJoin(messageTranslates, eq(messageTranslates.id, messages.id)) - .leftJoin(messageTTS, eq(messageTTS.id, messages.id)) - .orderBy(asc(messages.createdAt)) - .limit(pageSize) - .offset(offset); + ttsId: messageTTS.id, + ttsContentMd5: messageTTS.contentMd5, + ttsFile: messageTTS.fileId, + ttsVoice: messageTTS.voice, + }) + .from(messages) + .where( + and( + eq(messages.userId, this.userId), + // Filter out messages that belong to MessageGroups + isNull(messages.messageGroupId), + where, + ), + ) + .leftJoin(messagePlugins, eq(messagePlugins.id, messages.id)) + .leftJoin(messageTranslates, eq(messageTranslates.id, messages.id)) + .leftJoin(messageTTS, eq(messageTTS.id, messages.id)) + .orderBy(asc(messages.createdAt)) + .limit(pageSize) + .offset(offset), + { current, pageSize }, + ); + logTiming(timing, 'db.message.queryWithWhere.baseSelect:rows', { rowCount: result.length }); const messageIds = result.map((message) => message.id as string); - // 2. Query MessageGroups for this topic (if topicId is available) - // For pagination support: - // - First page (current === 0): fetch all MessageGroup nodes (no time filter) - // - Subsequent pages: only fetch groups within the current page's time range - let messageGroupNodes: UIChatMessage[] = []; - if (topicId && result.length > 0) { - if (current === 0) { - // First page: fetch all groups to include compressed history - messageGroupNodes = await this.queryMessageGroupNodes(topicId, undefined, postProcessUrl); - } else { - // Subsequent pages: filter by time range to avoid duplicates - const firstMessageTime = result[0].createdAt; - const lastMessageTime = result.at(-1)!.createdAt; - messageGroupNodes = await this.queryMessageGroupNodes( - topicId, - { - endTime: lastMessageTime, - startTime: firstMessageTime, - }, - postProcessUrl, - ); - } - } else if (topicId && current === 0) { - // First page with no messages: still fetch all groups - messageGroupNodes = await this.queryMessageGroupNodes(topicId, undefined, postProcessUrl); - } + const messageGroupNodesPromise = this.queryMessageGroupNodesForPage({ + current, + postProcessUrl, + result, + timing, + topicId, + }); - // If no messages and no group nodes, return empty - if (messageIds.length === 0 && messageGroupNodes.length === 0) return []; + const taskMessageIds = result + .filter((message) => message.role === 'task') + .map((message) => { + return message.id as string; + }); - // 3. get relative files (only if we have messages) - let relatedFileList: { - fileType: string | null; - id: string; - messageId: string; - name: string | null; - size: number | null; - url: string; - }[] = []; + const [ + messageGroupNodes, + { documentsMap, relatedFileList }, + chunksList, + messageQueriesList, + threadData, + ] = await Promise.all([ + messageGroupNodesPromise, + this.queryMessageFileRelations(messageIds, postProcessUrl, timing), + this.queryMessageChunkRelations(messageIds, timing), + this.queryMessageQueryRelations(messageIds, timing), + this.queryMessageThreadRelations(taskMessageIds, timing), + ]); - if (messageIds.length > 0) { - const rawRelatedFileList = await this.db - .select({ - fileType: files.fileType, - id: messagesFiles.fileId, - messageId: messagesFiles.messageId, - name: files.name, - size: files.size, - url: files.url, - }) - .from(messagesFiles) - .leftJoin(files, eq(files.id, messagesFiles.fileId)) - .where(inArray(messagesFiles.messageId, messageIds)); - - relatedFileList = await Promise.all( - rawRelatedFileList.map(async (file) => ({ - ...file, - url: postProcessUrl ? await postProcessUrl(file.url, file as any) : (file.url as string), - })), - ); - } - - // Get associated document content - const fileIds = relatedFileList.map((file) => file.id).filter(Boolean); - - let documentsMap: Record = {}; - - if (fileIds.length > 0) { - const documentsList = await this.db - .select({ - content: documents.content, - fileId: documents.fileId, - }) - .from(documents) - .where(inArray(documents.fileId, fileIds)); - - documentsMap = documentsList.reduce( - (acc, doc) => { - if (doc.fileId) acc[doc.fileId] = doc.content as string; - return acc; - }, - {} as Record, - ); + if (messageIds.length === 0 && messageGroupNodes.length === 0) { + logTiming(timing, 'db.message.queryWithWhere:done', { + messageGroupCount: 0, + rowCount: 0, + stageMs: getDurationMs(totalStartedAt), + }); + return []; } const imageList = relatedFileList.filter((i) => (i.fileType || '').startsWith('image')); @@ -372,151 +466,75 @@ export class MessageModel { (i) => !(i.fileType || '').startsWith('image') && !(i.fileType || '').startsWith('video'), ); - // 4. get relative file chunks - let chunksList: { - fileId: string; - fileType: string | null; - fileUrl: string | null; - filename: string | null; - id: string | null; - messageId: string | null; - similarity: string | null; - text: string | null; - }[] = []; - - if (messageIds.length > 0) { - chunksList = await this.db - .select({ - fileId: files.id, - fileType: files.fileType, - fileUrl: files.url, - filename: files.name, - id: chunks.id, - messageId: messageQueryChunks.messageId, - similarity: messageQueryChunks.similarity, - text: chunks.text, - }) - .from(messageQueryChunks) - .leftJoin(chunks, eq(chunks.id, messageQueryChunks.chunkId)) - .leftJoin(fileChunks, eq(fileChunks.chunkId, chunks.id)) - .innerJoin(files, eq(fileChunks.fileId, files.id)) - .where(inArray(messageQueryChunks.messageId, messageIds)); - } - - // 5. get relative message query - let messageQueriesList: { - id: string; - messageId: string; - rewriteQuery: string | null; - userQuery: string | null; - }[] = []; - - if (messageIds.length > 0) { - messageQueriesList = await this.db - .select({ - id: messageQueries.id, - messageId: messageQueries.messageId, - rewriteQuery: messageQueries.rewriteQuery, - userQuery: messageQueries.userQuery, - }) - .from(messageQueries) - .where(inArray(messageQueries.messageId, messageIds)); - } - - // 5. get thread info for task messages - const taskMessageIds = result.filter((m) => m.role === 'task').map((m) => m.id as string); - - let threadMap = new Map(); - - if (taskMessageIds.length > 0) { - const threadData = await this.db - .select({ - metadata: threads.metadata, - sourceMessageId: threads.sourceMessageId, - status: threads.status, - threadId: threads.id, - title: threads.title, - }) - .from(threads) - .where( - and(eq(threads.userId, this.userId), inArray(threads.sourceMessageId, taskMessageIds)), - ); - - threadMap = new Map( - threadData.map((t) => { - const metadata = t.metadata as Record | null; - return [ - t.sourceMessageId!, - { - clientMode: metadata?.clientMode as boolean | undefined, - duration: metadata?.duration as number | undefined, - status: t.status as ThreadStatus, - threadId: t.threadId, - title: t.title ?? undefined, - totalCost: metadata?.totalCost as number | undefined, - totalMessages: metadata?.totalMessages as number | undefined, - totalTokens: metadata?.totalTokens as number | undefined, - totalToolCalls: metadata?.totalToolCalls as number | undefined, - }, - ]; - }), - ); - } + const threadMap = this.createThreadMap(threadData); // 6. Transform regular messages - const transformedMessages = result.map( - ({ model, provider, translate, ttsId, ttsFile, ttsContentMd5, ttsVoice, ...item }) => { - const messageQuery = messageQueriesList.find((relation) => relation.messageId === item.id); - return { - ...item, - chunksList: chunksList - .filter((relation) => relation.messageId === item.id) - .map((c) => ({ - ...c, - similarity: c.similarity === null ? undefined : Number(c.similarity), - })), + const transformedMessages = await runTimedStage( + timing, + 'db.message.queryWithWhere.transform', + () => + result.map( + ({ model, provider, translate, ttsId, ttsFile, ttsContentMd5, ttsVoice, ...item }) => { + const messageQuery = messageQueriesList.find( + (relation) => relation.messageId === item.id, + ); + return { + ...item, + chunksList: chunksList + .filter((relation) => relation.messageId === item.id) + .map((c) => ({ + ...c, + similarity: c.similarity === null ? undefined : Number(c.similarity), + })), - extra: { - model, - provider, - translate, - tts: ttsId - ? { - contentMd5: ttsContentMd5, - file: ttsFile, - voice: ttsVoice, - } - : undefined, + extra: { + model, + provider, + translate, + tts: ttsId + ? { + contentMd5: ttsContentMd5, + file: ttsFile, + voice: ttsVoice, + } + : undefined, + }, + fileList: fileList + .filter((relation) => relation.messageId === item.id) + + .map(({ id, url, size, fileType, name }) => ({ + content: documentsMap[id], + fileType: fileType!, + id, + name: name!, + size: size!, + url, + })), + imageList: imageList + .filter((relation) => relation.messageId === item.id) + + .map(({ id, url, name }) => ({ alt: name!, id, url })), + + model, + + provider, + ragQuery: messageQuery?.rewriteQuery, + ragQueryId: messageQuery?.id, + ragRawQuery: messageQuery?.userQuery, + // Add taskDetail for task messages + taskDetail: item.role === 'task' ? threadMap.get(item.id as string) : undefined, + videoList: videoList + .filter((relation) => relation.messageId === item.id) + + .map(({ id, url, name }) => ({ alt: name!, id, url })), + } as unknown as UIChatMessage; }, - fileList: fileList - .filter((relation) => relation.messageId === item.id) - - .map(({ id, url, size, fileType, name }) => ({ - content: documentsMap[id], - fileType: fileType!, - id, - name: name!, - size: size!, - url, - })), - imageList: imageList - .filter((relation) => relation.messageId === item.id) - - .map(({ id, url, name }) => ({ alt: name!, id, url })), - - model, - - provider, - ragQuery: messageQuery?.rewriteQuery, - ragQueryId: messageQuery?.id, - ragRawQuery: messageQuery?.userQuery, - // Add taskDetail for task messages - taskDetail: item.role === 'task' ? threadMap.get(item.id as string) : undefined, - videoList: videoList - .filter((relation) => relation.messageId === item.id) - - .map(({ id, url, name }) => ({ alt: name!, id, url })), - } as unknown as UIChatMessage; + ), + { + chunkCount: chunksList.length, + fileCount: relatedFileList.length, + messageQueryCount: messageQueriesList.length, + rowCount: result.length, }, ); @@ -528,9 +546,254 @@ export class MessageModel { return aTime - bTime; }); + logTiming(timing, 'db.message.queryWithWhere:done', { + messageGroupCount: messageGroupNodes.length, + resultCount: allItems.length, + rowCount: result.length, + stageMs: getDurationMs(totalStartedAt), + }); + return allItems; }; + private queryMessageGroupNodesForPage = async ({ + current, + postProcessUrl, + result, + timing, + topicId, + }: { + current: number; + postProcessUrl?: (path: string | null, file: { fileType: string }) => Promise; + result: { createdAt: Date }[]; + timing?: ModelTimingContext; + topicId?: string; + }): Promise => { + if (!topicId) return []; + + if (result.length === 0) { + if (current !== 0) return []; + + return runTimedStage( + timing, + 'db.message.queryWithWhere.messageGroups', + () => this.queryMessageGroupNodes(topicId, undefined, postProcessUrl, timing), + { current, hasMessages: false, topicId }, + ); + } + + if (current === 0) { + return runTimedStage( + timing, + 'db.message.queryWithWhere.messageGroups', + () => this.queryMessageGroupNodes(topicId, undefined, postProcessUrl, timing), + { current, hasMessages: true, topicId }, + ); + } + + const firstMessageTime = result[0].createdAt; + const lastMessageTime = result.at(-1)!.createdAt; + + return runTimedStage( + timing, + 'db.message.queryWithWhere.messageGroups', + () => + this.queryMessageGroupNodes( + topicId, + { + endTime: lastMessageTime, + startTime: firstMessageTime, + }, + postProcessUrl, + timing, + ), + { current, hasMessages: true, topicId }, + ); + }; + + private queryMessageFileRelations = async ( + messageIds: string[], + postProcessUrl: QueryMessagesOptions['postProcessUrl'], + timing?: ModelTimingContext, + ): Promise => { + if (messageIds.length === 0) return { documentsMap: {}, relatedFileList: [] }; + + const rawRelatedFileList = await runTimedStage( + timing, + 'db.message.queryWithWhere.relatedFiles.select', + () => + this.db + .select({ + fileType: files.fileType, + id: messagesFiles.fileId, + messageId: messagesFiles.messageId, + name: files.name, + size: files.size, + url: files.url, + }) + .from(messagesFiles) + .leftJoin(files, eq(files.id, messagesFiles.fileId)) + .where(inArray(messagesFiles.messageId, messageIds)), + { messageCount: messageIds.length }, + ); + logTiming(timing, 'db.message.queryWithWhere.relatedFiles.select:rows', { + rowCount: rawRelatedFileList.length, + }); + + const relatedFileList = await runTimedStage( + timing, + 'db.message.queryWithWhere.relatedFiles.postProcess', + () => + Promise.all( + rawRelatedFileList.map(async (file) => ({ + ...file, + url: postProcessUrl + ? await postProcessUrl(file.url, file as unknown as { fileType: string }) + : (file.url as string), + })), + ), + { fileCount: rawRelatedFileList.length }, + ); + + const fileIds = relatedFileList.map((file) => file.id).filter(Boolean); + + if (fileIds.length === 0) return { documentsMap: {}, relatedFileList }; + + const documentsList = await runTimedStage( + timing, + 'db.message.queryWithWhere.documents.select', + () => + this.db + .select({ + content: documents.content, + fileId: documents.fileId, + }) + .from(documents) + .where(inArray(documents.fileId, fileIds)), + { fileCount: fileIds.length }, + ); + + const documentsMap = documentsList.reduce( + (acc, doc) => { + if (doc.fileId) acc[doc.fileId] = doc.content as string; + return acc; + }, + {} as Record, + ); + + return { documentsMap, relatedFileList }; + }; + + private queryMessageChunkRelations = async ( + messageIds: string[], + timing?: ModelTimingContext, + ): Promise => { + if (messageIds.length === 0) return []; + + const chunksList = await runTimedStage( + timing, + 'db.message.queryWithWhere.chunks.select', + () => + this.db + .select({ + fileId: files.id, + fileType: files.fileType, + fileUrl: files.url, + filename: files.name, + id: chunks.id, + messageId: messageQueryChunks.messageId, + similarity: messageQueryChunks.similarity, + text: chunks.text, + }) + .from(messageQueryChunks) + .leftJoin(chunks, eq(chunks.id, messageQueryChunks.chunkId)) + .leftJoin(fileChunks, eq(fileChunks.chunkId, chunks.id)) + .innerJoin(files, eq(fileChunks.fileId, files.id)) + .where(inArray(messageQueryChunks.messageId, messageIds)), + { messageCount: messageIds.length }, + ); + logTiming(timing, 'db.message.queryWithWhere.chunks.select:rows', { + rowCount: chunksList.length, + }); + + return chunksList; + }; + + private queryMessageQueryRelations = async ( + messageIds: string[], + timing?: ModelTimingContext, + ): Promise => { + if (messageIds.length === 0) return []; + + const messageQueriesList = await runTimedStage( + timing, + 'db.message.queryWithWhere.messageQueries.select', + () => + this.db + .select({ + id: messageQueries.id, + messageId: messageQueries.messageId, + rewriteQuery: messageQueries.rewriteQuery, + userQuery: messageQueries.userQuery, + }) + .from(messageQueries) + .where(inArray(messageQueries.messageId, messageIds)), + { messageCount: messageIds.length }, + ); + logTiming(timing, 'db.message.queryWithWhere.messageQueries.select:rows', { + rowCount: messageQueriesList.length, + }); + + return messageQueriesList; + }; + + private queryMessageThreadRelations = async ( + taskMessageIds: string[], + timing?: ModelTimingContext, + ): Promise => { + if (taskMessageIds.length === 0) return []; + + return runTimedStage( + timing, + 'db.message.queryWithWhere.taskThreads.select', + () => + this.db + .select({ + metadata: threads.metadata, + sourceMessageId: threads.sourceMessageId, + status: threads.status, + threadId: threads.id, + title: threads.title, + }) + .from(threads) + .where( + and(eq(threads.userId, this.userId), inArray(threads.sourceMessageId, taskMessageIds)), + ), + { taskMessageCount: taskMessageIds.length }, + ); + }; + + private createThreadMap = (threadData: MessageThreadRelation[]) => + new Map( + threadData.map((thread) => { + const metadata = thread.metadata as Record | null; + return [ + thread.sourceMessageId!, + { + clientMode: metadata?.clientMode as boolean | undefined, + duration: metadata?.duration as number | undefined, + status: thread.status as ThreadStatus, + threadId: thread.threadId, + title: thread.title ?? undefined, + totalCost: metadata?.totalCost as number | undefined, + totalMessages: metadata?.totalMessages as number | undefined, + totalTokens: metadata?.totalTokens as number | undefined, + totalToolCalls: metadata?.totalToolCalls as number | undefined, + }, + ]; + }), + ); + /** * Query messages by their IDs with full relations * @@ -804,6 +1067,7 @@ export class MessageModel { topicId: string, timeRange?: { endTime: Date; startTime: Date }, postProcessUrl?: (path: string | null, file: { fileType: string }) => Promise, + timing?: ModelTimingContext, ): Promise => { // 1. Query MessageGroups for this topic, optionally filtered by time range const whereConditions = [ @@ -819,30 +1083,51 @@ export class MessageModel { ); } - const groups = await this.db - .select() - .from(messageGroups) - .where(and(...whereConditions)) - .orderBy(asc(messageGroups.createdAt)); + const groups = await runTimedStage( + timing, + 'db.message.messageGroups.groups.select', + () => + this.db + .select() + .from(messageGroups) + .where(and(...whereConditions)) + .orderBy(asc(messageGroups.createdAt)), + { hasTimeRange: !!timeRange, topicId }, + ); + logTiming(timing, 'db.message.messageGroups.groups.select:rows', { rowCount: groups.length }); if (groups.length === 0) return []; const groupIds = groups.map((g) => g.id); // 2. Get all message IDs that belong to these groups (using messageGroupId relation) - const groupMessageRecords = await this.db - .select({ - favorite: messages.favorite, - id: messages.id, - messageGroupId: messages.messageGroupId, - }) - .from(messages) - .where(and(eq(messages.userId, this.userId), inArray(messages.messageGroupId, groupIds))) - .orderBy(asc(messages.createdAt)); + const groupMessageRecords = await runTimedStage( + timing, + 'db.message.messageGroups.messages.select', + () => + this.db + .select({ + favorite: messages.favorite, + id: messages.id, + messageGroupId: messages.messageGroupId, + }) + .from(messages) + .where(and(eq(messages.userId, this.userId), inArray(messages.messageGroupId, groupIds))) + .orderBy(asc(messages.createdAt)), + { groupCount: groupIds.length }, + ); + logTiming(timing, 'db.message.messageGroups.messages.select:rows', { + rowCount: groupMessageRecords.length, + }); // 3. Query full message data using queryByIds (reuses all transformation logic) const allMessageIds = groupMessageRecords.map((m) => m.id as string); - const fullMessages = await this.queryByIds(allMessageIds, { postProcessUrl }); + const fullMessages = await runTimedStage( + timing, + 'db.message.messageGroups.queryByIds', + () => this.queryByIds(allMessageIds, { postProcessUrl }), + { messageCount: allMessageIds.length }, + ); // Create a map for quick lookup const messageMap = new Map(fullMessages.map((m) => [m.id, m])); @@ -1244,45 +1529,76 @@ export class MessageModel { // **************** Create *************** // - create = async ( - { - model: fromModel, - provider: fromProvider, + private splitCreateMessageParams = ({ + fileChunks, + files, + model: fromModel, + plugin, + pluginIntervention, + pluginState, + provider: fromProvider, + ragQueryId, + updatedAt, + createdAt, + ...message + }: CreateMessageParams): SplitCreateMessageParams => ({ + insert: { + createdAt, + fromModel, + fromProvider, + message, + updatedAt, + }, + relations: { + fileChunks, files, plugin, pluginIntervention, pluginState, - fileChunks, ragQueryId, - updatedAt, - createdAt, - ...message - }: CreateMessageParams, - id: string = this.genId(), - ): Promise => { - return this.db.transaction(async (trx) => { - // Ensure group message does not populate sessionId - const normalizedMessage = message.groupId ? { ...message, sessionId: null } : message; + }, + }); - const [item] = (await trx - .insert(messages) - .values({ - ...normalizedMessage, - // Sanitize content to strip null bytes that PostgreSQL rejects - content: sanitizeNullBytes(normalizedMessage.content), - // TODO: remove this when the client is updated - createdAt: createdAt ? new Date(createdAt) : undefined, - id, - model: fromModel, - provider: fromProvider, - updatedAt: updatedAt ? new Date(updatedAt) : undefined, - userId: this.userId, - }) - .returning()) as DBMessageItem[]; + private buildMessageInsertValue = ( + { createdAt, fromModel, fromProvider, message, updatedAt }: CreateMessageInsertParams, + id: string, + ) => { + // Ensure group message does not populate sessionId + const normalizedMessage = message.groupId ? { ...message, sessionId: null } : message; - // Insert the plugin data if the message is a tool - if (message.role === 'tool') { - await trx.insert(messagePlugins).values({ + return { + ...normalizedMessage, + // Sanitize content to strip null bytes that PostgreSQL rejects + content: sanitizeNullBytes(normalizedMessage.content), + // TODO: remove this when the client is updated + createdAt: createdAt ? new Date(createdAt) : undefined, + id, + model: fromModel, + provider: fromProvider, + updatedAt: updatedAt ? new Date(updatedAt) : undefined, + userId: this.userId, + }; + }; + + private insertMessageRelationsInTransaction = async ( + trx: Transaction, + { + fileChunks, + files, + plugin, + pluginIntervention, + pluginState, + ragQueryId, + }: CreateMessageRelationParams, + message: CreateMessageInsertParams['message'], + id: string, + timing?: ModelTimingContext, + timingPrefix: string = 'db.message.create', + ): Promise => { + // Insert the plugin data if the message is a tool + if (message.role === 'tool') { + await runTimedStage(timing, `${timingPrefix}.plugin.insert`, () => + trx.insert(messagePlugins).values({ apiName: plugin?.apiName, arguments: sanitizeNullBytes(plugin?.arguments), id, @@ -1292,34 +1608,196 @@ export class MessageModel { toolCallId: message.tool_call_id, type: plugin?.type, userId: this.userId, - }); - } + }), + ); + } - if (files && files.length > 0) { - await trx - .insert(messagesFiles) - .values(files.map((file) => ({ fileId: file, messageId: id, userId: this.userId }))); - } + if (files && files.length > 0) { + await runTimedStage( + timing, + `${timingPrefix}.files.insert`, + () => + trx + .insert(messagesFiles) + .values(files.map((file) => ({ fileId: file, messageId: id, userId: this.userId }))), + { fileCount: files.length }, + ); + } - if (fileChunks && fileChunks.length > 0 && ragQueryId) { - await trx.insert(messageQueryChunks).values( - fileChunks.map((chunk) => ({ - chunkId: chunk.id, - messageId: id, - queryId: ragQueryId, - similarity: chunk.similarity?.toString(), - userId: this.userId, - })), - ); - } + if (fileChunks && fileChunks.length > 0 && ragQueryId) { + await runTimedStage( + timing, + `${timingPrefix}.fileChunks.insert`, + () => + trx.insert(messageQueryChunks).values( + fileChunks.map((chunk) => ({ + chunkId: chunk.id, + messageId: id, + queryId: ragQueryId, + similarity: chunk.similarity?.toString(), + userId: this.userId, + })), + ), + { chunkCount: fileChunks.length }, + ); + } + }; - // Touch topic's updatedAt when creating a message in a topic - if (message.topicId) { - await this.touchTopicUpdatedAt(trx, [message.topicId]); - } + private createInTransaction = async ( + trx: Transaction, + params: CreateMessageParams, + id: string, + timing?: ModelTimingContext, + timingPrefix: string = 'db.message.create', + ): Promise => { + const { insert, relations } = this.splitCreateMessageParams(params); - return item; - }); + const [item] = (await runTimedStage( + timing, + `${timingPrefix}.messages.insert`, + () => trx.insert(messages).values(this.buildMessageInsertValue(insert, id)).returning(), + { + hasGroupId: !!insert.message.groupId, + hasTopicId: !!insert.message.topicId, + role: insert.message.role, + }, + )) as DBMessageItem[]; + + await this.insertMessageRelationsInTransaction( + trx, + relations, + insert.message, + id, + timing, + timingPrefix, + ); + + return item; + }; + + create = async ( + params: CreateMessageParams, + id: string = this.genId(), + timing?: ModelTimingContext, + ): Promise => { + return runTimedStage( + timing, + 'db.message.create.transaction', + () => + this.db.transaction(async (trx) => { + const item = await this.createInTransaction(trx, params, id, timing); + + // Touch topic's updatedAt when creating a message in a topic + if (params.topicId) { + await runTimedStage( + timing, + 'db.message.create.topic.touchUpdatedAt', + () => this.touchTopicUpdatedAt(trx, [params.topicId!]), + { topicCount: 1 }, + ); + } + + return item; + }), + { + fileChunkCount: params.fileChunks?.length ?? 0, + fileCount: params.files?.length ?? 0, + hasTopicId: !!params.topicId, + role: params.role, + }, + ); + }; + + createUserAndAssistantMessages = async ( + { userMessage, assistantMessage }: CreateUserAndAssistantMessagesParams, + { timing, touchTopicUpdatedAt = true }: CreateUserAndAssistantMessagesOptions = {}, + ): Promise<{ assistantMessage: DBMessageItem; userMessage: DBMessageItem }> => { + const userMessageId = this.genId(); + const assistantMessageId = this.genId(); + const createdAt = Date.now(); + const defaultUserCreatedAt = createdAt; + const defaultAssistantCreatedAt = createdAt + 1; + const userMessageWithTimestamp = { + ...userMessage, + createdAt: userMessage.createdAt ?? defaultUserCreatedAt, + updatedAt: + userMessage.updatedAt ?? (userMessage.createdAt ? undefined : defaultUserCreatedAt), + }; + const assistantMessageWithParent = { + ...assistantMessage, + createdAt: assistantMessage.createdAt ?? defaultAssistantCreatedAt, + parentId: userMessageId, + updatedAt: + assistantMessage.updatedAt ?? + (assistantMessage.createdAt ? undefined : defaultAssistantCreatedAt), + }; + const topicIds = [ + ...new Set([userMessage.topicId, assistantMessage.topicId].filter(Boolean) as string[]), + ]; + + return runTimedStage( + timing, + 'db.message.createUserAndAssistant.transaction', + () => + this.db.transaction(async (trx) => { + const userPayload = this.splitCreateMessageParams(userMessageWithTimestamp); + const assistantPayload = this.splitCreateMessageParams(assistantMessageWithParent); + const insertedMessages = (await runTimedStage( + timing, + 'db.message.createUserAndAssistant.messages.insert', + () => + trx + .insert(messages) + .values([ + this.buildMessageInsertValue(userPayload.insert, userMessageId), + this.buildMessageInsertValue(assistantPayload.insert, assistantMessageId), + ]) + .returning(), + { hasTopicId: topicIds.length > 0, messageCount: 2 }, + )) as DBMessageItem[]; + const messageMap = new Map(insertedMessages.map((message) => [message.id, message])); + + await this.insertMessageRelationsInTransaction( + trx, + userPayload.relations, + userPayload.insert.message, + userMessageId, + timing, + 'db.message.createUserAndAssistant.user', + ); + await this.insertMessageRelationsInTransaction( + trx, + assistantPayload.relations, + assistantPayload.insert.message, + assistantMessageId, + timing, + 'db.message.createUserAndAssistant.assistant', + ); + + if (touchTopicUpdatedAt && topicIds.length > 0) { + await runTimedStage( + timing, + 'db.message.createUserAndAssistant.topic.touchUpdatedAt', + () => this.touchTopicUpdatedAt(trx, topicIds), + { topicCount: topicIds.length }, + ); + } + + const userMessageItem = messageMap.get(userMessageId); + const assistantMessageItem = messageMap.get(assistantMessageId); + + if (!userMessageItem || !assistantMessageItem) { + throw new Error('Failed to create user and assistant messages'); + } + + return { assistantMessage: assistantMessageItem, userMessage: userMessageItem }; + }), + { + assistantFileCount: assistantMessage.files?.length ?? 0, + hasTopicId: topicIds.length > 0, + userFileCount: userMessage.files?.length ?? 0, + }, + ); }; batchCreate = async (newMessages: DBMessageItem[]) => { @@ -1352,39 +1830,74 @@ export class MessageModel { update = async ( id: string, { imageList, metadata, ...message }: Partial, + timing?: ModelTimingContext, ): Promise<{ success: boolean }> => { try { - await this.db.transaction(async (trx) => { - // 1. insert message files - if (imageList && imageList.length > 0) { - await trx - .insert(messagesFiles) - .values( - imageList.map((file) => ({ fileId: file.id, messageId: id, userId: this.userId })), + await runTimedStage( + timing, + 'db.message.update.transaction', + () => + this.db.transaction(async (trx) => { + // 1. insert message files + if (imageList && imageList.length > 0) { + await runTimedStage( + timing, + 'db.message.update.imageFiles.insert', + () => + trx.insert(messagesFiles).values( + imageList.map((file) => ({ + fileId: file.id, + messageId: id, + userId: this.userId, + })), + ), + { imageCount: imageList.length }, + ); + } + + // 2. Handle metadata merge if provided + let mergedMetadata: Record | undefined; + if (metadata) { + const [existingMessage] = await runTimedStage( + timing, + 'db.message.update.metadata.select', + () => + trx + .select({ metadata: messages.metadata }) + .from(messages) + .where(and(eq(messages.id, id), eq(messages.userId, this.userId))), + ); + mergedMetadata = merge(existingMessage?.metadata || {}, metadata); + } + + const [updated] = await runTimedStage( + timing, + 'db.message.update.messages.update', + () => + trx + .update(messages) + .set({ ...message, ...(mergedMetadata && { metadata: mergedMetadata }) }) + .where(and(eq(messages.id, id), eq(messages.userId, this.userId))) + .returning({ topicId: messages.topicId }), + { hasMetadata: !!metadata, valueKeys: Object.keys(message) }, ); - } - // 2. Handle metadata merge if provided - let mergedMetadata: Record | undefined; - if (metadata) { - const [existingMessage] = await trx - .select({ metadata: messages.metadata }) - .from(messages) - .where(and(eq(messages.id, id), eq(messages.userId, this.userId))); - mergedMetadata = merge(existingMessage?.metadata || {}, metadata); - } - - const [updated] = await trx - .update(messages) - .set({ ...message, ...(mergedMetadata && { metadata: mergedMetadata }) }) - .where(and(eq(messages.id, id), eq(messages.userId, this.userId))) - .returning({ topicId: messages.topicId }); - - // Touch topic's updatedAt when updating a message - if (updated?.topicId) { - await this.touchTopicUpdatedAt(trx, [updated.topicId]); - } - }); + // Touch topic's updatedAt when updating a message + if (updated?.topicId) { + await runTimedStage( + timing, + 'db.message.update.topic.touchUpdatedAt', + () => this.touchTopicUpdatedAt(trx, [updated.topicId!]), + { topicCount: 1 }, + ); + } + }), + { + hasImageList: !!imageList?.length, + hasMetadata: !!metadata, + valueKeys: Object.keys(message), + }, + ); return { success: true }; } catch (error) { diff --git a/packages/database/src/models/topic.ts b/packages/database/src/models/topic.ts index 539c030440..24e095d8de 100644 --- a/packages/database/src/models/topic.ts +++ b/packages/database/src/models/topic.ts @@ -4,6 +4,12 @@ import type { DBMessageItem, TopicRankItem, } from '@lobechat/types'; +import type { TimingSink } from '@lobechat/utils'; +import { + getDurationMs, + logTimingSink as logTiming, + runTimedSinkStage as runTimedStage, +} from '@lobechat/utils'; import type { SQL } from 'drizzle-orm'; import { and, count, desc, eq, gt, gte, inArray, isNull, lte, ne, not, or, sql } from 'drizzle-orm'; @@ -62,12 +68,15 @@ interface QueryTopicParams { */ isInbox?: boolean; pageSize?: number; + timing?: ModelTimingContext; /** * Include only topics matching the given trigger types (positive filter) */ triggers?: string[]; } +export interface ModelTimingContext extends TimingSink {} + export interface ListTopicsForMemoryExtractorCursor { createdAt: Date; id: string; @@ -93,8 +102,18 @@ export class TopicModel { pageSize = 9999, groupId, isInbox, + timing, triggers, }: QueryTopicParams = {}) => { + const queryStartedAt = Date.now(); + logTiming(timing, 'db.topic.query:start', { + current, + hasAgentId: !!agentId, + hasContainerId: !!containerId, + hasGroupId: !!groupId, + isInbox: !!isInbox, + pageSize, + }); const offset = current * pageSize; const includeTriggerCondition = includeTriggers && includeTriggers.length > 0 @@ -127,29 +146,42 @@ export class TopicModel { ); const [items, totalResult] = await Promise.all([ - this.db - .select({ - completedAt: topics.completedAt, - createdAt: topics.createdAt, - favorite: topics.favorite, - historySummary: topics.historySummary, - id: topics.id, - metadata: topics.metadata, - status: topics.status, - title: topics.title, - updatedAt: topics.updatedAt, - }) - .from(topics) - .where(whereCondition) - .orderBy(desc(topics.favorite), desc(topics.updatedAt)) - .limit(pageSize) - .offset(offset), - this.db - .select({ count: count(topics.id) }) - .from(topics) - .where(whereCondition), + runTimedStage( + timing, + 'db.topic.query.group.items.select', + () => + this.db + .select({ + completedAt: topics.completedAt, + createdAt: topics.createdAt, + favorite: topics.favorite, + historySummary: topics.historySummary, + id: topics.id, + metadata: topics.metadata, + status: topics.status, + title: topics.title, + updatedAt: topics.updatedAt, + }) + .from(topics) + .where(whereCondition) + .orderBy(desc(topics.favorite), desc(topics.updatedAt)) + .limit(pageSize) + .offset(offset), + { current, pageSize }, + ), + runTimedStage(timing, 'db.topic.query.group.count.select', () => + this.db + .select({ count: count(topics.id) }) + .from(topics) + .where(whereCondition), + ), ]); + logTiming(timing, 'db.topic.query:done', { + itemCount: items.length, + stageMs: getDurationMs(queryStartedAt), + total: totalResult[0].count, + }); return { items, total: totalResult[0].count }; } @@ -159,11 +191,19 @@ export class TopicModel { // 3. For inbox: sessionId IS NULL AND groupId IS NULL AND agentId IS NULL (legacy inbox data) if (agentId) { // Get the associated sessionId for backward compatibility with legacy data - const agentSession = await this.db - .select({ sessionId: agentsToSessions.sessionId }) - .from(agentsToSessions) - .where(and(eq(agentsToSessions.agentId, agentId), eq(agentsToSessions.userId, this.userId))) - .limit(1); + const agentSession = await runTimedStage( + timing, + 'db.topic.query.agentSession.select', + () => + this.db + .select({ sessionId: agentsToSessions.sessionId }) + .from(agentsToSessions) + .where( + and(eq(agentsToSessions.agentId, agentId), eq(agentsToSessions.userId, this.userId)), + ) + .limit(1), + { hasAgentId: true }, + ); const associatedSessionId = agentSession[0]?.sessionId; @@ -201,29 +241,46 @@ export class TopicModel { ); const [items, totalResult] = await Promise.all([ - this.db - .select({ - completedAt: topics.completedAt, - createdAt: topics.createdAt, - favorite: topics.favorite, - historySummary: topics.historySummary, - id: topics.id, - metadata: topics.metadata, - status: topics.status, - title: topics.title, - updatedAt: topics.updatedAt, - }) - .from(topics) - .where(agentWhere) - .orderBy(desc(topics.favorite), desc(topics.updatedAt)) - .limit(pageSize) - .offset(offset), - this.db - .select({ count: count(topics.id) }) - .from(topics) - .where(agentWhere), + runTimedStage( + timing, + 'db.topic.query.agent.items.select', + () => + this.db + .select({ + completedAt: topics.completedAt, + createdAt: topics.createdAt, + favorite: topics.favorite, + historySummary: topics.historySummary, + id: topics.id, + metadata: topics.metadata, + status: topics.status, + title: topics.title, + updatedAt: topics.updatedAt, + }) + .from(topics) + .where(agentWhere) + .orderBy(desc(topics.favorite), desc(topics.updatedAt)) + .limit(pageSize) + .offset(offset), + { current, hasAssociatedSessionId: !!associatedSessionId, isInbox: !!isInbox, pageSize }, + ), + runTimedStage( + timing, + 'db.topic.query.agent.count.select', + () => + this.db + .select({ count: count(topics.id) }) + .from(topics) + .where(agentWhere), + { hasAssociatedSessionId: !!associatedSessionId, isInbox: !!isInbox }, + ), ]); + logTiming(timing, 'db.topic.query:done', { + itemCount: items.length, + stageMs: getDurationMs(queryStartedAt), + total: totalResult[0].count, + }); return { items, total: totalResult[0].count }; } @@ -238,37 +295,51 @@ export class TopicModel { ); const [items, totalResult] = await Promise.all([ - this.db - .select({ - agentId: topics.agentId, - completedAt: topics.completedAt, - createdAt: topics.createdAt, - favorite: topics.favorite, - historySummary: topics.historySummary, - id: topics.id, - metadata: topics.metadata, - sessionId: topics.sessionId, - status: topics.status, - title: topics.title, - updatedAt: topics.updatedAt, - }) - .from(topics) - .where(whereCondition) - // In boolean sorting, false is considered "smaller" than true. - // So here we use desc to ensure that topics with favorite as true are in front. - .orderBy(desc(topics.favorite), desc(topics.updatedAt)) - .limit(pageSize) - .offset(offset), - this.db - .select({ count: count(topics.id) }) - .from(topics) - .where(whereCondition), + runTimedStage( + timing, + 'db.topic.query.container.items.select', + () => + this.db + .select({ + agentId: topics.agentId, + completedAt: topics.completedAt, + createdAt: topics.createdAt, + favorite: topics.favorite, + historySummary: topics.historySummary, + id: topics.id, + metadata: topics.metadata, + sessionId: topics.sessionId, + status: topics.status, + title: topics.title, + updatedAt: topics.updatedAt, + }) + .from(topics) + .where(whereCondition) + // In boolean sorting, false is considered "smaller" than true. + // So here we use desc to ensure that topics with favorite as true are in front. + .orderBy(desc(topics.favorite), desc(topics.updatedAt)) + .limit(pageSize) + .offset(offset), + { current, pageSize }, + ), + runTimedStage(timing, 'db.topic.query.container.count.select', () => + this.db + .select({ count: count(topics.id) }) + .from(topics) + .where(whereCondition), + ), ]); // Remove internal fields before returning const cleanItems = items.map(({ agentId, sessionId, ...rest }) => rest); + logTiming(timing, 'db.topic.query:done', { + itemCount: cleanItems.length, + stageMs: getDurationMs(queryStartedAt), + total: totalResult[0].count, + }); + return { items: cleanItems, total: totalResult[0].count }; }; @@ -468,30 +539,67 @@ export class TopicModel { create = async ( { messages: messageIds, ...params }: CreateTopicParams, id: string = this.genId(), + timing?: ModelTimingContext, ): Promise => { - return this.db.transaction(async (tx) => { - const insertData = { - ...params, - agentId: params.agentId || null, - groupId: params.groupId || null, - id, - sessionId: params.sessionId || null, - userId: this.userId, - }; + const insertData = { + ...params, + agentId: params.agentId || null, + groupId: params.groupId || null, + id, + sessionId: params.sessionId || null, + userId: this.userId, + }; + const insertMeta = { + hasAgentId: !!params.agentId, + hasGroupId: !!params.groupId, + hasSessionId: !!params.sessionId, + }; - // Insert new topic - const [topic] = await tx.insert(topics).values(insertData).returning(); - - // Update associated messages' topicId - if (messageIds && messageIds.length > 0) { - await tx - .update(messages) - .set({ topicId: topic.id }) - .where(and(eq(messages.userId, this.userId), inArray(messages.id, messageIds))); - } + if (!messageIds || messageIds.length === 0) { + const [topic] = await runTimedStage( + timing, + 'db.topic.create.topics.insert', + () => this.db.insert(topics).values(insertData).returning(), + insertMeta, + ); return topic; - }); + } + + return runTimedStage( + timing, + 'db.topic.create.transaction', + () => + this.db.transaction(async (tx) => { + // Insert new topic + const [topic] = await runTimedStage( + timing, + 'db.topic.create.topics.insert', + () => tx.insert(topics).values(insertData).returning(), + insertMeta, + ); + + // Update associated messages' topicId + await runTimedStage( + timing, + 'db.topic.create.messages.updateTopic', + () => + tx + .update(messages) + .set({ topicId: topic.id }) + .where(and(eq(messages.userId, this.userId), inArray(messages.id, messageIds))), + { messageCount: messageIds.length }, + ); + + return topic; + }), + { + hasAgentId: !!params.agentId, + hasGroupId: !!params.groupId, + hasSessionId: !!params.sessionId, + messageCount: messageIds?.length ?? 0, + }, + ); }; batchCreate = async (topicParams: (CreateTopicParams & { id?: string })[]) => { diff --git a/packages/model-runtime/src/core/ModelRuntime.ts b/packages/model-runtime/src/core/ModelRuntime.ts index 4f183524c1..8c740a0bc2 100644 --- a/packages/model-runtime/src/core/ModelRuntime.ts +++ b/packages/model-runtime/src/core/ModelRuntime.ts @@ -1,4 +1,5 @@ import type { ModelUsage, TracePayload } from '@lobechat/types'; +import { createTimingHelpers, getDurationMs } from '@lobechat/utils'; import type { ClientOptions } from 'openai'; import type { LobeBedrockAIParams } from '../providers/bedrock'; @@ -32,6 +33,13 @@ import type { import { AgentRuntimeError } from '../utils/createError'; import type { LobeRuntimeAI } from './BaseAI'; +const { logger: timing } = createTimingHelpers('lobe-server:chat:lobehub:timing'); + +const getLobeHubTimingMetadata = (options?: { + metadata?: Record; +}): Record | undefined => + options?.metadata?.provider === 'lobehub' ? options.metadata : undefined; + export interface AgentChatOptions { enableTrace?: boolean; provider: string; @@ -126,6 +134,17 @@ export class ModelRuntime { * ``` */ async chat(payload: ChatStreamPayload, options?: ChatMethodOptions) { + const metadata = getLobeHubTimingMetadata(options); + const startedAt = Date.now(); + if (metadata) { + timing( + 'ModelRuntime.chat start model=%s trigger=%s traceId=%s', + payload.model, + metadata.trigger, + metadata.traceId, + ); + } + if (typeof this._runtime.chat !== 'function') { throw AgentRuntimeError.chat({ error: new Error('Chat is not supported by this provider'), @@ -135,11 +154,48 @@ export class ModelRuntime { } try { + const hooksStartedAt = Date.now(); const finalOptions = await this.applyHooks(payload, options); - return await this._runtime.chat(payload, finalOptions); + if (metadata) { + timing( + 'ModelRuntime.chat hooks done model=%s durationMs=%d traceId=%s', + payload.model, + getDurationMs(hooksStartedAt), + metadata.traceId, + ); + } + const runtimeStartedAt = Date.now(); + const response = await this._runtime.chat(payload, finalOptions); + if (metadata) { + timing( + 'ModelRuntime.chat runtime done model=%s durationMs=%d totalMs=%d traceId=%s', + payload.model, + getDurationMs(runtimeStartedAt), + getDurationMs(startedAt), + metadata.traceId, + ); + } + return response; } catch (error) { + if (metadata) { + timing( + 'ModelRuntime.chat error model=%s durationMs=%d traceId=%s', + payload.model, + getDurationMs(startedAt), + metadata.traceId, + ); + } if (this._hooks?.onChatError) { + const errorHookStartedAt = Date.now(); await this._hooks.onChatError(error as ChatCompletionErrorPayload, { options, payload }); + if (metadata) { + timing( + 'ModelRuntime.chat onChatError done model=%s durationMs=%d traceId=%s', + payload.model, + getDurationMs(errorHookStartedAt), + metadata.traceId, + ); + } } throw error; } @@ -152,7 +208,37 @@ export class ModelRuntime { payload: ChatStreamPayload, options?: ChatMethodOptions, ): Promise { - await this._hooks?.beforeChat?.(payload, options); + const metadata = getLobeHubTimingMetadata(options); + const beforeChatStartedAt = Date.now(); + if (metadata) { + timing( + 'ModelRuntime.beforeChat start model=%s trigger=%s traceId=%s', + payload.model, + metadata.trigger, + metadata.traceId, + ); + } + try { + await this._hooks?.beforeChat?.(payload, options); + } catch (error) { + if (metadata) { + timing( + 'ModelRuntime.beforeChat error model=%s durationMs=%d traceId=%s', + payload.model, + getDurationMs(beforeChatStartedAt), + metadata.traceId, + ); + } + throw error; + } + if (metadata) { + timing( + 'ModelRuntime.beforeChat done model=%s durationMs=%d traceId=%s', + payload.model, + getDurationMs(beforeChatStartedAt), + metadata.traceId, + ); + } if (!this._hooks?.onChatFinal) return options; @@ -163,10 +249,34 @@ export class ModelRuntime { callback: { ...options?.callback, async onFinal(data) { + const finalStartedAt = Date.now(); + if (metadata) { + timing( + 'ModelRuntime.onChatFinal start model=%s traceId=%s', + payload.model, + metadata.traceId, + ); + } await existingOnFinal?.(data); try { await hookFn(data, { options, payload }); + if (metadata) { + timing( + 'ModelRuntime.onChatFinal done model=%s durationMs=%d traceId=%s', + payload.model, + getDurationMs(finalStartedAt), + metadata.traceId, + ); + } } catch (e) { + if (metadata) { + timing( + 'ModelRuntime.onChatFinal error model=%s durationMs=%d traceId=%s', + payload.model, + getDurationMs(finalStartedAt), + metadata.traceId, + ); + } // Hook failures (billing, tracing) must not interfere with response completion console.error('[ModelRuntime] onChatFinal hook error:', e); } diff --git a/packages/model-runtime/src/core/RouterRuntime/createRuntime.ts b/packages/model-runtime/src/core/RouterRuntime/createRuntime.ts index 60b041594a..24bf847c2c 100644 --- a/packages/model-runtime/src/core/RouterRuntime/createRuntime.ts +++ b/packages/model-runtime/src/core/RouterRuntime/createRuntime.ts @@ -4,6 +4,7 @@ import type { GoogleGenAIOptions } from '@google/genai'; import type { ChatModelCard } from '@lobechat/types'; import { AgentRuntimeErrorType } from '@lobechat/types'; +import { createTimingHelpers, getDurationMs } from '@lobechat/utils'; import debug from 'debug'; import type { ClientOptions } from 'openai'; import type OpenAI from 'openai'; @@ -44,6 +45,7 @@ import type { import type { ApiType, RuntimeClass } from './apiTypes'; const log = debug('lobe-model-runtime:router-runtime'); +const { logger: timing } = createTimingHelpers('lobe-server:chat:lobehub:timing'); interface ProviderIniOptions extends Record { accessKeyId?: string; @@ -190,6 +192,7 @@ export const createRouterRuntime = ({ private _id: string; constructor(options: ClientOptions & Record = {}) { + const startedAt = Date.now(); this._options = { ...options, apiKey: options.apiKey?.trim() || DEFAULT_API_KEY, @@ -200,36 +203,76 @@ export const createRouterRuntime = ({ this._routers = routers; this._params = params; this._id = options.id ?? id; + + if (this._id === 'lobehub') { + timing( + 'constructor done providerId=%s durationMs=%d hasApiKey=%s hasBaseURL=%s', + this._id, + getDurationMs(startedAt), + !!this._options.apiKey, + !!this._options.baseURL, + ); + } } /** * Resolve routers configuration and validate */ private async resolveRouters(model?: string): Promise { - const resolvedRouters = - typeof this._routers === 'function' - ? await this._routers(this._options, { model }) - : this._routers; + const startedAt = Date.now(); + try { + const resolvedRouters = + typeof this._routers === 'function' + ? await this._routers(this._options, { model }) + : this._routers; - if (resolvedRouters.length === 0) { - throw AgentRuntimeError.chat({ - error: { message: 'empty providers' }, - errorType: AgentRuntimeErrorType.NoAvailableProvider, - provider: this._id, - }); + if (this._id === 'lobehub') { + timing( + 'resolveRouters done model=%s durationMs=%d routerCount=%d dynamic=%s', + model, + getDurationMs(startedAt), + resolvedRouters.length, + typeof this._routers === 'function', + ); + } + + if (resolvedRouters.length === 0) { + throw AgentRuntimeError.chat({ + error: { message: 'empty providers' }, + errorType: AgentRuntimeErrorType.NoAvailableProvider, + provider: this._id, + }); + } + + return resolvedRouters; + } catch (error) { + if (this._id === 'lobehub') { + timing('resolveRouters error model=%s durationMs=%d', model, getDurationMs(startedAt)); + } + throw error; } - - return resolvedRouters; } private async resolveMatchedRouter(model: string): Promise { + const startedAt = Date.now(); const resolvedRouters = await this.resolveRouters(model); const baseURL = this._options.baseURL; // Priority 1: Match by baseURLPattern (RegExp only) if (baseURL) { const baseURLMatch = resolvedRouters.find((router) => router.baseURLPattern?.test(baseURL)); - if (baseURLMatch) return baseURLMatch; + if (baseURLMatch) { + if (this._id === 'lobehub') { + timing( + 'resolveMatchedRouter done model=%s match=baseURL routerId=%s apiType=%s durationMs=%d', + model, + baseURLMatch.id, + baseURLMatch.apiType, + getDurationMs(startedAt), + ); + } + return baseURLMatch; + } } // Priority 2: Match by models @@ -239,19 +282,50 @@ export const createRouterRuntime = ({ } return false; }); - if (modelMatch) return modelMatch; + if (modelMatch) { + if (this._id === 'lobehub') { + timing( + 'resolveMatchedRouter done model=%s match=models routerId=%s apiType=%s durationMs=%d', + model, + modelMatch.id, + modelMatch.apiType, + getDurationMs(startedAt), + ); + } + return modelMatch; + } // Fallback: Use the last router - return resolvedRouters.at(-1)!; + const fallbackRouter = resolvedRouters.at(-1)!; + if (this._id === 'lobehub') { + timing( + 'resolveMatchedRouter done model=%s match=fallback routerId=%s apiType=%s durationMs=%d', + model, + fallbackRouter.id, + fallbackRouter.apiType, + getDurationMs(startedAt), + ); + } + return fallbackRouter; } private normalizeRouterOptions(router: RouterInstance): RouterOptionItem[] { + const startedAt = Date.now(); const routerOptions = Array.isArray(router.options) ? router.options : [router.options]; if (routerOptions.length === 0 || routerOptions.some((optionItem) => !optionItem)) { throw new Error('empty provider options'); } + if (this._id === 'lobehub') { + timing( + 'normalizeRouterOptions done routerId=%s options=%d durationMs=%d', + router.id, + routerOptions.length, + getDurationMs(startedAt), + ); + } + return routerOptions; } @@ -268,6 +342,7 @@ export const createRouterRuntime = ({ remark?: string; runtime: LobeRuntimeAI; }> { + const startedAt = Date.now(); const { apiType: optionApiType, id: channelId, remark, ...optionOverrides } = optionItem; const resolvedApiType = optionApiType ?? router.apiType; const finalOptions = { @@ -297,6 +372,16 @@ export const createRouterRuntime = ({ if (project) vertexOptions.project = project; if (location) vertexOptions.location = location as GoogleGenAIOptions['location']; + if (this._id === 'lobehub') { + timing( + 'createRuntimeFromOption done routerId=%s channelId=%s apiType=%s durationMs=%d vertex=true', + router.id, + channelId, + resolvedApiType, + getDurationMs(startedAt), + ); + } + return { channelId, id: resolvedApiType, @@ -312,6 +397,16 @@ export const createRouterRuntime = ({ : (baseRuntimeMap[resolvedApiType] ?? LobeOpenAI); const runtime: LobeRuntimeAI = new providerAI({ ...finalOptions, id: this._id }); + if (this._id === 'lobehub') { + timing( + 'createRuntimeFromOption done routerId=%s channelId=%s apiType=%s durationMs=%d', + router.id, + channelId, + resolvedApiType, + getDurationMs(startedAt), + ); + } + return { channelId, id: resolvedApiType, @@ -325,10 +420,22 @@ export const createRouterRuntime = ({ requestHandler: (runtime: LobeRuntimeAI) => Promise, metadata?: Record, ): Promise { + const totalStartedAt = Date.now(); const matchedRouter = await this.resolveMatchedRouter(model); const routerOptions = this.normalizeRouterOptions(matchedRouter); const totalOptions = routerOptions.length; + if (this._id === 'lobehub') { + timing( + 'runWithFallback start model=%s routerId=%s apiType=%s options=%d traceId=%s', + model, + matchedRouter.id, + matchedRouter.apiType, + totalOptions, + metadata?.traceId, + ); + } + log( 'resolve router for model=%s apiType=%s options=%d', model, @@ -349,7 +456,33 @@ export const createRouterRuntime = ({ } = await this.createRuntimeFromOption(matchedRouter, optionItem); try { + if (this._id === 'lobehub') { + timing( + 'attempt request start model=%s attempt=%d/%d routerId=%s channelId=%s apiType=%s traceId=%s', + model, + attempt, + totalOptions, + matchedRouter.id, + channelId, + resolvedApiType, + metadata?.traceId, + ); + } const result = await requestHandler(runtime); + if (this._id === 'lobehub') { + timing( + 'attempt request success model=%s attempt=%d/%d routerId=%s channelId=%s apiType=%s durationMs=%d totalMs=%d traceId=%s', + model, + attempt, + totalOptions, + matchedRouter.id, + channelId, + resolvedApiType, + getDurationMs(startTime), + getDurationMs(totalStartedAt), + metadata?.traceId, + ); + } if (totalOptions > 1 && attempt > 1) { log( @@ -392,6 +525,20 @@ export const createRouterRuntime = ({ return result; } catch (error) { lastError = error; + if (this._id === 'lobehub') { + timing( + 'attempt request error model=%s attempt=%d/%d routerId=%s channelId=%s apiType=%s durationMs=%d totalMs=%d traceId=%s', + model, + attempt, + totalOptions, + matchedRouter.id, + channelId, + resolvedApiType, + getDurationMs(startTime), + getDurationMs(totalStartedAt), + metadata?.traceId, + ); + } params .onRouteAttempt?.({ @@ -417,6 +564,7 @@ export const createRouterRuntime = ({ } try { + const shouldStopStartedAt = Date.now(); const shouldStopFallback = await params.shouldStopFallback?.({ error, metadata, @@ -424,6 +572,18 @@ export const createRouterRuntime = ({ optionIndex: index, }); + if (this._id === 'lobehub') { + timing( + 'shouldStopFallback done model=%s attempt=%d/%d durationMs=%d shouldStop=%s traceId=%s', + model, + attempt, + totalOptions, + getDurationMs(shouldStopStartedAt), + shouldStopFallback, + metadata?.traceId, + ); + } + if (shouldStopFallback) { throw error; } @@ -460,6 +620,17 @@ export const createRouterRuntime = ({ } } + if (this._id === 'lobehub') { + timing( + 'runWithFallback failed model=%s routerId=%s options=%d totalMs=%d traceId=%s', + model, + matchedRouter.id, + totalOptions, + getDurationMs(totalStartedAt), + metadata?.traceId, + ); + } + throw lastError ?? new Error('empty provider options'); } diff --git a/packages/types/src/aiChat.test.ts b/packages/types/src/aiChat.test.ts new file mode 100644 index 0000000000..242672d603 --- /dev/null +++ b/packages/types/src/aiChat.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from 'vitest'; + +import { AiSendMessageServerSchema } from './aiChat'; + +const createInput = (topicPageSize: number) => ({ + newAssistantMessage: { model: 'gpt-4o', provider: 'openai' }, + newUserMessage: { content: 'hello' }, + topicPageSize, +}); + +describe('AiSendMessageServerSchema', () => { + it('should only accept positive integer topic page sizes up to 100', () => { + for (const topicPageSize of [1, 20, 100]) { + expect(AiSendMessageServerSchema.safeParse(createInput(topicPageSize)).success).toBe(true); + } + + for (const topicPageSize of [-1, 0, 1.5, 101]) { + expect(AiSendMessageServerSchema.safeParse(createInput(topicPageSize)).success).toBe(false); + } + }); +}); diff --git a/packages/types/src/aiChat.ts b/packages/types/src/aiChat.ts index 4b7ed890a9..262f47177a 100644 --- a/packages/types/src/aiChat.ts +++ b/packages/types/src/aiChat.ts @@ -96,6 +96,10 @@ export interface SendMessageServerParams { }; // if there is activeTopicId, then add topicId to message topicId?: string; + /** + * Page size for the topic list returned after creating a new topic. + */ + topicPageSize?: number; } export const CreateThreadWithMessageSchema = z.object({ @@ -156,6 +160,7 @@ export const AiSendMessageServerSchema = z.object({ includeTriggers: z.array(z.string()).optional(), }) .optional(), + topicPageSize: z.number().int().min(1).max(100).optional(), topicId: z.string().optional(), }); diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index b1c20e9299..4dc150d78d 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -20,6 +20,7 @@ export * from './pricing'; export * from './safeParseJSON'; export * from './sanitizeToolCallArguments'; export * from './sleep'; +export * from './timing'; export * from './uriParser'; export * from './url'; export * from './uuid'; diff --git a/packages/utils/src/timing.ts b/packages/utils/src/timing.ts new file mode 100644 index 0000000000..d41e0bdffe --- /dev/null +++ b/packages/utils/src/timing.ts @@ -0,0 +1,173 @@ +import debug from 'debug'; + +export interface TimingContext { + requestId: string; + startedAt: number; +} + +export interface TimingMetadata { + [key: string]: unknown; +} + +export interface TimingParams { + timingRequestId?: string; + timingStartedAt?: number; +} + +export interface TimingSink { + log: (event: string, metadata?: TimingMetadata) => void; +} + +export type TimingLogger = (formatter: string, ...args: unknown[]) => void; + +export const createDebugTimingLogger = (namespace: string): TimingLogger => debug(namespace); + +export const getDurationMs = (startedAt: number) => Date.now() - startedAt; + +export const createTimingRequestId = () => + globalThis.crypto?.randomUUID?.() ?? + `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`; + +const isRecord = (value: unknown): value is Record => + !!value && typeof value === 'object'; + +export const getTimingErrorMetadata = (error: unknown): TimingMetadata => { + if (error instanceof Error) { + return { + errorMessage: error.message, + errorName: error.name, + }; + } + + if (isRecord(error)) { + return { + errorType: typeof error.errorType === 'string' ? error.errorType : undefined, + status: typeof error.status === 'number' ? error.status : undefined, + }; + } + + return { errorMessage: String(error) }; +}; + +export const toTimingContext = (params?: TimingParams): TimingContext | undefined => + params?.timingRequestId + ? { requestId: params.timingRequestId, startedAt: params.timingStartedAt ?? Date.now() } + : undefined; + +export const logTiming = ( + logger: TimingLogger, + context: TimingContext | undefined, + event: string, + metadata?: TimingMetadata, +) => { + if (!context) return; + + const totalMs = getDurationMs(context.startedAt); + if (metadata) { + logger('[%s] %s totalMs=%d %O', context.requestId, event, totalMs, metadata); + return; + } + + logger('[%s] %s totalMs=%d', context.requestId, event, totalMs); +}; + +export const logTimingSink = ( + timing: TimingSink | undefined, + event: string, + metadata?: TimingMetadata, +) => { + timing?.log(event, metadata); +}; + +export const runTimedStage = async ( + logger: TimingLogger, + context: TimingContext | undefined, + stage: string, + task: () => T | Promise, + metadata?: TimingMetadata, +): Promise> => { + if (!context) return await task(); + + const startedAt = Date.now(); + logTiming(logger, context, `${stage}:start`, metadata); + + try { + const result = await task(); + logTiming(logger, context, `${stage}:done`, { + ...metadata, + stageMs: getDurationMs(startedAt), + }); + + return result; + } catch (error) { + logTiming(logger, context, `${stage}:error`, { + ...metadata, + ...getTimingErrorMetadata(error), + stageMs: getDurationMs(startedAt), + }); + + throw error; + } +}; + +export const runTimedSinkStage = async ( + timing: TimingSink | undefined, + stage: string, + task: () => T | Promise, + metadata?: TimingMetadata, +): Promise> => { + if (!timing) return await task(); + + const startedAt = Date.now(); + logTimingSink(timing, `${stage}:start`, metadata); + + try { + const result = await task(); + logTimingSink(timing, `${stage}:done`, { + ...metadata, + stageMs: getDurationMs(startedAt), + }); + + return result; + } catch (error) { + logTimingSink(timing, `${stage}:error`, { + ...metadata, + ...getTimingErrorMetadata(error), + stageMs: getDurationMs(startedAt), + }); + + throw error; + } +}; + +export const createPrefixedTimingContext = ( + logger: TimingLogger, + context: TimingContext | undefined, + prefix: string, +): TimingSink | undefined => + context + ? { + log: (event: string, metadata?: TimingMetadata) => { + logTiming(logger, context, `${prefix}.${event}`, metadata); + }, + } + : undefined; + +export const createTimingHelpers = (namespace: string) => { + const logger = createDebugTimingLogger(namespace); + + return { + createPrefixedTimingContext: (context: TimingContext | undefined, prefix: string) => + createPrefixedTimingContext(logger, context, prefix), + logger, + logTiming: (context: TimingContext | undefined, event: string, metadata?: TimingMetadata) => + logTiming(logger, context, event, metadata), + runTimedStage: ( + context: TimingContext | undefined, + stage: string, + task: () => T | Promise, + metadata?: TimingMetadata, + ) => runTimedStage(logger, context, stage, task, metadata), + toTimingContext, + }; +}; diff --git a/src/server/routers/lambda/__tests__/aiChat.test.ts b/src/server/routers/lambda/__tests__/aiChat.test.ts index eb73cf69fa..6be9d88a31 100644 --- a/src/server/routers/lambda/__tests__/aiChat.test.ts +++ b/src/server/routers/lambda/__tests__/aiChat.test.ts @@ -1,4 +1,5 @@ // @vitest-environment node +import type { CreateMessageParams } from '@lobechat/types'; import { ThreadType } from '@lobechat/types'; import { describe, expect, it, vi } from 'vitest'; @@ -10,6 +11,8 @@ import { AiChatService } from '@/server/services/aiChat'; import { aiChatRouter } from '../aiChat'; +const flushAsyncTasks = () => new Promise((resolve) => setTimeout(resolve, 0)); + vi.mock('@/database/models/agent'); vi.mock('@/database/models/message'); vi.mock('@/database/models/thread'); @@ -24,6 +27,38 @@ vi.mock('@/server/modules/ModelRuntime', () => ({ describe('aiChatRouter', () => { const mockCtx = { userId: 'u1' }; + const mockMessageModel = (mockCreateMessage: ReturnType) => { + const mockCreateUserAndAssistantMessages = vi.fn( + async ( + { + assistantMessage, + userMessage, + }: { + assistantMessage: CreateMessageParams; + userMessage: CreateMessageParams; + }, + _options?: unknown, + ) => { + const userMessageItem = await mockCreateMessage(userMessage); + const assistantMessageItem = await mockCreateMessage({ + ...assistantMessage, + parentId: userMessageItem.id, + }); + + return { assistantMessage: assistantMessageItem, userMessage: userMessageItem }; + }, + ); + + vi.mocked(MessageModel).mockImplementation( + () => + ({ + create: mockCreateMessage, + createUserAndAssistantMessages: mockCreateUserAndAssistantMessages, + }) as any, + ); + + return mockCreateUserAndAssistantMessages; + }; it('should create topic optionally, create user/assistant messages, and return payload', async () => { const mockCreateTopic = vi.fn().mockResolvedValue({ id: 't1' }); @@ -37,7 +72,7 @@ describe('aiChatRouter', () => { }); vi.mocked(TopicModel).mockImplementation(() => ({ create: mockCreateTopic }) as any); - vi.mocked(MessageModel).mockImplementation(() => ({ create: mockCreateMessage }) as any); + const mockCreateUserAndAssistantMessages = mockMessageModel(mockCreateMessage); vi.mocked(AiChatService).mockImplementation(() => ({ getMessagesAndTopics: mockGet }) as any); const caller = aiChatRouter.createCaller(mockCtx as any); @@ -47,6 +82,7 @@ describe('aiChatRouter', () => { newTopic: { title: 'T', topicMessageIds: ['a', 'b'] }, newUserMessage: { content: 'hi', files: ['f1'] }, sessionId: 's1', + topicPageSize: 20, } as any; const res = await caller.sendMessageInServer(input); @@ -79,9 +115,19 @@ describe('aiChatRouter', () => { topicId: 't1', }), ); + expect(mockCreateUserAndAssistantMessages).toHaveBeenCalledTimes(1); + expect(mockCreateUserAndAssistantMessages).toHaveBeenCalledWith( + expect.any(Object), + expect.objectContaining({ touchTopicUpdatedAt: false }), + ); expect(mockGet).toHaveBeenCalledWith( - expect.objectContaining({ includeTopic: true, sessionId: 's1', topicId: 't1' }), + expect.objectContaining({ + includeTopic: true, + sessionId: 's1', + topicId: 't1', + topicPageSize: 20, + }), ); expect(res.assistantMessageId).toBe('m-assistant'); expect(res.userMessageId).toBe('m-user'); @@ -99,7 +145,7 @@ describe('aiChatRouter', () => { .mockResolvedValueOnce({ id: 'm-assistant' }); const mockGet = vi.fn().mockResolvedValue({ messages: [], topics: undefined }); - vi.mocked(MessageModel).mockImplementation(() => ({ create: mockCreateMessage }) as any); + const mockCreateUserAndAssistantMessages = mockMessageModel(mockCreateMessage); vi.mocked(AiChatService).mockImplementation(() => ({ getMessagesAndTopics: mockGet }) as any); const caller = aiChatRouter.createCaller(mockCtx as any); @@ -112,6 +158,10 @@ describe('aiChatRouter', () => { } as any); expect(mockCreateMessage).toHaveBeenCalled(); + expect(mockCreateUserAndAssistantMessages).toHaveBeenCalledWith( + expect.any(Object), + expect.objectContaining({ touchTopicUpdatedAt: true }), + ); expect(mockGet).toHaveBeenCalledWith( expect.objectContaining({ includeTopic: false, @@ -130,7 +180,7 @@ describe('aiChatRouter', () => { .mockResolvedValueOnce({ id: 'm-assistant' }); const mockGet = vi.fn().mockResolvedValue({ messages: [], topics: undefined }); - vi.mocked(MessageModel).mockImplementation(() => ({ create: mockCreateMessage }) as any); + mockMessageModel(mockCreateMessage); vi.mocked(AiChatService).mockImplementation(() => ({ getMessagesAndTopics: mockGet }) as any); const caller = aiChatRouter.createCaller(mockCtx as any); @@ -175,7 +225,7 @@ describe('aiChatRouter', () => { .mockResolvedValueOnce({ id: 'm-assistant' }); const mockGet = vi.fn().mockResolvedValue({ messages: [], topics: undefined }); - vi.mocked(MessageModel).mockImplementation(() => ({ create: mockCreateMessage }) as any); + mockMessageModel(mockCreateMessage); vi.mocked(AiChatService).mockImplementation(() => ({ getMessagesAndTopics: mockGet }) as any); const caller = aiChatRouter.createCaller(mockCtx as any); @@ -282,7 +332,7 @@ describe('aiChatRouter', () => { const mockGet = vi.fn().mockResolvedValue({ messages: [], topics: undefined }); vi.mocked(ThreadModel).mockImplementation(() => ({ create: mockCreateThread }) as any); - vi.mocked(MessageModel).mockImplementation(() => ({ create: mockCreateMessage }) as any); + mockMessageModel(mockCreateMessage); vi.mocked(AiChatService).mockImplementation(() => ({ getMessagesAndTopics: mockGet }) as any); const caller = aiChatRouter.createCaller(mockCtx as any); @@ -346,7 +396,7 @@ describe('aiChatRouter', () => { vi.mocked(TopicModel).mockImplementation(() => ({ create: mockCreateTopic }) as any); vi.mocked(ThreadModel).mockImplementation(() => ({ create: mockCreateThread }) as any); - vi.mocked(MessageModel).mockImplementation(() => ({ create: mockCreateMessage }) as any); + mockMessageModel(mockCreateMessage); vi.mocked(AiChatService).mockImplementation(() => ({ getMessagesAndTopics: mockGet }) as any); const caller = aiChatRouter.createCaller(mockCtx as any); @@ -402,7 +452,7 @@ describe('aiChatRouter', () => { .mockResolvedValueOnce({ id: 'm-assistant' }); const mockGet = vi.fn().mockResolvedValue({ messages: [], topics: undefined }); - vi.mocked(MessageModel).mockImplementation(() => ({ create: mockCreateMessage }) as any); + mockMessageModel(mockCreateMessage); vi.mocked(AiChatService).mockImplementation(() => ({ getMessagesAndTopics: mockGet }) as any); const caller = aiChatRouter.createCaller(mockCtx as any); @@ -427,7 +477,7 @@ describe('aiChatRouter', () => { const mockGet = vi.fn().mockResolvedValue({ messages: [], topics: [{}] }); vi.mocked(TopicModel).mockImplementation(() => ({ create: mockCreateTopic }) as any); - vi.mocked(MessageModel).mockImplementation(() => ({ create: mockCreateMessage }) as any); + mockMessageModel(mockCreateMessage); vi.mocked(AiChatService).mockImplementation(() => ({ getMessagesAndTopics: mockGet }) as any); const caller = aiChatRouter.createCaller(mockCtx as any); @@ -459,7 +509,7 @@ describe('aiChatRouter', () => { const mockGet = vi.fn().mockResolvedValue({ messages: [], topics: [{}] }); vi.mocked(TopicModel).mockImplementation(() => ({ create: mockCreateTopic }) as any); - vi.mocked(MessageModel).mockImplementation(() => ({ create: mockCreateMessage }) as any); + mockMessageModel(mockCreateMessage); vi.mocked(AiChatService).mockImplementation(() => ({ getMessagesAndTopics: mockGet }) as any); const caller = aiChatRouter.createCaller(mockCtx as any); @@ -489,7 +539,7 @@ describe('aiChatRouter', () => { .mockResolvedValueOnce({ id: 'm-assistant' }); const mockGet = vi.fn().mockResolvedValue({ messages: [], topics: undefined }); - vi.mocked(MessageModel).mockImplementation(() => ({ create: mockCreateMessage }) as any); + mockMessageModel(mockCreateMessage); vi.mocked(AiChatService).mockImplementation(() => ({ getMessagesAndTopics: mockGet }) as any); const caller = aiChatRouter.createCaller(mockCtx as any); @@ -537,7 +587,7 @@ describe('aiChatRouter', () => { .mockResolvedValueOnce({ id: 'm-assistant' }); const mockGet = vi.fn().mockResolvedValue({ messages: [], topics: undefined }); - vi.mocked(MessageModel).mockImplementation(() => ({ create: mockCreateMessage }) as any); + mockMessageModel(mockCreateMessage); vi.mocked(AiChatService).mockImplementation(() => ({ getMessagesAndTopics: mockGet }) as any); const caller = aiChatRouter.createCaller(mockCtx as any); @@ -569,7 +619,7 @@ describe('aiChatRouter', () => { .mockResolvedValueOnce({ id: 'm-assistant' }); const mockGet = vi.fn().mockResolvedValue({ messages: [], topics: undefined }); - vi.mocked(MessageModel).mockImplementation(() => ({ create: mockCreateMessage }) as any); + mockMessageModel(mockCreateMessage); vi.mocked(AiChatService).mockImplementation(() => ({ getMessagesAndTopics: mockGet }) as any); const caller = aiChatRouter.createCaller(mockCtx as any); @@ -621,7 +671,7 @@ describe('aiChatRouter', () => { .mockResolvedValueOnce({ id: 'm-assistant' }); const mockGet = vi.fn().mockResolvedValue({ messages: [], topics: undefined }); - vi.mocked(MessageModel).mockImplementation(() => ({ create: mockCreateMessage }) as any); + mockMessageModel(mockCreateMessage); vi.mocked(AiChatService).mockImplementation(() => ({ getMessagesAndTopics: mockGet }) as any); const caller = aiChatRouter.createCaller(mockCtx as any); @@ -677,7 +727,7 @@ describe('aiChatRouter', () => { const mockTouchUpdatedAt = vi.fn().mockResolvedValue(undefined); vi.mocked(TopicModel).mockImplementation(() => ({ create: mockCreateTopic }) as any); - vi.mocked(MessageModel).mockImplementation(() => ({ create: mockCreateMessage }) as any); + mockMessageModel(mockCreateMessage); vi.mocked(AiChatService).mockImplementation(() => ({ getMessagesAndTopics: mockGet }) as any); vi.mocked(AgentModel).mockImplementation( () => ({ touchUpdatedAt: mockTouchUpdatedAt }) as any, @@ -713,7 +763,7 @@ describe('aiChatRouter', () => { const mockTouchUpdatedAt = vi.fn().mockResolvedValue(undefined); vi.mocked(TopicModel).mockImplementation(() => ({ create: mockCreateTopic }) as any); - vi.mocked(MessageModel).mockImplementation(() => ({ create: mockCreateMessage }) as any); + mockMessageModel(mockCreateMessage); vi.mocked(AiChatService).mockImplementation(() => ({ getMessagesAndTopics: mockGet }) as any); vi.mocked(AgentModel).mockImplementation( () => ({ touchUpdatedAt: mockTouchUpdatedAt }) as any, @@ -733,6 +783,94 @@ describe('aiChatRouter', () => { expect(mockTouchUpdatedAt).toHaveBeenCalledWith('agent-1'); }); + it('should keep the message response when agent updatedAt touch fails', async () => { + const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined); + const mockCreateTopic = vi.fn().mockResolvedValue({ id: 't1' }); + const mockCreateMessage = vi + .fn() + .mockResolvedValueOnce({ id: 'm-user' }) + .mockResolvedValueOnce({ id: 'm-assistant' }); + const mockGet = vi.fn().mockResolvedValue({ + messages: [{ id: 'm-user' }, { id: 'm-assistant' }], + topics: undefined, + }); + const touchError = new Error('touch failed'); + const mockTouchUpdatedAt = vi.fn().mockRejectedValue(touchError); + + try { + vi.mocked(TopicModel).mockImplementation(() => ({ create: mockCreateTopic }) as any); + mockMessageModel(mockCreateMessage); + vi.mocked(AiChatService).mockImplementation( + () => ({ getMessagesAndTopics: mockGet }) as any, + ); + vi.mocked(AgentModel).mockImplementation( + () => ({ touchUpdatedAt: mockTouchUpdatedAt }) as any, + ); + + const caller = aiChatRouter.createCaller(mockCtx as any); + + const res = await caller.sendMessageInServer({ + agentId: 'agent-1', + newAssistantMessage: { model: 'gpt-4o', provider: 'openai' }, + newTopic: { title: 'New Topic' }, + newUserMessage: { content: 'hi' }, + sessionId: 's1', + } as any); + + expect(res.userMessageId).toBe('m-user'); + expect(res.assistantMessageId).toBe('m-assistant'); + expect(mockTouchUpdatedAt).toHaveBeenCalledWith('agent-1'); + expect(consoleErrorSpy).toHaveBeenCalledWith( + '[aiChat] Failed to touch agent updatedAt:', + touchError, + ); + } finally { + consoleErrorSpy.mockRestore(); + } + }); + + it('should create messages while agent updatedAt touch is still pending', async () => { + const mockCreateTopic = vi.fn().mockResolvedValue({ id: 't1' }); + const mockCreateMessage = vi + .fn() + .mockResolvedValueOnce({ id: 'm-user' }) + .mockResolvedValueOnce({ id: 'm-assistant' }); + const mockGet = vi.fn().mockResolvedValue({ messages: [], topics: [{}] }); + let resolveTouchUpdatedAt: () => void = () => {}; + const touchUpdatedAtPromise = new Promise((resolve) => { + resolveTouchUpdatedAt = resolve; + }); + const mockTouchUpdatedAt = vi.fn(() => touchUpdatedAtPromise); + + vi.mocked(TopicModel).mockImplementation(() => ({ create: mockCreateTopic }) as any); + const mockCreateUserAndAssistantMessages = mockMessageModel(mockCreateMessage); + vi.mocked(AiChatService).mockImplementation(() => ({ getMessagesAndTopics: mockGet }) as any); + vi.mocked(AgentModel).mockImplementation( + () => ({ touchUpdatedAt: mockTouchUpdatedAt }) as any, + ); + + const caller = aiChatRouter.createCaller(mockCtx as any); + + const request = caller.sendMessageInServer({ + agentId: 'agent-1', + newAssistantMessage: { model: 'gpt-4o', provider: 'openai' }, + newTopic: { title: 'New Topic' }, + newUserMessage: { content: 'hi' }, + sessionId: 's1', + } as any); + + await flushAsyncTasks(); + + try { + expect(mockTouchUpdatedAt).toHaveBeenCalledWith('agent-1'); + expect(mockCreateUserAndAssistantMessages).toHaveBeenCalledTimes(1); + } finally { + resolveTouchUpdatedAt(); + } + + await request; + }); + it('should not touch agent updatedAt when creating topic without agentId', async () => { const mockCreateTopic = vi.fn().mockResolvedValue({ id: 't1' }); const mockCreateMessage = vi @@ -743,7 +881,7 @@ describe('aiChatRouter', () => { const mockTouchUpdatedAt = vi.fn().mockResolvedValue(undefined); vi.mocked(TopicModel).mockImplementation(() => ({ create: mockCreateTopic }) as any); - vi.mocked(MessageModel).mockImplementation(() => ({ create: mockCreateMessage }) as any); + mockMessageModel(mockCreateMessage); vi.mocked(AiChatService).mockImplementation(() => ({ getMessagesAndTopics: mockGet }) as any); vi.mocked(AgentModel).mockImplementation( () => ({ touchUpdatedAt: mockTouchUpdatedAt }) as any, @@ -771,7 +909,7 @@ describe('aiChatRouter', () => { const mockGet = vi.fn().mockResolvedValue({ messages: [], topics: undefined }); const mockTouchUpdatedAt = vi.fn().mockResolvedValue(undefined); - vi.mocked(MessageModel).mockImplementation(() => ({ create: mockCreateMessage }) as any); + mockMessageModel(mockCreateMessage); vi.mocked(AiChatService).mockImplementation(() => ({ getMessagesAndTopics: mockGet }) as any); vi.mocked(AgentModel).mockImplementation( () => ({ touchUpdatedAt: mockTouchUpdatedAt }) as any, diff --git a/src/server/routers/lambda/aiChat.ts b/src/server/routers/lambda/aiChat.ts index 78a868605e..d36b49c7fa 100644 --- a/src/server/routers/lambda/aiChat.ts +++ b/src/server/routers/lambda/aiChat.ts @@ -1,5 +1,6 @@ -import { type CreateMessageParams, type SendMessageServerResponse } from '@lobechat/types'; +import type { CreateMessageParams, SendMessageServerResponse } from '@lobechat/types'; import { AiSendMessageServerSchema, RequestTrigger, StructureOutputSchema } from '@lobechat/types'; +import { createTimingHelpers, createTimingRequestId } from '@lobechat/utils'; import debug from 'debug'; import { LOADING_FLAT } from '@/const/message'; @@ -15,6 +16,9 @@ import { AiChatService } from '@/server/services/aiChat'; import { FileService } from '@/server/services/file'; const log = debug('lobe-lambda-router:ai-chat'); +const { createPrefixedTimingContext, logTiming, runTimedStage } = createTimingHelpers( + 'lobe-server:chat:lobehub:timing', +); const aiChatProcedure = authedProcedure.use(serverDatabase).use(async (opts) => { const { ctx } = opts; @@ -59,6 +63,17 @@ export const aiChatRouter = router({ sendMessageInServer: aiChatProcedure .input(AiSendMessageServerSchema) .mutation(async ({ input, ctx }) => { + const timingContext = + input.newAssistantMessage.provider === 'lobehub' + ? { requestId: createTimingRequestId(), startedAt: Date.now() } + : undefined; + logTiming(timingContext, 'lambda.aiChat.sendMessageInServer:start', { + hasNewThread: !!input.newThread, + hasNewTopic: !!input.newTopic, + hasSessionId: !!input.sessionId, + hasTopicId: !!input.topicId, + preloadCount: input.preloadMessages?.length ?? 0, + }); log('sendMessageInServer called for agentId: %s', input.agentId); log( 'topicId: %s, newTopic: %O, newThread: %O', @@ -68,7 +83,12 @@ export const aiChatRouter = router({ ); let sessionId = input.sessionId; if (!sessionId) { - const context = await resolveContext(input, ctx.serverDB, ctx.userId); + const context = await runTimedStage( + timingContext, + 'lambda.aiChat.resolveContext', + () => resolveContext(input, ctx.serverDB, ctx.userId), + { hasAgentId: !!input.agentId }, + ); if (!!context.sessionId) sessionId = context.sessionId; } @@ -77,27 +97,54 @@ export const aiChatRouter = router({ let createdThreadId: string | undefined; let isCreateNewTopic = false; + let agentTouchUpdatedAtTask: Promise | undefined; // create topic if there should be a new topic if (input.newTopic) { log('creating new topic with title: %s', input.newTopic.title); - const topicItem = await ctx.topicModel.create({ - agentId: input.agentId, - groupId: input.groupId, - messages: input.newTopic.topicMessageIds, - metadata: input.newTopic.metadata, - sessionId, - title: input.newTopic.title, - trigger: input.newTopic.trigger, - }); + const topicItem = await runTimedStage( + timingContext, + 'lambda.aiChat.topic.create', + () => { + const payload = { + agentId: input.agentId, + groupId: input.groupId, + messages: input.newTopic!.topicMessageIds, + metadata: input.newTopic!.metadata, + sessionId, + title: input.newTopic!.title, + trigger: input.newTopic!.trigger, + }; + const modelTiming = createPrefixedTimingContext( + timingContext, + 'lambda.aiChat.topic.create', + ); + return modelTiming + ? ctx.topicModel.create(payload, undefined, modelTiming) + : ctx.topicModel.create(payload); + }, + { + messageCount: input.newTopic.topicMessageIds?.length ?? 0, + trigger: input.newTopic.trigger, + }, + ); topicId = topicItem.id; isCreateNewTopic = true; log('new topic created with id: %s', topicId); // update agent's updatedAt to reflect new activity if (input.agentId) { - await ctx.agentModel.touchUpdatedAt(input.agentId); - log('agent updatedAt touched for agentId: %s', input.agentId); + agentTouchUpdatedAtTask = runTimedStage( + timingContext, + 'lambda.aiChat.agent.touchUpdatedAt', + async () => { + await ctx.agentModel.touchUpdatedAt(input.agentId!); + }, + { hasAgentId: true }, + ).catch((error) => { + console.error('[aiChat] Failed to touch agent updatedAt:', error); + }); + log('agent updatedAt touch scheduled for agentId: %s', input.agentId); } } @@ -108,13 +155,19 @@ export const aiChatRouter = router({ input.newThread.sourceMessageId, input.newThread.type, ); - const threadItem = await ctx.threadModel.create({ - parentThreadId: input.newThread.parentThreadId, - sourceMessageId: input.newThread.sourceMessageId, - title: input.newThread.title, - topicId, - type: input.newThread.type, - }); + const threadItem = await runTimedStage( + timingContext, + 'lambda.aiChat.thread.create', + () => + ctx.threadModel.create({ + parentThreadId: input.newThread!.parentThreadId, + sourceMessageId: input.newThread!.sourceMessageId, + title: input.newThread!.title, + topicId, + type: input.newThread!.type, + }), + { threadType: input.newThread.type }, + ); if (threadItem) { threadId = threadItem.id; createdThreadId = threadItem.id; @@ -127,24 +180,40 @@ export const aiChatRouter = router({ if (input.preloadMessages?.length) { log('creating %d preload messages before user message', input.preloadMessages.length); - for (const preloadMessage of input.preloadMessages) { - const preloadItem = await ctx.messageModel.create({ - agentId: input.agentId, - content: preloadMessage.content, - groupId: input.groupId, - metadata: preloadMessage.metadata, - parentId, - plugin: preloadMessage.plugin as CreateMessageParams['plugin'], - role: preloadMessage.role, - sessionId, - threadId, - tool_call_id: preloadMessage.tool_call_id, - tools: preloadMessage.tools as CreateMessageParams['tools'], - topicId, - }); + parentId = await runTimedStage( + timingContext, + 'lambda.aiChat.preloadMessages.create', + async () => { + let latestParentId = parentId; + for (const preloadMessage of input.preloadMessages!) { + const payload = { + agentId: input.agentId, + content: preloadMessage.content, + groupId: input.groupId, + metadata: preloadMessage.metadata, + parentId: latestParentId, + plugin: preloadMessage.plugin as CreateMessageParams['plugin'], + role: preloadMessage.role, + sessionId, + threadId, + tool_call_id: preloadMessage.tool_call_id, + tools: preloadMessage.tools as CreateMessageParams['tools'], + topicId, + }; + const modelTiming = createPrefixedTimingContext( + timingContext, + 'lambda.aiChat.preloadMessages.create', + ); + const preloadItem = await (modelTiming + ? ctx.messageModel.create(payload, undefined, modelTiming) + : ctx.messageModel.create(payload)); - parentId = preloadItem.id; - } + latestParentId = preloadItem.id; + } + return latestParentId; + }, + { count: input.preloadMessages.length }, + ); } // create user message @@ -161,58 +230,95 @@ export const aiChatRouter = router({ } : undefined; - const userMessageItem = await ctx.messageModel.create({ - agentId: input.agentId, - content: input.newUserMessage.content, - editorData: input.newUserMessage.editorData, - files: input.newUserMessage.files, - groupId: input.groupId, - metadata: userMessageMetadata, - parentId, - role: 'user', - sessionId, - threadId, - topicId, - }); + const createMessagePairPromise = runTimedStage( + timingContext, + 'lambda.aiChat.messages.createUserAndAssistant', + () => { + const userMessage = { + agentId: input.agentId, + content: input.newUserMessage.content, + editorData: input.newUserMessage.editorData, + files: input.newUserMessage.files, + groupId: input.groupId, + metadata: userMessageMetadata, + parentId, + role: 'user', + sessionId, + threadId, + topicId, + } satisfies CreateMessageParams; + const assistantMessage = { + agentId: input.agentId, + content: LOADING_FLAT, + groupId: input.groupId, + metadata: input.newAssistantMessage.metadata, + model: input.newAssistantMessage.model, + provider: input.newAssistantMessage.provider, + role: 'assistant', + sessionId, + threadId, + topicId, + } satisfies CreateMessageParams; + const modelTiming = createPrefixedTimingContext( + timingContext, + 'lambda.aiChat.messages.createUserAndAssistant', + ); + return ctx.messageModel.createUserAndAssistantMessages( + { assistantMessage, userMessage }, + { + ...(modelTiming ? { timing: modelTiming } : {}), + touchTopicUpdatedAt: !isCreateNewTopic, + }, + ); + }, + { + contentLength: input.newUserMessage.content.length, + fileCount: input.newUserMessage.files?.length ?? 0, + model: input.newAssistantMessage.model, + provider: input.newAssistantMessage.provider, + }, + ); + const { assistantMessage: assistantMessageItem, userMessage: userMessageItem } = + agentTouchUpdatedAtTask + ? (await Promise.all([createMessagePairPromise, agentTouchUpdatedAtTask]))[0] + : await createMessagePairPromise; const messageId = userMessageItem.id; log('user message created with id: %s', messageId); - // create assistant message - log( - 'creating assistant message with model: %s, provider: %s, metadata: %O', - input.newAssistantMessage.model, - input.newAssistantMessage.provider, - input.newAssistantMessage.metadata, - ); - const assistantMessageItem = await ctx.messageModel.create({ - agentId: input.agentId, - content: LOADING_FLAT, - groupId: input.groupId, - metadata: input.newAssistantMessage.metadata, - model: input.newAssistantMessage.model, - parentId: messageId, - provider: input.newAssistantMessage.provider, - role: 'assistant', - sessionId, - threadId, - topicId, - }); log('assistant message created with id: %s', assistantMessageItem.id); // retrieve latest messages and topic with log('retrieving messages and topics'); - const { messages, topics } = await ctx.aiChatService.getMessagesAndTopics({ - agentId: input.agentId, - groupId: input.groupId, - includeTopic: isCreateNewTopic, - sessionId, - threadId, - topicFilter: input.topicFilter, - topicId, - }); + const { messages, topics } = await runTimedStage( + timingContext, + 'lambda.aiChat.messagesAndTopics.query', + () => + ctx.aiChatService.getMessagesAndTopics({ + agentId: input.agentId, + groupId: input.groupId, + includeTopic: isCreateNewTopic, + sessionId, + threadId, + topicFilter: input.topicFilter, + topicId, + topicPageSize: input.topicPageSize, + ...(timingContext + ? { + timingRequestId: timingContext.requestId, + timingStartedAt: timingContext.startedAt, + } + : {}), + }), + { includeTopic: isCreateNewTopic }, + ); log('retrieved %d messages, %d topics', messages.length, topics?.items?.length ?? 0); + logTiming(timingContext, 'lambda.aiChat.sendMessageInServer:done', { + isCreateNewTopic, + messageCount: messages.length, + topicCount: topics?.items?.length ?? 0, + }); return { assistantMessageId: assistantMessageItem.id, diff --git a/src/server/routers/lambda/message.ts b/src/server/routers/lambda/message.ts index fc3f5947cb..b29711d73d 100644 --- a/src/server/routers/lambda/message.ts +++ b/src/server/routers/lambda/message.ts @@ -4,6 +4,7 @@ import { UpdateMessagePluginSchema, UpdateMessageRAGParamsSchema, } from '@lobechat/types'; +import { createTimingHelpers, createTimingRequestId } from '@lobechat/utils'; import { TRPCError } from '@trpc/server'; import { z } from 'zod'; @@ -18,6 +19,8 @@ import { MessageService } from '@/server/services/message'; import { resolveAgentIdFromSession, resolveContext } from './_helpers/resolveContext'; import { basicContextSchema } from './_schema/context'; +const { logTiming, runTimedStage } = createTimingHelpers('lobe-server:chat:lobehub:timing'); + const messageProcedure = authedProcedure.use(serverDatabase).use(async (opts) => { const { ctx } = opts; @@ -316,9 +319,37 @@ export const messageRouter = router({ ) .mutation(async ({ input, ctx }) => { const { id, value, agentId, ...options } = input; - const resolved = await resolveContext({ agentId, ...options }, ctx.serverDB, ctx.userId); + const timingContext = { requestId: createTimingRequestId(), startedAt: Date.now() }; + logTiming(timingContext, 'lambda.message.update:start', { + hasAgentId: !!agentId, + hasTopicId: !!options.topicId, + valueKeys: Object.keys(value ?? {}), + }); - return ctx.messageService.updateMessage(id, value as any, resolved); + const resolved = await runTimedStage( + timingContext, + 'lambda.message.update.resolveContext', + () => resolveContext({ agentId, ...options }, ctx.serverDB, ctx.userId), + { hasAgentId: !!agentId }, + ); + + const result = await runTimedStage( + timingContext, + 'lambda.message.update.service', + () => + ctx.messageService.updateMessage(id, value as any, { + ...resolved, + timingRequestId: timingContext.requestId, + timingStartedAt: timingContext.startedAt, + }), + { hasResolvedTopicId: !!resolved.topicId }, + ); + + logTiming(timingContext, 'lambda.message.update:done', { + messageCount: result.messages?.length ?? 0, + success: result.success, + }); + return result; }), /** diff --git a/src/server/services/aiChat/index.test.ts b/src/server/services/aiChat/index.test.ts index e2a05a1dce..401796269b 100644 --- a/src/server/services/aiChat/index.test.ts +++ b/src/server/services/aiChat/index.test.ts @@ -1,4 +1,4 @@ -import { type LobeChatDatabase } from '@lobechat/database'; +import type { LobeChatDatabase } from '@lobechat/database'; import { describe, expect, it, vi } from 'vitest'; import { MessageModel } from '@/database/models/message'; @@ -31,13 +31,18 @@ describe('AiChatService', () => { groupId: 'group-1', includeTopic: true, sessionId: 's1', + topicPageSize: 20, }); expect(mockQueryMessages).toHaveBeenCalledWith( { agentId: 'agent-1', groupId: 'group-1', includeTopic: true, sessionId: 's1' }, expect.objectContaining({ postProcessUrl: expect.any(Function) }), ); - expect(mockQueryTopics).toHaveBeenCalledWith({ agentId: 'agent-1', groupId: 'group-1' }); + expect(mockQueryTopics).toHaveBeenCalledWith({ + agentId: 'agent-1', + groupId: 'group-1', + pageSize: 20, + }); expect(res.messages).toEqual([{ id: 'm1' }]); expect(res.topics).toEqual([{ id: 't1' }]); }); @@ -63,6 +68,7 @@ describe('AiChatService', () => { excludeStatuses: ['completed'], excludeTriggers: ['cron', 'eval'], }, + topicPageSize: 20, }); expect(mockQueryTopics).toHaveBeenCalledWith({ @@ -70,12 +76,17 @@ describe('AiChatService', () => { excludeStatuses: ['completed'], excludeTriggers: ['cron', 'eval'], groupId: undefined, + pageSize: 20, }); // topicFilter must not leak into messageModel.query expect(mockQueryMessages).toHaveBeenCalledWith( expect.not.objectContaining({ topicFilter: expect.anything() }), expect.objectContaining({ postProcessUrl: expect.any(Function) }), ); + expect(mockQueryMessages).toHaveBeenCalledWith( + expect.not.objectContaining({ topicPageSize: 20 }), + expect.objectContaining({ postProcessUrl: expect.any(Function) }), + ); }); it('getMessagesAndTopics should not query topics when includeTopic is false', async () => { diff --git a/src/server/services/aiChat/index.ts b/src/server/services/aiChat/index.ts index a798011267..85a24c0ebb 100644 --- a/src/server/services/aiChat/index.ts +++ b/src/server/services/aiChat/index.ts @@ -1,9 +1,33 @@ -import { type LobeChatDatabase } from '@lobechat/database'; +import type { LobeChatDatabase } from '@lobechat/database'; +import { createTimingHelpers } from '@lobechat/utils'; import { MessageModel } from '@/database/models/message'; import { TopicModel } from '@/database/models/topic'; import { FileService } from '@/server/services/file'; +const { createPrefixedTimingContext, runTimedStage, toTimingContext } = createTimingHelpers( + 'lobe-server:chat:lobehub:timing', +); + +interface GetMessagesAndTopicsParams { + agentId?: string; + current?: number; + groupId?: string; + includeTopic?: boolean; + pageSize?: number; + sessionId?: string; + threadId?: string; + timingRequestId?: string; + timingStartedAt?: number; + topicFilter?: { + excludeStatuses?: string[]; + excludeTriggers?: string[]; + includeTriggers?: string[]; + }; + topicId?: string; + topicPageSize?: number; +} + export class AiChatService { private userId: string; private messageModel: MessageModel; @@ -18,32 +42,48 @@ export class AiChatService { this.fileService = new FileService(serverDB, userId); } - async getMessagesAndTopics(params: { - agentId?: string; - current?: number; - groupId?: string; - includeTopic?: boolean; - pageSize?: number; - sessionId?: string; - threadId?: string; - topicFilter?: { - excludeStatuses?: string[]; - excludeTriggers?: string[]; - includeTriggers?: string[]; - }; - topicId?: string; - }) { - const { topicFilter, ...messageParams } = params; + async getMessagesAndTopics(params: GetMessagesAndTopicsParams) { + const { topicFilter, topicPageSize, timingRequestId, timingStartedAt, ...messageParams } = + params; + const timingContext = toTimingContext({ timingRequestId, timingStartedAt }); + const messageTiming = createPrefixedTimingContext( + timingContext, + 'lambda.aiChat.messagesAndTopics.messageModel.query', + ); + const topicTiming = createPrefixedTimingContext( + timingContext, + 'lambda.aiChat.messagesAndTopics.topicModel.query', + ); + const messageQueryPromise = runTimedStage( + timingContext, + 'lambda.aiChat.messagesAndTopics.messageModel.query', + () => + this.messageModel.query(messageParams, { + postProcessUrl: (path) => this.fileService.getFullFileUrl(path), + ...(messageTiming ? { timing: messageTiming } : {}), + }), + { + hasAgentId: !!params.agentId, + hasThreadId: !!params.threadId, + hasTopicId: !!params.topicId, + }, + ); const [messages, topics] = await Promise.all([ - this.messageModel.query(messageParams, { - postProcessUrl: (path) => this.fileService.getFullFileUrl(path), - }), + messageQueryPromise, params.includeTopic - ? this.topicModel.query({ - agentId: params.agentId, - groupId: params.groupId, - ...topicFilter, - }) + ? runTimedStage( + timingContext, + 'lambda.aiChat.messagesAndTopics.topicModel.query', + () => + this.topicModel.query({ + agentId: params.agentId, + groupId: params.groupId, + pageSize: topicPageSize, + ...(topicTiming ? { timing: topicTiming } : {}), + ...topicFilter, + }), + { hasAgentId: !!params.agentId, hasGroupId: !!params.groupId }, + ) : undefined, ]); diff --git a/src/server/services/message/index.ts b/src/server/services/message/index.ts index 07f55cdc64..f6d180774a 100644 --- a/src/server/services/message/index.ts +++ b/src/server/services/message/index.ts @@ -5,6 +5,7 @@ import { type UIChatMessage, type UpdateMessageParams, } from '@lobechat/types'; +import { createTimingHelpers, getDurationMs } from '@lobechat/utils'; import { MessageModel } from '@/database/models/message'; @@ -15,9 +16,26 @@ interface QueryOptions { groupId?: string | null; sessionId?: string | null; threadId?: string | null; + timingRequestId?: string; + timingStartedAt?: number; topicId?: string | null; } +const { createPrefixedTimingContext, logTiming, toTimingContext } = createTimingHelpers( + 'lobe-server:chat:lobehub:timing', +); + +const logMessageTiming = ( + options: QueryOptions | undefined, + event: string, + metadata?: Record, +) => { + logTiming(toTimingContext(options), event, metadata); +}; + +const createModelTiming = (options: QueryOptions | undefined, prefix: string) => + createPrefixedTimingContext(toTimingContext(options), prefix); + interface CreateMessageResult { id: string; messages: any[]; @@ -70,15 +88,25 @@ export class MessageService { options.sessionId === undefined && options.topicId === undefined) ) { + logMessageTiming(options, 'lambda.message.update.queryMessages:skipped'); return { success: true }; } const { agentId, sessionId, topicId, groupId, threadId } = options; + const queryStartedAt = Date.now(); + const modelTiming = createModelTiming(options, 'lambda.message.update.queryMessages'); const messages = await this.messageModel.query( { agentId, groupId, sessionId, threadId, topicId }, - this.getQueryOptions(), + { + ...this.getQueryOptions(), + ...(modelTiming ? { timing: modelTiming } : {}), + }, ); + logMessageTiming(options, 'lambda.message.update.queryMessages:done', { + messageCount: messages.length, + stageMs: getDurationMs(queryStartedAt), + }); return { messages, success: true }; } @@ -188,7 +216,18 @@ export class MessageService { value: UpdateMessageParams, options: QueryOptions, ): Promise<{ messages?: UIChatMessage[]; success: boolean }> { - await this.messageModel.update(id, value as any); + const updateStartedAt = Date.now(); + const modelTiming = createModelTiming(options, 'lambda.message.update.dbUpdate'); + if (modelTiming) { + await this.messageModel.update(id, value as any, modelTiming); + } else { + await this.messageModel.update(id, value as any); + } + logMessageTiming(options, 'lambda.message.update.dbUpdate:done', { + stageMs: getDurationMs(updateStartedAt), + valueKeys: Object.keys(value ?? {}), + }); + return this.queryWithSuccess(options); } diff --git a/src/store/chat/slices/aiChat/actions/__tests__/conversationLifecycle.test.ts b/src/store/chat/slices/aiChat/actions/__tests__/conversationLifecycle.test.ts index c754be8045..411fe5abcc 100644 --- a/src/store/chat/slices/aiChat/actions/__tests__/conversationLifecycle.test.ts +++ b/src/store/chat/slices/aiChat/actions/__tests__/conversationLifecycle.test.ts @@ -9,6 +9,7 @@ import { chatService } from '@/services/chat'; import { messageService } from '@/services/message'; import * as agentGroupStore from '@/store/agentGroup'; import { messageMapKey } from '@/store/chat/utils/messageMapKey'; +import { topicMapKey } from '@/store/chat/utils/topicMapKey'; import { getSessionStoreState } from '@/store/session'; import * as toolStoreModule from '@/store/tool'; @@ -1622,7 +1623,6 @@ describe('ConversationLifecycle actions', () => { createMockMessage({ id: 'new-user-msg', role: 'user', topicId: newTopicId }), createMockMessage({ id: 'new-assistant-msg', role: 'assistant', topicId: newTopicId }), ], - topics: { items: [{ id: newTopicId, title: 'New Topic' }], total: 1 }, topicId: newTopicId, isCreateNewTopic: true, assistantMessageId: 'new-assistant-msg', @@ -1648,6 +1648,12 @@ describe('ConversationLifecycle actions', () => { // After new topic creation, the _new key should be cleared const messagesInNewKey = useChatStore.getState().messagesMap[newKey]; expect(messagesInNewKey ?? []).toHaveLength(0); + + const newTopicKey = messageMapKey({ agentId, topicId: newTopicId }); + expect(useChatStore.getState().messagesMap[newTopicKey]).toHaveLength(2); + expect(useChatStore.getState().topicDataMap[topicMapKey({ agentId })]?.items[0]).toEqual( + expect.objectContaining({ id: newTopicId }), + ); }); }); }); diff --git a/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts b/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts index 185043e603..151d7ffbaf 100644 --- a/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +++ b/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts @@ -523,6 +523,7 @@ export class ConversationLifecycleActionImpl { operationContext.agentId, operationContext.groupId ?? undefined, ), + topicPageSize: systemStatusSelectors.topicPageSize(useGlobalStore.getState()), topicId: operationContext.topicId ?? undefined, }, abortController, @@ -712,6 +713,7 @@ export class ConversationLifecycleActionImpl { const toolContext = formatSelectedToolsContext(dedupedTools); const contextSuffix = [skillContext, toolContext].filter(Boolean).join('\n'); const persistedContent = contextSuffix ? `${message}\n\n${contextSuffix}` : message; + const newTopicTitle = message.slice(0, 80) || t('defaultTitle', { ns: 'topic' }); data = await aiChatService.sendMessageInServer( { @@ -730,6 +732,7 @@ export class ConversationLifecycleActionImpl { operationContext.agentId, operationContext.groupId ?? undefined, ), + topicPageSize: systemStatusSelectors.topicPageSize(useGlobalStore.getState()), threadId: operationContext.threadId ?? undefined, // Support creating new thread along with message newThread: newThread @@ -741,7 +744,7 @@ export class ConversationLifecycleActionImpl { newTopic: !topicId ? { topicMessageIds: forceNewTopicFromExisting ? [] : messages.map((m) => m.id), - title: message.slice(0, 80) || t('defaultTitle', { ns: 'topic' }), + title: newTopicTitle, } : undefined, agentId: operationContext.agentId, @@ -757,7 +760,7 @@ export class ConversationLifecycleActionImpl { abortController, ); // Use created topicId/threadId if available, otherwise use original from context - let finalTopicId = operationContext.topicId; + let finalTopicId = data.topicId ?? operationContext.topicId; const finalThreadId = data.createdThreadId ?? operationContext.threadId; // refresh the total data @@ -780,6 +783,18 @@ export class ConversationLifecycleActionImpl { // Record the created topicId in metadata (not context) this.#get().updateOperationMetadata(operationId, { createdTopicId: data.topicId }); } + } else if (data.isCreateNewTopic && data.topicId && !context.isolatedTopic) { + this.#get().internal_dispatchTopic( + { + type: 'addTopic', + value: { + id: data.topicId, + title: newTopicTitle, + }, + }, + 'sendMessage/createTopicPlaceholder', + ); + this.#get().updateOperationMetadata(operationId, { createdTopicId: data.topicId }); } else if (operationContext.topicId) { // Optimistically update topic's updatedAt so sidebar re-groups immediately this.#get().internal_dispatchTopic({ From d35ee849ddf6aca22bc0e85f114a68d8efa55d0b Mon Sep 17 00:00:00 2001 From: Arvin Xu Date: Tue, 19 May 2026 13:37:15 +0800 Subject: [PATCH 021/224] chore: streamline issue triage to core business labels (1-3 per issue) (#14962) * refactor: streamline issue triage labels --------- Co-authored-by: lobehubbot --- .claude/prompts/issue-triage.md | 336 ++++++++++++++------------------ CHANGELOG.md | 33 ++++ changelog/v2.json | 5 + package.json | 2 +- 4 files changed, 187 insertions(+), 189 deletions(-) diff --git a/.claude/prompts/issue-triage.md b/.claude/prompts/issue-triage.md index 3bb1fd3c54..2cbd9e0c6c 100644 --- a/.claude/prompts/issue-triage.md +++ b/.claude/prompts/issue-triage.md @@ -1,6 +1,10 @@ # Issue Triage Guide -This guide is used for batch triaging GitHub issues - analyzing issues and applying appropriate labels. +This guide is used for triaging GitHub issues — analyzing issues and applying only the most essential business-domain labels. + +## Core Principle + +**Each issue should have 1-3 labels that describe its core business domain.** Do NOT apply redundant labels that can be inferred from other labels. Less is more. ## Workflow @@ -20,23 +24,76 @@ For each issue number, run: gh issue view [ISSUE_NUMBER] --json number,title,body,labels,comments ``` -### Step 3: Analyze and Select Labels +### Step 3: Select Labels (1-3 per issue) -Extract information from the issue template and content: +Only apply labels from these THREE categories: -#### Template Fields Mapping +#### Category 1: Technology Carrier -- 📦 Platform field → `platform:web/desktop/mobile` -- 💻 Operating System → `os:windows/macos/linux/ios` -- 🌐 Browser → `device:pc/mobile` -- 📦 Deployment mode → `deployment:server/client/pglite` -- Platform (hosting) → `hosting:cloud/self-host/vercel/zeabur/railway` +The runtime environment or technology wrapper where the issue occurs: -#### Provider Detection +| Label | When to apply | +|-------|--------------| +| `electron` | Desktop/Electron-specific issues. This REPLACES `platform:desktop`, `os:*`, `deployment:*`, `hosting:*` — do NOT add those. | +| `pwa` | PWA/mobile-app-specific issues | +| `docker` | Docker-specific deployment issues | -**IMPORTANT**: Always check issue title and body for provider mentions! +**Rule**: If `electron` is applied, do NOT add `platform:desktop`, `os:*`, `deployment:*`, or `hosting:*`. The `electron` label already implies all of these. -**Official Providers** (check for these keywords in title/body): +#### Category 2: Feature / Component + +The functional area affected. Select the 1-2 MOST relevant: + +Core Features: + +- `feature:agent` - Agent/Assistant functionality +- `feature:topic` - Topic/Conversation management +- `feature:marketplace` - Agent/plugin marketplace +- `feature:settings` - Settings and configuration + +Content & Knowledge: + +- `feature:editor` - Lobe Editor / rich text / markdown rendering +- `feature:markdown` - Markdown rendering (if separate from editor) +- `feature:files` - File upload/management +- `feature:knowledge-base` - Knowledge base and RAG +- `feature:export` - Export functionality + +Model Capabilities: + +- `feature:tool` - Tool calling and function execution +- `feature:streaming` - Streaming responses +- `feature:vision` - Vision/multimodal capabilities +- `feature:image` - AI image generation +- `feature:tts` - Text-to-speech + +Technical: + +- `feature:api` - Backend API +- `feature:auth` - Authentication/authorization +- `feature:sync` - Cloud sync functionality +- `feature:search` - Search functionality +- `feature:mcp` - MCP integration +- `feature:thread` - Thread/Subtopic functionality + +Collaboration: + +- `feature:group-chat` - Group chat functionality +- `feature:memory` - Memory feature +- `feature:team-workspace` - Team workspace +- `feature:im-integration` - IM and bot integration + +Other: + +- `feature:schedule-task` - Scheduled task functionality + +**Rule**: Pick only the 1-2 most specific feature labels. Don't stack multiple features unless the issue genuinely spans multiple areas. + +#### Category 3: Model Provider + +Only when the issue is SPECIFICALLY about a provider's behavior: + +**Official Providers** (check title and body for these keywords): - `openai`, `gpt` → `provider:openai` - `gemini` → `provider:gemini` @@ -57,197 +114,100 @@ Extract information from the issue template and content: **Third-party Aggregation Providers**: - `aihubmix`, `AIHubMix`, `AIHUBMIX` → `provider:aihubmix` -- Check environment variables like `AIHUBMIX_*` in issue body +- `zenmux` → `provider:zenmux` -**Multiple Providers**: If issue mentions multiple providers, add ALL applicable provider labels. +**Rule**: Only add a provider label if the issue is specifically about that provider's behavior (e.g., "Gemini returns error X"). Do NOT add provider labels just because the issue template mentions a provider. -### Label Categories - -#### a) Issue Type (select ONE if applicable) - -- `💄 Design` - UI/UX design issues -- `📝 Documentation` - Documentation improvements -- `⚡️ Performance` - Performance optimization - -#### b) Priority (select ONE if applicable) - -- `priority:high` - Critical issues, data loss, security, maintainer mentions "urgent"/"serious"/"critical" -- `priority:medium` - Important issues affecting multiple users, significant functionality impact -- `priority:low` - Nice to have, minor issues, edge cases - -**Priority Guidelines**: - -- Set `priority:high` for: data loss, authentication failures, deployment blockers, critical bugs -- Set `priority:medium` for: feature bugs affecting multiple users, workflow issues -- Set `priority:low` for: cosmetic issues, feature requests, configuration questions - -#### c) Platform (select ALL applicable) - -- `platform:web` -- `platform:desktop` -- `platform:mobile` - -#### d) Device (for platform:web, select ONE) - -- `device:pc` -- `device:mobile` - -#### e) Operating System (select ALL applicable) - -- `os:windows` -- `os:macos` -- `os:linux` -- `os:ios` -- `os:android` - -#### f) Hosting Platform (select ONE) - -- `hosting:cloud` - Official LobeHub Cloud -- `hosting:self-host` - Self-hosted deployment -- `hosting:vercel` - Vercel deployment -- `hosting:zeabur` - Zeabur deployment -- `hosting:railway` - Railway deployment - -#### g) Deployment Mode (select ONE if mentioned) - -- `deployment:server` - Server-side database mode -- `deployment:client` - Client-side database mode -- `deployment:pglite` - PGLite mode - -**Additional deployment tags**: - -- `docker` - If using Docker deployment -- `electron` - If desktop/Electron specific - -#### h) Model Provider (select ALL applicable) - -See "Provider Detection" section above for complete list. - -**IMPORTANT**: Always scan issue title and body for provider keywords! - -#### i) Feature/Component (select ALL applicable) - -Core Features: - -- `feature:settings` - Settings and configuration -- `feature:agent` - Agent/Assistant functionality -- `feature:topic` - Topic/Conversation management -- `feature:marketplace` - Agent marketplace - -File & Knowledge: - -- `feature:files` - File upload/management -- `feature:knowledge-base` - Knowledge base and RAG -- `feature:export` - Export functionality - -Model Capabilities: - -- `feature:streaming` - Streaming responses -- `feature:tool` - Tool calling -- `feature:vision` - Vision/multimodal capabilities -- `feature:image` - AI image generation -- `feature:dalle` - DALL-E specific -- `feature:tts` - Text-to-speech - -Technical: - -- `feature:api` - Backend API -- `feature:auth` - Authentication/authorization -- `feature:sync` - Cloud sync functionality -- `feature:search` - Search functionality -- `feature:mcp` - MCP integration -- `feature:editor` - Lobe Editor -- `feature:markdown` - Markdown rendering -- `feature:thread` - Thread/Subtopic functionality - -Collaboration: - -- `feature:group-chat` - Group chat functionality -- `feature:memory` - Memory feature -- `feature:team-workspace` - Team workspace - -#### j) Workflow/Status +#### Special Labels (use sparingly) +- `i18n` - Internationalization / translation issues - `Duplicate` - Only if duplicate of an OPEN issue (mention issue number) -- `needs-reproduction` - Cannot reproduce, needs more information -- `good-first-issue` - Good for first-time contributors - `🤔 Need Reproduce` - Needs reproduction steps +- `good-first-issue` - Good for first-time contributors ### Step 4: Apply Labels -Add labels (comma-separated, no spaces after commas): - -```bash -gh issue edit [ISSUE_NUMBER] --add-label "label1,label2,label3" -``` - -Remove "unconfirm" label if adding other labels: - ```bash +gh issue edit [ISSUE_NUMBER] --add-label "label1,label2" gh issue edit [ISSUE_NUMBER] --remove-label "unconfirm" ``` -**Important**: Combine both commands when possible for efficiency. - ### Step 5: Log Summary -For each issue, provide reasoning (2-4 sentences): +For each issue, provide a brief reasoning (1-2 sentences) explaining why each label was chosen. -- Labels applied and why -- Key factors from issue template/comments -- Provider detection reasoning (if applicable) +## What NOT to Label + +These categories are INTENTIONALLY OMITTED — do NOT apply them: + +| Do NOT apply | Reason | +|-------------|--------| +| `platform:web`, `platform:desktop`, `platform:mobile` | Inferred from `electron`/`pwa` or issue context | +| `os:windows`, `os:macos`, `os:linux`, `os:ios`, `os:android` | Low triage value; inferred from `electron` | +| `device:pc`, `device:mobile` | Redundant with platform | +| `hosting:cloud`, `hosting:self-host`, `hosting:vercel`, etc. | Low triage value unless deployment-specific | +| `deployment:server`, `deployment:client`, `deployment:pglite` | Low triage value; inferred from `electron` | +| `priority:high`, `priority:medium`, `priority:low` | Maintainers judge priority themselves | +| `🐛 Bug`, `💄 Design`, `📝 Documentation`, `⚡️ Performance` | Issue type is already indicated by GitHub issue template | +| `Inactive` | Handled separately; do NOT add during triage | + +## Examples + +### Example 1: Electron desktop bug + +**Issue**: "Connection failure when executing tasks on macOS desktop app" + +**Analysis**: Desktop Electron app issue with task scheduling. + +**Labels**: `electron,feature:schedule-task` + +**Why**: `electron` covers the desktop platform. `feature:schedule-task` identifies the affected feature. No need for `platform:desktop`, `os:macos`, `hosting:cloud`, `priority:*`, or `Bug`. + +### Example 2: Provider-specific issue + +**Issue**: "Gemini tool calling returns empty response on desktop" + +**Analysis**: Desktop app issue, but the core problem is Gemini provider behavior with tool calling. + +**Labels**: `electron,provider:gemini` + +**Why**: `electron` for the desktop context. `provider:gemini` because the issue is about Gemini's behavior. The tool calling aspect is secondary — the provider is the key domain. + +### Example 3: Feature-specific issue + +**Issue**: "Underscore auto-escaped in markdown editor" + +**Analysis**: Markdown rendering bug in the editor component. + +**Labels**: `feature:markdown` + +**Why**: Single label is sufficient — the issue is purely about markdown rendering. No need for platform, OS, or priority labels. + +### Example 4: Web-only feature request + +**Issue**: "Add search functionality to plugin marketplace" + +**Analysis**: Feature request for marketplace search. Web platform, no specific provider. + +**Labels**: `feature:marketplace,feature:search` + +**Why**: Two feature labels capture the core domain. No platform label needed — it's a web app by default. + +### Example 5: Ollama self-hosted issue + +**Issue**: "Ollama model not loading on self-hosted Docker deployment" + +**Analysis**: Provider-specific issue with Ollama on Docker. + +**Labels**: `docker,provider:ollama` + +**Why**: `docker` for the deployment context, `provider:ollama` for the model provider. No need for `hosting:self-host` or `platform:*`. ## Important Rules -1. **Read Carefully**: Read issue template fields AND issue body/title for complete context -2. **Provider Detection**: ALWAYS check title and body for provider keywords (including aihubmix, etc.) -3. **Multiple Categories**: Use ALL applicable labels from different categories -4. **Label Prefixes**: Always use proper prefixes (`feature:`, `provider:`, `os:`, `platform:`, etc.) -5. **Maintainer Comments**: Check maintainer comments for priority/status hints -6. **No Comments**: Only apply labels, DO NOT post comments to issues -7. **Batch Efficiency**: Process issues in parallel when possible - -## Common Patterns - -### Provider in Environment Variables - -If issue body contains `AIHUBMIX_*`, add `provider:aihubmix` - -### Multiple Provider Issues - -If comparing providers (e.g., "works with OpenAI but not Gemini"), add both provider labels - -### Desktop Issues - -Desktop issues often need: `platform:desktop`, `electron`, specific `os:*`, and `deployment:client` or `deployment:server` - -### Knowledge Base Issues - -Usually need: `feature:knowledge-base`, often with `feature:files`, may need `provider:*` for embedding models - -### Tool Calling Issues - -Usually need: `feature:tool`, specific `provider:*`, may need `feature:mcp` if MCP-related - -### Streaming Issues - -Usually need: `feature:streaming`, specific `provider:*`, check for timeout/performance issues - -## Example Triage - -**Issue #8850**: "aihubmix 的优惠 app 没有生效" - -**Analysis**: - -- Title contains "aihubmix" → `provider:aihubmix` -- Template shows: Windows, Chrome, Docker, Client mode -- About API discount codes not working - -**Labels Applied**: - -```bash -gh issue edit 8850 --add-label "provider:aihubmix,platform:web,os:windows,deployment:client,hosting:self-host,docker" -gh issue edit 8850 --remove-label "unconfirm" -``` - -**Reasoning**: AIHubMix provider discount feature not working. Client mode deployment on Windows with Docker. Provider detection from title keyword "aihubmix". +1. **1-3 labels per issue** — Never exceed 3 labels. If you find yourself adding more, you're being too granular. +2. **`electron` replaces all platform/OS/deployment labels** — Never combine `electron` with `platform:desktop`, `os:*`, `deployment:*`, or `hosting:*`. +3. **Provider only when relevant** — Only add `provider:*` if the issue is specifically about that provider's behavior. +4. **No priority, no type** — Do NOT add `priority:*`, `🐛 Bug`, `💄 Design`, etc. Maintainers handle these. +5. **No comments** — Only apply labels. Do NOT post comments to issues. +6. **Remove `unconfirm`** — Always remove the `unconfirm` label when applying triage labels. diff --git a/CHANGELOG.md b/CHANGELOG.md index cb21d17243..8deecd15d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,39 @@ # Changelog +### [Version 2.2.0](https://github.com/lobehub/lobe-chat/compare/v2.1.59-canary.27...v2.2.0) + +Released on **2026-05-18** + +#### 💄 Styles + +- **pricing**: restore DeepSeek models to official pricing. + +#### 🐛 Bug Fixes + +- **conversation**: animate only the last markdown block + drop clearMessages hotkey. + +
+ +
+Improvements and Fixes + +#### Styles + +- **pricing**: restore DeepSeek models to official pricing, closes [#14911](https://github.com/lobehub/lobe-chat/issues/14911) ([e566688](https://github.com/lobehub/lobe-chat/commit/e566688)) + +#### What's fixed + +- **conversation**: animate only the last markdown block + drop clearMessages hotkey, closes [#14906](https://github.com/lobehub/lobe-chat/issues/14906) ([469a8e6](https://github.com/lobehub/lobe-chat/commit/469a8e6)) + +
+ +
+ +[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) + +
+ ## [Version 2.1.58](https://github.com/lobehub/lobe-chat/compare/v2.1.57...v2.1.58) Released on **2026-05-13** diff --git a/changelog/v2.json b/changelog/v2.json index 30429a199a..c8c210b44d 100644 --- a/changelog/v2.json +++ b/changelog/v2.json @@ -1,4 +1,9 @@ [ + { + "children": {}, + "date": "2026-05-18", + "version": "2.2.0" + }, { "children": { "features": [ diff --git a/package.json b/package.json index 81b31d8386..34b2c49b9a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lobehub/lobehub", - "version": "2.1.58", + "version": "2.2.0", "description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.", "keywords": [ "framework", From 48ac76815d4e202d8fc6d5bb0c8fd87f9a6f06b9 Mon Sep 17 00:00:00 2001 From: YuTengjing Date: Tue, 19 May 2026 14:45:14 +0800 Subject: [PATCH 022/224] =?UTF-8?q?=F0=9F=90=9B=20fix:=20normalize=20Anthr?= =?UTF-8?q?opic-compatible=20base=20URLs=20(#14960)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../anthropicCompatibleFactory/index.test.ts | 39 ++++++++++++++++++- .../core/anthropicCompatibleFactory/index.ts | 17 ++++++-- .../src/providers/deepseek/index.test.ts | 9 +++++ 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/packages/model-runtime/src/core/anthropicCompatibleFactory/index.test.ts b/packages/model-runtime/src/core/anthropicCompatibleFactory/index.test.ts index be1ce29643..950051697b 100644 --- a/packages/model-runtime/src/core/anthropicCompatibleFactory/index.test.ts +++ b/packages/model-runtime/src/core/anthropicCompatibleFactory/index.test.ts @@ -2,7 +2,7 @@ import Anthropic from '@anthropic-ai/sdk'; import { describe, expect, it, vi } from 'vitest'; -import { createDefaultAnthropicClient } from './index'; +import { createAnthropicCompatibleRuntime, createDefaultAnthropicClient } from './index'; vi.mock('@anthropic-ai/sdk', () => { const MockAnthropic = vi.fn(); @@ -45,4 +45,41 @@ describe('createDefaultAnthropicClient', () => { 'X-Custom': 'value', }); }); + + it.each([ + ['https://aihubmix.com/v1', 'https://aihubmix.com'], + ['https://aihubmix.com/v1/messages', 'https://aihubmix.com'], + ['https://api.example.com/anthropic/v1', 'https://api.example.com/anthropic'], + ['https://api.example.com/anthropic', 'https://api.example.com/anthropic'], + ])('should normalize Anthropic SDK-managed baseURL path %s', (baseURL, expectedBaseURL) => { + MockedAnthropic.mockClear(); + + createDefaultAnthropicClient({ apiKey: 'test-key', baseURL }); + + expect(MockedAnthropic).toHaveBeenCalledWith( + expect.objectContaining({ + baseURL: expectedBaseURL, + }), + ); + }); +}); + +describe('createAnthropicCompatibleRuntime', () => { + it('should normalize default baseURL before creating a custom client', () => { + const createClient = vi.fn((options) => ({ baseURL: options.baseURL }) as unknown as Anthropic); + const Runtime = createAnthropicCompatibleRuntime({ + baseURL: 'https://aihubmix.com/v1', + customClient: { createClient }, + provider: 'test-provider', + }); + + const runtime = new Runtime({ apiKey: 'test-key' }); + + expect(createClient).toHaveBeenCalledWith( + expect.objectContaining({ + baseURL: 'https://aihubmix.com', + }), + ); + expect(runtime.baseURL).toBe('https://aihubmix.com'); + }); }); diff --git a/packages/model-runtime/src/core/anthropicCompatibleFactory/index.ts b/packages/model-runtime/src/core/anthropicCompatibleFactory/index.ts index a5e4d1e54b..2b9b4fd460 100644 --- a/packages/model-runtime/src/core/anthropicCompatibleFactory/index.ts +++ b/packages/model-runtime/src/core/anthropicCompatibleFactory/index.ts @@ -44,6 +44,10 @@ type ConstructorOptions = any> = ClientOptions & T type AnthropicTools = Anthropic.Tool | Anthropic.WebSearchTool20250305; export const DEFAULT_ANTHROPIC_BASE_URL = 'https://api.anthropic.com'; +const ANTHROPIC_SDK_MESSAGES_PATH_PATTERN = /\/v1(?:\/messages)?\/?$/; + +const normalizeAnthropicCompatibleBaseURL = (baseURL?: string | null) => + baseURL?.replace(ANTHROPIC_SDK_MESSAGES_PATH_PATTERN, ''); export interface CustomClientOptions = any> { createClient?: (options: ConstructorOptions) => Anthropic; @@ -250,13 +254,14 @@ export const createDefaultAnthropicClient = = any options: ConstructorOptions, ) => { const betaHeaders = process.env.ANTHROPIC_BETA_HEADERS; + const baseURL = normalizeAnthropicCompatibleBaseURL(options.baseURL); const defaultHeaders = { 'User-Agent': `lobehub/${CURRENT_VERSION}`, ...options.defaultHeaders, ...(betaHeaders ? { 'anthropic-beta': betaHeaders } : {}), }; - return new Anthropic({ ...options, defaultHeaders }); + return new Anthropic({ ...options, ...(baseURL ? { baseURL } : {}), defaultHeaders }); }; /** @@ -435,13 +440,17 @@ export const createAnthropicCompatibleRuntime = = constructor(options: ClientOptions & Record = {}) { const apiKey = typeof options.apiKey === 'string' ? options.apiKey.trim() : options.apiKey; - const baseURL = + const inputBaseURL = typeof options.baseURL === 'string' ? options.baseURL.trim() : options.baseURL; + // Anthropic SDK appends `/v1/messages`; normalize gateway URLs that already + // include that SDK-managed path segment before constructing any client. + const baseURL = normalizeAnthropicCompatibleBaseURL(inputBaseURL); + const defaultBaseURL = normalizeAnthropicCompatibleBaseURL(DEFAULT_BASE_URL); const resolvedOptions = { ...options, apiKey: apiKey || DEFAULT_API_KEY, - baseURL: baseURL || DEFAULT_BASE_URL, + baseURL: baseURL || defaultBaseURL, }; const { apiKey: finalApiKey, @@ -465,7 +474,7 @@ export const createAnthropicCompatibleRuntime = = this.client = new Anthropic(initOptions as ConstructorOptions); } - this.baseURL = baseURL || this.client.baseURL; + this.baseURL = finalBaseURL || this.client.baseURL; this.id = options.id || provider; this.logPrefix = `lobe-model-runtime:${this.id}`; } diff --git a/packages/model-runtime/src/providers/deepseek/index.test.ts b/packages/model-runtime/src/providers/deepseek/index.test.ts index 809e8bf052..a31a3da47c 100644 --- a/packages/model-runtime/src/providers/deepseek/index.test.ts +++ b/packages/model-runtime/src/providers/deepseek/index.test.ts @@ -102,6 +102,15 @@ describe('LobeDeepSeekAI', () => { expect((runtime as any).baseURL).toBe('https://aihubmix.com'); }); + it('should let Anthropic-compatible runtime normalize /v1 baseURL', async () => { + const { option } = await resolveFirstRouterOption('https://aihubmix.com/v1', 'anthropic'); + const runtime = new LobeDeepSeekAnthropicAI({ apiKey: 'test', baseURL: option.baseURL }); + + expect(option.baseURL).toBe('https://aihubmix.com/v1'); + expect(runtime).toBeInstanceOf(LobeDeepSeekAnthropicAI); + expect((runtime as any).baseURL).toBe('https://aihubmix.com'); + }); + it('should normalize /anthropic/v1/messages before creating an Anthropic SDK runtime', async () => { const { option } = await resolveFirstRouterOption( 'https://api.deepseek.com/anthropic/v1/messages', From d6dae46261e665051d5cce3cb9be3acdae91963c Mon Sep 17 00:00:00 2001 From: Innei Date: Tue, 19 May 2026 15:09:42 +0800 Subject: [PATCH 023/224] =?UTF-8?q?=F0=9F=90=9B=20fix(document):=20reject?= =?UTF-8?q?=20unsupported=20file=20parser=20types=20(#14966)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/file-loaders/src/loadFile.test.ts | 25 +++- packages/file-loaders/src/loadFile.ts | 30 ++-- src/locales/default/chat.ts | 2 + .../services/document/__tests__/index.test.ts | 29 +++- src/server/services/document/index.ts | 25 +++- src/store/file/slices/chat/action.test.ts | 30 ++++ src/store/file/slices/chat/action.ts | 17 ++- .../file/slices/chat/uploadGuard.test.ts | 40 ++++++ src/store/file/slices/chat/uploadGuard.ts | 128 ++++++++++++++++++ 9 files changed, 304 insertions(+), 22 deletions(-) create mode 100644 src/store/file/slices/chat/uploadGuard.test.ts create mode 100644 src/store/file/slices/chat/uploadGuard.ts diff --git a/packages/file-loaders/src/loadFile.test.ts b/packages/file-loaders/src/loadFile.test.ts index 25e3c63a75..cea88c799a 100644 --- a/packages/file-loaders/src/loadFile.test.ts +++ b/packages/file-loaders/src/loadFile.test.ts @@ -1,4 +1,6 @@ // @vitest-environment node +import { mkdtemp, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; import path from 'node:path'; import { describe, expect, it, vi } from 'vitest'; @@ -38,12 +40,25 @@ describe('loadFile', () => { expect(doc.metadata.error).toContain('Failed to access file stats:'); }); - it('falls back to TextLoader for unsupported type and warns', async () => { + it('rejects unsupported binary-like file types before text parsing', async () => { const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}); - const doc = await loadFile(fp('test.epub')); // epub is unsupported in current mapping - expect(warn).toHaveBeenCalled(); - expect(doc.content.length).toBeGreaterThanOrEqual(0); - warn.mockRestore(); + const tempDir = await mkdtemp(path.join(tmpdir(), 'lobe-file-loaders-')); + + try { + const file = path.join(tempDir, 'archive.zip'); + await writeFile(file, Buffer.from([80, 75, 3, 4, 0, 0])); + + await expect(loadFile(file)).rejects.toMatchObject({ + fileType: 'zip', + name: 'UnsupportedFileTypeError', + }); + expect(warn).toHaveBeenCalledWith( + "No specific loader found for file type 'zip'. Rejecting unsupported file type.", + ); + } finally { + warn.mockRestore(); + await rm(tempDir, { force: true, recursive: true }); + } }); it('allows overriding metadata via second parameter', async () => { diff --git a/packages/file-loaders/src/loadFile.ts b/packages/file-loaders/src/loadFile.ts index dae0617e4c..304d45ae9a 100644 --- a/packages/file-loaders/src/loadFile.ts +++ b/packages/file-loaders/src/loadFile.ts @@ -1,5 +1,5 @@ import { stat } from 'node:fs/promises'; -import * as path from 'node:path'; +import path from 'node:path'; import debug from 'debug'; @@ -9,6 +9,16 @@ import { isTextReadableFile } from './utils/isTextReadableFile'; const log = debug('file-loaders:loadFile'); +export class UnsupportedFileTypeError extends Error { + fileType: string; + + constructor(fileType: string, filename: string) { + super(`Unsupported file type '${fileType || 'unknown'}' for file '${filename}'.`); + this.name = 'UnsupportedFileTypeError'; + this.fileType = fileType; + } +} + /** * Determines the file type based on the filename extension. * @param filePath The path to the file. @@ -112,19 +122,19 @@ export const loadFile = async ( const parserType = getFileType(filePath); log('Parser type determined as:', parserType); - // Use lazy loading to get the loader class - this prevents heavy dependencies - // like pdfjs-dist from being loaded until they're actually needed - const loaderType = parserType ?? 'txt'; - const LoaderClass = await getFileLoader(loaderType); - log('Selected loader class:', LoaderClass.name); - - if (!parserType) { + if (!parserType && !fsError) { console.warn( - `No specific loader found for file type '${fileType}'. Using default loader (TextLoader) as fallback.`, + `No specific loader found for file type '${fileType}'. Rejecting unsupported file type.`, ); + throw new UnsupportedFileTypeError(fileType, filename); } - let pages: DocumentPage[] = []; + // Use lazy loading to get the loader class - this prevents heavy dependencies + // like pdfjs-dist from being loaded until they're actually needed + const LoaderClass = await getFileLoader(parserType ?? 'txt'); + log('Selected loader class:', LoaderClass.name); + + let pages: DocumentPage[]; let aggregatedContent = ''; let loaderError: string | undefined; let aggregationError: string | undefined; diff --git a/src/locales/default/chat.ts b/src/locales/default/chat.ts index eb083e0331..a19ac69d27 100644 --- a/src/locales/default/chat.ts +++ b/src/locales/default/chat.ts @@ -846,6 +846,8 @@ export default { 'upload.preview.prepareTasks': 'Preparing chunks...', 'upload.preview.status.pending': 'Preparing to upload...', 'upload.preview.status.processing': 'Processing file...', + 'upload.validation.unsupportedFileType': + 'Unsupported file type: {{files}}. Supported images: JPG, PNG, GIF, WebP. Supported documents include PDF, Word, Excel, PowerPoint, Markdown, text, CSV, JSON, and code files.', 'upload.validation.videoSizeExceeded': 'Video file size must not exceed 20MB. Current file size is {{actualSize}}.', 'viewMode.fullWidth': 'Full Width', diff --git a/src/server/services/document/__tests__/index.test.ts b/src/server/services/document/__tests__/index.test.ts index fa10c35754..d4d9c8519a 100644 --- a/src/server/services/document/__tests__/index.test.ts +++ b/src/server/services/document/__tests__/index.test.ts @@ -1,4 +1,5 @@ import { type LobeChatDatabase } from '@lobechat/database'; +import { TRPCError } from '@trpc/server'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { DocumentModel } from '@/database/models/document'; @@ -14,12 +15,21 @@ vi.mock('../../file'); vi.mock('../history'); vi.mock('@lobechat/file-loaders', () => ({ loadFile: vi.fn(), + UnsupportedFileTypeError: class UnsupportedFileTypeError extends Error { + fileType: string; + + constructor(fileType: string, filename: string) { + super(`Unsupported file type '${fileType || 'unknown'}' for file '${filename}'.`); + this.name = 'UnsupportedFileTypeError'; + this.fileType = fileType; + } + }, })); vi.mock('debug', () => ({ default: () => vi.fn(), })); -const { loadFile } = await import('@lobechat/file-loaders'); +const { loadFile, UnsupportedFileTypeError } = await import('@lobechat/file-loaders'); const createEditorDataWithDiffNode = () => ({ root: { @@ -1067,6 +1077,23 @@ describe('DocumentService', () => { expect(mockCleanup).toHaveBeenCalled(); }); + it('should surface unsupported file types as BAD_REQUEST', async () => { + vi.mocked(loadFile).mockRejectedValue(new UnsupportedFileTypeError('zip', 'archive.zip')); + + try { + await service.parseFile('file-1'); + throw new Error('parseFile should reject unsupported file types'); + } catch (error) { + expect(error).toBeInstanceOf(TRPCError); + expect(error).toMatchObject({ + code: 'BAD_REQUEST', + message: "Unsupported file type 'zip' for file 'archive.zip'.", + }); + } + + expect(mockCleanup).toHaveBeenCalled(); + }); + it('should NOT strip page tags in parseFile (unlike parseDocument)', async () => { const contentWithPageTags = 'First pageSecond page'; diff --git a/src/server/services/document/index.ts b/src/server/services/document/index.ts index b6ff385e03..f31873e67c 100644 --- a/src/server/services/document/index.ts +++ b/src/server/services/document/index.ts @@ -1,7 +1,8 @@ import { type LobeChatDatabase } from '@lobechat/database'; import { type DocumentItem } from '@lobechat/database/schemas'; import { documents, files } from '@lobechat/database/schemas'; -import { loadFile } from '@lobechat/file-loaders'; +import { loadFile, UnsupportedFileTypeError } from '@lobechat/file-loaders'; +import { TRPCError } from '@trpc/server'; import debug from 'debug'; import { and, eq } from 'drizzle-orm'; import isEqual from 'fast-deep-equal'; @@ -29,6 +30,18 @@ import type { const log = debug('lobe-chat:service:document'); +const normalizeParseFileError = (error: unknown) => { + if (error instanceof UnsupportedFileTypeError) { + return new TRPCError({ + cause: error, + code: 'BAD_REQUEST', + message: error.message, + }); + } + + return error; +}; + export class DocumentService { userId: string; private fileModel: FileModel; @@ -433,8 +446,9 @@ export class DocumentService { return document as LobeDocument; } catch (error) { - console.error(`${logPrefix} File parsing failed:`, error); - throw error; + const parseError = normalizeParseFileError(error); + console.error(`${logPrefix} File parsing failed:`, parseError); + throw parseError; } finally { cleanup(); } @@ -486,8 +500,9 @@ export class DocumentService { return document as LobeDocument; } catch (error) { - console.error(`${logPrefix} File parsing failed:`, error); - throw error; + const parseError = normalizeParseFileError(error); + console.error(`${logPrefix} File parsing failed:`, parseError); + throw parseError; } finally { cleanup(); } diff --git a/src/store/file/slices/chat/action.test.ts b/src/store/file/slices/chat/action.test.ts index 8d79e72a9a..504ea22202 100644 --- a/src/store/file/slices/chat/action.test.ts +++ b/src/store/file/slices/chat/action.test.ts @@ -1,3 +1,4 @@ +import { toast } from '@lobehub/ui/base-ui'; import { act, renderHook } from '@testing-library/react'; import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; @@ -5,6 +6,12 @@ import { useFileStore as useStore } from '../../store'; vi.mock('zustand/traditional'); +vi.mock('@lobehub/ui/base-ui', () => ({ + toast: { + error: vi.fn(), + }, +})); + // Mock necessary modules and functions vi.mock('@/components/AntdStaticMethods', () => ({ notification: { @@ -47,4 +54,27 @@ describe('useFileStore:chat', () => { expect(result.current.chatUploadFileList).toEqual([]); }); + + it('uploadChatFiles should reject unsupported files before upload', async () => { + const { result } = renderHook(() => useStore()); + const uploadWithProgress = vi.fn(); + + act(() => { + useStore.setState({ + chatUploadFileList: [], + uploadWithProgress: uploadWithProgress as any, + }); + }); + + await act(async () => { + await result.current.uploadChatFiles([ + new File([''], 'icon.svg', { type: 'image/svg+xml' }), + new File(['zip'], 'archive.zip', { type: 'application/zip' }), + ]); + }); + + expect(uploadWithProgress).not.toHaveBeenCalled(); + expect(result.current.chatUploadFileList).toEqual([]); + expect(toast.error).toHaveBeenCalledWith(expect.any(String)); + }); }); diff --git a/src/store/file/slices/chat/action.ts b/src/store/file/slices/chat/action.ts index 3834350af7..fd699373bb 100644 --- a/src/store/file/slices/chat/action.ts +++ b/src/store/file/slices/chat/action.ts @@ -1,5 +1,6 @@ import { type ChatContextContent } from '@lobechat/types'; import { COMPRESSIBLE_IMAGE_TYPES, compressImageFile } from '@lobechat/utils/compressImage'; +import { toast } from '@lobehub/ui/base-ui'; import { Buffer } from 'buffer.js'; import { t } from 'i18next'; @@ -18,6 +19,7 @@ import { sleep } from '@/utils/sleep'; import { setNamespace } from '@/utils/storeDebug'; import { type FileStore } from '../../store'; +import { filterSupportedChatUploadFiles } from './uploadGuard'; const n = setNamespace('chat'); @@ -111,9 +113,22 @@ export class FileActionImpl { const { dispatchChatUploadFileList } = this.#get(); // 0. skip file in blacklist const filteredFiles = rawFiles.filter((file) => !FILE_UPLOAD_BLACKLIST.includes(file.name)); + const { supportedFiles, unsupportedFiles } = filterSupportedChatUploadFiles(filteredFiles); + + if (unsupportedFiles.length > 0) { + toast.error( + t('upload.validation.unsupportedFileType', { + files: unsupportedFiles.map((file) => file.name).join(', '), + ns: 'chat', + }), + ); + } + + if (supportedFiles.length === 0) return; + // 1. compress images and add files with base64 const files = await Promise.all( - filteredFiles.map((file) => + supportedFiles.map((file) => COMPRESSIBLE_IMAGE_TYPES.has(file.type) ? compressImageFile(file) : file, ), ); diff --git a/src/store/file/slices/chat/uploadGuard.test.ts b/src/store/file/slices/chat/uploadGuard.test.ts new file mode 100644 index 0000000000..92cb7b462e --- /dev/null +++ b/src/store/file/slices/chat/uploadGuard.test.ts @@ -0,0 +1,40 @@ +import { describe, expect, it } from 'vitest'; + +import { isSupportedChatUploadFile } from './uploadGuard'; + +describe('isSupportedChatUploadFile', () => { + it('accepts supported chat image formats', () => { + expect(isSupportedChatUploadFile(new File(['image'], 'image.png', { type: 'image/png' }))).toBe( + true, + ); + expect( + isSupportedChatUploadFile(new File(['image'], 'image.webp', { type: 'image/webp' })), + ).toBe(true); + }); + + it('rejects unsupported image formats before upload', () => { + expect( + isSupportedChatUploadFile(new File([''], 'icon.svg', { type: 'image/svg+xml' })), + ).toBe(false); + expect( + isSupportedChatUploadFile(new File(['image'], 'photo.heic', { type: 'image/heic' })), + ).toBe(false); + }); + + it('accepts supported document formats', () => { + expect( + isSupportedChatUploadFile( + new File(['document'], 'document.pdf', { type: 'application/pdf' }), + ), + ).toBe(true); + expect( + isSupportedChatUploadFile(new File(['{}'], 'data.json', { type: 'application/json' })), + ).toBe(true); + }); + + it('rejects unsupported archive formats before upload', () => { + expect( + isSupportedChatUploadFile(new File(['zip'], 'archive.zip', { type: 'application/zip' })), + ).toBe(false); + }); +}); diff --git a/src/store/file/slices/chat/uploadGuard.ts b/src/store/file/slices/chat/uploadGuard.ts new file mode 100644 index 0000000000..c110d74290 --- /dev/null +++ b/src/store/file/slices/chat/uploadGuard.ts @@ -0,0 +1,128 @@ +const SUPPORTED_CHAT_IMAGE_TYPES = new Set([ + 'image/gif', + 'image/jpeg', + 'image/jpg', + 'image/png', + 'image/webp', +]); + +const SUPPORTED_CHAT_IMAGE_EXTENSIONS = new Set(['gif', 'jpeg', 'jpg', 'png', 'webp']); + +const SUPPORTED_CHAT_DOCUMENT_EXTENSIONS = new Set([ + 'bat', + 'bash', + 'c', + 'cfg', + 'conf', + 'cpp', + 'cs', + 'csv', + 'cts', + 'dart', + 'db', + 'diff', + 'doc', + 'docx', + 'env', + 'go', + 'gradle', + 'groovy', + 'h', + 'hpp', + 'htm', + 'html', + 'ini', + 'java', + 'js', + 'json', + 'json5', + 'jsonc', + 'jsx', + 'kt', + 'less', + 'log', + 'lua', + 'markdown', + 'md', + 'mdx', + 'mjs', + 'mts', + 'patch', + 'pdf', + 'php', + 'properties', + 'pptx', + 'ps1', + 'py', + 'rb', + 'rs', + 'scala', + 'scss', + 'sh', + 'sql', + 'svelte', + 'svg', + 'swift', + 'toml', + 'ts', + 'tsx', + 'txt', + 'vue', + 'xls', + 'xlsx', + 'xml', + 'yaml', + 'yml', +]); + +const SUPPORTED_CHAT_DOCUMENT_MIME_TYPES = new Set([ + 'application/json', + 'application/pdf', + 'application/vnd.ms-excel', + 'application/vnd.ms-powerpoint', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/msword', + 'text/csv', + 'text/markdown', + 'text/plain', +]); + +const getExtension = (filename: string) => filename.split('.').pop()?.toLowerCase() || ''; + +export const isSupportedChatUploadFile = (file: File) => { + const fileType = file.type.toLowerCase(); + const extension = getExtension(file.name); + + if (fileType.startsWith('image/')) { + return SUPPORTED_CHAT_IMAGE_TYPES.has(fileType); + } + + if (!fileType && SUPPORTED_CHAT_IMAGE_EXTENSIONS.has(extension)) return true; + + if (fileType.startsWith('video/')) return true; + + if (fileType.startsWith('audio/')) return false; + + if (extension) return SUPPORTED_CHAT_DOCUMENT_EXTENSIONS.has(extension); + + if (fileType.startsWith('text/')) return true; + + return SUPPORTED_CHAT_DOCUMENT_MIME_TYPES.has(fileType); +}; + +export const filterSupportedChatUploadFiles = (files: File[]) => { + const supportedFiles: File[] = []; + const unsupportedFiles: File[] = []; + + for (const file of files) { + if (isSupportedChatUploadFile(file)) { + supportedFiles.push(file); + } else { + unsupportedFiles.push(file); + } + } + + return { supportedFiles, unsupportedFiles }; +}; From cf16737668cf4b110546b9d8b4d0fe6d0057c3da Mon Sep 17 00:00:00 2001 From: Arvin Xu Date: Tue, 19 May 2026 15:15:06 +0800 Subject: [PATCH 024/224] =?UTF-8?q?=F0=9F=90=9B=20fix(local-file-shell):?= =?UTF-8?q?=20auto-enable=20hidden=20matching=20for=20dot-prefixed=20patte?= =?UTF-8?q?rns=20(#14965)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🐛 fix(local-file-shell): auto-enable hidden matching for dot-prefixed glob/grep patterns When callers passed patterns like `.github/workflows/*.yml` to `globLocalFiles`, `searchLocalFiles`, or `grepContent`, the underlying engines (`fast-glob` with `dot: false` and `rg` without `--hidden`) silently skipped dot-prefixed directories and returned zero results — making it look like the file didn't exist. Detect when the pattern explicitly references a hidden segment (`.foo/...` or `foo/.bar/...`, excluding `./` and `../` relative indicators) and auto-enable hidden matching. A `hint` field on the result explains the auto-adjustment so the agent doesn't treat an empty match as failure. The same fix is applied to the desktop `contentSearch` rg/ag argument builder. Co-authored-by: Claude Opus 4.7 (1M context) --- .../contentSearch/__tests__/base.test.ts | 35 ++++++++++ .../src/main/modules/contentSearch/base.ts | 8 +++ .../src/file/__tests__/file.test.ts | 67 +++++++++++++++++++ packages/local-file-shell/src/file/glob.ts | 16 ++++- packages/local-file-shell/src/file/grep.ts | 21 ++++-- .../src/file/hasHiddenSegment.ts | 13 ++++ packages/local-file-shell/src/file/search.ts | 5 +- packages/local-file-shell/src/types.ts | 4 ++ 8 files changed, 163 insertions(+), 6 deletions(-) create mode 100644 packages/local-file-shell/src/file/hasHiddenSegment.ts diff --git a/apps/desktop/src/main/modules/contentSearch/__tests__/base.test.ts b/apps/desktop/src/main/modules/contentSearch/__tests__/base.test.ts index 8c5401e7e0..24562f089a 100644 --- a/apps/desktop/src/main/modules/contentSearch/__tests__/base.test.ts +++ b/apps/desktop/src/main/modules/contentSearch/__tests__/base.test.ts @@ -143,6 +143,30 @@ describe('BaseContentSearch', () => { expect(args).toContain('*.ts'); }); + it('should add --hidden when glob references a dot-prefixed segment', () => { + const params: GrepContentParams = { + glob: '.github/workflows/*.yml', + pattern: 'jobs', + }; + + const args = contentSearch.testBuildGrepArgs('rg', params); + + expect(args).toContain('--hidden'); + expect(args).toContain('-g'); + expect(args).toContain('.github/workflows/*.yml'); + }); + + it('should not add --hidden for a normal glob', () => { + const params: GrepContentParams = { + glob: '*.ts', + pattern: 'test', + }; + + const args = contentSearch.testBuildGrepArgs('rg', params); + + expect(args).not.toContain('--hidden'); + }); + it('should build rg args with type filter', () => { const params: GrepContentParams = { pattern: 'test', @@ -205,6 +229,17 @@ describe('BaseContentSearch', () => { expect(args).toContain('*.tsx'); }); + it('should add --hidden when glob references a dot-prefixed segment', () => { + const params: GrepContentParams = { + glob: '.github/workflows/*.yml', + pattern: 'jobs', + }; + + const args = contentSearch.testBuildGrepArgs('ag', params); + + expect(args).toContain('--hidden'); + }); + it('should build ag args for count mode', () => { const params: GrepContentParams = { output_mode: 'count', diff --git a/apps/desktop/src/main/modules/contentSearch/base.ts b/apps/desktop/src/main/modules/contentSearch/base.ts index cde5524cad..00326f76c5 100644 --- a/apps/desktop/src/main/modules/contentSearch/base.ts +++ b/apps/desktop/src/main/modules/contentSearch/base.ts @@ -65,6 +65,12 @@ export abstract class BaseContentSearch { const { pattern, output_mode = 'files_with_matches' } = params; const args: string[] = []; + // When the caller's glob references a dot-prefixed segment (e.g. + // `.github/workflows/*.yml`), rg and ag both default to skipping hidden + // paths and would silently return zero results. `.git/` is still excluded + // explicitly below. + const wantsHidden = !!params.glob && /(?:^|\/)\.[^./]/.test(params.glob); + switch (tool) { case 'rg': { // ripgrep arguments @@ -74,6 +80,7 @@ export abstract class BaseContentSearch { if (params['-B']) args.push('-B', String(params['-B'])); if (params['-C']) args.push('-C', String(params['-C'])); if (params.multiline) args.push('-U'); + if (wantsHidden) args.push('--hidden'); if (params.glob) args.push('-g', params.glob); if (params.type) args.push('-t', params.type); @@ -100,6 +107,7 @@ export abstract class BaseContentSearch { if (params['-A']) args.push('-A', String(params['-A'])); if (params['-B']) args.push('-B', String(params['-B'])); if (params['-C']) args.push('-C', String(params['-C'])); + if (wantsHidden) args.push('--hidden'); if (params.glob) args.push('-G', params.glob); // Output mode diff --git a/packages/local-file-shell/src/file/__tests__/file.test.ts b/packages/local-file-shell/src/file/__tests__/file.test.ts index 6154cf4c02..602b8e90af 100644 --- a/packages/local-file-shell/src/file/__tests__/file.test.ts +++ b/packages/local-file-shell/src/file/__tests__/file.test.ts @@ -515,6 +515,38 @@ describe('file operations', () => { expect(result.files).toEqual(['src.ts']); }); + + it('should auto-enable hidden matching when pattern contains a dot-prefixed segment', async () => { + await mkdir(path.join(tmpDir, '.github', 'workflows'), { recursive: true }); + await writeFile(path.join(tmpDir, '.github', 'workflows', 'ci.yml'), 'name: ci'); + await writeFile(path.join(tmpDir, '.github', 'workflows', 'release.yaml'), 'name: release'); + + const result = await globLocalFiles({ + cwd: tmpDir, + pattern: '.github/workflows/*.{yml,yaml}', + }); + + expect(result.files).toHaveLength(2); + expect(result.files).toContain('.github/workflows/ci.yml'); + expect(result.files).toContain('.github/workflows/release.yaml'); + expect(result.hint).toContain('hidden'); + }); + + it('should not return a hint when pattern has no dot-prefixed segment', async () => { + await writeFile(path.join(tmpDir, 'a.ts'), 'a'); + + const result = await globLocalFiles({ cwd: tmpDir, pattern: '*.ts' }); + + expect(result.hint).toBeUndefined(); + }); + + it('should treat ./ and ../ as relative path indicators, not hidden segments', async () => { + await writeFile(path.join(tmpDir, 'a.ts'), 'a'); + + const result = await globLocalFiles({ cwd: tmpDir, pattern: './*.ts' }); + + expect(result.hint).toBeUndefined(); + }); }); // ─── grepContent ─── @@ -535,6 +567,29 @@ describe('file operations', () => { const result = await grepContent({ cwd: tmpDir, pattern: 'xyz_not_found' }); expect(result.matches).toEqual([]); }); + + it('should return a hidden-matching hint when filePattern contains a dot-prefixed segment', async () => { + // The hint is set regardless of whether rg is installed on the host — + // it signals to the agent why we're auto-enabling --hidden so a zero + // match doesn't look like a silent failure. + const result = await grepContent({ + cwd: tmpDir, + filePattern: '.github/workflows/*.yml', + pattern: 'jobs', + }); + + expect(result.hint).toContain('hidden'); + }); + + it('should not return a hint for a normal filePattern', async () => { + const result = await grepContent({ + cwd: tmpDir, + filePattern: '*.ts', + pattern: 'jobs', + }); + + expect(result.hint).toBeUndefined(); + }); }); // ─── searchLocalFiles ─── @@ -573,5 +628,17 @@ describe('file operations', () => { expect(result).toEqual([]); }); + + it('should find dot-prefixed files when keywords starts with a dot', async () => { + await writeFile(path.join(tmpDir, '.env'), 'A=1'); + await writeFile(path.join(tmpDir, '.envrc'), 'export A=1'); + await writeFile(path.join(tmpDir, 'env.txt'), 'unrelated'); + + const result = await searchLocalFiles({ directory: tmpDir, keywords: '.env' }); + + const names = result.map((r) => r.name); + expect(names).toContain('.env'); + expect(names).toContain('.envrc'); + }); }); }); diff --git a/packages/local-file-shell/src/file/glob.ts b/packages/local-file-shell/src/file/glob.ts index d09ad9990d..13fa316b2e 100644 --- a/packages/local-file-shell/src/file/glob.ts +++ b/packages/local-file-shell/src/file/glob.ts @@ -2,14 +2,28 @@ import fg from 'fast-glob'; import type { GlobFilesParams, GlobFilesResult } from '../types'; import { expandTilde } from './expandTilde'; +import { hasHiddenSegment } from './hasHiddenSegment'; export async function globLocalFiles({ pattern, cwd }: GlobFilesParams): Promise { try { + // When the pattern explicitly references a dot-prefixed segment (e.g. + // `.github/workflows/*.yml`), the caller clearly wants to traverse a + // hidden directory — auto-enable hidden matching so it doesn't silently + // return zero results. + const wantsHidden = hasHiddenSegment(pattern); + const files = await fg(pattern, { cwd: expandTilde(cwd) || process.cwd(), - dot: false, + dot: wantsHidden, ignore: ['**/node_modules/**', '**/.git/**'], }); + + if (wantsHidden) { + return { + files, + hint: `Auto-enabled hidden-file matching because pattern contains a dot-prefixed segment.`, + }; + } return { files }; } catch (error) { return { error: (error as Error).message, files: [] }; diff --git a/packages/local-file-shell/src/file/grep.ts b/packages/local-file-shell/src/file/grep.ts index 510b3c0db7..5256a5db7e 100644 --- a/packages/local-file-shell/src/file/grep.ts +++ b/packages/local-file-shell/src/file/grep.ts @@ -2,14 +2,27 @@ import { spawn } from 'node:child_process'; import type { GrepContentParams, GrepContentResult } from '../types'; import { expandTilde } from './expandTilde'; +import { hasHiddenSegment } from './hasHiddenSegment'; export async function grepContent({ pattern, cwd, filePattern, }: GrepContentParams): Promise { + // When the filePattern explicitly references a dot-prefixed segment, the + // caller wants to scan inside a hidden directory — pass `--hidden` to rg so + // it doesn't silently skip these paths. We still rely on rg's built-in + // `.git/` exclusion via .gitignore semantics, plus add an explicit guard. + const wantsHidden = hasHiddenSegment(filePattern); + const hint = wantsHidden + ? `Auto-enabled hidden-file matching because filePattern contains a dot-prefixed segment.` + : undefined; + return new Promise((resolve) => { const args = ['--json', '-n']; + if (wantsHidden) { + args.push('--hidden', '--glob', '!**/.git/**'); + } if (filePattern) args.push('--glob', filePattern); args.push(pattern); @@ -25,7 +38,7 @@ export async function grepContent({ child.on('close', (code) => { if (code !== 0 && code !== 1) { - resolve({ matches: [], success: false }); + resolve({ hint, matches: [], success: false }); return; } @@ -42,14 +55,14 @@ export async function grepContent({ }) .filter(Boolean); - resolve({ matches, success: true }); + resolve({ hint, matches, success: true }); } catch { - resolve({ matches: [], success: true }); + resolve({ hint, matches: [], success: true }); } }); child.on('error', () => { - resolve({ matches: [], success: false }); + resolve({ hint, matches: [], success: false }); }); }); } diff --git a/packages/local-file-shell/src/file/hasHiddenSegment.ts b/packages/local-file-shell/src/file/hasHiddenSegment.ts new file mode 100644 index 0000000000..a03778d6ed --- /dev/null +++ b/packages/local-file-shell/src/file/hasHiddenSegment.ts @@ -0,0 +1,13 @@ +/** + * Detect whether a glob pattern (or path) contains a dot-prefixed segment such + * as `.github`, `.husky`, or `foo/.config`. Used to auto-enable hidden-file + * matching when the caller's intent is clearly to traverse a hidden directory + * — otherwise `fast-glob` (`dot: false`) and `ripgrep` (no `--hidden`) silently + * return zero results. + * + * Skips `.` and `..` (relative path indicators), which are not hidden segments. + */ +export const HIDDEN_SEGMENT_RE = /(?:^|\/)\.[^./]/; + +export const hasHiddenSegment = (pattern: string | undefined): boolean => + !!pattern && HIDDEN_SEGMENT_RE.test(pattern); diff --git a/packages/local-file-shell/src/file/search.ts b/packages/local-file-shell/src/file/search.ts index 93c2c8aa4b..d29a137a2a 100644 --- a/packages/local-file-shell/src/file/search.ts +++ b/packages/local-file-shell/src/file/search.ts @@ -14,9 +14,12 @@ export async function searchLocalFiles({ }: SearchFilesParams): Promise { try { const cwd = expandTilde(directory) || process.cwd(); + // If the caller is searching for a dot-prefixed name (e.g. `.env`, `.github`), + // auto-enable hidden matching so the file/directory is actually reachable. + const wantsHidden = keywords.startsWith('.'); const files = await fg(`**/*${keywords}*`, { cwd, - dot: false, + dot: wantsHidden, ignore: ['**/node_modules/**', '**/.git/**'], }); diff --git a/packages/local-file-shell/src/types.ts b/packages/local-file-shell/src/types.ts index dba34ba323..b748948571 100644 --- a/packages/local-file-shell/src/types.ts +++ b/packages/local-file-shell/src/types.ts @@ -124,6 +124,8 @@ export interface GlobFilesParams { export interface GlobFilesResult { error?: string; files: string[]; + /** Diagnostic note returned when the engine had to adjust behavior, e.g. auto-enabling hidden-file matching. */ + hint?: string; } export interface SearchFilesParams { @@ -173,6 +175,8 @@ export interface GrepContentParams { export interface GrepContentResult { error?: string; + /** Diagnostic note returned when the engine had to adjust behavior, e.g. auto-enabling hidden-file matching. */ + hint?: string; matches: any[]; success: boolean; } From 03c79bfb62c471c3068dc7005651518ecb4f9674 Mon Sep 17 00:00:00 2001 From: Arvin Xu Date: Tue, 19 May 2026 15:16:28 +0800 Subject: [PATCH 025/224] =?UTF-8?q?=F0=9F=90=9B=20fix:=20surface=20stderr?= =?UTF-8?q?=20in=20errorOutput=20fallback=20and=20add=20`UNKNOWN=5FEXEC=5F?= =?UTF-8?q?ERROR`=20prefix=20(#14964)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: surface stderr in errorOutput fallback and add UNKNOWN_EXEC_ERROR prefix When a shell command fails with a non-zero exit code (e.g. git commit with nothing to commit), the runner puts the error message in stderr but does not set the error field. This caused errorOutput() to fall through to the hardcoded 'Tool execution failed' string, losing the actual error. Changes: - errorOutput() now checks state.stderr and state.error before the final fallback, so real error messages from stderr are surfaced - Final fallback changed from 'Tool execution failed' to '[UNKNOWN_EXEC_ERROR] Tool execution failed' for easier grepping - Same prefix applied to toResult() in the executor for consistency * fix: pass stderr/stdout into errorOutput state for runCommand failures runCommand() called errorOutput() with a state that only contained { error, isBackground, success }, missing result.result.stderr. Since normalizeResult() stores the shell stderr under result.result.stderr (not result.error), the state.stderr fallback in errorOutput() was never reached for non-zero exit commands like 'git commit' with nothing to commit. --- .../src/client/executor/index.ts | 3 ++- packages/tool-runtime/src/ComputerRuntime.ts | 14 +++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/builtin-tool-local-system/src/client/executor/index.ts b/packages/builtin-tool-local-system/src/client/executor/index.ts index 9fd8fafa47..a38a5e8ba4 100644 --- a/packages/builtin-tool-local-system/src/client/executor/index.ts +++ b/packages/builtin-tool-local-system/src/client/executor/index.ts @@ -66,7 +66,8 @@ class LocalSystemExecutor extends BaseExecutor { }): BuiltinToolResult { const errorMessage = typeof output.error?.message === 'string' ? output.error.message : undefined; - const safeContent = output.content || errorMessage || 'Tool execution failed'; + const safeContent = + output.content || errorMessage || '[UNKNOWN_EXEC_ERROR] Tool execution failed'; if (!output.success) { return { diff --git a/packages/tool-runtime/src/ComputerRuntime.ts b/packages/tool-runtime/src/ComputerRuntime.ts index b750af98dd..34e85eb31e 100644 --- a/packages/tool-runtime/src/ComputerRuntime.ts +++ b/packages/tool-runtime/src/ComputerRuntime.ts @@ -298,7 +298,10 @@ export abstract class ComputerRuntime { if (!result.success) { return this.errorOutput(result, { error: result.error?.message, + exitCode: result.result?.exitCode ?? result.result?.exit_code, isBackground: args.background || false, + stderr: result.result?.stderr, + stdout: result.result?.stdout, success: false, }); } @@ -468,10 +471,19 @@ export abstract class ComputerRuntime { // error object, JSON.stringify(undefined) returns the value `undefined` // (not the string "undefined"), which collapsed downstream into an empty // tool-message content while pluginState still got persisted. + // + // Priority chain: + // 1. result.error.message (explicit error from service layer) + // 2. JSON.stringify(result.error) (non-Error error objects) + // 3. state.stderr (e.g. git commit failure — exit ≠ 0, error in stderr) + // 4. state.error (runtime-level error message) + // 5. [UNKNOWN_EXEC_ERROR] Tool execution failed (last-resort fallback) const errorText = result.error?.message || (result.error !== undefined ? JSON.stringify(result.error) : undefined) || - 'Tool execution failed'; + (typeof state?.stderr === 'string' ? state.stderr : undefined) || + (typeof state?.error === 'string' ? state.error : undefined) || + '[UNKNOWN_EXEC_ERROR] Tool execution failed'; return { content: errorText, state, From e5c9a1a054560c1a9288cc89914c98d36ad2b0f9 Mon Sep 17 00:00:00 2001 From: LiJian Date: Tue, 19 May 2026 16:36:35 +0800 Subject: [PATCH 026/224] =?UTF-8?q?=F0=9F=90=9B=20fix(hetero-agent):=20fir?= =?UTF-8?q?e=20IM=20bot-callback=20webhook=20from=20heteroFinish=20(#14968?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🐛 fix(hetero-agent): fire IM bot-callback completion webhook from heteroFinish When an IM bot triggers a heterogeneous agent (Cloud Claude Code / Codex), the execAgent hetero early-exit path discards all registered hooks, so the `bot-completion` webhook registered by AgentBridgeService is never fired and the IM user never receives a response. Fix: - Persist the `onComplete` webhook config into `topic.metadata.runningOperation.completionWebhook` when the hetero operation starts, alongside the existing `operationId` / `assistantMessageId`. - In `heteroFinish`, read the stored webhook and deliver it via the existing `deliverWebhook` helper (export it from HookDispatcher), which honours QStash vs fetch delivery and resolves relative URLs with APP_URL. - Add `completionWebhook` to the `runningOperation` Zod schema in the topic tRPC router and to the `ChatTopicMetadata` TypeScript interface. Co-Authored-By: Claude Sonnet 4.6 * ♻️ refactor(hetero-finish): fix idempotency + clear runningOperation + import AgentHookWebhook Three follow-up fixes from self-review of the completionWebhook change: 1. Idempotency — heteroFinish can be called more than once (signal path sends cancelled, normal exit sends the real result, transport retries). Now reads completionWebhook and clears runningOperation in the same block before delivery, so a second call finds runningOperation already null and skips the webhook. 2. Clear runningOperation — the normal LLM path clears this field in RuntimeExecutors after completion to prevent page-reload reconnects. The hetero path never did. Now cleared unconditionally in heteroFinish. 3. Payload order — align with HookDispatcher convention: spread hook.webhook.body last so it can override base fields if needed. (Was: `{ ...body, hookId, hookType }`. Now: `{ hookId, hookType, ...body }`) 4. Import AgentHookWebhook from hooks/types instead of inlining the type. Co-Authored-By: Claude Sonnet 4.6 * 🐛 fix(hetero-finish): skip completionWebhook delivery on cancelled result heteroFinish can be called twice: once with result=cancelled (from termination signal) and once with result=success (from normal process exit). The previous guard cleared runningOperation before delivering, so the first call (cancelled) would fire the webhook with truncated content, and the second call (success) would find runningOperation=null and skip delivery — leaving the IM user with a partial response. Fix: skip webhook delivery when result=cancelled. The subsequent success or error call delivers the complete content. Transport-level retries of the same result are accepted; BotCallbackService reads the latest DB content on each invocation so duplicate deliveries are idempotent. Co-Authored-By: Claude Sonnet 4.6 * 🐛 fix(hetero-finish): include lastAssistantContent and reason in completionWebhook payload BotCallbackService.handleCompletion checks lastAssistantContent before sending — without it the handler logs "no lastAssistantContent, skipping" and returns, leaving the IM user with no reply despite the fix reaching the delivery point. Changes: - Add messageModel field to HeterogeneousAgentService (reused by HeterogeneousPersistenceHandler so no extra DB connection) - Read assistantMessageId from runningOperation before clearing it - Fetch the final assistant message content via messageModel.findById - Include lastAssistantContent, operationId, and reason (mapped from hetero result: success→done, error→error) in the webhook payload - Include errorMessage/errorType on error result so handleCompletion can render the agent error card - Spread completionWebhook.body last, matching HookDispatcher convention Co-Authored-By: Claude Sonnet 4.6 * 🐛 fix(hetero-finish): don't clear runningOperation on cancelled result When heteroFinish is called with result=cancelled (signal path) followed by result=success (normal exit), the previous code cleared runningOperation on the cancelled call. The subsequent success call then found runningOperation already null, couldn't read completionWebhook or assistantMessageId, and skipped delivery — leaving the IM user with no final reply. Fix: early-return on result=cancelled without touching runningOperation, so the subsequent success/error call still finds the stored webhook config. runningOperation is only cleared on the delivering call (success/error). Co-Authored-By: Claude Sonnet 4.6 --------- Co-authored-by: Claude Sonnet 4.6 --- packages/types/src/topic/topic.ts | 11 ++++ src/server/routers/lambda/topic.ts | 7 ++ .../agentRuntime/hooks/HookDispatcher.ts | 2 +- src/server/services/aiAgent/index.ts | 3 + .../services/heterogeneousAgent/index.ts | 65 ++++++++++++++++++- 5 files changed, 86 insertions(+), 2 deletions(-) diff --git a/packages/types/src/topic/topic.ts b/packages/types/src/topic/topic.ts index ec048672e9..963eda1d1d 100644 --- a/packages/types/src/topic/topic.ts +++ b/packages/types/src/topic/topic.ts @@ -144,6 +144,17 @@ export interface ChatTopicMetadata { */ runningOperation?: { assistantMessageId: string; + /** + * Webhook to fire when the operation completes. + * Populated by the IM bot path so heterogeneous agents (Claude Code / Codex) + * can call back to the bot-callback endpoint even though they bypass the + * normal hook registration flow. + */ + completionWebhook?: { + body?: Record; + delivery?: 'fetch' | 'qstash'; + url: string; + }; operationId: string; scope?: string; threadId?: string | null; diff --git a/src/server/routers/lambda/topic.ts b/src/server/routers/lambda/topic.ts index 47e57530ad..c5f5f96cbd 100644 --- a/src/server/routers/lambda/topic.ts +++ b/src/server/routers/lambda/topic.ts @@ -625,6 +625,13 @@ export const topicRouter = router({ runningOperation: z .object({ assistantMessageId: z.string(), + completionWebhook: z + .object({ + body: z.record(z.unknown()).optional(), + delivery: z.enum(['fetch', 'qstash']).optional(), + url: z.string(), + }) + .optional(), operationId: z.string(), scope: z.string().optional(), threadId: z.string().nullable().optional(), diff --git a/src/server/services/agentRuntime/hooks/HookDispatcher.ts b/src/server/services/agentRuntime/hooks/HookDispatcher.ts index 7eae3a8b86..5d2e8a3ee3 100644 --- a/src/server/services/agentRuntime/hooks/HookDispatcher.ts +++ b/src/server/services/agentRuntime/hooks/HookDispatcher.ts @@ -18,7 +18,7 @@ const log = debug('lobe-server:hook-dispatcher'); /** * Delivers a webhook via HTTP POST (fetch or QStash) */ -async function deliverWebhook( +export async function deliverWebhook( webhook: AgentHookWebhook, payload: Record, ): Promise { diff --git a/src/server/services/aiAgent/index.ts b/src/server/services/aiAgent/index.ts index 1131d0fb35..c250121726 100644 --- a/src/server/services/aiAgent/index.ts +++ b/src/server/services/aiAgent/index.ts @@ -738,9 +738,12 @@ export class AiAgentService { }; // Seed topic.metadata.runningOperation so heteroIngest can validate the operation. + // completionWebhook is stored so heteroFinish can call back to the IM bot-callback + // endpoint even though the hetero path bypasses the normal hook registration flow. await this.topicModel.updateMetadata(topicId, { runningOperation: { assistantMessageId: assistantMsg.id, + completionWebhook: hooks?.find((h) => h.type === 'onComplete')?.webhook, operationId, scope: appContext?.scope ?? undefined, threadId: appContext?.threadId ?? undefined, diff --git a/src/server/services/heterogeneousAgent/index.ts b/src/server/services/heterogeneousAgent/index.ts index c1caaaf552..bc846ddb97 100644 --- a/src/server/services/heterogeneousAgent/index.ts +++ b/src/server/services/heterogeneousAgent/index.ts @@ -7,6 +7,8 @@ import { ThreadModel } from '@/database/models/thread'; import { TopicModel } from '@/database/models/topic'; import { createStreamEventManager } from '@/server/modules/AgentRuntime/factory'; import { type IStreamEventManager } from '@/server/modules/AgentRuntime/types'; +import { deliverWebhook } from '@/server/services/agentRuntime/hooks/HookDispatcher'; +import type { AgentHookWebhook } from '@/server/services/agentRuntime/hooks/types'; import { HeterogeneousPersistenceHandler } from './HeterogeneousPersistenceHandler'; @@ -59,6 +61,7 @@ export interface HeterogeneousAgentServiceOptions { */ export class HeterogeneousAgentService { private readonly db: LobeChatDatabase; + private readonly messageModel: MessageModel; private readonly persistenceHandler: HeterogeneousPersistenceHandler; private readonly streamEventManager: IStreamEventManager; private readonly topicModel: TopicModel; @@ -71,12 +74,13 @@ export class HeterogeneousAgentService { ) { this.db = db; this.userId = userId; + this.messageModel = new MessageModel(db, userId); this.streamEventManager = options.streamEventManager ?? createStreamEventManager(); this.topicModel = options.topicModel ?? new TopicModel(db, userId); this.persistenceHandler = options.persistenceHandler ?? new HeterogeneousPersistenceHandler({ - messageModel: new MessageModel(db, userId), + messageModel: this.messageModel, threadModel: new ThreadModel(db, userId), topicModel: this.topicModel, }); @@ -150,6 +154,65 @@ export class HeterogeneousAgentService { stepIndex: 0, type: 'agent_runtime_end', }); + + // Fire the IM bot-callback completion webhook if one was registered. + // The hetero path bypasses the normal AgentHook registration flow, so + // we persist the webhook config in topic.metadata.runningOperation and + // deliver it here instead. + // + // Skip on `cancelled` — heteroFinish may be called twice: first with + // result=cancelled (termination signal) then with result=success/error + // (normal process exit). We must NOT clear runningOperation on cancelled + // so the subsequent success/error call can still find completionWebhook + // and assistantMessageId. runningOperation is only cleared on the + // delivering call (success/error) so reconnect doesn't retrigger after + // completion — mirrors RuntimeExecutors cleanup for the normal LLM path. + // Transport-level retries of the same result are accepted: BotCallbackService + // reads the latest DB content each time, so duplicates are idempotent. + if (result === 'cancelled') return; + + let completionWebhook: AgentHookWebhook | undefined; + let assistantMessageId: string | undefined; + try { + const topic = await this.topicModel.findById(topicId); + completionWebhook = topic?.metadata?.runningOperation?.completionWebhook; + assistantMessageId = topic?.metadata?.runningOperation?.assistantMessageId; + await this.topicModel.updateMetadata(topicId, { runningOperation: null }); + } catch (err) { + log('heteroFinish: failed to clear runningOperation (non-fatal): %O', err); + } + + if (completionWebhook?.url) { + try { + // Read the final assistant message content so BotCallbackService.handleCompletion + // has lastAssistantContent to render. Without it the handler skips delivery. + let lastAssistantContent: string | undefined; + if (assistantMessageId) { + const msg = await this.messageModel.findById(assistantMessageId); + lastAssistantContent = msg?.content as string | undefined; + } + + // Map hetero result → reason expected by handleCompletion + const reason = result === 'success' ? 'done' : 'error'; + + await deliverWebhook(completionWebhook, { + // Dynamic completion fields (event-like payload) + ...(error ? { errorMessage: error.message, errorType: error.type } : {}), + hookId: 'bot-completion', + hookType: 'onComplete', + lastAssistantContent, + operationId, + reason, + // Static IM context stored at hook registration time — spread last so + // platform fields (applicationId, platformThreadId, type, userPrompt) + // are authoritative, matching HookDispatcher's { ...event, ...body } order. + ...completionWebhook.body, + }); + log('heteroFinish: completionWebhook delivered for op=%s result=%s', operationId, result); + } catch (err) { + log('heteroFinish: completionWebhook delivery failed (non-fatal): %O', err); + } + } } /** From 1285f601df348c35294c3e7f7088f2097a92fdad Mon Sep 17 00:00:00 2001 From: YuTengjing Date: Tue, 19 May 2026 16:58:20 +0800 Subject: [PATCH 027/224] =?UTF-8?q?=F0=9F=94=A8=20chore:=20skip=20branded?= =?UTF-8?q?=20provider=20llm=20retries=20(#14975)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modules/AgentRuntime/RuntimeExecutors.ts | 19 +++++++++--- .../__tests__/RuntimeExecutors.test.ts | 31 +++++++++++++++++++ 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/server/modules/AgentRuntime/RuntimeExecutors.ts b/src/server/modules/AgentRuntime/RuntimeExecutors.ts index b27ef7e1df..57449afdcb 100644 --- a/src/server/modules/AgentRuntime/RuntimeExecutors.ts +++ b/src/server/modules/AgentRuntime/RuntimeExecutors.ts @@ -15,6 +15,7 @@ import { import { LobeActivatorIdentifier } from '@lobechat/builtin-tool-activator'; import { CredsIdentifier, type CredSummary, generateCredsList } from '@lobechat/builtin-tool-creds'; import { LocalSystemManifest } from '@lobechat/builtin-tool-local-system'; +import { BRANDING_PROVIDER } from '@lobechat/business-const'; import { AGENT_DOCUMENT_INJECTION_POSITIONS, type AgentContextDocument, @@ -138,6 +139,11 @@ const buildPostProcessUrl = (ctx: Pick kind === 'retry' && attempt <= maxRetries; +const resolveLLMMaxRetries = (provider: string) => + // The branded provider already routes through its own fallback chain. Retrying + // again here multiplies the same failed routed request across every channel. + provider === BRANDING_PROVIDER ? 0 : LLM_MAX_RETRIES; + const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); const getLLMRetryDelayMs = (attempt: number) => @@ -783,7 +789,8 @@ export const createRuntimeExecutors = ( } }; - const maxAttempts = LLM_MAX_RETRIES + 1; + const llmMaxRetries = resolveLLMMaxRetries(provider); + const maxAttempts = llmMaxRetries + 1; for (let attempt = 1; attempt <= maxAttempts; attempt++) { let content = ''; @@ -1136,7 +1143,7 @@ export const createRuntimeExecutors = ( const classified = classifyLLMError(error); const interrupted = await isOperationInterrupted(ctx); - if (!interrupted && shouldRetryLLM(classified.kind, attempt, LLM_MAX_RETRIES)) { + if (!interrupted && shouldRetryLLM(classified.kind, attempt, llmMaxRetries)) { const delayMs = getLLMRetryDelayMs(attempt); log( @@ -1522,7 +1529,9 @@ export const createRuntimeExecutors = ( typeof chatToolPayload.arguments === 'string' ? JSON.parse(chatToolPayload.arguments) : (chatToolPayload.arguments ?? {}); - } catch {} + } catch { + // Keep malformed tool arguments as an empty preview payload; execution still uses raw args. + } try { // Check if this is a client-side function tool — pause instead of executing @@ -2043,7 +2052,9 @@ export const createRuntimeExecutors = ( typeof chatToolPayload.arguments === 'string' ? JSON.parse(chatToolPayload.arguments) : (chatToolPayload.arguments ?? {}); - } catch {} + } catch { + // Keep malformed tool arguments as an empty preview payload; execution still uses raw args. + } try { log(`[${operationLogId}] Executing tool ${toolName} ...`); diff --git a/src/server/modules/AgentRuntime/__tests__/RuntimeExecutors.test.ts b/src/server/modules/AgentRuntime/__tests__/RuntimeExecutors.test.ts index 53aafb739a..038fa12b1e 100644 --- a/src/server/modules/AgentRuntime/__tests__/RuntimeExecutors.test.ts +++ b/src/server/modules/AgentRuntime/__tests__/RuntimeExecutors.test.ts @@ -3365,6 +3365,37 @@ describe('RuntimeExecutors', () => { ); }); + it('should disable llm execution retry for the branding provider', async () => { + const mockChat = vi + .fn() + .mockRejectedValueOnce(new Error('network timeout')) + .mockResolvedValueOnce(new Response('done')); + + vi.mocked(initModelRuntimeFromDB).mockResolvedValue({ chat: mockChat } as any); + + const executors = createRuntimeExecutors(ctx); + const state = createMockState(); + const instruction = { + payload: { + messages: [{ content: 'Hello', role: 'user' }], + model: 'gpt-4', + parentMessageId: 'parent-msg-123', + provider: 'lobehub', + tools: [], + }, + type: 'call_llm' as const, + }; + + await expect(executors.call_llm!(instruction, state)).rejects.toThrow('network timeout'); + + expect(mockChat).toHaveBeenCalledTimes(1); + expect( + mockStreamManager.publishStreamEvent.mock.calls.some( + ([, event]: [string, { type: string }]) => event.type === 'stream_retry', + ), + ).toBe(false); + }); + it('should retry llm execution, emit stream_retry, and commit only the successful attempt', async () => { vi.useFakeTimers(); From a91385aabcf0cb2db9d152bae7e30d245334e0d9 Mon Sep 17 00:00:00 2001 From: YuTengjing Date: Tue, 19 May 2026 17:30:05 +0800 Subject: [PATCH 028/224] =?UTF-8?q?=F0=9F=90=9B=20fix:=20nano=20banana=204?= =?UTF-8?q?K=20resolution=20dropped=20when=20aspect=20ratio=20is=20auto=20?= =?UTF-8?q?(#14977)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/model-bank/src/aiModels/google.ts | 4 +- .../src/providers/anthropic/index.test.ts | 5 +- .../src/providers/google/createImage.test.ts | 158 ++++++++++++++++++ .../src/providers/google/createImage.ts | 21 ++- .../src/providers/openrouter/index.test.ts | 4 +- .../src/providers/openrouter/index.ts | 6 +- packages/model-runtime/src/types/chat.ts | 4 +- packages/types/src/agent/chatConfig.ts | 6 +- .../ControlsForm/ImageResolution2Slider.tsx | 2 +- 9 files changed, 190 insertions(+), 20 deletions(-) diff --git a/packages/model-bank/src/aiModels/google.ts b/packages/model-bank/src/aiModels/google.ts index 09ff0e35c8..9fd75f20be 100644 --- a/packages/model-bank/src/aiModels/google.ts +++ b/packages/model-bank/src/aiModels/google.ts @@ -746,7 +746,9 @@ export const nanoBanana2Parameters: ModelParamsSchema = { prompt: { default: '' }, resolution: { default: '1K', - enum: ['512px', '1K', '2K', '4K'], + // Gemini image generation API accepts `"512" | "1K" | "2K" | "4K"`. + // See https://ai.google.dev/gemini-api/docs/image-generation + enum: ['512', '1K', '2K', '4K'], }, }; diff --git a/packages/model-runtime/src/providers/anthropic/index.test.ts b/packages/model-runtime/src/providers/anthropic/index.test.ts index 8a46f2ac82..8b951706cc 100644 --- a/packages/model-runtime/src/providers/anthropic/index.test.ts +++ b/packages/model-runtime/src/providers/anthropic/index.test.ts @@ -548,6 +548,9 @@ describe('LobeAnthropicAI', () => { vi.spyOn(customInstance['client'].messages, 'create').mockRejectedValue(apiError); // Act & Assert + // anthropicCompatibleFactory normalizes the `/v1` suffix away (see #14960), + // then desensitizeUrl reconstructs via the WHATWG URL parser which always + // emits a trailing `/` in the pathname. await expect( customInstance.chat({ messages: [{ content: 'Hello', role: 'user' }], @@ -555,7 +558,7 @@ describe('LobeAnthropicAI', () => { temperature: 0, }), ).rejects.toEqual({ - endpoint: 'https://api.cu****om.com/v1', + endpoint: 'https://api.cu****om.com/', error: apiError, errorType: invalidErrorType, provider, diff --git a/packages/model-runtime/src/providers/google/createImage.test.ts b/packages/model-runtime/src/providers/google/createImage.test.ts index b1d8c655fe..18dbae79e9 100644 --- a/packages/model-runtime/src/providers/google/createImage.test.ts +++ b/packages/model-runtime/src/providers/google/createImage.test.ts @@ -436,6 +436,164 @@ describe('createGoogleImage', () => { }); }); + // Regression: nano banana 4K selection used to be silently dropped because + // imageSize was gated on aspectRatio !== 'auto'. See LOBE-9115. + it('should pass imageSize when resolution is set even if aspectRatio is auto', async () => { + // Arrange + const realBase64ImageData = + 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; + const mockContentResponse = { + candidates: [ + { + content: { + parts: [ + { + inlineData: { + data: realBase64ImageData, + mimeType: 'image/png', + }, + }, + ], + }, + }, + ], + }; + vi.spyOn(mockClient.models, 'generateContent').mockResolvedValue(mockContentResponse as any); + + const payload: CreateImagePayload = { + model: 'gemini-2.5-flash-image:image', + params: { + prompt: 'Create a beautiful sunset landscape', + aspectRatio: 'auto', + resolution: '4K', + }, + }; + + // Act + await createGoogleImage(mockClient, provider, payload); + + // Assert - imageConfig.imageSize must reach Google when only resolution is set + expect(mockClient.models.generateContent).toHaveBeenCalledWith({ + contents: [ + { + role: 'user', + parts: [{ text: 'Create a beautiful sunset landscape' }], + }, + ], + model: 'gemini-2.5-flash-image', + config: { + responseModalities: ['Image'], + imageConfig: { + imageSize: '4K', + }, + }, + }); + }); + + it('should pass both aspectRatio and imageSize when both are set', async () => { + // Arrange + const realBase64ImageData = + 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; + const mockContentResponse = { + candidates: [ + { + content: { + parts: [ + { + inlineData: { + data: realBase64ImageData, + mimeType: 'image/png', + }, + }, + ], + }, + }, + ], + }; + vi.spyOn(mockClient.models, 'generateContent').mockResolvedValue(mockContentResponse as any); + + const payload: CreateImagePayload = { + model: 'gemini-2.5-flash-image:image', + params: { + prompt: 'Cinematic widescreen shot', + aspectRatio: '16:9', + resolution: '2K', + }, + }; + + // Act + await createGoogleImage(mockClient, provider, payload); + + // Assert + expect(mockClient.models.generateContent).toHaveBeenCalledWith({ + contents: [ + { + role: 'user', + parts: [{ text: 'Cinematic widescreen shot' }], + }, + ], + model: 'gemini-2.5-flash-image', + config: { + responseModalities: ['Image'], + imageConfig: { + aspectRatio: '16:9', + imageSize: '2K', + }, + }, + }); + }); + + it('should pass aspectRatio only when resolution is unset', async () => { + // Arrange + const realBase64ImageData = + 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; + const mockContentResponse = { + candidates: [ + { + content: { + parts: [ + { + inlineData: { + data: realBase64ImageData, + mimeType: 'image/png', + }, + }, + ], + }, + }, + ], + }; + vi.spyOn(mockClient.models, 'generateContent').mockResolvedValue(mockContentResponse as any); + + const payload: CreateImagePayload = { + model: 'gemini-2.5-flash-image:image', + params: { + prompt: 'Portrait orientation', + aspectRatio: '9:16', + }, + }; + + // Act + await createGoogleImage(mockClient, provider, payload); + + // Assert + expect(mockClient.models.generateContent).toHaveBeenCalledWith({ + contents: [ + { + role: 'user', + parts: [{ text: 'Portrait orientation' }], + }, + ], + model: 'gemini-2.5-flash-image', + config: { + responseModalities: ['Image'], + imageConfig: { + aspectRatio: '9:16', + }, + }, + }); + }); + it('should support image editing with base64 imageUrl', async () => { // Arrange const inputImageBase64 = diff --git a/packages/model-runtime/src/providers/google/createImage.ts b/packages/model-runtime/src/providers/google/createImage.ts index e732441781..1b59ee7a60 100644 --- a/packages/model-runtime/src/providers/google/createImage.ts +++ b/packages/model-runtime/src/providers/google/createImage.ts @@ -141,16 +141,21 @@ async function generateImageByChatModel( }, ]; + // Build imageConfig independently for aspectRatio and resolution so that + // selecting only one (e.g. resolution=4K while aspectRatio stays 'auto') + // still reaches the Google API. Previously both fields were gated on + // aspectRatio !== 'auto', which silently dropped the user's resolution. + const imageConfig: { aspectRatio?: string; imageSize?: string } = {}; + if (params.aspectRatio && params.aspectRatio !== 'auto') { + imageConfig.aspectRatio = params.aspectRatio; + } + if (params.resolution) { + imageConfig.imageSize = params.resolution; + } + const config: GenerateContentConfig = { responseModalities: ['Image'], - ...(params.aspectRatio && params.aspectRatio !== 'auto' - ? { - imageConfig: { - aspectRatio: params.aspectRatio, - imageSize: params.resolution, - }, - } - : {}), + ...(Object.keys(imageConfig).length > 0 ? { imageConfig } : {}), }; const response = await client.models.generateContent({ diff --git a/packages/model-runtime/src/providers/openrouter/index.test.ts b/packages/model-runtime/src/providers/openrouter/index.test.ts index 63abb49e7d..0974a7be75 100644 --- a/packages/model-runtime/src/providers/openrouter/index.test.ts +++ b/packages/model-runtime/src/providers/openrouter/index.test.ts @@ -423,11 +423,11 @@ describe('LobeOpenRouterAI - custom features', () => { ); }); - it('should map 512px to 0.5K in image_config.image_size', async () => { + it("should map '512' to '0.5K' in image_config.image_size", async () => { await instance.chat({ messages: [{ content: 'Generate an image', role: 'user' }], model: 'openai/dall-e-3-image', - imageResolution: '512px', + imageResolution: '512', } as any); expect(instance['client'].chat.completions.create).toHaveBeenCalledWith( diff --git a/packages/model-runtime/src/providers/openrouter/index.ts b/packages/model-runtime/src/providers/openrouter/index.ts index 189676672e..ebac9b1d54 100644 --- a/packages/model-runtime/src/providers/openrouter/index.ts +++ b/packages/model-runtime/src/providers/openrouter/index.ts @@ -51,9 +51,11 @@ export const params = { const modalities = (payload as any).modalities ?? (isImageModel ? ['image', 'text'] : undefined); - // Map imageResolution to image_size: '512px' → '0.5K', others pass through + // Map imageResolution to image_size: '512' → '0.5K', others pass through. + // OpenRouter's image_size field expects '0.5K' for 512px output; the rest + // ('1K'/'2K'/'4K') are passed through verbatim. const imageSizeValue = imageResolution - ? imageResolution === '512px' + ? imageResolution === '512' ? '0.5K' : imageResolution : undefined; diff --git a/packages/model-runtime/src/types/chat.ts b/packages/model-runtime/src/types/chat.ts index a941745720..c2d09891fe 100644 --- a/packages/model-runtime/src/types/chat.ts +++ b/packages/model-runtime/src/types/chat.ts @@ -84,9 +84,9 @@ export interface ChatStreamPayload { */ imageAspectRatio?: string; /** - * @title Image resolution for image generation (e.g., '512px', '1K', '2K', '4K') + * @title Image resolution for image generation (e.g., '512', '1K', '2K', '4K') */ - imageResolution?: '512px' | '1K' | '2K' | '4K'; + imageResolution?: '512' | '1K' | '2K' | '4K'; logprobs?: boolean; /** * @title Maximum length of generated text diff --git a/packages/types/src/agent/chatConfig.ts b/packages/types/src/agent/chatConfig.ts index 51958ac5b1..d0404298ec 100644 --- a/packages/types/src/agent/chatConfig.ts +++ b/packages/types/src/agent/chatConfig.ts @@ -106,9 +106,9 @@ export interface LobeAgentChatConfig extends AgentMemoryChatConfig, AgentSelfIte */ imageResolution?: '1K' | '2K' | '4K'; /** - * Image resolution for image generation models (with 512px support) + * Image resolution for image generation models (with 512 support) */ - imageResolution2?: '512px' | '1K' | '2K' | '4K'; + imageResolution2?: '512' | '1K' | '2K' | '4K'; inputTemplate?: string; /** * Effort level for Claude Opus 4.7 (adds xhigh tier between high and max) @@ -227,7 +227,7 @@ export const AgentChatConfigSchema = z imageAspectRatio: z.string().optional(), imageAspectRatio2: z.string().optional(), imageResolution: z.enum(['1K', '2K', '4K']).optional(), - imageResolution2: z.enum(['512px', '1K', '2K', '4K']).optional(), + imageResolution2: z.enum(['512', '1K', '2K', '4K']).optional(), opus47Effort: z.enum(['low', 'medium', 'high', 'xhigh', 'max']).optional(), runtimeEnv: RuntimeEnvConfigSchema.optional(), reasoningBudgetToken: z.number().optional(), diff --git a/src/features/ModelSwitchPanel/components/ControlsForm/ImageResolution2Slider.tsx b/src/features/ModelSwitchPanel/components/ControlsForm/ImageResolution2Slider.tsx index 1589b102d0..2039a860fa 100644 --- a/src/features/ModelSwitchPanel/components/ControlsForm/ImageResolution2Slider.tsx +++ b/src/features/ModelSwitchPanel/components/ControlsForm/ImageResolution2Slider.tsx @@ -1,7 +1,7 @@ import { type CreatedLevelSliderProps } from './createLevelSlider'; import { createLevelSliderComponent } from './createLevelSlider'; -const IMAGE_RESOLUTIONS_2 = ['512px', '1K', '2K', '4K'] as const; +const IMAGE_RESOLUTIONS_2 = ['512', '1K', '2K', '4K'] as const; type ImageResolution2 = (typeof IMAGE_RESOLUTIONS_2)[number]; export type ImageResolution2SliderProps = CreatedLevelSliderProps; From 6a7a20176a68432804df00c687d8d09d6dec6a8d Mon Sep 17 00:00:00 2001 From: AmAzing- <115673583+AmAzing129@users.noreply.github.com> Date: Tue, 19 May 2026 18:23:33 +0800 Subject: [PATCH 029/224] =?UTF-8?q?=F0=9F=90=9B=20fix(agent-builder):=20op?= =?UTF-8?q?en=20builder=20panel=20after=20prompt=20creation=20(#14978)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/AgentBuilder/index.tsx | 13 +- src/hooks/useHotkeys/globalScope.test.ts | 35 +++- src/hooks/useHotkeys/globalScope.ts | 24 ++- .../agent/profile/features/Header/index.tsx | 16 +- src/store/global/action.test.ts | 38 ++++ src/store/global/actions/workspacePane.ts | 10 ++ src/store/global/initialState.ts | 6 + .../global/selectors/systemStatus.test.ts | 3 + src/store/global/selectors/systemStatus.ts | 3 + .../home/slices/homeInput/action.test.ts | 166 ++++++++++++++++++ src/store/home/slices/homeInput/action.ts | 10 ++ 11 files changed, 315 insertions(+), 9 deletions(-) create mode 100644 src/store/home/slices/homeInput/action.test.ts diff --git a/src/features/AgentBuilder/index.tsx b/src/features/AgentBuilder/index.tsx index 2a05a1bf1a..2b101d129c 100644 --- a/src/features/AgentBuilder/index.tsx +++ b/src/features/AgentBuilder/index.tsx @@ -15,10 +15,13 @@ const AgentBuilder = memo(() => { const agentId = useAgentStore((s) => s.activeAgentId); const agentBuilderId = useAgentStore(builtinAgentSelectors.agentBuilderId); - const [width, updateSystemStatus] = useGlobalStore((s) => [ - systemStatusSelectors.agentBuilderPanelWidth(s), - s.updateSystemStatus, - ]); + const [showAgentBuilderPanel, toggleAgentBuilderPanel, width, updateSystemStatus] = + useGlobalStore((s) => [ + systemStatusSelectors.showAgentBuilderPanel(s), + s.toggleAgentBuilderPanel, + systemStatusSelectors.agentBuilderPanelWidth(s), + s.updateSystemStatus, + ]); const useInitBuiltinAgent = useAgentStore((s) => s.useInitBuiltinAgent); useInitBuiltinAgent(BUILTIN_AGENT_SLUGS.agentBuilder); @@ -26,6 +29,8 @@ const AgentBuilder = memo(() => { return ( { if (size?.width) { const w = typeof size.width === 'string' ? Number.parseInt(size.width) : size.width; diff --git a/src/hooks/useHotkeys/globalScope.test.ts b/src/hooks/useHotkeys/globalScope.test.ts index d71c720988..281f4c079e 100644 --- a/src/hooks/useHotkeys/globalScope.test.ts +++ b/src/hooks/useHotkeys/globalScope.test.ts @@ -4,10 +4,15 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import type { HotkeyId } from '@/types/hotkey'; -import { isTaskPanelRoute, useToggleRightPanelHotkey } from './globalScope'; +import { + isAgentProfilePanelRoute, + isTaskPanelRoute, + useToggleRightPanelHotkey, +} from './globalScope'; interface MockGlobalState { status: { zenMode: boolean }; + toggleAgentBuilderPanel: () => void; toggleRightPanel: () => void; toggleTaskAgentPanel: () => void; updateSystemStatus: () => void; @@ -18,6 +23,7 @@ type HotkeyRegistrationArgs = [HotkeyId, () => void, ...unknown[]]; const mocks = vi.hoisted(() => ({ hotkeyCallback: undefined as (() => void) | undefined, pathname: '/', + toggleAgentBuilderPanel: vi.fn(), toggleRightPanel: vi.fn(), toggleTaskAgentPanel: vi.fn(), useHotkeyById: vi.fn(), @@ -39,6 +45,7 @@ vi.mock('@/store/global', () => ({ useGlobalStore: (selector: (state: MockGlobalState) => unknown) => selector({ status: { zenMode: false }, + toggleAgentBuilderPanel: mocks.toggleAgentBuilderPanel, toggleRightPanel: mocks.toggleRightPanel, toggleTaskAgentPanel: mocks.toggleTaskAgentPanel, updateSystemStatus: vi.fn(), @@ -53,6 +60,7 @@ describe('globalScope hotkeys', () => { beforeEach(() => { mocks.hotkeyCallback = undefined; mocks.pathname = '/'; + mocks.toggleAgentBuilderPanel.mockReset(); mocks.toggleRightPanel.mockReset(); mocks.toggleTaskAgentPanel.mockReset(); mocks.useHotkeyById.mockReset(); @@ -72,6 +80,16 @@ describe('globalScope hotkeys', () => { }); }); + describe('isAgentProfilePanelRoute', () => { + it('should match agent profile routes only', () => { + expect(isAgentProfilePanelRoute('/agent/agent-1/profile')).toBe(true); + expect(isAgentProfilePanelRoute('/agent/agent-1/profile/')).toBe(true); + expect(isAgentProfilePanelRoute('/agent/agent-1')).toBe(false); + expect(isAgentProfilePanelRoute('/agent/agent-1/profile/edit')).toBe(false); + expect(isAgentProfilePanelRoute('/group/group-1/profile')).toBe(false); + }); + }); + describe('useToggleRightPanelHotkey', () => { it('should toggle task agent panel on task routes', () => { mocks.pathname = '/tasks'; @@ -109,6 +127,21 @@ describe('globalScope hotkeys', () => { }); expect(mocks.toggleRightPanel).toHaveBeenCalledTimes(1); + expect(mocks.toggleAgentBuilderPanel).not.toHaveBeenCalled(); + expect(mocks.toggleTaskAgentPanel).not.toHaveBeenCalled(); + }); + + it('should toggle the agent builder panel on agent profile routes', () => { + mocks.pathname = '/agent/agent-1/profile'; + + renderHook(() => useToggleRightPanelHotkey()); + + act(() => { + mocks.hotkeyCallback?.(); + }); + + expect(mocks.toggleAgentBuilderPanel).toHaveBeenCalledTimes(1); + expect(mocks.toggleRightPanel).not.toHaveBeenCalled(); expect(mocks.toggleTaskAgentPanel).not.toHaveBeenCalled(); }); }); diff --git a/src/hooks/useHotkeys/globalScope.ts b/src/hooks/useHotkeys/globalScope.ts index df71f9e302..70b08cfbbb 100644 --- a/src/hooks/useHotkeys/globalScope.ts +++ b/src/hooks/useHotkeys/globalScope.ts @@ -15,6 +15,13 @@ import { useHotkeyById } from './useHotkeyById'; export const isTaskPanelRoute = (pathname: string) => pathname === '/tasks' || pathname.startsWith('/tasks/') || pathname.startsWith('/task/'); +/** + * Agent profile renders AgentBuilder, whose panel status is intentionally + * independent from the generic right panel used by chat routes. + */ +export const isAgentProfilePanelRoute = (pathname: string) => + /^\/agent\/[^/]+\/profile\/?$/.test(pathname); + // Switch to chat tab (and focus on Lobe AI) export const useNavigateToChatHotkey = () => { const navigateToAgent = useNavigateToAgent(); @@ -49,10 +56,12 @@ export const useToggleLeftPanelHotkey = () => { export const useToggleRightPanelHotkey = () => { const { pathname } = useLocation(); const isZenMode = useGlobalStore((s) => s.status.zenMode); - const [toggleRightPanel, toggleTaskAgentPanel] = useGlobalStore((s) => [ + const [toggleAgentBuilderPanel, toggleRightPanel, toggleTaskAgentPanel] = useGlobalStore((s) => [ + s.toggleAgentBuilderPanel, s.toggleRightPanel, s.toggleTaskAgentPanel, ]); + const isAgentProfileRoute = isAgentProfilePanelRoute(pathname); const isTaskRoute = isTaskPanelRoute(pathname); return useHotkeyById( @@ -63,13 +72,24 @@ export const useToggleRightPanelHotkey = () => { return; } + if (isAgentProfileRoute) { + toggleAgentBuilderPanel(); + return; + } + toggleRightPanel(); }, { enableOnContentEditable: true, enabled: !isZenMode, }, - [isTaskRoute, toggleRightPanel, toggleTaskAgentPanel], + [ + isAgentProfileRoute, + isTaskRoute, + toggleAgentBuilderPanel, + toggleRightPanel, + toggleTaskAgentPanel, + ], ); }; diff --git a/src/routes/(main)/agent/profile/features/Header/index.tsx b/src/routes/(main)/agent/profile/features/Header/index.tsx index 79db0d2079..e4a032779a 100644 --- a/src/routes/(main)/agent/profile/features/Header/index.tsx +++ b/src/routes/(main)/agent/profile/features/Header/index.tsx @@ -15,6 +15,8 @@ import { useMarketAuth } from '@/layout/AuthProvider/MarketAuth'; import { resolveMarketAuthError } from '@/layout/AuthProvider/MarketAuth/errors'; import { useAgentStore } from '@/store/agent'; import { agentSelectors } from '@/store/agent/selectors'; +import { useGlobalStore } from '@/store/global'; +import { systemStatusSelectors } from '@/store/global/selectors'; import { useHomeStore } from '@/store/home'; import AgentForkTag from './AgentForkTag'; @@ -34,6 +36,11 @@ const Header = memo(() => { const systemRole = useAgentStore(agentSelectors.currentAgentSystemRole); const activeAgentId = useAgentStore((s) => s.activeAgentId); const isHeterogeneous = useAgentStore(agentSelectors.isCurrentAgentHeterogeneous); + const [showAgentBuilderPanel, toggleAgentBuilderPanel, isStatusInit] = useGlobalStore((s) => [ + systemStatusSelectors.showAgentBuilderPanel(s), + s.toggleAgentBuilderPanel, + systemStatusSelectors.isStatusInit(s), + ]); const removeAgent = useHomeStore((s) => s.removeAgent); const { isAuthenticated, isLoading: isAuthLoading, signIn } = useMarketAuth(); const { isUnderReview } = useVersionReviewStatus(); @@ -179,8 +186,13 @@ const Header = memo(() => { size={DESKTOP_HEADER_ICON_SMALL_SIZE} /> - {!isHeterogeneous && ( - + {!isHeterogeneous && isStatusInit && ( + toggleAgentBuilderPanel()} + /> )}
} diff --git a/src/store/global/action.test.ts b/src/store/global/action.test.ts index 2fe98d700d..ed2b8f7b07 100644 --- a/src/store/global/action.test.ts +++ b/src/store/global/action.test.ts @@ -61,6 +61,44 @@ describe('createPreferenceSlice', () => { }); }); + describe('toggleAgentBuilderPanel', () => { + it('should toggle agent builder panel without changing chat right panel', () => { + const { result } = renderHook(() => useGlobalStore()); + + act(() => { + useGlobalStore.setState({ + isStatusInit: true, + status: { + ...initialState.status, + showAgentBuilderPanel: false, + showRightPanel: false, + }, + }); + result.current.toggleAgentBuilderPanel(); + }); + + expect(result.current.status.showAgentBuilderPanel).toBe(true); + expect(result.current.status.showRightPanel).toBe(false); + }); + + it('should set agent builder panel to specified value', () => { + const { result } = renderHook(() => useGlobalStore()); + + act(() => { + useGlobalStore.setState({ isStatusInit: true }); + result.current.toggleAgentBuilderPanel(true); + }); + + expect(result.current.status.showAgentBuilderPanel).toBe(true); + + act(() => { + result.current.toggleAgentBuilderPanel(false); + }); + + expect(result.current.status.showAgentBuilderPanel).toBe(false); + }); + }); + describe('toggleExpandSessionGroup', () => { it('should toggle expand session group', () => { const { result } = renderHook(() => useGlobalStore()); diff --git a/src/store/global/actions/workspacePane.ts b/src/store/global/actions/workspacePane.ts index 0fb5bfbca5..b8183ad345 100644 --- a/src/store/global/actions/workspacePane.ts +++ b/src/store/global/actions/workspacePane.ts @@ -81,6 +81,16 @@ export class GlobalWorkspacePaneActionImpl { this.#get().updateSystemStatus({ showLeftPanel }, n('toggleLeftPanel', newValue)); }; + toggleAgentBuilderPanel = (newValue?: boolean): void => { + const showAgentBuilderPanel = + typeof newValue === 'boolean' ? newValue : !this.#get().status.showAgentBuilderPanel; + + this.#get().updateSystemStatus( + { showAgentBuilderPanel }, + n('toggleAgentBuilderPanel', newValue), + ); + }; + togglePageAgentPanel = (newValue?: boolean): void => { const showPageAgentPanel = typeof newValue === 'boolean' ? newValue : !this.#get().status.showPageAgentPanel; diff --git a/src/store/global/initialState.ts b/src/store/global/initialState.ts index 77f8601787..3de8f5c3fd 100644 --- a/src/store/global/initialState.ts +++ b/src/store/global/initialState.ts @@ -204,6 +204,11 @@ export interface SystemStatus { name: number; size: number; }; + /** + * Visibility of the Agent profile right-side Agent Builder panel. + * Independent from `showRightPanel` so builder creation flows do not affect chat pages. + */ + showAgentBuilderPanel?: boolean; showCommandMenu?: boolean; showFilePanel?: boolean; showHotkeyHelper?: boolean; @@ -368,6 +373,7 @@ export const INITIAL_STATUS = { showHotkeyHelper: false, showImagePanel: true, showImageTopicPanel: true, + showAgentBuilderPanel: false, showLeftPanel: true, showPageAgentPanel: true, showRightPanel: false, diff --git a/src/store/global/selectors/systemStatus.test.ts b/src/store/global/selectors/systemStatus.test.ts index bb3b2763ef..d7cbea63e7 100644 --- a/src/store/global/selectors/systemStatus.test.ts +++ b/src/store/global/selectors/systemStatus.test.ts @@ -46,6 +46,7 @@ describe('systemStatusSelectors', () => { showSystemRole: true, mobileShowTopic: true, mobileShowPortal: true, + showAgentBuilderPanel: true, showRightPanel: true, showLeftPanel: true, showFilePanel: true, @@ -64,6 +65,7 @@ describe('systemStatusSelectors', () => { expect(systemStatusSelectors.showSystemRole(s)).toBe(true); expect(systemStatusSelectors.mobileShowTopic(s)).toBe(true); expect(systemStatusSelectors.mobileShowPortal(s)).toBe(true); + expect(systemStatusSelectors.showAgentBuilderPanel(s)).toBe(true); expect(systemStatusSelectors.showRightPanel(s)).toBe(true); expect(systemStatusSelectors.showLeftPanel(s)).toBe(true); expect(systemStatusSelectors.showFilePanel(s)).toBe(true); @@ -81,6 +83,7 @@ describe('systemStatusSelectors', () => { const zenState = merge(s, { status: { zenMode: true }, }); + expect(systemStatusSelectors.showAgentBuilderPanel(zenState)).toBe(false); expect(systemStatusSelectors.showRightPanel(zenState)).toBe(false); expect(systemStatusSelectors.showLeftPanel(zenState)).toBe(false); expect(systemStatusSelectors.showChatHeader(zenState)).toBe(false); diff --git a/src/store/global/selectors/systemStatus.ts b/src/store/global/selectors/systemStatus.ts index 07e58fdcbe..cc480c2e69 100644 --- a/src/store/global/selectors/systemStatus.ts +++ b/src/store/global/selectors/systemStatus.ts @@ -172,6 +172,8 @@ const sidebarItems = (s: GlobalState): string[] => { const showSystemRole = (s: GlobalState) => s.status.showSystemRole; const mobileShowTopic = (s: GlobalState) => s.status.mobileShowTopic; const mobileShowPortal = (s: GlobalState) => s.status.mobileShowPortal; +const showAgentBuilderPanel = (s: GlobalState) => + !s.status.zenMode && s.status.showAgentBuilderPanel; const showRightPanel = (s: GlobalState) => !s.status.zenMode && s.status.showRightPanel; const showLeftPanel = (s: GlobalState) => !s.status.zenMode && s.status.showLeftPanel; const showPageAgentPanel = (s: GlobalState) => !s.status.zenMode && s.status.showPageAgentPanel; @@ -279,6 +281,7 @@ export const systemStatusSelectors = { sidebarExpandedKeys, sidebarItems, sessionGroupKeys, + showAgentBuilderPanel, showChatHeader, showFilePanel, showImagePanel, diff --git a/src/store/home/slices/homeInput/action.test.ts b/src/store/home/slices/homeInput/action.test.ts new file mode 100644 index 0000000000..252f45f9cb --- /dev/null +++ b/src/store/home/slices/homeInput/action.test.ts @@ -0,0 +1,166 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import type { HomeStore } from '@/store/home/store'; +import type { StoreSetter } from '@/store/types'; + +import { HomeInputActionImpl } from './action'; + +const navigateMock = vi.hoisted(() => vi.fn()); +const createAgentMock = vi.hoisted(() => vi.fn()); +const updateAgentConfigByIdMock = vi.hoisted(() => vi.fn()); +const refreshBuiltinAgentMock = vi.hoisted(() => vi.fn()); +const sendMessageMock = vi.hoisted(() => vi.fn()); +const refreshAgentListMock = vi.hoisted(() => vi.fn()); +const toggleAgentBuilderPanelMock = vi.hoisted(() => vi.fn()); +const toggleRightPanelMock = vi.hoisted(() => vi.fn()); +const setChatPanelExpandedMock = vi.hoisted(() => vi.fn()); +const createGroupMock = vi.hoisted(() => vi.fn()); +const loadGroupsMock = vi.hoisted(() => vi.fn()); + +const agentState = vi.hoisted(() => ({ + agentConfigMap: { + inbox: { + model: 'gpt-4o-mini', + provider: 'openai', + }, + }, + agentMap: { + agentBuilder: {}, + groupAgentBuilder: {}, + }, + builtinAgentIdMap: { + 'agent-builder': 'agentBuilder', + 'group-agent-builder': 'groupAgentBuilder', + }, + createAgent: createAgentMock, + inboxAgentId: 'inbox', + refreshBuiltinAgent: refreshBuiltinAgentMock, + updateAgentConfigById: updateAgentConfigByIdMock, +})); + +vi.mock('@lobechat/builtin-agents', () => ({ + BUILTIN_AGENT_SLUGS: { + agentBuilder: 'agent-builder', + groupAgentBuilder: 'group-agent-builder', + }, +})); + +vi.mock('@/services/chatGroup', () => ({ + chatGroupService: { + createGroup: createGroupMock, + }, +})); + +vi.mock('@/store/agent', () => ({ + getAgentStoreState: () => agentState, +})); + +vi.mock('@/store/agent/selectors', () => ({ + agentSelectors: { + getAgentConfigById: + (id: string) => + (state: typeof agentState): { model: string; provider: string } | undefined => + state.agentConfigMap[id as keyof typeof state.agentConfigMap], + }, + builtinAgentSelectors: { + inboxAgentId: (state: typeof agentState) => state.inboxAgentId, + }, +})); + +vi.mock('@/store/agentGroup', () => ({ + getChatGroupStoreState: () => ({ + loadGroups: loadGroupsMock, + }), +})); + +vi.mock('@/store/chat', () => ({ + useChatStore: { + getState: () => ({ + sendMessage: sendMessageMock, + }), + }, +})); + +vi.mock('@/store/global', () => ({ + useGlobalStore: { + getState: () => ({ + toggleAgentBuilderPanel: toggleAgentBuilderPanelMock, + toggleRightPanel: toggleRightPanelMock, + }), + }, +})); + +vi.mock('@/store/groupProfile', () => ({ + useGroupProfileStore: { + getState: () => ({ + setChatPanelExpanded: setChatPanelExpandedMock, + }), + }, +})); + +vi.mock('@/utils/stableNavigate', () => ({ + getStableNavigate: () => navigateMock, +})); + +const createAction = () => { + const homeState: Partial = { + refreshAgentList: refreshAgentListMock, + }; + + const setState: StoreSetter = ((partial) => { + if (typeof partial === 'function') { + Object.assign(homeState, partial(homeState as HomeStore)); + return; + } + Object.assign(homeState, partial); + }) as StoreSetter; + + return new HomeInputActionImpl(setState, () => homeState as HomeStore); +}; + +describe('HomeInputActionImpl', () => { + beforeEach(() => { + vi.clearAllMocks(); + createAgentMock.mockResolvedValue({ agentId: 'agent-new' }); + createGroupMock.mockResolvedValue({ + group: { + id: 'group-new', + }, + }); + }); + + describe('sendAsAgent', () => { + it('opens the agent builder panel without touching the generic right panel', async () => { + const action = createAction(); + + await action.sendAsAgent({ message: 'build a support agent' }); + + expect(toggleAgentBuilderPanelMock).toHaveBeenCalledWith(true); + expect(toggleRightPanelMock).not.toHaveBeenCalled(); + expect(navigateMock).toHaveBeenCalledWith('/agent/agent-new/profile'); + expect(sendMessageMock).toHaveBeenCalledWith( + expect.objectContaining({ + context: { agentId: 'agentBuilder', scope: 'agent_builder' }, + message: 'build a support agent', + }), + ); + }); + }); + + describe('sendAsGroup', () => { + it('opens the existing group agent builder panel for prompt-based group creation', async () => { + const action = createAction(); + + await action.sendAsGroup({ message: 'build a research group' }); + + expect(setChatPanelExpandedMock).toHaveBeenCalledWith(true); + expect(navigateMock).toHaveBeenCalledWith('/group/group-new/profile'); + expect(sendMessageMock).toHaveBeenCalledWith( + expect.objectContaining({ + context: { agentId: 'groupAgentBuilder', scope: 'group_agent_builder' }, + message: 'build a research group', + }), + ); + }); + }); +}); diff --git a/src/store/home/slices/homeInput/action.ts b/src/store/home/slices/homeInput/action.ts index a21cecad86..4f68ea4105 100644 --- a/src/store/home/slices/homeInput/action.ts +++ b/src/store/home/slices/homeInput/action.ts @@ -6,6 +6,8 @@ import { getAgentStoreState } from '@/store/agent'; import { agentSelectors, builtinAgentSelectors } from '@/store/agent/selectors'; import { getChatGroupStoreState } from '@/store/agentGroup'; import { useChatStore } from '@/store/chat'; +import { useGlobalStore } from '@/store/global'; +import { useGroupProfileStore } from '@/store/groupProfile'; import { type HomeStore } from '@/store/home/store'; import { type StoreSetter } from '@/store/types'; import { getStableNavigate } from '@/utils/stableNavigate'; @@ -86,6 +88,10 @@ export class HomeInputActionImpl { groupId, }); + if (message.trim()) { + useGlobalStore.getState().toggleAgentBuilderPanel(true); + } + // 3. Navigate to Agent profile page getStableNavigate()?.(`/agent/${result.agentId}/profile`); @@ -157,6 +163,10 @@ export class HomeInputActionImpl { // 4. Refresh sidebar agent list this.#get().refreshAgentList(); + if (message.trim()) { + useGroupProfileStore.getState().setChatPanelExpanded(true); + } + // 5. Navigate to Group profile page getStableNavigate()?.(`/group/${group.id}/profile`); From 6ab1fb2a77b12105094fc2cef8a54cfca02af5c4 Mon Sep 17 00:00:00 2001 From: Innei Date: Tue, 19 May 2026 18:56:58 +0800 Subject: [PATCH 030/224] =?UTF-8?q?=E2=9C=A8=20feat(onboarding):=20add=20M?= =?UTF-8?q?arket=20Agent=20Picker=20as=20a=20classic=20onboarding=20step?= =?UTF-8?q?=20(#14980)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ feat(onboarding): add Market Agent Picker as a classic onboarding step - Add AgentPickerStep as the final classic onboarding step (step 4) - Agent onboarding skip now routes to the picker step instead of finishing - Hide the footer skip link on the classic flow - Relocate installMarketplaceAgents to src/services for shared use - Map collected interests to marketplace category hints * 💄 style(onboarding): widen agent picker step and polish card layout - Widen the classic picker step container to 780px (other steps stay 600px) - Left-align the LobeMessage logo to match the title - Always reserve the agent card check slot to avoid text reflow on select --- locales/en-US/onboarding.json | 8 + locales/zh-CN/onboarding.json | 8 + .../src/agentMarketplace/index.ts | 1 + packages/types/src/user/onboarding.ts | 2 +- .../customInteractionHandlers.test.ts | 5 +- .../Intervention/customInteractionHandlers.ts | 3 +- src/features/Onboarding/Classic/index.tsx | 10 +- src/features/Onboarding/Common/index.test.tsx | 2 +- src/features/Onboarding/Common/index.tsx | 12 +- src/locales/default/onboarding.ts | 9 + src/routes/onboarding/_layout/index.test.tsx | 23 +- src/routes/onboarding/_layout/index.tsx | 50 ++-- .../onboarding/components/LobeMessage.tsx | 2 +- .../features/AgentPickerStep/AgentCard.tsx | 56 +++++ .../AgentPickerStep/CategoryFilter.tsx | 43 ++++ .../features/AgentPickerStep/Skeleton.tsx | 31 +++ .../AgentPickerStep/categoryLabels.ts | 17 ++ .../features/AgentPickerStep/index.test.tsx | 136 +++++++++++ .../features/AgentPickerStep/index.tsx | 216 ++++++++++++++++++ .../features/AgentPickerStep/style.ts | 195 ++++++++++++++++ .../onboarding/features/ProSettingsStep.tsx | 21 +- .../onboarding/interestCategoryMap.test.ts | 33 +++ src/routes/onboarding/interestCategoryMap.ts | 34 +++ .../installMarketplaceAgents.test.ts | 0 .../installMarketplaceAgents.ts | 0 25 files changed, 852 insertions(+), 65 deletions(-) create mode 100644 src/routes/onboarding/features/AgentPickerStep/AgentCard.tsx create mode 100644 src/routes/onboarding/features/AgentPickerStep/CategoryFilter.tsx create mode 100644 src/routes/onboarding/features/AgentPickerStep/Skeleton.tsx create mode 100644 src/routes/onboarding/features/AgentPickerStep/categoryLabels.ts create mode 100644 src/routes/onboarding/features/AgentPickerStep/index.test.tsx create mode 100644 src/routes/onboarding/features/AgentPickerStep/index.tsx create mode 100644 src/routes/onboarding/features/AgentPickerStep/style.ts create mode 100644 src/routes/onboarding/interestCategoryMap.test.ts create mode 100644 src/routes/onboarding/interestCategoryMap.ts rename src/{features/Conversation/Messages/AssistantGroup/Tool/Detail/Intervention => services}/installMarketplaceAgents.test.ts (100%) rename src/{features/Conversation/Messages/AssistantGroup/Tool/Detail/Intervention => services}/installMarketplaceAgents.ts (100%) diff --git a/locales/en-US/onboarding.json b/locales/en-US/onboarding.json index 753e5594ae..164316ae47 100644 --- a/locales/en-US/onboarding.json +++ b/locales/en-US/onboarding.json @@ -27,6 +27,7 @@ "agent.layout.skipConfirm.ok": "Skip for now", "agent.layout.skipConfirm.title": "Skip onboarding for now?", "agent.layout.switchMessage": "Not feeling it today? You can switch to {{mode}} or {{skip}}.", + "agent.layout.switchMessageClassic": "Not feeling it today? You can switch to {{mode}}.", "agent.modeSwitch.agent": "Conversational", "agent.modeSwitch.classic": "Classic", "agent.modeSwitch.collapse": "Collapse", @@ -68,6 +69,13 @@ "agent.wrapUp.confirm.content": "I'll save what we've covered so far. You can always come back and chat more later.", "agent.wrapUp.confirm.ok": "Finish now", "agent.wrapUp.confirm.title": "Finish onboarding now?", + "agentPicker.allCategories": "All", + "agentPicker.continue": "Continue", + "agentPicker.skip": "Skip for now", + "agentPicker.subtitle": "Add a few to your library now — discover more anytime later.", + "agentPicker.title": "Let's add a few agents to your library", + "agentPicker.title2": "Pick the ones that match how you work", + "agentPicker.title3": "You can always add more later — start with a few", "back": "Back", "finish": "Get Started", "interests.area.business": "Business & Strategy", diff --git a/locales/zh-CN/onboarding.json b/locales/zh-CN/onboarding.json index 790ab15f18..959e4f09ef 100644 --- a/locales/zh-CN/onboarding.json +++ b/locales/zh-CN/onboarding.json @@ -27,6 +27,7 @@ "agent.layout.skipConfirm.ok": "暂时跳过", "agent.layout.skipConfirm.title": "暂时跳过入门引导?", "agent.layout.switchMessage": "暂时不想继续?可以切换到 {{mode}}{{skip}}。", + "agent.layout.switchMessageClassic": "暂时不想继续?可以切换到 {{mode}}。", "agent.modeSwitch.agent": "对话模式", "agent.modeSwitch.classic": "经典模式", "agent.modeSwitch.collapse": "收起", @@ -68,6 +69,13 @@ "agent.wrapUp.confirm.content": "目前了解到的信息我都会保存,你随时都可以回来继续和我聊。", "agent.wrapUp.confirm.ok": "结束引导", "agent.wrapUp.confirm.title": "现在结束引导吗?", + "agentPicker.allCategories": "全部", + "agentPicker.continue": "继续", + "agentPicker.skip": "暂时跳过", + "agentPicker.subtitle": "先把几个加入你的资料库——之后随时可以发现更多。", + "agentPicker.title": "先把几个 Agent 加入你的资料库吧", + "agentPicker.title2": "挑几个契合你工作方式的", + "agentPicker.title3": "之后随时能再添加——先选几个就好", "back": "上一步", "finish": "开始使用", "interests.area.business": "商业与战略", diff --git a/packages/builtin-tool-web-onboarding/src/agentMarketplace/index.ts b/packages/builtin-tool-web-onboarding/src/agentMarketplace/index.ts index fb19636051..d4f18f3619 100644 --- a/packages/builtin-tool-web-onboarding/src/agentMarketplace/index.ts +++ b/packages/builtin-tool-web-onboarding/src/agentMarketplace/index.ts @@ -3,6 +3,7 @@ export { fetchAgentTemplates, type FetchAgentTemplatesOptions, getTemplatesByCategories, + getTemplatesByCategoryPriority, normalizeAgentTemplate, type OnboardingFullResponse, type RawAgentTemplate, diff --git a/packages/types/src/user/onboarding.ts b/packages/types/src/user/onboarding.ts index d04972d18e..34ac471067 100644 --- a/packages/types/src/user/onboarding.ts +++ b/packages/types/src/user/onboarding.ts @@ -9,7 +9,7 @@ export interface UserOnboarding { version: number; } -export const MAX_ONBOARDING_STEPS = 3; +export const MAX_ONBOARDING_STEPS = 4; export const UserOnboardingSchema = z.object({ currentStep: z.number().min(1).max(MAX_ONBOARDING_STEPS).optional(), diff --git a/src/features/Conversation/Messages/AssistantGroup/Tool/Detail/Intervention/customInteractionHandlers.test.ts b/src/features/Conversation/Messages/AssistantGroup/Tool/Detail/Intervention/customInteractionHandlers.test.ts index feae8eb104..f1dfe79d14 100644 --- a/src/features/Conversation/Messages/AssistantGroup/Tool/Detail/Intervention/customInteractionHandlers.test.ts +++ b/src/features/Conversation/Messages/AssistantGroup/Tool/Detail/Intervention/customInteractionHandlers.test.ts @@ -4,13 +4,14 @@ import { } from '@lobechat/builtin-tool-web-onboarding'; import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { installMarketplaceAgents } from '@/services/installMarketplaceAgents'; + import { prepareCustomInteractionSubmit, recordCustomInteractionResolution, } from './customInteractionHandlers'; -import { installMarketplaceAgents } from './installMarketplaceAgents'; -vi.mock('./installMarketplaceAgents', () => ({ +vi.mock('@/services/installMarketplaceAgents', () => ({ installMarketplaceAgents: vi.fn(), })); diff --git a/src/features/Conversation/Messages/AssistantGroup/Tool/Detail/Intervention/customInteractionHandlers.ts b/src/features/Conversation/Messages/AssistantGroup/Tool/Detail/Intervention/customInteractionHandlers.ts index a499cd9240..0ad1d9c6ee 100644 --- a/src/features/Conversation/Messages/AssistantGroup/Tool/Detail/Intervention/customInteractionHandlers.ts +++ b/src/features/Conversation/Messages/AssistantGroup/Tool/Detail/Intervention/customInteractionHandlers.ts @@ -7,10 +7,9 @@ import { import { buildAgentMarketplaceToolResult } from '@lobechat/builtin-tool-web-onboarding/agentMarketplace'; import type { OnboardingAgentMarketplacePickSnapshot } from '@lobechat/types'; +import { installMarketplaceAgents } from '@/services/installMarketplaceAgents'; import { topicService } from '@/services/topic'; -import { installMarketplaceAgents } from './installMarketplaceAgents'; - interface SubmitToolInteractionOptions { createUserMessage?: boolean; pluginState?: Record; diff --git a/src/features/Onboarding/Classic/index.tsx b/src/features/Onboarding/Classic/index.tsx index d8e5167d1e..6b42288568 100644 --- a/src/features/Onboarding/Classic/index.tsx +++ b/src/features/Onboarding/Classic/index.tsx @@ -8,6 +8,7 @@ import { Navigate, useNavigate } from 'react-router-dom'; import Loading from '@/components/Loading/BrandTextLoading'; import ModeSwitch from '@/features/Onboarding/components/ModeSwitch'; import OnboardingContainer from '@/routes/onboarding/_layout'; +import AgentPickerStep from '@/routes/onboarding/features/AgentPickerStep'; import FullNameStep from '@/routes/onboarding/features/FullNameStep'; import InterestsStep from '@/routes/onboarding/features/InterestsStep'; import ProSettingsStep from '@/routes/onboarding/features/ProSettingsStep'; @@ -47,8 +48,11 @@ const ClassicOnboardingPage = memo(() => { case 2: { return ; } + case 3: { + return ; + } case MAX_ONBOARDING_STEPS: { - return ; + return ; } default: { return null; @@ -56,9 +60,11 @@ const ClassicOnboardingPage = memo(() => { } }; + const contentMaxWidth = currentStep === MAX_ONBOARDING_STEPS ? 780 : 600; + return ( - + {renderStep()} diff --git a/src/features/Onboarding/Common/index.test.tsx b/src/features/Onboarding/Common/index.test.tsx index 9eb9914733..a121e235a8 100644 --- a/src/features/Onboarding/Common/index.test.tsx +++ b/src/features/Onboarding/Common/index.test.tsx @@ -201,7 +201,7 @@ describe('CommonOnboardingPage', () => { await waitFor(() => expect(setOnboardingStep).toHaveBeenCalledWith(2)); }); - it('remaps legacy step 4+ (old Language/ProSettings) to MAX', async () => { + it('remaps legacy step 4+ (old Language/ProSettings) to the ProSettings step', async () => { const setOnboardingStep = vi.fn(); await renderCommon({ commonStepsCompleted: false, persistedStep: 5, setOnboardingStep }); await waitFor(() => expect(setOnboardingStep).toHaveBeenCalledWith(3)); diff --git a/src/features/Onboarding/Common/index.tsx b/src/features/Onboarding/Common/index.tsx index 6edc070a80..407cfd20f6 100644 --- a/src/features/Onboarding/Common/index.tsx +++ b/src/features/Onboarding/Common/index.tsx @@ -18,18 +18,18 @@ import { onboardingSelectors } from '@/store/user/selectors'; /** * Remap a `currentStep` persisted under the old 5-step classic flow * (1=Telemetry, 2=FullName, 3=Interests, 4=Language, 5=ProSettings) onto - * the new 3-step classic flow (1=FullName, 2=Interests, 3=ProSettings). + * the current classic flow (1=FullName, 2=Interests, 3=ProSettings, + * 4=AgentPicker). * * Telemetry/Language are extracted into the shared prefix, so an in-progress - * legacy user must skip those positions when resuming classic. Without this - * remap, persisted step 2 (FullName) would render Interests and persisted - * step 3 (Interests) would render ProSettings — silently skipping required - * profile steps. Idempotent for already-new values within [1, 3]. + * legacy user must skip those positions when resuming classic. Legacy + * Language/ProSettings (raw >= 4) resume at the new ProSettings step + * (MAX_ONBOARDING_STEPS - 1) — never the trailing agent-picker step. */ const remapLegacyClassicStep = (raw: number): number => { if (raw <= 2) return 1; if (raw === 3) return 2; - return MAX_ONBOARDING_STEPS; + return MAX_ONBOARDING_STEPS - 1; }; const CommonOnboardingPage = memo(() => { diff --git a/src/locales/default/onboarding.ts b/src/locales/default/onboarding.ts index 8307e0968b..da63fe650b 100644 --- a/src/locales/default/onboarding.ts +++ b/src/locales/default/onboarding.ts @@ -11,6 +11,8 @@ export default { 'agent.layout.skipConfirm.title': 'Skip onboarding for now?', 'agent.layout.switchMessage': 'Not feeling it today? You can switch to {{mode}} or {{skip}}.', + 'agent.layout.switchMessageClassic': + 'Not feeling it today? You can switch to {{mode}}.', 'agent.modeSwitch.agent': 'Conversational', 'agent.modeSwitch.classic': 'Classic', 'agent.modeSwitch.collapse': 'Collapse', @@ -75,6 +77,13 @@ export default { 'agent.telemetryDecline': 'No thanks', 'agent.telemetryHint': 'You can also answer in your own words.', 'agent.title': 'Conversation Onboarding', + 'agentPicker.allCategories': 'All', + 'agentPicker.continue': 'Continue', + 'agentPicker.skip': 'Skip for now', + 'agentPicker.subtitle': 'Add a few to your library now — discover more anytime later.', + 'agentPicker.title': "Let's add a few agents to your library", + 'agentPicker.title2': 'Pick the ones that match how you work', + 'agentPicker.title3': 'You can always add more later — start with a few', 'back': 'Back', 'finish': 'Get Started', 'interests.area.business': 'Business & Strategy', diff --git a/src/routes/onboarding/_layout/index.test.tsx b/src/routes/onboarding/_layout/index.test.tsx index 72dbecb923..0d164ab178 100644 --- a/src/routes/onboarding/_layout/index.test.tsx +++ b/src/routes/onboarding/_layout/index.test.tsx @@ -46,8 +46,19 @@ vi.mock('@/hooks/useIsDark', () => ({ })); vi.mock('react-i18next', () => ({ - Trans: ({ values }: { values?: { mode?: string; skip?: string } }) => { + Trans: ({ + i18nKey, + values, + }: { + i18nKey?: string; + values?: { mode?: string; skip?: string }; + }) => { const modeText = values?.mode ?? ''; + + if (i18nKey === 'agent.layout.switchMessageClassic') { + return `Not feeling it today? You can switch to ${modeText}.`; + } + const skipText = values?.skip ?? ''; return `Not feeling it today? You can switch to ${modeText} or ${skipText}.`; @@ -67,7 +78,7 @@ vi.mock('@/store/serverConfig', () => ({ vi.mock('@/store/user', () => ({ useUserStore: (selector: (state: Record) => unknown) => - selector({ finishOnboarding: vi.fn() }), + selector({ setOnboardingStep: vi.fn() }), })); const renderAt = (initialPath: string) => @@ -108,6 +119,14 @@ describe('OnBoardingContainer', () => { expect(hasSkipFooter()).toBe(true); }); + it('shows the switch footer without a skip link on /onboarding/classic', () => { + renderAt('/onboarding/classic'); + expect(hasSkipFooter()).toBe(false); + expect( + screen.getByText((content) => content.includes('switch to')), + ).toBeInTheDocument(); + }); + it('hides footer when AGENT_ONBOARDING_ENABLED master switch is off', () => { mocks.AGENT_ONBOARDING_ENABLED = false; renderAt('/onboarding/agent'); diff --git a/src/routes/onboarding/_layout/index.tsx b/src/routes/onboarding/_layout/index.tsx index 32db11d6ac..974924c159 100644 --- a/src/routes/onboarding/_layout/index.tsx +++ b/src/routes/onboarding/_layout/index.tsx @@ -2,8 +2,9 @@ import { AGENT_ONBOARDING_ENABLED } from '@lobechat/business-const'; import { isDesktop } from '@lobechat/const'; -import { Center, Flexbox, FluentEmoji, Text } from '@lobehub/ui'; -import { Divider, Popconfirm } from 'antd'; +import { MAX_ONBOARDING_STEPS } from '@lobechat/types'; +import { Center, Flexbox, Text } from '@lobehub/ui'; +import { Divider } from 'antd'; import { cx, useTheme } from 'antd-style'; import { type FC, type MouseEvent, type PropsWithChildren, useCallback } from 'react'; import { Trans, useTranslation } from 'react-i18next'; @@ -21,10 +22,10 @@ import { styles } from './style'; const OnBoardingContainer: FC = ({ children }) => { const isDarkMode = useIsDark(); const theme = useTheme(); - const { t } = useTranslation(['onboarding', 'common']); + const { t } = useTranslation('onboarding'); const { pathname } = useLocation(); const navigate = useNavigate(); - const finishOnboarding = useUserStore((s) => s.finishOnboarding); + const setOnboardingStep = useUserStore((s) => s.setOnboardingStep); const enableAgentOnboarding = useServerConfigStore((s) => s.featureFlags.enableAgentOnboarding); const serverConfigInit = useServerConfigStore((s) => s.serverConfigInit); const isAgentOnboarding = pathname.startsWith('/onboarding/agent'); @@ -37,10 +38,10 @@ const OnBoardingContainer: FC = ({ children }) => { !!enableAgentOnboarding && isBranchOnboarding; - const handleConfirmSkip = useCallback(() => { - finishOnboarding(); - navigate('/'); - }, [finishOnboarding, navigate]); + const handleSkip = useCallback(() => { + void setOnboardingStep(MAX_ONBOARDING_STEPS); + navigate('/onboarding/classic?entry=skip'); + }, [navigate, setOnboardingStep]); const switchMode = useCallback( (e: MouseEvent) => { @@ -82,7 +83,6 @@ const OnBoardingContainer: FC = ({ children }) => {
= ({ children }) => { ), modeText: , skipLink: ( - - {t('agent.layout.skipConfirm.content', { ns: 'onboarding' })} - - } - icon={ - - } - title={ - - {t('agent.completionTitle', { ns: 'onboarding' })} - - } - onConfirm={handleConfirmSkip} - /> + ), skipText: , }} + i18nKey={ + isAgentOnboarding + ? 'agent.layout.switchMessage' + : 'agent.layout.switchMessageClassic' + } values={{ mode: isAgentOnboarding ? t('agent.layout.mode.classic') diff --git a/src/routes/onboarding/components/LobeMessage.tsx b/src/routes/onboarding/components/LobeMessage.tsx index 40071cca0c..b7f99febcd 100644 --- a/src/routes/onboarding/components/LobeMessage.tsx +++ b/src/routes/onboarding/components/LobeMessage.tsx @@ -23,7 +23,7 @@ const LobeMessage = memo( return ( - + {avatar ? ( ) : ( diff --git a/src/routes/onboarding/features/AgentPickerStep/AgentCard.tsx b/src/routes/onboarding/features/AgentPickerStep/AgentCard.tsx new file mode 100644 index 0000000000..4b03c4abbb --- /dev/null +++ b/src/routes/onboarding/features/AgentPickerStep/AgentCard.tsx @@ -0,0 +1,56 @@ +import type { AgentTemplate } from '@lobechat/builtin-tool-web-onboarding/agentMarketplace'; +import { Avatar, Icon } from '@lobehub/ui'; +import { cx } from 'antd-style'; +import { CheckIcon } from 'lucide-react'; +import type { KeyboardEvent } from 'react'; +import { memo, useCallback } from 'react'; + +import { styles } from './style'; + +interface AgentCardProps { + onToggle: (id: string) => void; + selected: boolean; + template: AgentTemplate; +} + +const AgentCard = memo(({ onToggle, selected, template }) => { + const handleClick = useCallback(() => onToggle(template.id), [onToggle, template.id]); + + const handleKeyDown = useCallback( + (event: KeyboardEvent) => { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault(); + onToggle(template.id); + } + }, + [onToggle, template.id], + ); + + return ( +
+ +
+
{template.title}
+ {template.description && ( +
{template.description}
+ )} +
+ +
+ ); +}); + +AgentCard.displayName = 'AgentCard'; + +export default AgentCard; diff --git a/src/routes/onboarding/features/AgentPickerStep/CategoryFilter.tsx b/src/routes/onboarding/features/AgentPickerStep/CategoryFilter.tsx new file mode 100644 index 0000000000..f78ac5bc65 --- /dev/null +++ b/src/routes/onboarding/features/AgentPickerStep/CategoryFilter.tsx @@ -0,0 +1,43 @@ +import type { MarketplaceCategory } from '@lobechat/builtin-tool-web-onboarding/agentMarketplace'; +import { cx } from 'antd-style'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { CATEGORY_LABEL_KEYS } from './categoryLabels'; +import { styles } from './style'; + +export type ActiveCategory = MarketplaceCategory | 'all'; + +interface CategoryFilterProps { + active: ActiveCategory; + allLabel: string; + categories: MarketplaceCategory[]; + onChange: (next: ActiveCategory) => void; +} + +const CategoryFilter = memo(({ active, allLabel, categories, onChange }) => { + const { t: tTool } = useTranslation('tool'); + + const renderPill = (value: ActiveCategory, label: string) => ( + + ); + + return ( +
+ {renderPill('all', allLabel)} + {categories.map((category) => renderPill(category, tTool(CATEGORY_LABEL_KEYS[category])))} +
+ ); +}); + +CategoryFilter.displayName = 'CategoryFilter'; + +export default CategoryFilter; diff --git a/src/routes/onboarding/features/AgentPickerStep/Skeleton.tsx b/src/routes/onboarding/features/AgentPickerStep/Skeleton.tsx new file mode 100644 index 0000000000..3031eca7f2 --- /dev/null +++ b/src/routes/onboarding/features/AgentPickerStep/Skeleton.tsx @@ -0,0 +1,31 @@ +import { memo } from 'react'; + +import { styles } from './style'; + +const CARD_COUNT = 6; +const PILL_COUNT = 4; + +const AgentPickerSkeleton = memo(() => ( + <> +
+ {Array.from({ length: PILL_COUNT }).map((_, index) => ( +
+ ))} +
+
+ {Array.from({ length: CARD_COUNT }).map((_, index) => ( +
+
+
+
+
+
+
+ ))} +
+ +)); + +AgentPickerSkeleton.displayName = 'AgentPickerSkeleton'; + +export default AgentPickerSkeleton; diff --git a/src/routes/onboarding/features/AgentPickerStep/categoryLabels.ts b/src/routes/onboarding/features/AgentPickerStep/categoryLabels.ts new file mode 100644 index 0000000000..864f079edd --- /dev/null +++ b/src/routes/onboarding/features/AgentPickerStep/categoryLabels.ts @@ -0,0 +1,17 @@ +import { MarketplaceCategory } from '@lobechat/builtin-tool-web-onboarding/agentMarketplace'; + +export const CATEGORY_LABEL_KEYS = { + [MarketplaceCategory.BusinessStrategy]: 'agentMarketplace.category.businessStrategy', + [MarketplaceCategory.ContentCreation]: 'agentMarketplace.category.contentCreation', + [MarketplaceCategory.CreatorEconomy]: 'agentMarketplace.category.creatorEconomy', + [MarketplaceCategory.DesignCreative]: 'agentMarketplace.category.designCreative', + [MarketplaceCategory.Engineering]: 'agentMarketplace.category.engineering', + [MarketplaceCategory.FinanceLegal]: 'agentMarketplace.category.financeLegal', + [MarketplaceCategory.LearningResearch]: 'agentMarketplace.category.learningResearch', + [MarketplaceCategory.Marketing]: 'agentMarketplace.category.marketing', + [MarketplaceCategory.Operations]: 'agentMarketplace.category.operations', + [MarketplaceCategory.PeopleHR]: 'agentMarketplace.category.peopleHR', + [MarketplaceCategory.PersonalLife]: 'agentMarketplace.category.personalLife', + [MarketplaceCategory.ProductManagement]: 'agentMarketplace.category.productManagement', + [MarketplaceCategory.SalesCustomer]: 'agentMarketplace.category.salesCustomer', +} as const satisfies Record; diff --git a/src/routes/onboarding/features/AgentPickerStep/index.test.tsx b/src/routes/onboarding/features/AgentPickerStep/index.test.tsx new file mode 100644 index 0000000000..a56ec1f748 --- /dev/null +++ b/src/routes/onboarding/features/AgentPickerStep/index.test.tsx @@ -0,0 +1,136 @@ +import { + type AgentTemplate, + MarketplaceCategory, +} from '@lobechat/builtin-tool-web-onboarding/agentMarketplace'; +import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import AgentPickerStep from './index'; + +const navigate = vi.fn(); +const finishOnboarding = vi.fn().mockResolvedValue(undefined); +const installMarketplaceAgents = vi.fn().mockResolvedValue({ + installedAgentIds: [], + skippedAgentIds: [], + summaries: [], +}); + +const templates: AgentTemplate[] = [ + { + avatar: '🤖', + category: MarketplaceCategory.Engineering, + description: 'Reviews pull requests', + id: 't1', + title: 'Code Reviewer', + }, + { + avatar: '✍️', + category: MarketplaceCategory.ContentCreation, + description: 'Drafts marketing copy', + id: 't2', + title: 'Copywriter', + }, +]; + +let swrReturn: { data: AgentTemplate[]; error?: unknown; isLoading: boolean } = { + data: templates, + error: undefined, + isLoading: false, +}; +let searchParams = new URLSearchParams(); + +vi.mock('swr', () => ({ default: () => swrReturn })); + +vi.mock('react-router-dom', () => ({ + useNavigate: () => navigate, + useSearchParams: () => [searchParams], +})); + +vi.mock('react-i18next', () => ({ + useTranslation: () => ({ + i18n: { language: 'en-US', resolvedLanguage: 'en-US' }, + t: (key: string) => key, + }), +})); + +vi.mock('../../components/LobeMessage', () => ({ + default: ({ sentences }: { sentences: string[] }) =>
{sentences.join(' / ')}
, +})); + +vi.mock('@/services/agentMarketplace', () => ({ + fetchOnboardingAgentTemplates: vi.fn(), +})); + +vi.mock('@/services/installMarketplaceAgents', () => ({ + installMarketplaceAgents: (...args: unknown[]) => installMarketplaceAgents(...args), +})); + +vi.mock('@/services/onboardingMetrics', () => ({ + trackOnboardingMarketplacePicked: vi.fn(), + trackOnboardingMarketplaceShown: vi.fn(), +})); + +const userState = { finishOnboarding, user: { interests: [] as string[] } }; +vi.mock('@/store/user', () => ({ + useUserStore: (selector: (state: typeof userState) => unknown) => selector(userState), +})); +vi.mock('@/store/user/selectors', () => ({ + userProfileSelectors: { interests: (s: typeof userState) => s.user?.interests ?? [] }, +})); + +beforeEach(() => { + navigate.mockClear(); + finishOnboarding.mockClear(); + installMarketplaceAgents.mockClear(); + swrReturn = { data: templates, error: undefined, isLoading: false }; + searchParams = new URLSearchParams(); +}); + +afterEach(() => { + cleanup(); +}); + +describe('AgentPickerStep', () => { + it('renders an agent card for each template', () => { + render(); + expect(screen.getByText('Code Reviewer')).toBeInTheDocument(); + expect(screen.getByText('Copywriter')).toBeInTheDocument(); + }); + + it('installs the selected agents then finishes onboarding on Continue', async () => { + render(); + + fireEvent.click(screen.getByText('Code Reviewer')); + const continueButton = screen.getByRole('button', { name: 'agentPicker.continue (1)' }); + fireEvent.click(continueButton); + + await waitFor(() => expect(navigate).toHaveBeenCalledWith('/')); + expect(installMarketplaceAgents).toHaveBeenCalledWith(['t1']); + expect(finishOnboarding).toHaveBeenCalledTimes(1); + }); + + it('finishes onboarding without installing on Skip', async () => { + render(); + + fireEvent.click(screen.getByRole('button', { name: 'agentPicker.skip' })); + + await waitFor(() => expect(navigate).toHaveBeenCalledWith('/')); + expect(finishOnboarding).toHaveBeenCalledTimes(1); + expect(installMarketplaceAgents).not.toHaveBeenCalled(); + }); + + it('shows a Back button that calls onBack for a normal classic entry', () => { + const onBack = vi.fn(); + render(); + + fireEvent.click(screen.getByRole('button', { name: 'back' })); + expect(onBack).toHaveBeenCalledTimes(1); + }); + + it('hides the Back button when entered via agent-onboarding skip', () => { + searchParams = new URLSearchParams('entry=skip'); + render(); + + expect(screen.queryByRole('button', { name: 'back' })).not.toBeInTheDocument(); + }); +}); diff --git a/src/routes/onboarding/features/AgentPickerStep/index.tsx b/src/routes/onboarding/features/AgentPickerStep/index.tsx new file mode 100644 index 0000000000..6e61674d75 --- /dev/null +++ b/src/routes/onboarding/features/AgentPickerStep/index.tsx @@ -0,0 +1,216 @@ +'use client'; + +import { + type AgentTemplate, + getTemplatesByCategoryPriority, + type MarketplaceCategory, +} from '@lobechat/builtin-tool-web-onboarding/agentMarketplace'; +import { Button, Flexbox, Text } from '@lobehub/ui'; +import { cssVar } from 'antd-style'; +import { Undo2Icon } from 'lucide-react'; +import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useNavigate, useSearchParams } from 'react-router-dom'; +import useSWR from 'swr'; + +import { fetchOnboardingAgentTemplates } from '@/services/agentMarketplace'; +import { installMarketplaceAgents } from '@/services/installMarketplaceAgents'; +import { + trackOnboardingMarketplacePicked, + trackOnboardingMarketplaceShown, +} from '@/services/onboardingMetrics'; +import { useUserStore } from '@/store/user'; +import { userProfileSelectors } from '@/store/user/selectors'; + +import LobeMessage from '../../components/LobeMessage'; +import { interestsToCategoryHints } from '../../interestCategoryMap'; +import AgentCard from './AgentCard'; +import CategoryFilter, { type ActiveCategory } from './CategoryFilter'; +import AgentPickerSkeleton from './Skeleton'; +import { styles } from './style'; + +interface AgentPickerStepProps { + onBack: () => void; +} + +const EMPTY_TEMPLATES: AgentTemplate[] = []; + +const AgentPickerStep = memo(({ onBack }) => { + const { t, i18n } = useTranslation('onboarding'); + const { t: tTool } = useTranslation('tool'); + const navigate = useNavigate(); + const [searchParams] = useSearchParams(); + const showBack = searchParams.get('entry') !== 'skip'; + + const finishOnboarding = useUserStore((s) => s.finishOnboarding); + const interests = useUserStore(userProfileSelectors.interests); + + const categoryHints = useMemo(() => interestsToCategoryHints(interests), [interests]); + const [requestId] = useState(() => Math.random().toString(36).slice(2)); + const swrLocale = i18n.resolvedLanguage || i18n.language; + + const { + data: allTemplates = EMPTY_TEMPLATES, + error, + isLoading, + } = useSWR( + ['onboarding-agent-picker-templates', swrLocale], + () => fetchOnboardingAgentTemplates(), + { dedupingInterval: 60_000, revalidateOnFocus: false, shouldRetryOnError: false }, + ); + + const orderedTemplates = useMemo( + () => getTemplatesByCategoryPriority(allTemplates, categoryHints), + [allTemplates, categoryHints], + ); + + const availableCategories = useMemo(() => { + const seen = new Set(); + const result: MarketplaceCategory[] = []; + for (const tpl of orderedTemplates) { + if (seen.has(tpl.category)) continue; + seen.add(tpl.category); + result.push(tpl.category); + } + return result; + }, [orderedTemplates]); + + const [active, setActive] = useState('all'); + const visibleTemplates = useMemo( + () => + active === 'all' + ? orderedTemplates + : orderedTemplates.filter((tpl) => tpl.category === active), + [active, orderedTemplates], + ); + + const [selected, setSelected] = useState>(() => new Set()); + const toggle = useCallback((id: string) => { + setSelected((prev) => { + const next = new Set(prev); + if (next.has(id)) next.delete(id); + else next.add(id); + return next; + }); + }, []); + + const [pending, setPending] = useState<'continue' | 'skip'>(); + const pendingRef = useRef(false); + + const shownRef = useRef(false); + useEffect(() => { + if (shownRef.current) return; + shownRef.current = true; + trackOnboardingMarketplaceShown({ categoryHints, requestId }); + }, [categoryHints, requestId]); + + const finish = useCallback(async () => { + await finishOnboarding(); + navigate('/'); + }, [finishOnboarding, navigate]); + + const handleSkip = useCallback(async () => { + if (pendingRef.current) return; + pendingRef.current = true; + setPending('skip'); + await finish(); + }, [finish]); + + const handleContinue = useCallback(async () => { + if (pendingRef.current || selected.size === 0) return; + pendingRef.current = true; + setPending('continue'); + + const selectedTemplateIds = [...selected]; + trackOnboardingMarketplacePicked({ categoryHints, requestId, selectedTemplateIds }); + try { + await installMarketplaceAgents(selectedTemplateIds); + } catch (installError) { + console.error('[AgentPickerStep] install failed', installError); + } + await finish(); + }, [categoryHints, finish, requestId, selected]); + + const handleBack = useCallback(() => { + if (pendingRef.current) return; + onBack(); + }, [onBack]); + + const showLoading = isLoading && allTemplates.length === 0; + const showEmpty = !isLoading && visibleTemplates.length === 0; + + return ( + + + + {t('agentPicker.subtitle')} + + + {showLoading ? ( + + ) : showEmpty ? ( +
+ {error + ? tTool('agentMarketplace.picker.failedToLoad') + : tTool('agentMarketplace.picker.empty')} +
+ ) : ( + <> + +
+
+ {visibleTemplates.map((tpl) => ( + + ))} +
+
+ + )} + +
+ {showBack ? ( + + ) : ( + + )} +
+ + +
+
+
+ ); +}); + +AgentPickerStep.displayName = 'AgentPickerStep'; + +export default AgentPickerStep; diff --git a/src/routes/onboarding/features/AgentPickerStep/style.ts b/src/routes/onboarding/features/AgentPickerStep/style.ts new file mode 100644 index 0000000000..71896044c9 --- /dev/null +++ b/src/routes/onboarding/features/AgentPickerStep/style.ts @@ -0,0 +1,195 @@ +import { createStaticStyles, keyframes } from 'antd-style'; + +const pulse = keyframes` + 0%, + 100% { + opacity: 1; + } + + 50% { + opacity: 0.5; + } +`; + +export const styles = createStaticStyles(({ css, cssVar }) => ({ + card: css` + cursor: pointer; + + display: flex; + gap: 12px; + align-items: flex-start; + + padding: 12px 14px; + border: 1px solid ${cssVar.colorFillSecondary}; + border-radius: ${cssVar.borderRadiusLG}; + + background: ${cssVar.colorBgContainer}; + + transition: + border-color ${cssVar.motionDurationMid}, + background ${cssVar.motionDurationMid}; + + &:hover { + border-color: ${cssVar.colorPrimaryHover}; + } + + &:focus-visible { + outline: 2px solid ${cssVar.colorPrimary}; + outline-offset: 2px; + } + `, + cardBody: css` + overflow: hidden; + display: flex; + flex: 1; + flex-direction: column; + gap: 2px; + + min-width: 0; + `, + cardCheck: css` + flex: none; + color: ${cssVar.colorPrimary}; + `, + cardCheckHidden: css` + visibility: hidden; + `, + cardDescription: css` + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + + font-size: 12px; + line-height: 1.5; + color: ${cssVar.colorTextSecondary}; + `, + cardSelected: css` + border-color: ${cssVar.colorPrimary}; + background: ${cssVar.colorPrimaryBg}; + + &:hover { + border-color: ${cssVar.colorPrimary}; + } + `, + cardTitle: css` + overflow: hidden; + + font-size: 14px; + font-weight: 600; + color: ${cssVar.colorText}; + text-overflow: ellipsis; + white-space: nowrap; + `, + empty: css` + display: flex; + align-items: center; + justify-content: center; + + min-height: 160px; + padding: 24px; + + font-size: 13px; + color: ${cssVar.colorTextTertiary}; + text-align: center; + `, + filterBar: css` + display: flex; + flex-wrap: wrap; + gap: 6px; + `, + footer: css` + display: flex; + align-items: center; + justify-content: space-between; + margin-top: 8px; + `, + footerActions: css` + display: flex; + gap: 8px; + align-items: center; + `, + grid: css` + display: grid; + grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); + gap: 10px; + align-content: start; + `, + pill: css` + cursor: pointer; + + padding: 4px 12px; + border: 1px solid ${cssVar.colorBorderSecondary}; + border-radius: 999px; + + font-size: 13px; + color: ${cssVar.colorTextSecondary}; + white-space: nowrap; + + background: transparent; + + transition: + border-color ${cssVar.motionDurationMid}, + background ${cssVar.motionDurationMid}, + color ${cssVar.motionDurationMid}; + + &:hover { + color: ${cssVar.colorText}; + background: ${cssVar.colorFillTertiary}; + } + + &:focus-visible { + outline: 2px solid ${cssVar.colorPrimary}; + outline-offset: 2px; + } + `, + pillActive: css` + font-weight: 500; + color: ${cssVar.colorText}; + background: ${cssVar.colorFillSecondary}; + border-color: ${cssVar.colorFillSecondary}; + + &:hover { + background: ${cssVar.colorFillSecondary}; + } + `, + scrollArea: css` + overflow-y: auto; + overscroll-behavior: contain; + + max-height: min(46vh, 360px); + margin-inline: -4px; + padding-inline: 4px; + `, + skeletonAvatar: css` + flex: none; + + width: 36px; + height: 36px; + border-radius: ${cssVar.borderRadius}; + + background: ${cssVar.colorFillTertiary}; + animation: ${pulse} 1.5s ease-in-out infinite; + `, + skeletonCard: css` + display: flex; + gap: 12px; + + padding: 12px 14px; + border: 1px solid ${cssVar.colorFillSecondary}; + border-radius: ${cssVar.borderRadiusLG}; + `, + skeletonLine: css` + height: 10px; + border-radius: ${cssVar.borderRadius}; + background: ${cssVar.colorFillTertiary}; + animation: ${pulse} 1.5s ease-in-out infinite; + `, + skeletonPill: css` + width: 72px; + height: 28px; + border-radius: 999px; + background: ${cssVar.colorFillTertiary}; + animation: ${pulse} 1.5s ease-in-out infinite; + `, +})); diff --git a/src/routes/onboarding/features/ProSettingsStep.tsx b/src/routes/onboarding/features/ProSettingsStep.tsx index 6c4b3ca1d3..11c03e142f 100644 --- a/src/routes/onboarding/features/ProSettingsStep.tsx +++ b/src/routes/onboarding/features/ProSettingsStep.tsx @@ -5,7 +5,6 @@ import { cssVar } from 'antd-style'; import { Undo2Icon } from 'lucide-react'; import { memo, useCallback, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useNavigate } from 'react-router-dom'; import ModelSelect from '@/features/ModelSelect'; import LobeMessage from '@/routes/onboarding/components/LobeMessage'; @@ -17,18 +16,15 @@ import KlavisServerList from '../components/KlavisServerList'; interface ProSettingsStepProps { onBack: () => void; + onNext: () => void; } -const ProSettingsStep = memo(({ onBack }) => { +const ProSettingsStep = memo(({ onBack, onNext }) => { const { t } = useTranslation('onboarding'); - const navigate = useNavigate(); const enableKlavis = useServerConfigStore(serverConfigSelectors.enableKlavis); - const [updateDefaultModel, finishOnboarding] = useUserStore((s) => [ - s.updateDefaultModel, - s.finishOnboarding, - ]); + const updateDefaultModel = useUserStore((s) => s.updateDefaultModel); const defaultAgentConfig = useUserStore( (s) => settingsSelectors.currentSettings(s).defaultAgent?.config, @@ -37,13 +33,12 @@ const ProSettingsStep = memo(({ onBack }) => { const [isNavigating, setIsNavigating] = useState(false); const isNavigatingRef = useRef(false); - const handleFinish = useCallback(async () => { + const handleNext = useCallback(() => { if (isNavigatingRef.current) return; isNavigatingRef.current = true; setIsNavigating(true); - await finishOnboarding(); - navigate('/'); - }, [finishOnboarding, navigate]); + onNext(); + }, [onNext]); const handleBack = useCallback(() => { if (isNavigatingRef.current) return; @@ -98,9 +93,9 @@ const ProSettingsStep = memo(({ onBack }) => { disabled={isNavigating} style={{ minWidth: 120 }} type="primary" - onClick={() => void handleFinish()} + onClick={handleNext} > - {t('finish')} + {t('next')} diff --git a/src/routes/onboarding/interestCategoryMap.test.ts b/src/routes/onboarding/interestCategoryMap.test.ts new file mode 100644 index 0000000000..7818e0d938 --- /dev/null +++ b/src/routes/onboarding/interestCategoryMap.test.ts @@ -0,0 +1,33 @@ +import { MarketplaceCategory } from '@lobechat/builtin-tool-web-onboarding/agentMarketplace'; +import { describe, expect, it } from 'vitest'; + +import { interestsToCategoryHints } from './interestCategoryMap'; + +describe('interestsToCategoryHints', () => { + it('maps known interest keys to marketplace categories in order', () => { + expect(interestsToCategoryHints(['coding', 'writing'])).toEqual([ + MarketplaceCategory.Engineering, + MarketplaceCategory.ContentCreation, + ]); + }); + + it('deduplicates categories that several interests map to', () => { + expect(interestsToCategoryHints(['investing', 'finance-legal'])).toEqual([ + MarketplaceCategory.FinanceLegal, + ]); + }); + + it('drops interests without a mapped category', () => { + expect(interestsToCategoryHints(['health', 'hobbies', 'parenting'])).toEqual([]); + }); + + it('drops free-form custom interests', () => { + expect(interestsToCategoryHints(['my own thing', 'coding'])).toEqual([ + MarketplaceCategory.Engineering, + ]); + }); + + it('returns an empty list for no interests', () => { + expect(interestsToCategoryHints([])).toEqual([]); + }); +}); diff --git a/src/routes/onboarding/interestCategoryMap.ts b/src/routes/onboarding/interestCategoryMap.ts new file mode 100644 index 0000000000..df282d7899 --- /dev/null +++ b/src/routes/onboarding/interestCategoryMap.ts @@ -0,0 +1,34 @@ +import { MarketplaceCategory } from '@lobechat/builtin-tool-web-onboarding/agentMarketplace'; +import type { InterestAreaKey } from '@lobechat/const'; + +const INTEREST_CATEGORY_MAP: Partial> = { + 'business': MarketplaceCategory.BusinessStrategy, + 'coding': MarketplaceCategory.Engineering, + 'creator': MarketplaceCategory.CreatorEconomy, + 'design': MarketplaceCategory.DesignCreative, + 'education': MarketplaceCategory.LearningResearch, + 'finance-legal': MarketplaceCategory.FinanceLegal, + 'hr': MarketplaceCategory.PeopleHR, + 'investing': MarketplaceCategory.FinanceLegal, + 'marketing': MarketplaceCategory.Marketing, + 'operations': MarketplaceCategory.Operations, + 'personal': MarketplaceCategory.PersonalLife, + 'product': MarketplaceCategory.ProductManagement, + 'sales': MarketplaceCategory.SalesCustomer, + 'writing': MarketplaceCategory.ContentCreation, +}; + +export const interestsToCategoryHints = (interests: string[]): MarketplaceCategory[] => { + const seen = new Set(); + const hints: MarketplaceCategory[] = []; + + for (const interest of interests) { + const category = INTEREST_CATEGORY_MAP[interest as InterestAreaKey]; + if (category && !seen.has(category)) { + seen.add(category); + hints.push(category); + } + } + + return hints; +}; diff --git a/src/features/Conversation/Messages/AssistantGroup/Tool/Detail/Intervention/installMarketplaceAgents.test.ts b/src/services/installMarketplaceAgents.test.ts similarity index 100% rename from src/features/Conversation/Messages/AssistantGroup/Tool/Detail/Intervention/installMarketplaceAgents.test.ts rename to src/services/installMarketplaceAgents.test.ts diff --git a/src/features/Conversation/Messages/AssistantGroup/Tool/Detail/Intervention/installMarketplaceAgents.ts b/src/services/installMarketplaceAgents.ts similarity index 100% rename from src/features/Conversation/Messages/AssistantGroup/Tool/Detail/Intervention/installMarketplaceAgents.ts rename to src/services/installMarketplaceAgents.ts From d3973a5cc011d56b90319fb1f4d55b45cb2c426a Mon Sep 17 00:00:00 2001 From: YuTengjing Date: Tue, 19 May 2026 19:14:47 +0800 Subject: [PATCH 031/224] =?UTF-8?q?=E2=9C=A8=20feat:=20add=20chat=20cost?= =?UTF-8?q?=20estimate=20support=20(#14876)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/en-US/common.json | 4 +- locales/en-US/spend.json | 4 +- locales/en-US/subscription.json | 40 +-- locales/zh-CN/chat.json | 7 + locales/zh-CN/common.json | 4 +- locales/zh-CN/components.json | 18 +- locales/zh-CN/subscription.json | 30 +- packages/const/src/settings/common.ts | 3 + packages/context-engine/src/index.ts | 12 +- .../context-engine/src/processors/index.ts | 2 +- .../__tests__/attachmentTokenBuckets.test.ts | 139 +++++++++ .../tokenAccounting/attachmentTokenBuckets.ts | 215 ++++++++++++++ .../src/tokenAccounting/index.ts | 3 + .../src/core/usageConverters/index.ts | 11 + .../utils/estimateChatCost.test.ts | 263 ++++++++++++++++++ .../usageConverters/utils/estimateChatCost.ts | 236 ++++++++++++++++ .../src/core/usageConverters/utils/index.ts | 11 + packages/types/src/user/settings/general.ts | 1 + packages/utils/src/format.test.ts | 26 ++ packages/utils/src/format.ts | 6 + .../useBusinessChatInputSendAreaPrefix.tsx | 4 + src/features/Conversation/ChatInput/index.tsx | 4 +- .../Usage/UsageDetail/TokenProgress.test.ts | 28 -- .../Usage/UsageDetail/TokenProgress.tsx | 8 +- .../Extras/Usage/UsageDetail/index.tsx | 3 +- .../components/ModelDetailPanel.test.tsx | 196 +++++++++++++ .../components/ModelDetailPanel.tsx | 191 +++++++++---- src/locales/default/chat.ts | 8 + src/locales/default/common.ts | 4 +- src/locales/default/components.ts | 10 + src/locales/default/spend.ts | 5 +- src/locales/default/subscription.ts | 47 ++-- src/store/user/selectors.ts | 1 + .../slices/settings/selectors/general.test.ts | 1 + .../user/slices/settings/selectors/index.ts | 1 + .../slices/settings/selectors/usage.test.ts | 30 ++ .../user/slices/settings/selectors/usage.ts | 12 + 37 files changed, 1425 insertions(+), 163 deletions(-) create mode 100644 packages/context-engine/src/tokenAccounting/__tests__/attachmentTokenBuckets.test.ts create mode 100644 packages/context-engine/src/tokenAccounting/attachmentTokenBuckets.ts create mode 100644 packages/model-runtime/src/core/usageConverters/utils/estimateChatCost.test.ts create mode 100644 packages/model-runtime/src/core/usageConverters/utils/estimateChatCost.ts create mode 100644 src/business/client/hooks/useBusinessChatInputSendAreaPrefix.tsx delete mode 100644 src/features/Conversation/Messages/components/Extras/Usage/UsageDetail/TokenProgress.test.ts create mode 100644 src/features/ModelSwitchPanel/components/ModelDetailPanel.test.tsx create mode 100644 src/store/user/slices/settings/selectors/usage.test.ts create mode 100644 src/store/user/slices/settings/selectors/usage.ts diff --git a/locales/en-US/common.json b/locales/en-US/common.json index 3314139c72..eb0dc911f4 100644 --- a/locales/en-US/common.json +++ b/locales/en-US/common.json @@ -5,8 +5,8 @@ "agentOnboardingPromo.description": "Set up your agent teams in a quick chat with Lobe AI. Your existing agents remain unchanged.", "agentOnboardingPromo.title": "Quick Wizard", "alert.cloud.action": "Try now", - "alert.cloud.desc": "All registered users get {{credit}} free computing credits per month—no setup needed. Includes global cloud sync and advanced web search.", - "alert.cloud.descOnMobile": "All registered users get {{credit}} free computing credits per month—no setup needed.", + "alert.cloud.desc": "All registered users get {{credit}} free credits per month—no setup needed. Includes global cloud sync and advanced web search.", + "alert.cloud.descOnMobile": "All registered users get {{credit}} free credits per month—no setup needed.", "alert.cloud.title": "{{name}} beta is live", "alreadyUpToDate": "Already Up to Date", "appLoading.appIdle": "Ready to start", diff --git a/locales/en-US/spend.json b/locales/en-US/spend.json index 14a4ae5b90..fbc615e50c 100644 --- a/locales/en-US/spend.json +++ b/locales/en-US/spend.json @@ -35,9 +35,9 @@ "table.columns.type.enums.imageGeneration": "Image Generation", "table.columns.type.enums.videoGeneration": "Video Generation", "table.columns.type.title": "Type", - "table.desc": "Details of computing credits usage for text generation, embedding, image generation, etc.", + "table.desc": "Details of credit usage for text generation, embedding, image generation, etc.", "table.more": "View Details", - "table.title": "Computing Credits Usage Details", + "table.title": "Credit Usage Details", "table.totalToken.input": "Input", "table.totalToken.output": "Output" } diff --git a/locales/en-US/subscription.json b/locales/en-US/subscription.json index 67fa0138a8..392bd8750a 100644 --- a/locales/en-US/subscription.json +++ b/locales/en-US/subscription.json @@ -29,8 +29,8 @@ "cancelPlan.desc": "After cancellation, you will be downgraded to the free version when the current plan expires.", "cancelPlan.title": "Cancel Subscription", "cancelSubscription": "Cancel Subscription", - "compare.hobbyCreditTooltip": "Does not include monthly computing credits, you need to configure your own model API", - "compare.monthlyCredit": "Monthly Computing Credits", + "compare.hobbyCreditTooltip": "Does not include monthly credits, you need to configure your own model API", + "compare.monthlyCredit": "Monthly Credits", "compare.title": "Plan Comparison", "compareAllPlans": "View All Plans", "comparePlans": "View Plans", @@ -132,11 +132,11 @@ "limitation.chat.topupSuccess.action": "Continue Chatting", "limitation.chat.topupSuccess.desc": "Your top-up credits are now active. Enjoy AI chatting. Your current plan includes:", "limitation.chat.topupSuccess.title": "Top-up Successful", - "limitation.expired.desc": "Your {{plan}} computing credits expired on {{expiredAt}}. Upgrade your plan now to get computing credits.", - "limitation.expired.title": "Computing Credits Expired", + "limitation.expired.desc": "Your {{plan}} credits expired on {{expiredAt}}. Upgrade your plan now to get credits.", + "limitation.expired.title": "Credits Expired", "limitation.hobby.action": "Configured, continue chatting", "limitation.hobby.configAPI": "Configure API", - "limitation.hobby.desc": "Your free computing credits have been exhausted. Please configure a custom model API to continue.", + "limitation.hobby.desc": "Your free credits have been exhausted. Please configure a custom model API to continue.", "limitation.hobby.docs": "View configuration docs", "limitation.hobby.tip": "Remember to switch to a model with custom API Key", "limitation.hobby.title": "Please Configure Model Service API", @@ -160,10 +160,10 @@ "limitation.insufficientBudget.title": "Insufficient Credits", "limitation.limited.action": "Upgrade Now", "limitation.limited.advanceFeature": "Upgrade to enjoy premium features:", - "limitation.limited.desc": "Your {{plan}} computing credits have been exhausted. Upgrade now to get more credits.", - "limitation.limited.descUltimate": "Your {{plan}} computing credits have been exhausted. Please top up credits to continue.", + "limitation.limited.desc": "Your {{plan}} credits have been exhausted. Upgrade now to get more credits.", + "limitation.limited.descUltimate": "Your {{plan}} credits have been exhausted. Please top up credits to continue.", "limitation.limited.referralTip": "Invite friends, both get {{reward}}M", - "limitation.limited.title": "Computing Credits Exhausted", + "limitation.limited.title": "Credits Exhausted", "limitation.limited.topup": "Top-Up Credits", "limitation.limited.upgrade": "Upgrade to Higher Plan", "limitation.limited.upgradeToPlan": "Upgrade to {{plan}}", @@ -187,7 +187,7 @@ "limitation.video.topupSuccess.desc": "Your top-up credits are now active. Enjoy AI video generation. Your current plan includes:", "limitation.video.topupSuccess.title": "Top-up Successful", "modelPricing.button": "View Pricing Documentation", - "modelPricing.desc": "{{name}} uses Credits to measure AI model usage. The table below shows computing credits per 1M Tokens.", + "modelPricing.desc": "{{name}} uses Credits to measure AI model usage. The table below shows credits per 1M Tokens.", "modelPricing.title": "Text Model Pricing", "models.input": "Input", "models.intro": "Introduction", @@ -231,12 +231,12 @@ "plans.credit.api": "Custom API", "plans.credit.apiDesc": "Requires your own model API configuration", "plans.credit.apiProvider": "Supports 20+ mainstream model providers including OpenAI / Anthropic / OpenRouter", - "plans.credit.buy": "Purchase Computing Credits", - "plans.credit.buyDesc": "Also supports purchasing computing credits on demand", - "plans.credit.none": "No built-in computing credits", + "plans.credit.buy": "Purchase Credits", + "plans.credit.buyDesc": "Also supports purchasing credits on demand", + "plans.credit.none": "No built-in credits", "plans.credit.tip": "{{credit}} free credits per month", - "plans.credit.title": "Computing Credits", - "plans.credit.tooltip": "Monthly model message computing credits", + "plans.credit.title": "Credits", + "plans.credit.tooltip": "Monthly model message credits", "plans.current": "Current Plan", "plans.downgradePlan": "Target Downgrade Plan", "plans.downgradeTip": "Your subscription has been canceled. You cannot perform other operations until the cancellation is complete", @@ -306,16 +306,16 @@ "plans.unlimited": "Unlimited", "qa.desc": "If your question is not answered, check <1>Product Documentation for more FAQs, or contact us.", "qa.detail": "View Details", - "qa.list.credit.a": "Computing credits are a metric used by {{cloud}} to measure AI model usage when calling models. Different AI models consume different amounts of computing credits.", - "qa.list.credit.q": "What are computing credits?", + "qa.list.credit.a": "Credits are how {{cloud}} measures AI model usage. Different AI models consume different amounts of credits.", + "qa.list.credit.q": "What are credits?", "qa.list.embeddings.a": "Vector storage is not equal to the original size of your uploaded or imported dataset, but is calculated based on the vectorization of pure text content in your files. For example, a 1-page PDF file (1000-1500 characters) may only take up about 1 vector storage entry when extracted and vectorized into pure text. You can view your usage under \"{{usage}}\".", "qa.list.embeddings.q": "How is vector storage calculated?", - "qa.list.free.a": "{{name}} has always adhered to open source principles. For professional developers, you can use all open source capabilities through self-deployment of the community version. In {{cloud}}, we provide all registered users with {{credit}} free computing credits per month, ready to use without complex configuration. If you need more usage, you can subscribe to {{starter}}, {{premium}} or {{ultimate}}.", + "qa.list.free.a": "{{name}} has always adhered to open source principles. For professional developers, you can use all open source capabilities through self-deployment of the community version. In {{cloud}}, we provide all registered users with {{credit}} free credits per month, ready to use without complex configuration. If you need more usage, you can subscribe to {{starter}}, {{premium}} or {{ultimate}}.", "qa.list.free.q": "Can {{name}} be used for free?", "qa.list.highUsage.a": "Credit consumption depends heavily on conversation length. As a conversation grows, each new message sends the entire history as input tokens, which increases cost significantly. We recommend starting a new conversation when topics change. Also, if there is a long gap between messages, the prompt cache may expire, causing a spike in cost. Some models like the Gemini Pro series use tiered pricing — input beyond 200K tokens is charged at double the rate.", "qa.list.highUsage.q": "Why are my credits being consumed faster than expected?", - "qa.list.limit.a": "{{cloud}} subscription plans are divided into {{starter}}, {{premium}} and {{ultimate}}, each providing different computing credits. If your current plan credits are insufficient, we recommend upgrading. You can also purchase credit packages on the \"{{funds}}\" page for pay-as-you-go usage. Alternatively, you can set up a custom model API key to use API credits purchased from other sources.", - "qa.list.limit.q": "What if I run out of computing credits?", + "qa.list.limit.a": "{{cloud}} subscription plans are divided into {{starter}}, {{premium}} and {{ultimate}}, each providing different credit amounts. If your current plan credits are insufficient, we recommend upgrading. You can also purchase credit packages on the \"{{funds}}\" page for pay-as-you-go usage. Alternatively, you can set up a custom model API key to use API credits purchased from other sources.", + "qa.list.limit.q": "What if I run out of credits?", "qa.list.management.a": "On the {{subscribe}} page, you can \"Upgrade / Downgrade\" your current subscription plan, or switch between yearly and monthly billing. Through \"{{usage}}-{{management}}\" you can go to Stripe for subscription management, and you can cancel your subscription at any time. After cancellation, you will be automatically downgraded to the free version when your current plan expires.", "qa.list.management.q": "How do I change or cancel my subscription?", "qa.support.community": "Community Support", @@ -426,7 +426,7 @@ "usage.credit.time.days": "{{days}} days", "usage.credit.time.daysAndHours": "{{days}} days {{hours}} hours", "usage.credit.time.hours": "{{hours}} hours", - "usage.credit.title": "Computing Credits Usage", + "usage.credit.title": "Credit Usage", "usage.overview.charge": "Charges", "usage.overview.included": "Plan Usage", "usage.overview.onDemand": "On-demand", diff --git a/locales/zh-CN/chat.json b/locales/zh-CN/chat.json index 5b1c706545..7b8dd3fd98 100644 --- a/locales/zh-CN/chat.json +++ b/locales/zh-CN/chat.json @@ -220,6 +220,13 @@ "inbox.title": "Lobe AI", "input.addAi": "添加一条助理消息", "input.addUser": "添加一条用户消息", + "input.costEstimate.creditsPerMillionTokens": "{{credits}} 积分/M tokens", + "input.costEstimate.hint": "预估费用:约 {{credits}} 积分", + "input.costEstimate.inputLabel": "输入", + "input.costEstimate.outputLabel": "输出", + "input.costEstimate.settingsLink": "调整提醒阈值", + "input.costEstimate.tokenCount": "约 {{tokens}} tokens", + "input.costEstimate.tooltip": "基于当前上下文、工具和模型定价估算。实际费用可能不同。", "input.disclaimer": "助理也可能出错。关键信息请以你的判断为准", "input.errorMsg": "发送遇到了问题:{{errorMsg}}。你可以重试,或稍后再发", "input.more": "更多", diff --git a/locales/zh-CN/common.json b/locales/zh-CN/common.json index fb91a57f76..a8b5659382 100644 --- a/locales/zh-CN/common.json +++ b/locales/zh-CN/common.json @@ -5,8 +5,8 @@ "agentOnboardingPromo.description": "与 Lobe AI 快速聊几句,即可设置你的助理团队。你现有的助理不会受到影响。", "agentOnboardingPromo.title": "快速向导", "alert.cloud.action": "立即体验", - "alert.cloud.desc": "所有注册用户每月可获得 {{credit}} 免费计算额度—无需设置。包括全球云同步和高级网页搜索功能。", - "alert.cloud.descOnMobile": "所有注册用户每月可获得 {{credit}} 免费计算额度—无需设置。", + "alert.cloud.desc": "所有注册用户每月可获得 {{credit}} 免费积分—无需设置。包括全球云同步和高级网页搜索功能。", + "alert.cloud.descOnMobile": "所有注册用户每月可获得 {{credit}} 免费积分—无需设置。", "alert.cloud.title": "{{name}} 开始公测", "alreadyUpToDate": "已是最新版本", "appLoading.appIdle": "准备启动", diff --git a/locales/zh-CN/components.json b/locales/zh-CN/components.json index 9cc719095b..d00fbea2ef 100644 --- a/locales/zh-CN/components.json +++ b/locales/zh-CN/components.json @@ -71,10 +71,10 @@ "FileParsingStatus.chunks.status.errorResult": "分块失败,请检查后重试。失败原因:", "FileParsingStatus.chunks.status.processing": "分块中…", "FileParsingStatus.chunks.status.processingTip": "服务端正在拆分文本块,关闭页面不会影响进度", - "GenerationModelItem.creditsPerImageApproximate": "约 {{amount}} 算力积分/张", - "GenerationModelItem.creditsPerImageExact": "{{amount}} 算力积分/张", - "GenerationModelItem.creditsPerVideoApproximate": "约 {{amount}} 算力积分/条", - "GenerationModelItem.creditsPerVideoExact": "{{amount}} 算力积分/条", + "GenerationModelItem.creditsPerImageApproximate": "约 {{amount}} 积分/张", + "GenerationModelItem.creditsPerImageExact": "{{amount}} 积分/张", + "GenerationModelItem.creditsPerVideoApproximate": "约 {{amount}} 积分/条", + "GenerationModelItem.creditsPerVideoExact": "{{amount}} 积分/条", "GoBack.back": "返回", "HtmlPreview.actions.download": "下载", "HtmlPreview.actions.preview": "预览", @@ -121,6 +121,16 @@ "ModelSwitchPanel.detail.context": "上下文长度", "ModelSwitchPanel.detail.pricing": "价格", "ModelSwitchPanel.detail.pricing.cachedInput": "缓存输入 ${{amount}}/百万", + "ModelSwitchPanel.detail.pricing.credits.cachedInput": "缓存输入 {{amount}} 积分/M tokens", + "ModelSwitchPanel.detail.pricing.credits.image": "积分/张", + "ModelSwitchPanel.detail.pricing.credits.input": "输入 {{amount}} 积分/M tokens", + "ModelSwitchPanel.detail.pricing.credits.megapixel": "积分/MP", + "ModelSwitchPanel.detail.pricing.credits.millionCharacters": "积分/M chars", + "ModelSwitchPanel.detail.pricing.credits.millionTokens": "积分/M tokens", + "ModelSwitchPanel.detail.pricing.credits.output": "输出 {{amount}} 积分/M tokens", + "ModelSwitchPanel.detail.pricing.credits.perImage": "约 {{amount}} 积分/张", + "ModelSwitchPanel.detail.pricing.credits.perVideo": "约 {{amount}} 积分/条", + "ModelSwitchPanel.detail.pricing.credits.second": "积分/s", "ModelSwitchPanel.detail.pricing.group.audio": "音频", "ModelSwitchPanel.detail.pricing.group.image": "图像", "ModelSwitchPanel.detail.pricing.group.text": "文本", diff --git a/locales/zh-CN/subscription.json b/locales/zh-CN/subscription.json index 667a31c20d..928a8b1757 100644 --- a/locales/zh-CN/subscription.json +++ b/locales/zh-CN/subscription.json @@ -55,6 +55,12 @@ "credits.autoTopUp.toggle": "启用自动充值", "credits.autoTopUp.upgradeHint": "订阅付费计划以启用自动充值", "credits.autoTopUp.validation.targetMustExceedThreshold": "目标余额必须大于触发阈值", + "credits.costEstimateHint.desc": "当下一次模型调用的预估费用达到阈值时,在发送前显示轻量提醒", + "credits.costEstimateHint.saveError": "保存费用预估提醒设置失败", + "credits.costEstimateHint.saveSuccess": "费用预估提醒设置已保存", + "credits.costEstimateHint.threshold": "提醒阈值", + "credits.costEstimateHint.title": "费用预估提醒", + "credits.costEstimateHint.validation.threshold": "阈值必须大于或等于 0", "credits.packages.auto": "自动", "credits.packages.charged": "已扣费 ${{amount}}", "credits.packages.expired": "已过期", @@ -231,12 +237,12 @@ "plans.credit.api": "自定义 API", "plans.credit.apiDesc": "需配置您自己的模型 API", "plans.credit.apiProvider": "支持 OpenAI / Anthropic / OpenRouter 等 20+ 主流模型提供商", - "plans.credit.buy": "购买算力积分", - "plans.credit.buyDesc": "也支持按需购买算力积分", - "plans.credit.none": "无内置算力积分", - "plans.credit.tip": "每月赠送 {{credit}} 点免费算力", - "plans.credit.title": "算力积分", - "plans.credit.tooltip": "每月模型消息计算所用的算力积分", + "plans.credit.buy": "购买积分", + "plans.credit.buyDesc": "也支持按需购买积分", + "plans.credit.none": "无内置积分", + "plans.credit.tip": "每月赠送 {{credit}} 免费积分", + "plans.credit.title": "积分", + "plans.credit.tooltip": "每月可用于模型调用的积分", "plans.current": "当前计划", "plans.downgradePlan": "目标降级计划", "plans.downgradeTip": "您已取消订阅,需等待取消生效后才能进行其他操作", @@ -306,16 +312,16 @@ "plans.unlimited": "无限制", "qa.desc": "如果您的问题未被解答,请查看 <1>产品文档 获取更多常见问题,或联系我们。", "qa.detail": "查看详情", - "qa.list.credit.a": "算力积分是 {{cloud}} 用于衡量调用 AI 模型时的使用量指标。不同模型消耗的积分不同。", - "qa.list.credit.q": "什么是算力积分?", + "qa.list.credit.a": "积分是 {{cloud}} 用于衡量 AI 模型调用用量的指标。不同模型消耗的积分不同。", + "qa.list.credit.q": "什么是积分?", "qa.list.embeddings.a": "向量存储并不等于您上传或导入数据集的原始大小,而是根据文件中纯文本内容向量化后的结果计算。例如,一页 PDF 文件(1000-1500 字符)提取为纯文本后,可能只占用约 1 个向量存储条目。您可在“{{usage}}”中查看使用情况。", "qa.list.embeddings.q": "向量存储是如何计算的?", - "qa.list.free.a": "{{name}} 始终坚持开源原则。专业开发者可通过部署社区版使用全部开源功能。在 {{cloud}} 中,我们为所有注册用户每月提供 {{credit}} 点免费算力,无需复杂配置即可使用。如需更多使用量,可订阅 {{starter}}、{{premium}} 或 {{ultimate}}。", + "qa.list.free.a": "{{name}} 始终坚持开源原则。专业开发者可通过部署社区版使用全部开源功能。在 {{cloud}} 中,我们为所有注册用户每月提供 {{credit}} 免费积分,无需复杂配置即可使用。如需更多使用量,可订阅 {{starter}}、{{premium}} 或 {{ultimate}}。", "qa.list.free.q": "{{name}} 可以免费使用吗?", "qa.list.highUsage.a": "积分消耗与对话长度密切相关。随着对话变长,每条新消息都会将完整历史作为输入 token 发送,导致费用显著增加。建议在切换话题时开启新对话。此外,如果两次发送间隔较长,提示词缓存可能失效,也会导致费用骤增。部分模型(如 Gemini Pro 系列)采用分梯度定价,输入超过 200K token 后费率翻倍。", "qa.list.highUsage.q": "为什么我的积分消耗比预期快?", - "qa.list.limit.a": "{{cloud}} 的订阅计划分为 {{starter}}、{{premium}} 和 {{ultimate}},每个计划提供不同的算力积分。如当前计划积分不足,建议升级计划。您也可以在「{{funds}}」页面购买积分包进行按量付费充值。此外,您也可以配置自定义模型 API 密钥,使用其他来源购买的 API 积分。", - "qa.list.limit.q": "算力积分用完怎么办?", + "qa.list.limit.a": "{{cloud}} 的订阅计划分为 {{starter}}、{{premium}} 和 {{ultimate}},每个计划提供不同的积分额度。如当前计划积分不足,建议升级计划。您也可以在「{{funds}}」页面购买积分包进行按量付费充值。此外,您也可以配置自定义模型 API 密钥,使用其他来源购买的 API 积分。", + "qa.list.limit.q": "积分用完怎么办?", "qa.list.management.a": "在 {{subscribe}} 页面,您可以“升级 / 降级”当前订阅计划,或切换年付与月付。在“{{usage}}-{{management}}”中可跳转至 Stripe 进行订阅管理,您可随时取消订阅。取消后,当前计划到期时将自动降级为免费版。", "qa.list.management.q": "如何更改或取消订阅?", "qa.support.community": "社区支持", @@ -426,7 +432,7 @@ "usage.credit.time.days": "{{days}} 天", "usage.credit.time.daysAndHours": "{{days}} 天 {{hours}} 小时", "usage.credit.time.hours": "{{hours}} 小时", - "usage.credit.title": "算力积分使用情况", + "usage.credit.title": "积分使用情况", "usage.overview.charge": "按量计费", "usage.overview.included": "计划内使用", "usage.overview.onDemand": "按需使用", diff --git a/packages/const/src/settings/common.ts b/packages/const/src/settings/common.ts index efd285641d..d7dbc27c6f 100644 --- a/packages/const/src/settings/common.ts +++ b/packages/const/src/settings/common.ts @@ -1,8 +1,11 @@ import type { UserGeneralConfig } from '@lobechat/types'; +export const DEFAULT_COST_ESTIMATE_WARNING_THRESHOLD = 2; + export const DEFAULT_COMMON_SETTINGS: UserGeneralConfig = { animationMode: 'agile', // contextMenuMode not set default value, use env to calc + costEstimateWarningThreshold: DEFAULT_COST_ESTIMATE_WARNING_THRESHOLD, fontSize: 14, highlighterTheme: 'lobe-theme', isDevMode: false, diff --git a/packages/context-engine/src/index.ts b/packages/context-engine/src/index.ts index 0ba4ab4940..a1207331b1 100644 --- a/packages/context-engine/src/index.ts +++ b/packages/context-engine/src/index.ts @@ -21,16 +21,26 @@ export * from './providers'; export type { ContextTokenAccounting, CountContextTokensParams, + InputTokenBuckets, MessageTokenBreakdown, TokenSourceType, ToolDefinitionTokenBreakdown, } from './tokenAccounting'; -export { countContextTokens, DEFAULT_DRIFT_MULTIPLIER } from './tokenAccounting'; +export { + addTokenBuckets, + countContextTokens, + DEFAULT_DRIFT_MULTIPLIER, + EMPTY_TOKEN_BUCKETS, + estimatePendingUploadTokenBuckets, + estimateSentMessageAttachmentTokenBuckets, + isTextLikeUploadFile, +} from './tokenAccounting'; // Processors export type { PlaceholderValue, PlaceholderValueMap } from './processors'; export { buildPlaceholderGenerators, formatPlaceholderValues, + getSlicedMessages, GroupMessageFlattenProcessor, HistoryTruncateProcessor, InputTemplateProcessor, diff --git a/packages/context-engine/src/processors/index.ts b/packages/context-engine/src/processors/index.ts index 577951eb82..3e01099240 100644 --- a/packages/context-engine/src/processors/index.ts +++ b/packages/context-engine/src/processors/index.ts @@ -9,7 +9,7 @@ export { type OrchestrationAgentInfo, } from './GroupOrchestrationFilter'; export { GroupRoleTransformProcessor } from './GroupRoleTransform'; -export { HistoryTruncateProcessor } from './HistoryTruncate'; +export { getSlicedMessages, HistoryTruncateProcessor } from './HistoryTruncate'; export { InputTemplateProcessor } from './InputTemplate'; export { MessageCleanupProcessor } from './MessageCleanup'; export { MessageContentProcessor } from './MessageContent'; diff --git a/packages/context-engine/src/tokenAccounting/__tests__/attachmentTokenBuckets.test.ts b/packages/context-engine/src/tokenAccounting/__tests__/attachmentTokenBuckets.test.ts new file mode 100644 index 0000000000..595ca36285 --- /dev/null +++ b/packages/context-engine/src/tokenAccounting/__tests__/attachmentTokenBuckets.test.ts @@ -0,0 +1,139 @@ +import type { UIChatMessage, UploadFileItem } from '@lobechat/types'; +import { describe, expect, it } from 'vitest'; + +import { + estimatePendingUploadTokenBuckets, + estimateSentMessageAttachmentTokenBuckets, + isTextLikeUploadFile, +} from '../attachmentTokenBuckets'; + +const mkMsg = (m: Partial & { role: UIChatMessage['role'] }): UIChatMessage => + ({ + content: '', + createdAt: 0, + id: 'm', + updatedAt: 0, + ...m, + }) as UIChatMessage; + +const mkUploadFile = ({ + id, + name, + size, + type, +}: { + id: string; + name: string; + size: number; + type: string; +}): UploadFileItem => ({ + file: { name, size, type } as File, + fileUrl: `https://example.com/${name}`, + id, + status: 'success', +}); + +describe('attachment token buckets', () => { + it('estimates persisted file context and visual input buckets', () => { + const result = estimateSentMessageAttachmentTokenBuckets( + [ + mkMsg({ + fileList: [ + { + content: 'parsed text', + fileType: 'text/plain', + id: 'file-id', + name: 'note.txt', + size: 11, + url: 'https://example.com/note.txt', + }, + ], + imageList: [{ alt: 'image.png', id: 'image-id', url: 'https://example.com/image.png' }], + role: 'user', + videoList: [{ alt: 'video.mp4', id: 'video-id', url: 'https://example.com/video.mp4' }], + }), + ], + { canUseVideo: true, canUseVision: true }, + ); + + expect(result.textTokens).toBeGreaterThan(0); + expect(result.imageTokens).toBe(1000); + expect(result.videoTokens).toBe(1000); + }); + + it('keeps file context but skips visual buckets when the model cannot consume them', () => { + const result = estimateSentMessageAttachmentTokenBuckets( + [ + mkMsg({ + imageList: [{ alt: 'image.png', id: 'image-id', url: 'https://example.com/image.png' }], + role: 'user', + videoList: [{ alt: 'video.mp4', id: 'video-id', url: 'https://example.com/video.mp4' }], + }), + ], + { canUseVideo: false, canUseVision: false }, + ); + + expect(result.textTokens).toBeGreaterThan(0); + expect(result.imageTokens).toBe(0); + expect(result.videoTokens).toBe(0); + }); + + it('estimates pending upload buckets with text fallback and loaded text content', () => { + const files = [ + mkUploadFile({ id: 'text-file', name: 'note.txt', size: 40, type: 'text/plain' }), + mkUploadFile({ id: 'image-file', name: 'image.png', size: 100, type: 'image/png' }), + mkUploadFile({ id: 'video-file', name: 'video.mp4', size: 200, type: 'video/mp4' }), + ]; + + const fallbackResult = estimatePendingUploadTokenBuckets( + files, + { canUseVideo: true, canUseVision: true }, + {}, + ); + const loadedResult = estimatePendingUploadTokenBuckets( + files, + { canUseVideo: true, canUseVision: true }, + { 'text-file': 'loaded text content' }, + ); + + expect(fallbackResult.textTokens).toBeGreaterThanOrEqual(10); + expect(loadedResult.textTokens).toBeGreaterThan(0); + expect(fallbackResult.imageTokens).toBe(1000); + expect(fallbackResult.videoTokens).toBe(1000); + expect(loadedResult.imageTokens).toBe(1000); + expect(loadedResult.videoTokens).toBe(1000); + }); + + it('detects text-like upload files by mime type and extension', () => { + expect( + isTextLikeUploadFile( + mkUploadFile({ id: 'json', name: 'data.bin', size: 1, type: 'application/json' }), + ), + ).toBe(true); + expect( + isTextLikeUploadFile( + mkUploadFile({ + id: 'markdown', + name: 'README.md', + size: 1, + type: 'application/octet-stream', + }), + ), + ).toBe(true); + expect( + isTextLikeUploadFile( + mkUploadFile({ + id: 'typescript', + name: 'app.ts', + size: 1, + type: 'application/octet-stream', + }), + ), + ).toBe(true); + expect( + isTextLikeUploadFile( + mkUploadFile({ id: 'pdf', name: 'document.pdf', size: 1, type: 'application/pdf' }), + ), + ).toBe(false); + }); +}); diff --git a/packages/context-engine/src/tokenAccounting/attachmentTokenBuckets.ts b/packages/context-engine/src/tokenAccounting/attachmentTokenBuckets.ts new file mode 100644 index 0000000000..392884244b --- /dev/null +++ b/packages/context-engine/src/tokenAccounting/attachmentTokenBuckets.ts @@ -0,0 +1,215 @@ +// cspell:ignore tokenx +import { filesPrompts } from '@lobechat/prompts'; +import type { + ChatFileItem, + ChatImageItem, + ChatVideoItem, + UIChatMessage, + UploadFileItem, +} from '@lobechat/types'; +import { getMimeType } from '@lobechat/utils/mimeType'; +import { estimateTokenCount } from 'tokenx'; + +const ESTIMATE_INPUT_MESSAGE_ID = '__cost_estimate_input__'; +const VISUAL_INPUT_TOKEN_ESTIMATE = 1000; +const BYTES_PER_TEXT_TOKEN_ESTIMATE = 4; +const TEXT_UPLOAD_MIME_TYPES = new Set([ + 'application/javascript', + 'application/json', + 'application/ld+json', + 'application/sql', + 'application/toml', + 'application/typescript', + 'application/x-httpd-php', + 'application/x-javascript', + 'application/x-ndjson', + 'application/x-sh', + 'application/x-typescript', + 'application/x-yaml', + 'application/xml', +]); +const TEXT_UPLOAD_EXTENSION_OVERRIDES = new Set(['.cs', '.jsonl', '.ts', '.tsx']); + +export interface InputTokenBuckets { + imageTokens: number; + textTokens: number; + videoTokens: number; +} + +interface AttachmentTokenOptions { + canUseVideo: boolean; + canUseVision: boolean; +} + +export const EMPTY_TOKEN_BUCKETS: InputTokenBuckets = { + imageTokens: 0, + textTokens: 0, + videoTokens: 0, +}; + +const countPromptTextTokens = (content: string) => { + if (!content) return 0; + + return estimateTokenCount(content); +}; + +const countFileContextTokens = ({ + fileList, + imageList, + messageId, + videoList, +}: { + fileList?: ChatFileItem[]; + imageList?: ChatImageItem[]; + messageId: string; + videoList?: ChatVideoItem[]; +}) => { + const prompt = filesPrompts({ + addUrl: false, + fileList, + imageList, + messageId, + videoList, + }); + + return countPromptTextTokens(prompt); +}; + +export const estimateSentMessageAttachmentTokenBuckets = ( + messages: UIChatMessage[], + { canUseVideo, canUseVision }: AttachmentTokenOptions, +): InputTokenBuckets => { + let textTokens = 0; + let imageTokens = 0; + let videoTokens = 0; + + for (const message of messages) { + if (message.role !== 'user') continue; + + const fileList = message.fileList ?? []; + const imageList = message.imageList ?? []; + const videoList = message.videoList ?? []; + + if (fileList.length === 0 && imageList.length === 0 && videoList.length === 0) continue; + + textTokens += countFileContextTokens({ + fileList, + imageList, + messageId: message.id, + videoList, + }); + + if (canUseVision) { + imageTokens += imageList.length * VISUAL_INPUT_TOKEN_ESTIMATE; + } + + if (canUseVideo) { + videoTokens += videoList.length * VISUAL_INPUT_TOKEN_ESTIMATE; + } + } + + return { + imageTokens, + textTokens, + videoTokens, + }; +}; + +const getUploadFileUrl = (item: UploadFileItem) => + item.fileUrl || item.base64Url || item.previewUrl || ''; + +const isTextLikeMimeType = (type: string) => + type.startsWith('text/') || TEXT_UPLOAD_MIME_TYPES.has(type); + +const getFileExtension = (name: string) => { + const dotIndex = name.lastIndexOf('.'); + + return dotIndex >= 0 ? name.slice(dotIndex).toLowerCase() : ''; +}; + +export const isTextLikeUploadFile = (item: UploadFileItem) => { + const declaredType = item.file.type.toLowerCase(); + const inferredType = getMimeType(item.file.name).toLowerCase(); + const name = item.file.name.toLowerCase(); + + return ( + isTextLikeMimeType(declaredType) || + isTextLikeMimeType(inferredType) || + TEXT_UPLOAD_EXTENSION_OVERRIDES.has(getFileExtension(name)) + ); +}; + +const estimateTextFileTokensBySize = (size: number) => + Math.ceil(size / BYTES_PER_TEXT_TOKEN_ESTIMATE); + +export const estimatePendingUploadTokenBuckets = ( + files: UploadFileItem[], + { canUseVideo, canUseVision }: AttachmentTokenOptions, + textFileContents: Record, +): InputTokenBuckets => { + if (files.length === 0) return EMPTY_TOKEN_BUCKETS; + + const fileList: ChatFileItem[] = []; + const imageList: ChatImageItem[] = []; + const videoList: ChatVideoItem[] = []; + let pendingTextFallbackTokens = 0; + + for (const item of files) { + const type = item.file.type || ''; + const url = getUploadFileUrl(item); + + if (type.startsWith('image')) { + imageList.push({ + alt: item.file.name || item.id, + id: item.id, + url, + }); + continue; + } + + if (type.startsWith('video')) { + videoList.push({ + alt: item.file.name || item.id, + id: item.id, + url, + }); + continue; + } + + const textContent = textFileContents[item.id]; + if (isTextLikeUploadFile(item) && textContent === undefined) { + pendingTextFallbackTokens += estimateTextFileTokensBySize(item.file.size || 0); + } + + fileList.push({ + content: textContent, + fileType: type, + id: item.id, + name: item.file.name || item.id, + size: item.file.size || 0, + url, + }); + } + + return { + imageTokens: canUseVision ? imageList.length * VISUAL_INPUT_TOKEN_ESTIMATE : 0, + textTokens: + countFileContextTokens({ + fileList, + imageList, + messageId: ESTIMATE_INPUT_MESSAGE_ID, + videoList, + }) + pendingTextFallbackTokens, + videoTokens: canUseVideo ? videoList.length * VISUAL_INPUT_TOKEN_ESTIMATE : 0, + }; +}; + +export const addTokenBuckets = (...buckets: InputTokenBuckets[]): InputTokenBuckets => + buckets.reduce( + (sum, bucket) => ({ + imageTokens: sum.imageTokens + bucket.imageTokens, + textTokens: sum.textTokens + bucket.textTokens, + videoTokens: sum.videoTokens + bucket.videoTokens, + }), + EMPTY_TOKEN_BUCKETS, + ); diff --git a/packages/context-engine/src/tokenAccounting/index.ts b/packages/context-engine/src/tokenAccounting/index.ts index 0fc6a3ceb6..e6d2e45beb 100644 --- a/packages/context-engine/src/tokenAccounting/index.ts +++ b/packages/context-engine/src/tokenAccounting/index.ts @@ -1,3 +1,4 @@ +// cspell:ignore tokenx import { estimateTokenCount } from 'tokenx'; import type { @@ -8,6 +9,8 @@ import type { ToolDefinitionTokenBreakdown, } from './types'; +export * from './attachmentTokenBuckets'; + export const DEFAULT_DRIFT_MULTIPLIER = 1.25; const ZERO_BY_SOURCE = (): Record => ({ diff --git a/packages/model-runtime/src/core/usageConverters/index.ts b/packages/model-runtime/src/core/usageConverters/index.ts index 853ddd1988..d4f6636920 100644 --- a/packages/model-runtime/src/core/usageConverters/index.ts +++ b/packages/model-runtime/src/core/usageConverters/index.ts @@ -3,5 +3,16 @@ export { convertGoogleAIUsage } from './google-ai'; export { convertOpenAIResponseUsage, convertOpenAIUsage } from './openai'; export { computeImageCost } from './utils/computeImageCost'; export { computeVideoCost } from './utils/computeVideoCost'; +export { + type ChatCostEstimate, + type ChatInputTokenEstimate, + estimateChatCostFromMessages, + type EstimateChatCostFromMessagesOptions, + estimateChatCostFromTokens, + type EstimateChatCostFromTokensInput, + estimateChatOutputTokens, + estimateOpenAIChatInputTokens, + type EstimateOpenAIChatInputTokensOptions, +} from './utils/estimateChatCost'; export { resolveImageSinglePrice } from './utils/resolveImageSinglePrice'; export { resolveVideoSinglePrice } from './utils/resolveVideoSinglePrice'; diff --git a/packages/model-runtime/src/core/usageConverters/utils/estimateChatCost.test.ts b/packages/model-runtime/src/core/usageConverters/utils/estimateChatCost.test.ts new file mode 100644 index 0000000000..4b04e89804 --- /dev/null +++ b/packages/model-runtime/src/core/usageConverters/utils/estimateChatCost.test.ts @@ -0,0 +1,263 @@ +import type { Pricing } from 'model-bank'; +import { describe, expect, it } from 'vitest'; + +import { + estimateChatCostFromMessages, + estimateChatCostFromTokens, + estimateChatOutputTokens, + estimateOpenAIChatInputTokens, +} from './estimateChatCost'; + +describe('estimateChatCost', () => { + describe('estimateChatOutputTokens', () => { + it('applies the output ratio below the cap', () => { + expect(estimateChatOutputTokens(4000)).toBe(2000); + expect(estimateChatOutputTokens(1000)).toBe(500); + }); + + it('caps output tokens for large inputs', () => { + expect(estimateChatOutputTokens(20_000)).toBe(8192); + expect(estimateChatOutputTokens(1_000_000)).toBe(8192); + }); + }); + + describe('estimateOpenAIChatInputTokens', () => { + it('counts text, tools, reasoning, and image input buckets', () => { + const estimate = estimateOpenAIChatInputTokens( + [ + { + content: [ + { text: 'hello world', type: 'text' }, + { image_url: { url: 'https://example.com/image.png' }, type: 'image_url' }, + ], + role: 'user', + }, + { + content: 'assistant response', + reasoning: { content: 'hidden reasoning' }, + role: 'assistant', + tool_calls: [ + { + function: { arguments: '{"city":"Shanghai"}', name: 'weather' }, + id: 'call_1', + type: 'function', + }, + ], + }, + ], + { + tools: [ + { + function: { + description: 'Get weather', + name: 'weather', + parameters: { type: 'object' }, + }, + type: 'function', + }, + ], + }, + ); + + expect(estimate.imageTokens).toBe(1000); + expect(estimate.textTokens).toBeGreaterThan(0); + expect(estimate.totalTokens).toBe(estimate.textTokens + 1000); + expect(estimate.videoTokens).toBe(0); + }); + + it('counts video inputs in the video token bucket', () => { + const estimate = estimateOpenAIChatInputTokens( + [ + { + content: [ + { text: 'summarize this clip', type: 'text' }, + { type: 'video_url', video_url: { url: 'https://example.com/video.mp4' } }, + ], + role: 'user', + }, + ], + { videoTokenEstimate: 1200 }, + ); + + expect(estimate.imageTokens).toBe(0); + expect(estimate.textTokens).toBeGreaterThan(0); + expect(estimate.totalTokens).toBe(estimate.textTokens + 1200); + expect(estimate.videoTokens).toBe(1200); + }); + + it('handles assistant tool-call messages with null content', () => { + const estimate = estimateOpenAIChatInputTokens([ + { + // @ts-expect-error OpenAI-compatible runtime payloads can contain null content. + content: null, + role: 'assistant', + tool_calls: [ + { + function: { arguments: '{"city":"Shanghai"}', name: 'weather' }, + id: 'call_1', + type: 'function', + }, + ], + }, + ]); + + expect(estimate.imageTokens).toBe(0); + expect(estimate.textTokens).toBeGreaterThan(0); + expect(estimate.totalTokens).toBe(estimate.textTokens); + expect(estimate.videoTokens).toBe(0); + }); + }); + + describe('estimateChatCostFromTokens', () => { + it('uses total input tokens to select tiered rates', () => { + const pricing: Pricing = { + currency: 'USD', + units: [ + { + name: 'textInput', + strategy: 'tiered', + tiers: [ + { rate: 1, upTo: 100 }, + { rate: 2, upTo: 'infinity' }, + ], + unit: 'millionTokens', + }, + { + name: 'textOutput', + strategy: 'tiered', + tiers: [ + { rate: 10, upTo: 100 }, + { rate: 20, upTo: 'infinity' }, + ], + unit: 'millionTokens', + }, + ], + }; + + const estimate = estimateChatCostFromTokens(pricing, { + outputTextTokens: 10, + textTokens: 120, + }); + + expect(estimate?.estimatedCost).toBe(0.000_44); + expect(estimate?.breakdown.map((item) => item.segments?.[0]?.rate)).toEqual([2, 20]); + }); + + it('returns undefined when pricing is missing', () => { + expect(estimateChatCostFromTokens(undefined, { textTokens: 1000 })).toBeUndefined(); + }); + + it('falls multimodal input back to text pricing when dedicated modality units are missing', () => { + const pricing: Pricing = { + units: [{ name: 'textInput', rate: 1, strategy: 'fixed', unit: 'millionTokens' }], + }; + + const estimate = estimateChatCostFromTokens(pricing, { + audioTokens: 400, + imageTokens: 200, + outputTextTokens: 0, + textTokens: 100, + videoTokens: 300, + }); + + expect(estimate?.estimatedCost).toBe(0.001); + expect(estimate?.inputAudioTokens).toBe(400); + expect(estimate?.inputImageTokens).toBe(200); + expect(estimate?.inputTextTokens).toBe(100); + expect(estimate?.inputVideoTokens).toBe(300); + expect(estimate?.totalInputTokens).toBe(1000); + expect(estimate?.breakdown.find((item) => item.unit.name === 'textInput')?.quantity).toBe( + 1000, + ); + }); + + it('keeps modality input separate when dedicated modality units exist', () => { + const pricing: Pricing = { + units: [ + { name: 'textInput', rate: 1, strategy: 'fixed', unit: 'millionTokens' }, + { name: 'imageInput', rate: 5, strategy: 'fixed', unit: 'millionTokens' }, + ], + }; + + const estimate = estimateChatCostFromTokens(pricing, { + imageTokens: 200, + outputTextTokens: 0, + textTokens: 100, + }); + + expect(estimate?.breakdown.find((item) => item.unit.name === 'textInput')?.quantity).toBe( + 100, + ); + expect(estimate?.breakdown.find((item) => item.unit.name === 'imageInput')?.quantity).toBe( + 200, + ); + }); + }); + + describe('estimateChatCostFromMessages', () => { + it('builds a cost estimate from OpenAI chat messages', () => { + const pricing: Pricing = { + units: [ + { name: 'textInput', rate: 1, strategy: 'fixed', unit: 'millionTokens' }, + { name: 'textOutput', rate: 2, strategy: 'fixed', unit: 'millionTokens' }, + ], + }; + + const estimate = estimateChatCostFromMessages(pricing, [ + { content: 'hello world', role: 'user' }, + ]); + + expect(estimate?.estimatedCost).toBeGreaterThan(0); + expect(estimate?.estimatedOutputTokens).toBeGreaterThan(0); + expect(estimate?.totalInputTokens).toBeGreaterThan(0); + expect(estimate?.usage.totalTokens).toBe( + estimate!.totalInputTokens + estimate!.estimatedOutputTokens, + ); + }); + + it('forwards video inputs to video pricing units', () => { + const pricing: Pricing = { + units: [{ name: 'videoInput', rate: 3, strategy: 'fixed', unit: 'millionTokens' }], + }; + + const estimate = estimateChatCostFromMessages( + pricing, + [ + { + content: [{ type: 'video_url', video_url: { url: 'https://example.com/video.mp4' } }], + role: 'user', + }, + ], + { videoTokenEstimate: 2000 }, + ); + + expect(estimate?.estimatedCost).toBe(0.006); + expect(estimate?.inputVideoTokens).toBe(2000); + expect(estimate?.totalInputTokens).toBeGreaterThan(2000); + expect(estimate?.breakdown.map((item) => item.unit.name)).toEqual(['videoInput']); + }); + + it('bills estimated image inputs through text pricing when image pricing is unavailable', () => { + const pricing: Pricing = { + units: [{ name: 'textInput', rate: 1, strategy: 'fixed', unit: 'millionTokens' }], + }; + + const estimate = estimateChatCostFromMessages( + pricing, + [ + { + content: [{ image_url: { url: 'https://example.com/image.png' }, type: 'image_url' }], + role: 'user', + }, + ], + { imageTokenEstimate: 2000 }, + ); + + expect(estimate?.inputImageTokens).toBe(2000); + expect(estimate?.totalInputTokens).toBeGreaterThan(2000); + expect(estimate?.breakdown.find((item) => item.unit.name === 'textInput')?.quantity).toBe( + estimate?.totalInputTokens, + ); + }); + }); +}); diff --git a/packages/model-runtime/src/core/usageConverters/utils/estimateChatCost.ts b/packages/model-runtime/src/core/usageConverters/utils/estimateChatCost.ts new file mode 100644 index 0000000000..4d9ad7d014 --- /dev/null +++ b/packages/model-runtime/src/core/usageConverters/utils/estimateChatCost.ts @@ -0,0 +1,236 @@ +import type { ModelTokensUsage } from '@lobechat/types'; +import type { Pricing, PricingUnitName } from 'model-bank'; +import { estimateTokenCount } from 'tokenx'; + +import type { ChatCompletionTool, OpenAIChatMessage } from '../../../types/chat'; +import type { ComputeChatCostOptions, PricingComputationResult } from './computeChatCost'; +import { computeChatCost } from './computeChatCost'; + +const DEFAULT_IMAGE_INPUT_TOKEN_ESTIMATE = 1000; +const DEFAULT_VIDEO_INPUT_TOKEN_ESTIMATE = 1000; +const OUTPUT_INPUT_RATIO = 0.5; +const OUTPUT_TOKEN_CAP = 8192; + +export interface ChatInputTokenEstimate { + imageTokens: number; + textTokens: number; + totalTokens: number; + videoTokens: number; +} + +export interface EstimateOpenAIChatInputTokensOptions { + /** + * Conservative token estimate for each image input when exact image accounting is unavailable. + */ + imageTokenEstimate?: number; + /** + * Tool definitions are counted as input prompt tokens because providers receive them with the + * chat request. + */ + tools?: ChatCompletionTool[]; + /** + * Conservative token estimate for each video input when exact video accounting is unavailable. + */ + videoTokenEstimate?: number; +} + +/** + * Token buckets used to estimate chat costs before a provider returns real usage. + * + * Prefer `computeChatCost` with actual provider usage for final billing and reconciliation. + */ +export interface EstimateChatCostFromTokensInput { + audioTokens?: number; + imageTokens?: number; + /** + * Optional expected output tokens. When omitted, the estimator uses a heuristic based on input + * tokens. + */ + outputTextTokens?: number; + textTokens: number; + videoTokens?: number; +} + +/** + * Cost estimate for budget pre-checks and UI hints. This is intentionally approximate and should + * not be treated as the authoritative charged amount. + */ +export interface ChatCostEstimate extends PricingComputationResult { + estimatedCost: number; + estimatedOutputTokens: number; + inputAudioTokens: number; + inputImageTokens: number; + inputTextTokens: number; + inputVideoTokens: number; + totalInputTokens: number; + usage: ModelTokensUsage; +} + +export interface EstimateChatCostFromMessagesOptions + extends ComputeChatCostOptions, EstimateOpenAIChatInputTokensOptions {} + +const estimateSerializableTokens = (value: unknown): number => { + if (value === undefined || value === null) return 0; + + const text = typeof value === 'string' ? value : JSON.stringify(value); + return text ? estimateTokenCount(text) : 0; +}; + +const hasPricingUnit = (pricing: Pricing | undefined, unitName: PricingUnitName) => + pricing?.units.some((unit) => unit.name === unitName) ?? false; + +/** + * Estimates output tokens for budget pre-checks and UI hints. + * + * The default heuristic assumes output is half of total input tokens, capped at 8192 tokens. + */ +export function estimateChatOutputTokens(totalInputTokens: number): number { + return Math.min(totalInputTokens * OUTPUT_INPUT_RATIO, OUTPUT_TOKEN_CAP); +} + +/** + * Estimates OpenAI-compatible chat input tokens without requiring provider-side usage. + * + * This helper is for pre-flight checks only. It counts text-like fields, tool definitions, and a + * conservative per-image estimate; use provider usage plus `computeChatCost` for final billing. + */ +export function estimateOpenAIChatInputTokens( + messages: OpenAIChatMessage[], + options: EstimateOpenAIChatInputTokensOptions = {}, +): ChatInputTokenEstimate { + const imageTokenEstimate = options.imageTokenEstimate ?? DEFAULT_IMAGE_INPUT_TOKEN_ESTIMATE; + const videoTokenEstimate = options.videoTokenEstimate ?? DEFAULT_VIDEO_INPUT_TOKEN_ESTIMATE; + let textTokens = 0; + let imageTokens = 0; + let videoTokens = 0; + + for (const message of messages) { + textTokens += estimateSerializableTokens(message.role); + textTokens += estimateSerializableTokens(message.name); + textTokens += estimateSerializableTokens(message.tool_call_id); + textTokens += estimateSerializableTokens(message.tool_calls); + textTokens += estimateSerializableTokens(message.reasoning?.content); + + if (typeof message.content === 'string') { + textTokens += estimateSerializableTokens(message.content); + continue; + } + + if (!Array.isArray(message.content)) { + textTokens += estimateSerializableTokens(message.content); + continue; + } + + for (const part of message.content) { + if (part.type === 'text') { + textTokens += estimateSerializableTokens(part.text); + continue; + } + + if (part.type === 'image_url') { + imageTokens += imageTokenEstimate; + continue; + } + + if (part.type === 'video_url') { + videoTokens += videoTokenEstimate; + continue; + } + + textTokens += estimateSerializableTokens(part); + } + } + + textTokens += estimateSerializableTokens(options.tools); + + return { + imageTokens, + textTokens, + totalTokens: textTokens + imageTokens + videoTokens, + videoTokens, + }; +} + +/** + * Estimates chat cost from known input token buckets. + * + * `outputTextTokens` defaults to `estimateChatOutputTokens(totalInputTokens)`. Pricing lookup + * options are forwarded to `computeChatCost`. + */ +export function estimateChatCostFromTokens( + pricing: Pricing | undefined, + input: EstimateChatCostFromTokensInput, + options?: ComputeChatCostOptions, +): ChatCostEstimate | undefined { + const inputAudioTokens = input.audioTokens ?? 0; + const inputImageTokens = input.imageTokens ?? 0; + const inputTextTokens = input.textTokens; + const inputVideoTokens = input.videoTokens ?? 0; + const totalInputTokens = inputTextTokens + inputImageTokens + inputAudioTokens + inputVideoTokens; + const estimatedOutputTokens = + input.outputTextTokens ?? estimateChatOutputTokens(totalInputTokens); + const hasAudioInputUnit = hasPricingUnit(pricing, 'audioInput'); + const hasImageInputUnit = hasPricingUnit(pricing, 'imageInput'); + const hasVideoInputUnit = hasPricingUnit(pricing, 'videoInput'); + // Some model cards price multimodal inputs through text input unless a dedicated unit exists. + const billableTextTokens = + inputTextTokens + + (hasAudioInputUnit ? 0 : inputAudioTokens) + + (hasImageInputUnit ? 0 : inputImageTokens) + + (hasVideoInputUnit ? 0 : inputVideoTokens); + + const usage: ModelTokensUsage = { + inputAudioTokens: hasAudioInputUnit ? inputAudioTokens : undefined, + inputImageTokens: hasImageInputUnit ? inputImageTokens : undefined, + inputTextTokens: billableTextTokens, + inputVideoTokens: hasVideoInputUnit ? inputVideoTokens : undefined, + outputTextTokens: estimatedOutputTokens, + totalInputTokens, + totalOutputTokens: estimatedOutputTokens, + totalTokens: totalInputTokens + estimatedOutputTokens, + }; + + const result = computeChatCost(pricing, usage, options); + if (!result) return; + + return { + ...result, + estimatedCost: result.totalCost, + estimatedOutputTokens, + inputAudioTokens, + inputImageTokens, + inputTextTokens, + inputVideoTokens, + totalInputTokens, + usage, + }; +} + +/** + * Estimates chat cost directly from OpenAI-compatible messages and optional tool definitions. + * + * This is intended for budget pre-checks and UI hints before the model call. Final charged cost + * should still be computed from actual provider usage with `computeChatCost`. + */ +export function estimateChatCostFromMessages( + pricing: Pricing | undefined, + messages: OpenAIChatMessage[], + options: EstimateChatCostFromMessagesOptions = {}, +): ChatCostEstimate | undefined { + const { tools, imageTokenEstimate, lookupParams, usdToCnyRate, videoTokenEstimate } = options; + const inputTokens = estimateOpenAIChatInputTokens(messages, { + imageTokenEstimate, + tools, + videoTokenEstimate, + }); + + return estimateChatCostFromTokens( + pricing, + { + imageTokens: inputTokens.imageTokens, + textTokens: inputTokens.textTokens, + videoTokens: inputTokens.videoTokens, + }, + { lookupParams, usdToCnyRate }, + ); +} diff --git a/packages/model-runtime/src/core/usageConverters/utils/index.ts b/packages/model-runtime/src/core/usageConverters/utils/index.ts index 4c39a68228..62f3ca03d6 100644 --- a/packages/model-runtime/src/core/usageConverters/utils/index.ts +++ b/packages/model-runtime/src/core/usageConverters/utils/index.ts @@ -13,4 +13,15 @@ export { type VideoCostResult, type VideoGenerationParams, } from './computeVideoCost'; +export { + type ChatCostEstimate, + type ChatInputTokenEstimate, + estimateChatCostFromMessages, + type EstimateChatCostFromMessagesOptions, + estimateChatCostFromTokens, + type EstimateChatCostFromTokensInput, + estimateChatOutputTokens, + estimateOpenAIChatInputTokens, + type EstimateOpenAIChatInputTokensOptions, +} from './estimateChatCost'; export { withUsageCost } from './withUsageCost'; diff --git a/packages/types/src/user/settings/general.ts b/packages/types/src/user/settings/general.ts index a18acbad33..6352ee1081 100644 --- a/packages/types/src/user/settings/general.ts +++ b/packages/types/src/user/settings/general.ts @@ -9,6 +9,7 @@ export type ContextMenuMode = 'disabled' | 'default'; export interface UserGeneralConfig { animationMode?: AnimationMode; contextMenuMode?: ContextMenuMode; + costEstimateWarningThreshold?: number; /** * Whether to auto-scroll during AI streaming output * @default true diff --git a/packages/utils/src/format.test.ts b/packages/utils/src/format.test.ts index 3f27bf6f00..031056033e 100644 --- a/packages/utils/src/format.test.ts +++ b/packages/utils/src/format.test.ts @@ -13,6 +13,7 @@ import { formatSpeed, formatTime, formatTokenNumber, + formatUsageValue, } from './format'; describe('format', () => { @@ -254,6 +255,31 @@ describe('format', () => { }); }); + describe('formatUsageValue', () => { + it('formats token usage details with short units', () => { + expect(formatUsageValue(93_405)).toBe('93.4K'); + expect(formatUsageValue(92_119)).toBe('92.1K'); + expect(formatUsageValue(3_488)).toBe('3.5K'); + expect(formatUsageValue(189_018)).toBe('189K'); + }); + + it('formats credit usage details with the same short units', () => { + expect(formatUsageValue(16_127)).toBe('16.1K'); + expect(formatUsageValue(16_179)).toBe('16.2K'); + }); + + it('keeps small token counts readable without suffixes', () => { + expect(formatUsageValue(0)).toBe('0'); + expect(formatUsageValue(6)).toBe('6'); + expect(formatUsageValue(999)).toBe('999'); + }); + + it('formats million-level token counts with M suffix', () => { + expect(formatUsageValue(1_000_000)).toBe('1M'); + expect(formatUsageValue(1_500_000)).toBe('1.5M'); + }); + }); + describe('formatDate', () => { it('should format date correctly', () => { const date = new Date('2023-05-15T12:00:00Z'); diff --git a/packages/utils/src/format.ts b/packages/utils/src/format.ts index d30ffae746..c0dd984655 100644 --- a/packages/utils/src/format.ts +++ b/packages/utils/src/format.ts @@ -77,6 +77,12 @@ export const formatIntergerNumber = (num?: any) => { return numeral(num).format('0,0'); }; +export const formatUsageValue = (number: number) => { + if (number >= 1_000_000) return `${numeral(number / 1_000_000).format('0.[0]')}M`; + if (number >= 1_000) return `${numeral(number / 1_000).format('0.[0]')}K`; + return numeral(number).format('0,0'); +}; + export const formatTokenNumber = (num: number): string => { if (!num && num !== 0) return '--'; diff --git a/src/business/client/hooks/useBusinessChatInputSendAreaPrefix.tsx b/src/business/client/hooks/useBusinessChatInputSendAreaPrefix.tsx new file mode 100644 index 0000000000..97fb8f4c67 --- /dev/null +++ b/src/business/client/hooks/useBusinessChatInputSendAreaPrefix.tsx @@ -0,0 +1,4 @@ +import type { ReactNode } from 'react'; + +export const useBusinessChatInputSendAreaPrefix = (sendAreaPrefix?: ReactNode): ReactNode => + sendAreaPrefix; diff --git a/src/features/Conversation/ChatInput/index.tsx b/src/features/Conversation/ChatInput/index.tsx index 642fb11b1e..bac70258f0 100644 --- a/src/features/Conversation/ChatInput/index.tsx +++ b/src/features/Conversation/ChatInput/index.tsx @@ -8,6 +8,7 @@ import { type ReactNode } from 'react'; import { memo, useCallback, useEffect, useMemo, useRef } from 'react'; import { useTranslation } from 'react-i18next'; +import { useBusinessChatInputSendAreaPrefix } from '@/business/client/hooks/useBusinessChatInputSendAreaPrefix'; import type { ActionKeys, ChatInputFeature } from '@/features/ChatInput'; import { ChatInputProvider, DesktopChatInput } from '@/features/ChatInput'; import { @@ -229,6 +230,7 @@ const ChatInput = memo( // When disableQueue is set (e.g. onboarding), block sending while loading. const disabled = isInputEmpty || isUploadingFiles || (!!disableQueue && isInputLoading); const shouldUsePlainSendButton = !showSendMenu && !!sendMenu; + const businessSendAreaPrefix = useBusinessChatInputSendAreaPrefix(sendAreaPrefix); // Send handler - gets message, clears editor immediately, then sends const handleSend: SendButtonHandler = useCallback( @@ -322,7 +324,7 @@ const ChatInput = memo( leftContent={leftContent} placeholderVariant={placeholderVariant} runtimeConfigSlot={runtimeConfigSlot} - sendAreaPrefix={sendAreaPrefix} + sendAreaPrefix={businessSendAreaPrefix} showRuntimeConfig={showRuntimeConfig} /> diff --git a/src/features/Conversation/Messages/components/Extras/Usage/UsageDetail/TokenProgress.test.ts b/src/features/Conversation/Messages/components/Extras/Usage/UsageDetail/TokenProgress.test.ts deleted file mode 100644 index 4bed73159b..0000000000 --- a/src/features/Conversation/Messages/components/Extras/Usage/UsageDetail/TokenProgress.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import { formatUsageValue } from './TokenProgress'; - -describe('formatUsageValue', () => { - it('formats token usage details with short units', () => { - expect(formatUsageValue(93_405)).toBe('93.4K'); - expect(formatUsageValue(92_119)).toBe('92.1K'); - expect(formatUsageValue(3_488)).toBe('3.5K'); - expect(formatUsageValue(189_018)).toBe('189K'); - }); - - it('formats credit usage details with the same short units', () => { - expect(formatUsageValue(16_127)).toBe('16.1K'); - expect(formatUsageValue(16_179)).toBe('16.2K'); - }); - - it('keeps small token counts readable without suffixes', () => { - expect(formatUsageValue(0)).toBe('0'); - expect(formatUsageValue(6)).toBe('6'); - expect(formatUsageValue(999)).toBe('999'); - }); - - it('formats million-level token counts with M suffix', () => { - expect(formatUsageValue(1_000_000)).toBe('1M'); - expect(formatUsageValue(1_500_000)).toBe('1.5M'); - }); -}); diff --git a/src/features/Conversation/Messages/components/Extras/Usage/UsageDetail/TokenProgress.tsx b/src/features/Conversation/Messages/components/Extras/Usage/UsageDetail/TokenProgress.tsx index c194175c78..ffcef8ae8e 100644 --- a/src/features/Conversation/Messages/components/Extras/Usage/UsageDetail/TokenProgress.tsx +++ b/src/features/Conversation/Messages/components/Extras/Usage/UsageDetail/TokenProgress.tsx @@ -1,6 +1,6 @@ +import { formatUsageValue } from '@lobechat/utils'; import { Flexbox } from '@lobehub/ui'; import { cssVar } from 'antd-style'; -import numeral from 'numeral'; import { memo } from 'react'; export interface TokenProgressItem { @@ -15,12 +15,6 @@ interface TokenProgressProps { showIcon?: boolean; } -export const formatUsageValue = (number: number) => { - if (number >= 1_000_000) return numeral(number / 1_000_000).format('0.[0]') + 'M'; - if (number >= 1_000) return numeral(number / 1_000).format('0.[0]') + 'K'; - return numeral(number).format('0,0'); -}; - const TokenProgress = memo(({ data, showIcon }) => { const total = data.reduce((acc, item) => acc + item.value, 0); diff --git a/src/features/Conversation/Messages/components/Extras/Usage/UsageDetail/index.tsx b/src/features/Conversation/Messages/components/Extras/Usage/UsageDetail/index.tsx index 8173ad3912..1ffd429819 100644 --- a/src/features/Conversation/Messages/components/Extras/Usage/UsageDetail/index.tsx +++ b/src/features/Conversation/Messages/components/Extras/Usage/UsageDetail/index.tsx @@ -1,4 +1,5 @@ import { type ModelPerformance, type ModelUsage } from '@lobechat/types'; +import { formatUsageValue } from '@lobechat/utils'; import { Center, Flexbox, Icon, Popover } from '@lobehub/ui'; import { Divider } from 'antd'; import { cssVar } from 'antd-style'; @@ -15,7 +16,7 @@ import { formatNumber, formatShortenNumber } from '@/utils/format'; import AnimatedNumber from './AnimatedNumber'; import ModelCard from './ModelCard'; import type { TokenProgressItem } from './TokenProgress'; -import TokenProgress, { formatUsageValue } from './TokenProgress'; +import TokenProgress from './TokenProgress'; import { getDetailsToken } from './tokens'; interface TokenDetailProps { diff --git a/src/features/ModelSwitchPanel/components/ModelDetailPanel.test.tsx b/src/features/ModelSwitchPanel/components/ModelDetailPanel.test.tsx new file mode 100644 index 0000000000..ebbf9f4096 --- /dev/null +++ b/src/features/ModelSwitchPanel/components/ModelDetailPanel.test.tsx @@ -0,0 +1,196 @@ +/** + * @vitest-environment happy-dom + */ +import { render, screen } from '@testing-library/react'; +import type { ReactNode } from 'react'; +import { describe, expect, it, vi } from 'vitest'; + +import type { EnabledProviderWithModels } from '@/types/aiProvider'; + +import ModelDetailPanel from './ModelDetailPanel'; + +vi.mock('antd-style', () => ({ + createStaticStyles: () => ({ + actionText: 'actionText', + container: 'container', + originalPriceText: 'originalPriceText', + priceValue: 'priceValue', + row: 'row', + titleText: 'titleText', + }), +})); + +vi.mock('@lobehub/ui', () => ({ + Accordion: ({ children }: { children: ReactNode }) =>
{children}
, + AccordionItem: ({ + action, + children, + title, + }: { + action?: ReactNode; + children?: ReactNode; + title?: ReactNode; + }) => ( +
+
{title}
+
{action}
+
{children}
+
+ ), + Flexbox: ({ children, ...props }: { children?: ReactNode }) =>
{children}
, + Icon: () => , + Tag: ({ children }: { children: ReactNode }) => {children}, + Tooltip: ({ children, title }: { children: ReactNode; title?: ReactNode }) => ( + + {title} + {children} + + ), +})); + +vi.mock('@/hooks/useEnabledChatModels', () => ({ + useEnabledChatModels: () => [], +})); + +const globalState = { + status: { + modelDetailPanelExpandedKeys: ['pricing'], + }, + updateModelDetailPanelExpandedKeys: vi.fn(), +}; + +vi.mock('@/store/global', () => ({ + useGlobalStore: (selector: (state: typeof globalState) => unknown) => selector(globalState), +})); + +vi.mock('@/store/global/selectors', () => ({ + systemStatusSelectors: { + modelDetailPanelExpandedKeys: (state: typeof globalState) => + state.status.modelDetailPanelExpandedKeys, + }, +})); + +const translations: Record = { + 'ModelSwitchPanel.detail.context': 'Context Length', + 'ModelSwitchPanel.detail.pricing': 'Pricing', + 'ModelSwitchPanel.detail.pricing.credits.input': 'Input {{amount}} credits/M tokens', + 'ModelSwitchPanel.detail.pricing.credits.output': 'Output {{amount}} credits/M tokens', + 'ModelSwitchPanel.detail.pricing.credits.perImage': '~ {{amount}} credits / image', + 'ModelSwitchPanel.detail.pricing.credits.perVideo': '~ {{amount}} credits / video', + 'ModelSwitchPanel.detail.pricing.credits.image': 'credits/img', + 'ModelSwitchPanel.detail.pricing.credits.millionTokens': 'credits/M tokens', + 'ModelSwitchPanel.detail.pricing.group.image': 'Image', + 'ModelSwitchPanel.detail.pricing.group.text': 'Text', + 'ModelSwitchPanel.detail.pricing.input': 'Input ${{amount}}/M', + 'ModelSwitchPanel.detail.pricing.output': 'Output ${{amount}}/M', + 'ModelSwitchPanel.detail.pricing.perImage': '~ ${{amount}} / image', + 'ModelSwitchPanel.detail.pricing.perVideo': '~ ${{amount}} / video', + 'ModelSwitchPanel.detail.pricing.unit.imageGeneration': 'Image Generation', + 'ModelSwitchPanel.detail.pricing.unit.textInput': 'Input', + 'ModelSwitchPanel.detail.pricing.unit.textOutput': 'Output', +}; + +vi.mock('react-i18next', () => ({ + useTranslation: () => ({ + t: (key: string, options?: Record) => { + const template = translations[key] ?? options?.defaultValue ?? key; + + return template.replaceAll(/\{\{(\w+)\}\}/g, (_, name) => options?.[name] ?? ''); + }, + }), +})); + +const textPricing = { + currency: 'USD', + units: [ + { name: 'textInput', rate: 5, strategy: 'fixed', unit: 'millionTokens' }, + { name: 'textOutput', rate: 25, strategy: 'fixed', unit: 'millionTokens' }, + ], +}; + +const imagePricing = { + approximatePricePerImage: 0.04, + approximatePricePerVideo: 0.8, + currency: 'USD', + units: [{ name: 'imageGeneration', rate: 0.04, strategy: 'fixed', unit: 'image' }], +}; + +const createEnabledList = ( + provider: string, + pricing: Record, +): EnabledProviderWithModels[] => [ + { + children: [ + { + abilities: {}, + contextWindowTokens: 1_000_000, + displayName: 'Test Model', + id: 'test-model', + pricing, + type: 'chat', + } as any, + ], + id: provider, + name: provider, + source: 'builtin', + }, +]; + +describe('ModelDetailPanel pricing', () => { + it('renders branding provider token pricing in credits', () => { + const { container } = render( + , + ); + + expect(screen.getByText('5M credits/M tokens')).toBeInTheDocument(); + expect(screen.getByText('25M credits/M tokens')).toBeInTheDocument(); + expect(container).not.toHaveTextContent('$5.00'); + }); + + it('keeps dollar pricing for non-branding providers', () => { + const { container } = render( + , + ); + + expect(container).toHaveTextContent('$5.00/M tokens'); + expect(container).toHaveTextContent('$25.00/M tokens'); + expect(container).not.toHaveTextContent('credits/M tokens'); + }); + + it('renders branding provider image and video pricing in credits', () => { + const imageResult = render( + , + ); + + expect(imageResult.container).toHaveTextContent('~ 40.0K credits / image'); + expect(imageResult.container).toHaveTextContent('40.0K credits/img'); + expect(imageResult.container).not.toHaveTextContent('$0.04'); + + imageResult.unmount(); + + const videoResult = render( + , + ); + + expect(videoResult.container).toHaveTextContent('~ 800.0K credits / video'); + expect(videoResult.container).not.toHaveTextContent('$0.80'); + }); +}); diff --git a/src/features/ModelSwitchPanel/components/ModelDetailPanel.tsx b/src/features/ModelSwitchPanel/components/ModelDetailPanel.tsx index 20bf35c2fc..9a7af5e054 100644 --- a/src/features/ModelSwitchPanel/components/ModelDetailPanel.tsx +++ b/src/features/ModelSwitchPanel/components/ModelDetailPanel.tsx @@ -1,7 +1,8 @@ +import { BRANDING_PROVIDER } from '@lobechat/business-const'; import { getCachedTextInputUnitRate } from '@lobechat/utils'; import { Accordion, AccordionItem, Flexbox, Icon, Tag, Tooltip } from '@lobehub/ui'; import { createStaticStyles } from 'antd-style'; -import { type LucideIcon } from 'lucide-react'; +import type { LucideIcon } from 'lucide-react'; import { ArrowDownToDot, ArrowUpFromDot, @@ -14,15 +15,15 @@ import { VideoIcon, WrenchIcon, } from 'lucide-react'; -import { - type FixedPricingUnit, - type ModelPriceCurrency, - type Pricing, - type PricingUnit, - type PricingUnitName, - type TieredPricingUnit, +import type { + FixedPricingUnit, + ModelPriceCurrency, + Pricing, + PricingUnit, + PricingUnitName, + TieredPricingUnit, } from 'model-bank'; -import { type FC } from 'react'; +import type { FC } from 'react'; import { memo, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -31,7 +32,7 @@ import { useGlobalStore } from '@/store/global'; import type { ModelDetailPanelExpandedKey } from '@/store/global/initialState'; import { systemStatusSelectors } from '@/store/global/selectors'; import type { EnabledProviderWithModels } from '@/types/aiProvider'; -import { formatTokenNumber } from '@/utils/format'; +import { formatNumber, formatShortenNumber, formatTokenNumber } from '@/utils/format'; import { formatPriceByCurrency, getOriginalUnitRateByName, @@ -75,10 +76,37 @@ interface FormattedUnitPrice { original?: string; } -const formatPricingRate = (rate: number | undefined, currency?: ModelPriceCurrency) => - typeof rate === 'number' ? formatPriceByCurrency(rate, currency) : '0'; +const BRANDING_CREDIT_UNIT = 1_000_000; +const MILLION_SCALE_UNITS = new Set(['millionCharacters', 'millionTokens']); -const getFormattedUnitPrice = (pricing: Pricing, unitName: PricingUnitName): FormattedUnitPrice => { +interface FormatPricingRateOptions { + isCreditPricing?: boolean; + unit?: PricingUnit['unit']; +} + +const formatBrandingCreditRate = (rate: number, unit?: PricingUnit['unit']) => { + if (unit && MILLION_SCALE_UNITS.has(unit)) return `${formatNumber(rate)}M`; + + return String(formatShortenNumber(Math.round(rate * BRANDING_CREDIT_UNIT))); +}; + +const formatPricingRate = ( + rate: number | undefined, + currency?: ModelPriceCurrency, + options: FormatPricingRateOptions = {}, +) => { + if (typeof rate !== 'number') return '0'; + + return options.isCreditPricing + ? formatBrandingCreditRate(rate, options.unit) + : formatPriceByCurrency(rate, currency); +}; + +const getFormattedUnitPrice = ( + pricing: Pricing, + unitName: PricingUnitName, + isCreditPricing: boolean, +): FormattedUnitPrice => { const currency = pricing.currency as ModelPriceCurrency | undefined; const currentRate = unitName === 'textInput' @@ -89,17 +117,25 @@ const getFormattedUnitPrice = (pricing: Pricing, unitName: PricingUnitName): For const originalRate = getOriginalUnitRateByName(pricing, unitName); return { - current: formatPricingRate(currentRate, currency), + current: formatPricingRate(currentRate, currency, { + isCreditPricing, + unit: 'millionTokens', + }), original: - typeof originalRate === 'number' ? formatPriceByCurrency(originalRate, currency) : undefined, + typeof originalRate === 'number' + ? formatPricingRate(originalRate, currency, { + isCreditPricing, + unit: 'millionTokens', + }) + : undefined, }; }; -const getPrice = (pricing: Pricing) => { +const getPrice = (pricing: Pricing, isCreditPricing: boolean) => { return { - cachedInput: getFormattedUnitPrice(pricing, 'textInput_cacheRead'), - input: getFormattedUnitPrice(pricing, 'textInput'), - output: getFormattedUnitPrice(pricing, 'textOutput'), + cachedInput: getFormattedUnitPrice(pricing, 'textInput_cacheRead', isCreditPricing), + input: getFormattedUnitPrice(pricing, 'textInput', isCreditPricing), + output: getFormattedUnitPrice(pricing, 'textOutput', isCreditPricing), }; }; @@ -186,14 +222,23 @@ const PriceValue: FC = ({ price, prefix = '', suffix = '' }) => ); -const formatUnitRate = (unit: PricingUnit, currency?: ModelPriceCurrency): FormattedUnitPrice => { +const formatUnitRate = ( + unit: PricingUnit, + currency?: ModelPriceCurrency, + isCreditPricing?: boolean, +): FormattedUnitPrice => { + const formatRate = (rate: number) => + formatPricingRate(rate, currency, { isCreditPricing, unit: unit.unit }); + const formatRange = (low: string, high: string) => + isCreditPricing ? `${low} ~ ${high}` : `${low} ~ $${high}`; + if (unit.strategy === 'fixed') { const fixedUnit = unit as FixedPricingUnit; return { - current: formatPriceByCurrency(fixedUnit.rate, currency), + current: formatRate(fixedUnit.rate), original: typeof fixedUnit.originalRate === 'number' && fixedUnit.originalRate > fixedUnit.rate - ? formatPriceByCurrency(fixedUnit.originalRate, currency) + ? formatRate(fixedUnit.originalRate) : undefined, }; } @@ -201,25 +246,25 @@ const formatUnitRate = (unit: PricingUnit, currency?: ModelPriceCurrency): Forma if (unit.strategy === 'tiered') { const tiers = (unit as TieredPricingUnit).tiers; if (tiers.length === 1) { - const price = formatPriceByCurrency(tiers[0].rate, currency); + const price = formatRate(tiers[0].rate); return { current: price }; } - const low = formatPriceByCurrency(tiers[0].rate, currency); - const high = formatPriceByCurrency(tiers.at(-1)!.rate, currency); - return { current: `${low} ~ $${high}` }; + const low = formatRate(tiers[0].rate); + const high = formatRate(tiers.at(-1)!.rate); + return { current: formatRange(low, high) }; } // lookup strategy if (unit.strategy === 'lookup') { const prices = Object.values(unit.lookup.prices); if (prices.length === 1) { - const price = formatPriceByCurrency(prices[0], currency); + const price = formatRate(prices[0]); return { current: price }; } const sorted = [...prices].sort((a, b) => a - b); - const low = formatPriceByCurrency(sorted[0], currency); - const high = formatPriceByCurrency(sorted.at(-1)!, currency); - return { current: `${low} ~ $${high}` }; + const low = formatRate(sorted[0]); + const high = formatRate(sorted.at(-1)!); + return { current: formatRange(low, high) }; } return { current: '-' }; @@ -285,8 +330,9 @@ const ModelDetailPanel: FC = memo( const updateExpandedKeys = useGlobalStore((s) => s.updateModelDetailPanelExpandedKeys); const pricing = model?.pricing; + const isCreditPricing = provider === BRANDING_PROVIDER; const hasPricing = !!pricing; - const formatPrice = pricing ? getPrice(pricing) : null; + const formatPrice = pricing ? getPrice(pricing, isCreditPricing) : null; const pricingGroups = useMemo( () => (pricing ? groupPricingUnits(pricing.units) : []), [pricing], @@ -296,21 +342,57 @@ const ModelDetailPanel: FC = memo( if (!pricing || !pricingMode) return null; const currency = pricing.currency as ModelPriceCurrency | undefined; if (pricingMode === 'image' && typeof pricing.approximatePricePerImage === 'number') { - const amount = formatPriceByCurrency(pricing.approximatePricePerImage, currency); - return t('ModelSwitchPanel.detail.pricing.perImage', { - amount, - defaultValue: '~ ${{amount}} / image', - }); + const amount = isCreditPricing + ? formatBrandingCreditRate(pricing.approximatePricePerImage, 'image') + : formatPriceByCurrency(pricing.approximatePricePerImage, currency); + return t( + isCreditPricing + ? 'ModelSwitchPanel.detail.pricing.credits.perImage' + : 'ModelSwitchPanel.detail.pricing.perImage', + { + amount, + defaultValue: isCreditPricing + ? '~ {{amount}} credits / image' + : '~ ${{amount}} / image', + }, + ); } if (pricingMode === 'video' && typeof pricing.approximatePricePerVideo === 'number') { - const amount = formatPriceByCurrency(pricing.approximatePricePerVideo, currency); - return t('ModelSwitchPanel.detail.pricing.perVideo', { - amount, - defaultValue: '~ ${{amount}} / video', - }); + const amount = isCreditPricing + ? formatBrandingCreditRate(pricing.approximatePricePerVideo) + : formatPriceByCurrency(pricing.approximatePricePerVideo, currency); + return t( + isCreditPricing + ? 'ModelSwitchPanel.detail.pricing.credits.perVideo' + : 'ModelSwitchPanel.detail.pricing.perVideo', + { + amount, + defaultValue: isCreditPricing + ? '~ {{amount}} credits / video' + : '~ ${{amount}} / video', + }, + ); } return null; - }, [pricing, pricingMode, t]); + }, [isCreditPricing, pricing, pricingMode, t]); + + const getCreditsUnitLabel = (unit: PricingUnit['unit']) => + t(`ModelSwitchPanel.detail.pricing.credits.${unit}` as any, { + defaultValue: `credits${UNIT_LABEL_MAP[unit] || ''}`, + }); + + const getPricingTooltip = (key: 'cachedInput' | 'input' | 'output', amount: string): string => { + if (isCreditPricing) { + return t(`ModelSwitchPanel.detail.pricing.credits.${key}` as any, { amount }); + } + + const fallbackKey = + key === 'cachedInput' + ? 'ModelSwitchPanel.detail.pricing.cachedInput' + : `ModelSwitchPanel.detail.pricing.${key}`; + + return t(fallbackKey as any, { amount }); + }; if (!model) return null; @@ -440,9 +522,7 @@ const ModelDetailPanel: FC = memo( {getCachedTextInputUnitRate(model.pricing!) && ( @@ -450,21 +530,13 @@ const ModelDetailPanel: FC = memo( )} - + - + @@ -518,12 +590,17 @@ const ModelDetailPanel: FC = memo( ))} diff --git a/src/locales/default/chat.ts b/src/locales/default/chat.ts index a19ac69d27..6c8efcc296 100644 --- a/src/locales/default/chat.ts +++ b/src/locales/default/chat.ts @@ -185,6 +185,14 @@ export default { 'inbox.title': 'Lobe AI', 'input.addAi': 'Add an AI message', 'input.addUser': 'Add a user message', + 'input.costEstimate.creditsPerMillionTokens': '{{credits}} credits/M tokens', + 'input.costEstimate.hint': 'Estimated cost: ~{{credits}} credits', + 'input.costEstimate.inputLabel': 'Input', + 'input.costEstimate.outputLabel': 'Output', + 'input.costEstimate.settingsLink': 'Adjust warning threshold', + 'input.costEstimate.tokenCount': '~{{tokens}} tokens', + 'input.costEstimate.tooltip': + 'Estimated from current context, tools, and model pricing. Actual cost may vary.', 'input.disclaimer': 'Agents can make mistakes. Use your judgment for critical info.', 'input.errorMsg': 'Send failed: {{errorMsg}}. Retry, or send again later.', 'input.more': 'More', diff --git a/src/locales/default/common.ts b/src/locales/default/common.ts index d477222df5..54a3733d3b 100644 --- a/src/locales/default/common.ts +++ b/src/locales/default/common.ts @@ -3,9 +3,9 @@ export default { 'advanceSettings': 'Advanced Settings', 'alert.cloud.action': 'Try now', 'alert.cloud.desc': - 'All registered users get {{credit}} free computing credits per month—no setup needed. Includes global cloud sync and advanced web search.', + 'All registered users get {{credit}} free credits per month—no setup needed. Includes global cloud sync and advanced web search.', 'alert.cloud.descOnMobile': - 'All registered users get {{credit}} free computing credits per month—no setup needed.', + 'All registered users get {{credit}} free credits per month—no setup needed.', 'alert.cloud.title': '{{name}} beta is live', 'agentOnboardingPromo.actionLabel': 'Try it now', 'agentOnboardingPromo.description': diff --git a/src/locales/default/components.ts b/src/locales/default/components.ts index f07d3e834c..40666d210e 100644 --- a/src/locales/default/components.ts +++ b/src/locales/default/components.ts @@ -140,6 +140,16 @@ export default { 'ModelSwitchPanel.detail.context': 'Context Length', 'ModelSwitchPanel.detail.pricing': 'Pricing', 'ModelSwitchPanel.detail.pricing.cachedInput': 'Cached input ${{amount}}/M', + 'ModelSwitchPanel.detail.pricing.credits.cachedInput': 'Cached input {{amount}} credits/M tokens', + 'ModelSwitchPanel.detail.pricing.credits.input': 'Input {{amount}} credits/M tokens', + 'ModelSwitchPanel.detail.pricing.credits.output': 'Output {{amount}} credits/M tokens', + 'ModelSwitchPanel.detail.pricing.credits.perImage': '~ {{amount}} credits / image', + 'ModelSwitchPanel.detail.pricing.credits.perVideo': '~ {{amount}} credits / video', + 'ModelSwitchPanel.detail.pricing.credits.image': 'credits/img', + 'ModelSwitchPanel.detail.pricing.credits.megapixel': 'credits/MP', + 'ModelSwitchPanel.detail.pricing.credits.millionCharacters': 'credits/M chars', + 'ModelSwitchPanel.detail.pricing.credits.millionTokens': 'credits/M tokens', + 'ModelSwitchPanel.detail.pricing.credits.second': 'credits/s', 'ModelSwitchPanel.detail.pricing.group.audio': 'Audio', 'ModelSwitchPanel.detail.pricing.group.image': 'Image', 'ModelSwitchPanel.detail.pricing.group.text': 'Text', diff --git a/src/locales/default/spend.ts b/src/locales/default/spend.ts index 18923615c1..0d2a5fd2cc 100644 --- a/src/locales/default/spend.ts +++ b/src/locales/default/spend.ts @@ -35,10 +35,9 @@ export default { 'table.columns.type.enums.imageGeneration': 'Image Generation', 'table.columns.type.enums.videoGeneration': 'Video Generation', 'table.columns.type.title': 'Type', - 'table.desc': - 'Details of computing credits usage for text generation, embedding, image generation, etc.', + 'table.desc': 'Details of credit usage for text generation, embedding, image generation, etc.', 'table.more': 'View Details', - 'table.title': 'Computing Credits Usage Details', + 'table.title': 'Credit Usage Details', 'table.totalToken.input': 'Input', 'table.totalToken.output': 'Output', }; diff --git a/src/locales/default/subscription.ts b/src/locales/default/subscription.ts index ef23429bbd..19488cd4d6 100644 --- a/src/locales/default/subscription.ts +++ b/src/locales/default/subscription.ts @@ -38,8 +38,8 @@ export default { 'cancelPlan.title': 'Cancel Subscription', 'cancelSubscription': 'Cancel Subscription', 'compare.hobbyCreditTooltip': - 'Does not include monthly computing credits, you need to configure your own model API', - 'compare.monthlyCredit': 'Monthly Computing Credits', + 'Does not include monthly credits, you need to configure your own model API', + 'compare.monthlyCredit': 'Monthly Credits', 'compare.title': 'Plan Comparison', 'compareAllPlans': 'View All Plans', 'comparePlans': 'View Plans', @@ -87,6 +87,13 @@ export default { 'credits.autoTopUp.upgradeHint': 'Subscribe to a paid plan to enable auto top-up', 'credits.autoTopUp.validation.targetMustExceedThreshold': 'Target balance must be greater than threshold', + 'credits.costEstimateHint.desc': + 'Show a lightweight warning before sending when the estimated model cost reaches your threshold', + 'credits.costEstimateHint.saveError': 'Failed to save cost estimate alert settings', + 'credits.costEstimateHint.saveSuccess': 'Cost estimate alert settings saved', + 'credits.costEstimateHint.threshold': 'Warning Threshold', + 'credits.costEstimateHint.title': 'Cost Estimate Alert', + 'credits.costEstimateHint.validation.threshold': 'Threshold must be greater than or equal to 0', 'credits.packages.expired': 'Expired', 'credits.packages.expiresIn': 'Expires in {{days}} days', 'credits.packages.expiresToday': 'Expires today', @@ -150,8 +157,8 @@ export default { 'Your top-up credits are now active. Enjoy AI chatting. Your current plan includes:', 'limitation.chat.topupSuccess.title': 'Top-up Successful', 'limitation.expired.desc': - 'Your {{plan}} computing credits expired on {{expiredAt}}. Upgrade your plan now to get computing credits.', - 'limitation.expired.title': 'Computing Credits Expired', + 'Your {{plan}} credits expired on {{expiredAt}}. Upgrade your plan now to get credits.', + 'limitation.expired.title': 'Credits Expired', 'limitation.insufficientBudget.approximateDesc': 'This request may need more credits. Top up credits or upgrade your plan.', 'limitation.insufficientBudget.available': 'Available Credits', @@ -168,7 +175,7 @@ export default { 'limitation.hobby.action': 'Configured, continue chatting', 'limitation.hobby.configAPI': 'Configure API', 'limitation.hobby.desc': - 'Your free computing credits have been exhausted. Please configure a custom model API to continue.', + 'Your free credits have been exhausted. Please configure a custom model API to continue.', 'limitation.hobby.docs': 'View configuration docs', 'limitation.hobby.tip': 'Remember to switch to a model with custom API Key', 'limitation.hobby.title': 'Please Configure Model Service API', @@ -197,11 +204,11 @@ export default { 'limitation.limited.action': 'Upgrade Now', 'limitation.limited.advanceFeature': 'Upgrade to enjoy premium features:', 'limitation.limited.desc': - 'Your {{plan}} computing credits have been exhausted. Upgrade now to get more credits.', + 'Your {{plan}} credits have been exhausted. Upgrade now to get more credits.', 'limitation.limited.descUltimate': - 'Your {{plan}} computing credits have been exhausted. Please top up credits to continue.', + 'Your {{plan}} credits have been exhausted. Please top up credits to continue.', 'limitation.limited.referralTip': 'Invite friends, both get {{reward}}M', - 'limitation.limited.title': 'Computing Credits Exhausted', + 'limitation.limited.title': 'Credits Exhausted', 'limitation.limited.topup': 'Top-Up Credits', 'limitation.limited.upgrade': 'Upgrade to Higher Plan', 'limitation.limited.upgradeToPlan': 'Upgrade to {{plan}}', @@ -219,7 +226,7 @@ export default { 'limitation.providers.tooltip': 'Custom API service is only available for paid plans', 'modelPricing.button': 'View Pricing Documentation', 'modelPricing.desc': - '{{name}} uses Credits to measure AI model usage. The table below shows computing credits per 1M Tokens.', + '{{name}} uses Credits to measure AI model usage. The table below shows credits per 1M Tokens.', 'modelPricing.title': 'Text Model Pricing', 'models.input': 'Input', 'models.intro': 'Introduction', @@ -265,12 +272,12 @@ export default { 'plans.credit.apiDesc': 'Requires your own model API configuration', 'plans.credit.apiProvider': 'Supports 20+ mainstream model providers including OpenAI / Anthropic / OpenRouter', - 'plans.credit.buy': 'Purchase Computing Credits', - 'plans.credit.buyDesc': 'Also supports purchasing computing credits on demand', - 'plans.credit.none': 'No built-in computing credits', + 'plans.credit.buy': 'Purchase Credits', + 'plans.credit.buyDesc': 'Also supports purchasing credits on demand', + 'plans.credit.none': 'No built-in credits', 'plans.credit.tip': '{{credit}} free credits per month', - 'plans.credit.title': 'Computing Credits', - 'plans.credit.tooltip': 'Monthly model message computing credits', + 'plans.credit.title': 'Credits', + 'plans.credit.tooltip': 'Monthly model message credits', 'plans.current': 'Current Plan', 'plans.downgradePlan': 'Target Downgrade Plan', 'plans.downgradeTip': @@ -351,17 +358,17 @@ export default { 'If your question is not answered, check <1>Product Documentation for more FAQs, or contact us.', 'qa.detail': 'View Details', 'qa.list.credit.a': - 'Computing credits are a metric used by {{cloud}} to measure AI model usage when calling models. Different AI models consume different amounts of computing credits.', - 'qa.list.credit.q': 'What are computing credits?', + 'Credits are how {{cloud}} measures AI model usage. Different AI models consume different amounts of credits.', + 'qa.list.credit.q': 'What are credits?', 'qa.list.embeddings.a': 'Vector storage is not equal to the original size of your uploaded or imported dataset, but is calculated based on the vectorization of pure text content in your files. For example, a 1-page PDF file (1000-1500 characters) may only take up about 1 vector storage entry when extracted and vectorized into pure text. You can view your usage under "{{usage}}".', 'qa.list.embeddings.q': 'How is vector storage calculated?', 'qa.list.free.a': - '{{name}} has always adhered to open source principles. For professional developers, you can use all open source capabilities through self-deployment of the community version. In {{cloud}}, we provide all registered users with {{credit}} free computing credits per month, ready to use without complex configuration. If you need more usage, you can subscribe to {{starter}}, {{premium}} or {{ultimate}}.', + '{{name}} has always adhered to open source principles. For professional developers, you can use all open source capabilities through self-deployment of the community version. In {{cloud}}, we provide all registered users with {{credit}} free credits per month, ready to use without complex configuration. If you need more usage, you can subscribe to {{starter}}, {{premium}} or {{ultimate}}.', 'qa.list.free.q': 'Can {{name}} be used for free?', 'qa.list.limit.a': - '{{cloud}} subscription plans are divided into {{starter}}, {{premium}} and {{ultimate}}, each providing different computing credits. If your current plan credits are insufficient, we recommend upgrading. You can also purchase credit packages on the "{{funds}}" page for pay-as-you-go usage. Alternatively, you can set up a custom model API key to use API credits purchased from other sources.', - 'qa.list.limit.q': 'What if I run out of computing credits?', + '{{cloud}} subscription plans are divided into {{starter}}, {{premium}} and {{ultimate}}, each providing different credit amounts. If your current plan credits are insufficient, we recommend upgrading. You can also purchase credit packages on the "{{funds}}" page for pay-as-you-go usage. Alternatively, you can set up a custom model API key to use API credits purchased from other sources.', + 'qa.list.limit.q': 'What if I run out of credits?', 'qa.list.highUsage.a': 'Credit consumption depends heavily on conversation length. As a conversation grows, each new message sends the entire history as input tokens, which increases cost significantly. We recommend starting a new conversation when topics change. Also, if there is a long gap between messages, the prompt cache may expire, causing a spike in cost. Some models like the Gemini Pro series use tiered pricing — input beyond 200K tokens is charged at double the rate.', 'qa.list.highUsage.q': 'Why are my credits being consumed faster than expected?', @@ -493,7 +500,7 @@ export default { 'usage.credit.time.days': '{{days}} days', 'usage.credit.time.daysAndHours': '{{days}} days {{hours}} hours', 'usage.credit.time.hours': '{{hours}} hours', - 'usage.credit.title': 'Computing Credits Usage', + 'usage.credit.title': 'Credit Usage', 'usage.overview.charge': 'Charges', 'usage.overview.included': 'Plan Usage', 'usage.overview.onDemand': 'On-demand', diff --git a/src/store/user/selectors.ts b/src/store/user/selectors.ts index 4ac0f042a2..a3342d2892 100644 --- a/src/store/user/selectors.ts +++ b/src/store/user/selectors.ts @@ -8,4 +8,5 @@ export { systemAgentSelectors, toolInterventionSelectors, userGeneralSettingsSelectors, + userUsageSettingsSelectors, } from './slices/settings/selectors'; diff --git a/src/store/user/slices/settings/selectors/general.test.ts b/src/store/user/slices/settings/selectors/general.test.ts index bfc77e3822..82ec9e3137 100644 --- a/src/store/user/slices/settings/selectors/general.test.ts +++ b/src/store/user/slices/settings/selectors/general.test.ts @@ -18,6 +18,7 @@ describe('settingsSelectors', () => { expect(result).toEqual({ animationMode: 'agile', + costEstimateWarningThreshold: 2, fontSize: 12, highlighterTheme: 'lobe-theme', isDevMode: false, diff --git a/src/store/user/slices/settings/selectors/index.ts b/src/store/user/slices/settings/selectors/index.ts index fb0e6e474c..2f85a1c90b 100644 --- a/src/store/user/slices/settings/selectors/index.ts +++ b/src/store/user/slices/settings/selectors/index.ts @@ -3,3 +3,4 @@ export { keyVaultsConfigSelectors } from './keyVaults'; export { settingsSelectors } from './settings'; export { systemAgentSelectors } from './systemAgent'; export { type ApprovalMode, toolInterventionSelectors } from './toolIntervention'; +export { userUsageSettingsSelectors } from './usage'; diff --git a/src/store/user/slices/settings/selectors/usage.test.ts b/src/store/user/slices/settings/selectors/usage.test.ts new file mode 100644 index 0000000000..476ee7ee9f --- /dev/null +++ b/src/store/user/slices/settings/selectors/usage.test.ts @@ -0,0 +1,30 @@ +import type { UserStore } from '@/store/user'; +import type { UserState } from '@/store/user/initialState'; +import { initialState } from '@/store/user/initialState'; +import { merge } from '@/utils/merge'; + +import { userUsageSettingsSelectors } from './usage'; + +describe('userUsageSettingsSelectors', () => { + describe('costEstimateWarningThreshold', () => { + it('should return the default threshold', () => { + const result = userUsageSettingsSelectors.costEstimateWarningThreshold( + initialState as UserStore, + ); + + expect(result).toBe(2); + }); + + it('should read the persisted threshold from general settings', () => { + const s: UserState = merge(initialState, { + settings: { + general: { costEstimateWarningThreshold: 0.5 }, + }, + }); + + const result = userUsageSettingsSelectors.costEstimateWarningThreshold(s as UserStore); + + expect(result).toBe(0.5); + }); + }); +}); diff --git a/src/store/user/slices/settings/selectors/usage.ts b/src/store/user/slices/settings/selectors/usage.ts new file mode 100644 index 0000000000..6ca831d1a7 --- /dev/null +++ b/src/store/user/slices/settings/selectors/usage.ts @@ -0,0 +1,12 @@ +import { DEFAULT_COST_ESTIMATE_WARNING_THRESHOLD } from '@lobechat/const'; + +import type { UserStore } from '../../../store'; +import { currentSettings } from './settings'; + +const costEstimateWarningThreshold = (s: UserStore) => + currentSettings(s).general.costEstimateWarningThreshold ?? + DEFAULT_COST_ESTIMATE_WARNING_THRESHOLD; + +export const userUsageSettingsSelectors = { + costEstimateWarningThreshold, +}; From 632c1e6c4940632d48fa74e014839b856abb0af1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Wang?= <52880665+rivertwilight@users.noreply.github.com> Date: Tue, 19 May 2026 19:18:57 +0800 Subject: [PATCH 032/224] =?UTF-8?q?=F0=9F=93=9D=20docs:=20add=20May=2019?= =?UTF-8?q?=20weekly=20changelog=20(#14973)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-05-19-chief-agent-operator.mdx | 39 ++++ .../2026-05-19-chief-agent-operator.zh-CN.mdx | 38 ++++ docs/changelog/index.json | 202 ++++-------------- 3 files changed, 123 insertions(+), 156 deletions(-) create mode 100644 docs/changelog/2026-05-19-chief-agent-operator.mdx create mode 100644 docs/changelog/2026-05-19-chief-agent-operator.zh-CN.mdx diff --git a/docs/changelog/2026-05-19-chief-agent-operator.mdx b/docs/changelog/2026-05-19-chief-agent-operator.mdx new file mode 100644 index 0000000000..4d1d0d9dd9 --- /dev/null +++ b/docs/changelog/2026-05-19-chief-agent-operator.mdx @@ -0,0 +1,39 @@ +--- +title: Introducing CAO — Your Chief Agent Operator +description: >- + Meet CAO: agents that review their own work, recruit teammates when they need help, and only stop to ask you when it really matters. +tags: + - CAO + - Agent Teams + - Tasks + - Models +--- + +# Introducing CAO — Your Chief Agent Operator + +## Meet CAO + +CAO (Chief Agent Operator) turns short back-and-forth chats into agents that just keep going. Instead of waiting for you to say "ok, next step", your agent now checks its own work, decides what to do next, and gets on with it. When something genuinely needs your call, it pauses, tells you what it tried, and asks a clear question — no more guessing in the dark. + +CAO can also bring in help. If a task gets big, your agent can put together a small team of sub-agents and hand pieces off to them. You can peek into any of their conversations to see what they're up to, while your main agent stays in charge of the overall plan. + +[Learn more about CAO →](https://x.com/lobehub/status/2056371816265097337?s=20) + +## What else is new + +- Recurring tasks are more reliable, with a cap on how many times a task can run and protection against schedules that fire too often +- A cleaner Agent Documents view — easier to find what you put there, and no more clutter from old web crawls +- Project skills now show up in the working sidebar, with a built-in markdown preview +- You can pick a different model for each kind of follow-up suggestion +- New model: Baidu Ernie 5.1 +- DeepSeek pricing is back to official rates + +## Improvements and fixes + +- Desktop no longer signs you out from background token refreshes, and remembers which page you were on after an app update. +- Chat input actions show as icon + label, and only the latest reply animates as it streams. +- Cleaner tab bar styling, and task schedule labels no longer wrap awkwardly. +- Local-file search handles hidden files starting with `.`, and grep parameters now flow through correctly. +- Documents fail clearly when they aren't supported, instead of producing confusing errors. +- Gemini tool calls handle thinking signatures and enums correctly, and per-tool timeouts now stick. +- Task pages keep the right agent context; memory updates stay out of your main thread. diff --git a/docs/changelog/2026-05-19-chief-agent-operator.zh-CN.mdx b/docs/changelog/2026-05-19-chief-agent-operator.zh-CN.mdx new file mode 100644 index 0000000000..3847d48055 --- /dev/null +++ b/docs/changelog/2026-05-19-chief-agent-operator.zh-CN.mdx @@ -0,0 +1,38 @@ +--- +title: 隆重推出 CAO —— 你的首席智能体运营官 +description: 认识 CAO:会自己复盘、能临时组队,只在真正需要时才停下来问你的智能体。 +tags: + - CAO + - 智能体团队 + - 任务 + - 模型 +--- + +# 隆重推出 CAO —— 你的首席智能体运营官 + +## 认识 CAO + +CAO(首席智能体运营官)让原本一问一答的对话,变成一个能自己往前推进的智能体。不必再每一步都告诉它「好,下一步」—— 它会自己复盘已完成的部分,决定接下来要做什么,然后继续推进。当遇到真正需要你来拍板的事,它会停下来,告诉你已经尝试了什么,并提出一个清晰的问题,而不是凭感觉硬猜。 + +CAO 也会找帮手。任务变大时,它可以临时组建一支子智能体小队,把不同部分交给队友处理。你随时可以看任意一个子智能体的对话进展,而主智能体始终掌控整体节奏。 + +[了解更多 CAO →](https://x.com/lobehub/status/2056371816265097337?s=20) + +## 还有这些新东西 + +- 周期性任务更稳了:可以限制总执行次数,也会阻止设置得过于频繁的计划 +- 智能体文档视图更清爽 —— 更容易找到自己放进去的东西,也不再混着旧的网页抓取记录 +- 项目技能现在会出现在工作侧栏,并自带 Markdown 预览 +- 不同类型的后续建议可以分别选择不同的模型 +- 新增模型:百度文心一言 5.1 +- DeepSeek 价格已恢复官方定价 + +## 体验优化与修复 + +- 桌面端不再因为后台刷新 token 而把你踢出登录,应用更新后也能回到你之前所在的页面。 +- 聊天输入框的动作按钮调整为「图标 + 标签」样式,只有最新一条回复会有打字动画。 +- 标签栏样式更清爽,任务计划标签不再换行错位。 +- 本地文件搜索可以正确处理以 `.` 开头的隐藏文件,搜索参数也能完整传递。 +- 不支持的文档现在会明确报错,而不是抛出令人困惑的提示。 +- Gemini 工具调用对思考签名和枚举类型的处理已修复,每个工具的超时设置也确实生效。 +- 任务页面会保留正确的智能体上下文,记忆更新也不会再混入主对话。 diff --git a/docs/changelog/index.json b/docs/changelog/index.json index cfb548c90d..5c58627acd 100644 --- a/docs/changelog/index.json +++ b/docs/changelog/index.json @@ -2,359 +2,249 @@ "$schema": "https://github.com/lobehub/lobe-chat/blob/main/docs/changelog/schema.json", "cloud": [], "community": [ + { + "image": "https://hub-apac-1.lobeobjects.space/billboard/covers/1778838542538-MDEMAEav.png", + "id": "2026-05-19-chief-agent-operator", + "date": "2026-05-19", + "versionRange": ["2.1.58", "2.2.0"] + }, { "image": "/blog/assets4aa1732a45832afc780600e6e329860c.webp", "id": "2026-05-11-agent-tasks-ga", "date": "2026-05-11", - "versionRange": [ - "2.1.57" - ] + "versionRange": ["2.1.57"] }, { "image": "/blog/assetsb2bf4ddf0a45ff887a993c18cb7ab983.webp", "id": "2026-05-04-task-scheduler", "date": "2026-05-04", - "versionRange": [ - "2.1.54", - "2.1.56" - ] + "versionRange": ["2.1.54", "2.1.56"] }, { "image": "/blog/assetsfa267a02f20bc5ba6f1273bcf27b7c9f.webp", "id": "2026-04-27-heterogeneous-agent", "date": "2026-04-27", - "versionRange": [ - "2.1.53" - ] + "versionRange": ["2.1.53"] }, { "image": "/blog/assetsdfda32866c4bc59af0526e52f31d1da2.webp", "id": "2026-04-20-daily-brief", "date": "2026-04-20", - "versionRange": [ - "2.1.50", - "2.1.52" - ] + "versionRange": ["2.1.50", "2.1.52"] }, { "image": "/blog/assets300abe7e259d293da6c5ed4f642a1be6.webp", "id": "2026-04-13-gateway-sidebar", "date": "2026-04-13", - "versionRange": [ - "2.1.48", - "2.1.49" - ] + "versionRange": ["2.1.48", "2.1.49"] }, { "image": "/blog/assets7ea204859aeb5aa9be5810a20ba1669a.webp", "id": "2026-04-06-auto-completion", "date": "2026-04-06", - "versionRange": [ - "2.1.47" - ] + "versionRange": ["2.1.47"] }, { "id": "2026-03-30-agent-tasks", "date": "2026-03-30", - "versionRange": [ - "2.1.45", - "2.1.46" - ] + "versionRange": ["2.1.45", "2.1.46"] }, { "image": "/blog/assets53e6ec9cf72554dbc1f8224fc0550a03.webp", "id": "2026-03-23-media-memory", "date": "2026-03-23", - "versionRange": [ - "2.1.44" - ] + "versionRange": ["2.1.44"] }, { "image": "https://hub-apac-1.lobeobjects.space/blog/assets/4a68a7644501cb513d08670b102a446e.webp", "id": "2026-03-16-search", "date": "2026-03-16", - "versionRange": [ - "2.1.38", - "2.1.43" - ] + "versionRange": ["2.1.38", "2.1.43"] }, { "id": "2026-02-08-runtime-auth", "date": "2026-02-08", - "versionRange": [ - "2.1.6", - "2.1.26" - ] + "versionRange": ["2.1.6", "2.1.26"] }, { "image": "/blog/assetsa8e504275f2cd891fabecca985998de0.webp", "id": "2026-01-27-v2", "date": "2026-01-27", - "versionRange": [ - "2.0.1", - "2.1.5" - ] + "versionRange": ["2.0.1", "2.1.5"] }, { "image": "/blog/assets7f3b38c1d76cceb91edb29d6b1eb60db.webp", "id": "2025-12-20-mcp", "date": "2025-12-20", - "versionRange": [ - "1.142.8", - "1.143.0" - ] + "versionRange": ["1.142.8", "1.143.0"] }, { "image": "/blog/assets3a7f0b29839603336e39e923b423409b.webp", "id": "2025-11-08-comfy-ui", "date": "2025-11-08", - "versionRange": [ - "1.133.5", - "1.142.8" - ] + "versionRange": ["1.133.5", "1.142.8"] }, { "image": "/blog/assets35e6aa692b0c16009c61964279514166.webp", "id": "2025-10-08-python", "date": "2025-10-08", - "versionRange": [ - "1.120.7", - "1.133.5" - ] + "versionRange": ["1.120.7", "1.133.5"] }, { "image": "/blog/assetsce5d6dc93676f974be2e162e8ace03f0.webp", "id": "2025-09-08-gemini", "date": "2025-09-08", - "versionRange": [ - "1.109.1", - "1.120.7" - ] + "versionRange": ["1.109.1", "1.120.7"] }, { "image": "/blog/assetsdf48eed9de76b7e37c269b294285f09d.webp", "id": "2025-08-08-image-generation", "date": "2025-08-08", - "versionRange": [ - "1.97.10", - "1.109.1" - ] + "versionRange": ["1.97.10", "1.109.1"] }, { "image": "/blog/assets902eb746fe2042fc2ea831c71002be72.webp", "id": "2025-07-08-mcp-market", "date": "2025-07-08", - "versionRange": [ - "1.93.3", - "1.97.10" - ] + "versionRange": ["1.93.3", "1.97.10"] }, { "image": "/blog/assets5cc27b8cae995074da20d4ffe06a1460.webp", "id": "2025-06-08-claude-4", "date": "2025-06-08", - "versionRange": [ - "1.84.27", - "1.93.3" - ] + "versionRange": ["1.84.27", "1.93.3"] }, { "image": "/blog/assets2a36d86a4eed6e7938dd6e9c684701ed.webp", "id": "2025-05-08-desktop-app", "date": "2025-05-08", - "versionRange": [ - "1.77.17", - "1.84.27" - ] + "versionRange": ["1.77.17", "1.84.27"] }, { "image": "/blog/assetsc0efdb82443556ae3acefe00099b3f23.webp", "id": "2025-04-06-exports", "date": "2025-04-06", - "versionRange": [ - "1.67.2", - "1.77.17" - ] + "versionRange": ["1.67.2", "1.77.17"] }, { "image": "/blog/assetse743f0a47127390dde766a0a790476db.webp", "id": "2025-03-02-new-models", "date": "2025-03-02", - "versionRange": [ - "1.49.13", - "1.67.2" - ] + "versionRange": ["1.49.13", "1.67.2"] }, { "image": "/blog/assets18168d5fe64ea34905a7e52fd82d0e9d.webp", "id": "2025-02-02-deepseek-r1", "date": "2025-02-02", - "versionRange": [ - "1.47.8", - "1.49.12" - ] + "versionRange": ["1.47.8", "1.49.12"] }, { "image": "/blog/assetsf9ed064fe764cbeff2f46910e7099a91.webp", "id": "2025-01-22-new-ai-provider", "date": "2025-01-22", - "versionRange": [ - "1.43.1", - "1.47.7" - ] + "versionRange": ["1.43.1", "1.47.7"] }, { "image": "/blog/assets2d409f43b58953ad5396c6beab8a0719.webp", "id": "2025-01-03-user-profile", "date": "2025-01-03", - "versionRange": [ - "1.34.1", - "1.43.0" - ] + "versionRange": ["1.34.1", "1.43.0"] }, { "image": "/blog/assets/d9cbfcbef130183bc490d515d8a38aa4.webp", "id": "2024-11-27-forkable-chat", "date": "2024-11-27", - "versionRange": [ - "1.33.1", - "1.34.0" - ] + "versionRange": ["1.33.1", "1.34.0"] }, { "image": "/blog/assets/2d678631c55369ba7d753c3ffcb73782.webp", "id": "2024-11-25-november-providers", "date": "2024-11-25", - "versionRange": [ - "1.30.1", - "1.33.0" - ] + "versionRange": ["1.30.1", "1.33.0"] }, { "image": "/blog/assets/f10a4b98782e36797c38071eed785c6f.webp", "id": "2024-11-06-share-text-json", "date": "2024-11-06", - "versionRange": [ - "1.26.1", - "1.28.0" - ] + "versionRange": ["1.26.1", "1.28.0"] }, { "image": "/blog/assets/944c671604833cd2457445b211ebba33.webp", "id": "2024-10-27-pin-assistant", "date": "2024-10-27", - "versionRange": [ - "1.19.1", - "1.26.0" - ] + "versionRange": ["1.19.1", "1.26.0"] }, { "image": "/blog/assets/f6d047a345e47a52592cff916c9a64ce.webp", "id": "2024-09-20-artifacts", "date": "2024-09-20", - "versionRange": [ - "1.17.1", - "1.19.0" - ] + "versionRange": ["1.17.1", "1.19.0"] }, { "image": "/blog/assets/d7e57f8e69f97b76b3c2414f3441b6e4.webp", "id": "2024-09-13-openai-o1-models", "date": "2024-09-13", - "versionRange": [ - "1.12.1", - "1.17.0" - ] + "versionRange": ["1.12.1", "1.17.0"] }, { "image": "/blog/assets/d6129350de510a62fe87b2d2f0fb9477.webp", "id": "2024-08-21-file-upload-and-knowledge-base", "date": "2024-08-21", - "versionRange": [ - "1.8.1", - "1.12.0" - ] + "versionRange": ["1.8.1", "1.12.0"] }, { "image": "/blog/assets/37d85fdfccff9ed56e9c6827faee01c7.webp", "id": "2024-08-02-lobe-chat-database-docker", "date": "2024-08-02", - "versionRange": [ - "1.6.1", - "1.8.0" - ] + "versionRange": ["1.6.1", "1.8.0"] }, { "image": "/blog/assets/39d7890f8cbe21e77db8d3c94f7f22e4.webp", "id": "2024-07-19-gpt-4o-mini", "date": "2024-07-19", - "versionRange": [ - "1.0.1", - "1.6.0" - ] + "versionRange": ["1.0.1", "1.6.0"] }, { "image": "/blog/assets/eb477e62217f4d1b644eff975c7ac168.webp", "id": "2024-06-19-lobe-chat-v1", "date": "2024-06-19", - "versionRange": [ - "0.147.0", - "1.0.0" - ] + "versionRange": ["0.147.0", "1.0.0"] }, { "image": "/blog/assets/8a8d361b4c0cce6da350cc0de65c0ad6.webp", "id": "2024-02-14-ollama", "date": "2024-02-14", - "versionRange": [ - "0.125.1", - "0.127.0" - ] + "versionRange": ["0.125.1", "0.127.0"] }, { "image": "/blog/assets/9498087e85f27e692716a63cb3b58d79.webp", "id": "2024-02-08-sso-oauth", "date": "2024-02-08", - "versionRange": [ - "0.118.1", - "0.125.0" - ] + "versionRange": ["0.118.1", "0.125.0"] }, { "image": "/blog/assets/603fefbb944bc6761ebdab5956fc0084.webp", "id": "2023-12-22-dalle-3", "date": "2023-12-22", - "versionRange": [ - "0.102.1", - "0.118.0" - ] + "versionRange": ["0.102.1", "0.118.0"] }, { "image": "/blog/assets/8d4c2cc0ce8654fa8ac06cc036a7f941.webp", "id": "2023-11-19-tts-stt", "date": "2023-11-19", - "versionRange": [ - "0.101.1", - "0.102.0" - ] + "versionRange": ["0.101.1", "0.102.0"] }, { "image": "/blog/assets/d47654360d626f80144cdedb979a3526.webp", "id": "2023-11-14-gpt4-vision", "date": "2023-11-14", - "versionRange": [ - "0.90.0", - "0.101.0" - ] + "versionRange": ["0.90.0", "0.101.0"] }, { "image": "/blog/assets/50b38eac1769ae6f13aef72f3d725eec.webp", "id": "2023-09-09-plugin-system", "date": "2023-09-09", - "versionRange": [ - "0.67.0", - "0.72.0" - ] + "versionRange": ["0.67.0", "0.72.0"] } ] } From e7524c4f1af78da10d27f82dfea4709345438fb5 Mon Sep 17 00:00:00 2001 From: Innei Date: Tue, 19 May 2026 20:19:57 +0800 Subject: [PATCH 033/224] =?UTF-8?q?=F0=9F=90=9B=20fix(nav):=20align=20home?= =?UTF-8?q?=20sidebar=20layout=20(#14974)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🐛 fix(nav): align home sidebar layout * 🐛 fix(nav): preserve sidebar bottom grouping --- .../_layout/Body/Agent/CreateAgentButton.tsx | 8 +- .../_layout/Body/CustomizeSidebarModal.tsx | 67 ++++--------- .../(main)/home/_layout/Body/index.test.tsx | 39 +++++++- src/routes/(main)/home/_layout/Body/index.tsx | 95 ++++++++++++------- 4 files changed, 125 insertions(+), 84 deletions(-) diff --git a/src/routes/(main)/home/_layout/Body/Agent/CreateAgentButton.tsx b/src/routes/(main)/home/_layout/Body/Agent/CreateAgentButton.tsx index 89f1819b24..b9dd46085b 100644 --- a/src/routes/(main)/home/_layout/Body/Agent/CreateAgentButton.tsx +++ b/src/routes/(main)/home/_layout/Body/Agent/CreateAgentButton.tsx @@ -53,7 +53,10 @@ const CreateAgentButton = memo(({ groupId, className }) } = useCreateMenuItems(); const isCustomGroup = Boolean(groupId) && groupId !== SessionDefaultGroup.Default; - const menuOptions = isCustomGroup ? { groupId } : undefined; + const menuOptions = useMemo( + () => (isCustomGroup ? { groupId } : undefined), + [groupId, isCustomGroup], + ); const dropdownItems = useMemo(() => { const heteroItems = createHeterogeneousAgentMenuItems(menuOptions); @@ -84,8 +87,9 @@ const CreateAgentButton = memo(({ groupId, className }) align={'center'} className={cx(styles.container, className)} gap={8} - height={32} + height={36} paddingInline={4} + style={{ height: 36 }} variant={'borderless'} onClick={handleClick} > diff --git a/src/routes/(main)/home/_layout/Body/CustomizeSidebarModal.tsx b/src/routes/(main)/home/_layout/Body/CustomizeSidebarModal.tsx index 537d9ae98a..0424dbeca0 100644 --- a/src/routes/(main)/home/_layout/Body/CustomizeSidebarModal.tsx +++ b/src/routes/(main)/home/_layout/Body/CustomizeSidebarModal.tsx @@ -21,13 +21,13 @@ import { } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; import { ActionIcon, Button, Flexbox, Icon, Text, Tooltip } from '@lobehub/ui'; -import { Modal } from '@lobehub/ui/base-ui'; +import { createModal, type ModalInstance } from '@lobehub/ui/base-ui'; import { createStaticStyles, cssVar, cx } from 'antd-style'; +import { t } from 'i18next'; import { Eye, EyeOff, GripVertical, PinIcon, RotateCcw } from 'lucide-react'; import { memo, useCallback, useEffect, useMemo, useState } from 'react'; import { createPortal } from 'react-dom'; import { useTranslation } from 'react-i18next'; -import { create } from 'zustand'; import { getRouteById } from '@/config/routes'; import { useGlobalStore } from '@/store/global'; @@ -62,21 +62,6 @@ const ITEM_MAP = new Map(ALL_SIDEBAR_ITEMS.map((item) => [item.id, item])); const isAccordionKey = (id: string) => SIDEBAR_ACCORDION_KEYS.has(id); -// --------------------------------------------------------------------------- -// Modal store -// --------------------------------------------------------------------------- - -const useCustomizeSidebarModalStore = create<{ - open: boolean; - setOpen: (open: boolean) => void; -}>((set) => ({ - open: false, - setOpen: (open) => set({ open }), -})); - -export const openCustomizeSidebarModal = () => - useCustomizeSidebarModalStore.getState().setOpen(true); - // --------------------------------------------------------------------------- // Styles // --------------------------------------------------------------------------- @@ -383,35 +368,23 @@ const CustomizeSidebarContent = memo(() => { }); // --------------------------------------------------------------------------- -// Modal wrapper +// Modal entry // --------------------------------------------------------------------------- -export const CustomizeSidebarModal = memo(() => { - const { t } = useTranslation('common'); - const open = useCustomizeSidebarModalStore((s) => s.open); - const setOpen = useCustomizeSidebarModalStore((s) => s.setOpen); - const resetSidebarCustomization = useGlobalStore((s) => s.resetSidebarCustomization); - - return ( - } - type={'text'} - onClick={resetSidebarCustomization} - > - {t('navPanel.resetDefault' as any)} - - } - onCancel={() => setOpen(false)} - > - - - ); -}); +export const openCustomizeSidebarModal = (): ModalInstance => + createModal({ + content: , + footer: ( + + ), + maskClosable: true, + title: t('navPanel.customizeSidebar', { ns: 'common' }), + width: 360, + }); diff --git a/src/routes/(main)/home/_layout/Body/index.test.tsx b/src/routes/(main)/home/_layout/Body/index.test.tsx index c1b6fb17bf..5bc9280194 100644 --- a/src/routes/(main)/home/_layout/Body/index.test.tsx +++ b/src/routes/(main)/home/_layout/Body/index.test.tsx @@ -14,6 +14,10 @@ interface MockGlobalState { const mocks = vi.hoisted(() => ({ globalState: undefined as unknown as MockGlobalState, + navLayout: { + bottomMenuItems: [] as { key: string; title: string; url: string }[], + topNavItems: [] as { key: string; title: string; url: string }[], + }, updateSystemStatus: vi.fn(), })); @@ -34,7 +38,9 @@ vi.mock('@lobehub/ui', () => ({ ), ActionIcon: () => , DropdownMenu: ({ children }: { children: React.ReactNode }) => <>{children}, - Flexbox: ({ children }: { children: React.ReactNode }) =>
{children}
, + Flexbox: ({ children }: { children: React.ReactNode }) => ( +
{children}
+ ), Icon: () => , })); @@ -58,7 +64,7 @@ vi.mock('@/hooks/useActiveTabKey', () => ({ })); vi.mock('@/hooks/useNavLayout', () => ({ - useNavLayout: () => ({ bottomMenuItems: [], topNavItems: [] }), + useNavLayout: () => mocks.navLayout, })); vi.mock('@/utils/navigation', () => ({ @@ -78,7 +84,6 @@ vi.mock('./Agent', () => ({ })); vi.mock('./CustomizeSidebarModal', () => ({ - CustomizeSidebarModal: () => null, openCustomizeSidebarModal: vi.fn(), })); @@ -88,6 +93,10 @@ vi.mock('@/store/global', () => ({ beforeEach(() => { mocks.updateSystemStatus.mockReset(); + mocks.navLayout = { + bottomMenuItems: [], + topNavItems: [], + }; mocks.globalState = { status: { hiddenSidebarSections: [], @@ -121,4 +130,28 @@ describe('Home sidebar body', () => { expect(mocks.updateSystemStatus).toHaveBeenCalledWith({ sidebarExpandedKeys: ['agent'] }); }); + + it('keeps custom top ordering above the bottom spacer', () => { + mocks.navLayout = { + bottomMenuItems: [ + { key: 'image', title: 'Image', url: '/image' }, + { key: 'resource', title: 'Resource', url: '/resource' }, + ], + topNavItems: [{ key: 'pages', title: 'Pages', url: '/page' }], + }; + mocks.globalState.status.sidebarItems = ['image', 'pages', 'recents', 'agent', 'resource']; + + render(); + + const children = Array.from(screen.getByTestId('sidebar-body').children); + const spacerIndex = children.findIndex((child) => + child.hasAttribute('data-sidebar-bottom-spacer'), + ); + + expect(spacerIndex).toBe(2); + expect(children[0]).toHaveTextContent('Pages'); + expect(children[1]).toHaveAttribute('data-testid', 'sidebar-accordion'); + expect(children[3]).toHaveTextContent('Image'); + expect(children[4]).toHaveTextContent('Resource'); + }); }); diff --git a/src/routes/(main)/home/_layout/Body/index.tsx b/src/routes/(main)/home/_layout/Body/index.tsx index 96ba6a7958..680a3173dc 100644 --- a/src/routes/(main)/home/_layout/Body/index.tsx +++ b/src/routes/(main)/home/_layout/Body/index.tsx @@ -19,7 +19,7 @@ import { isModifierClick } from '@/utils/navigation'; import { prefetchRoute } from '@/utils/router'; import Agent from './Agent'; -import { CustomizeSidebarModal, openCustomizeSidebarModal } from './CustomizeSidebarModal'; +import { openCustomizeSidebarModal } from './CustomizeSidebarModal'; export enum GroupKey { Agent = 'agent', @@ -101,6 +101,11 @@ const Body = memo(() => { return map; }, [topNavItems, bottomMenuItems]); + const bottomNavKeys = useMemo( + () => new Set(bottomMenuItems.map((item) => item.key)), + [bottomMenuItems], + ); + // Items that must always be visible regardless of hiddenSections const isVisible = useCallback( (k: string) => k === GroupKey.Agent || !hiddenSections.includes(k), @@ -160,45 +165,71 @@ const Body = memo(() => { // Render the flat list: group consecutive accordion items into an Accordion, // interleave non-accordion keys as nav links. const content = useMemo(() => { - const elements: ReactElement[] = []; - let accGroup: { element: ReactElement; key: string }[] = []; + const renderSection = (keys: string[], section: 'bottom' | 'top') => { + const elements: ReactElement[] = []; + let accGroup: { element: ReactElement; key: string }[] = []; - const flushAccordion = () => { - if (accGroup.length > 0) { - const accordionKeys = accGroup.map((item) => item.key); + const flushAccordion = () => { + if (accGroup.length > 0) { + const accordionKeys = accGroup.map((item) => item.key); - elements.push( - handleAccordionExpandedChange(accordionKeys, keys)} - > - {accGroup.map((item) => item.element)} - , - ); - accGroup = []; + elements.push( + handleAccordionExpandedChange(accordionKeys, keys)} + > + {accGroup.map((item) => item.element)} + , + ); + accGroup = []; + } + }; + + for (const key of keys) { + if (ACCORDION_KEYS.has(key)) { + const comp = accordionComponents[key]?.(key); + if (comp) accGroup.push({ element: comp, key }); + } else { + flushAccordion(); + const link = renderNavLink(key); + if (link) elements.push(link); + } } + flushAccordion(); + + return elements; }; - for (const key of visibleKeys) { - if (ACCORDION_KEYS.has(key)) { - const comp = accordionComponents[key]?.(key); - if (comp) accGroup.push({ element: comp, key }); - } else { - flushAccordion(); - const link = renderNavLink(key); - if (link) elements.push(link); - } - } - flushAccordion(); - return elements; - }, [visibleKeys, renderNavLink, sidebarExpandedKeys, handleAccordionExpandedChange]); + const topKeys = visibleKeys.filter((key) => !bottomNavKeys.has(key)); + const bottomKeys = visibleKeys.filter((key) => bottomNavKeys.has(key)); + const topElements = renderSection(topKeys, 'top'); + const bottomElements = renderSection(bottomKeys, 'bottom'); + + if (bottomElements.length === 0) return topElements; + + return [ + ...topElements, +
, + ...bottomElements, + ]; + }, [ + visibleKeys, + renderNavLink, + sidebarExpandedKeys, + handleAccordionExpandedChange, + bottomNavKeys, + ]); return ( - + {content} - ); }); From d2d3888f43b9d4336f9a41d5d3afeb1b998fe10f Mon Sep 17 00:00:00 2001 From: YuTengjing Date: Tue, 19 May 2026 20:46:20 +0800 Subject: [PATCH 034/224] =?UTF-8?q?=F0=9F=90=9B=20fix(command-menu):=20pro?= =?UTF-8?q?mote=20inline=20type=20filters=20from=20setSearch=20(#14986)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CommandMenu/CommandMenuContext.test.tsx | 41 +++++++++++++++++++ .../CommandMenu/CommandMenuContext.tsx | 19 +++++++-- 2 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 src/features/CommandMenu/CommandMenuContext.test.tsx diff --git a/src/features/CommandMenu/CommandMenuContext.test.tsx b/src/features/CommandMenu/CommandMenuContext.test.tsx new file mode 100644 index 0000000000..050950bb42 --- /dev/null +++ b/src/features/CommandMenu/CommandMenuContext.test.tsx @@ -0,0 +1,41 @@ +import { act, renderHook } from '@testing-library/react'; +import type { ReactNode } from 'react'; +import { describe, expect, it, vi } from 'vitest'; + +import { CommandMenuProvider, useCommandMenuContext } from './CommandMenuContext'; + +const createWrapper = () => { + const onClose = vi.fn(); + + const Wrapper = ({ children }: { children: ReactNode }) => ( + + {children} + + ); + + return Wrapper; +}; + +describe('CommandMenuProvider', () => { + it('should promote inline type filters into command menu state', () => { + const { result } = renderHook(() => useCommandMenuContext(), { wrapper: createWrapper() }); + + act(() => { + result.current.setSearch('type:message search content'); + }); + + expect(result.current.search).toBe('search content'); + expect(result.current.typeFilter).toBe('message'); + }); + + it('should keep invalid type filters as literal search text', () => { + const { result } = renderHook(() => useCommandMenuContext(), { wrapper: createWrapper() }); + + act(() => { + result.current.setSearch('type:unknown search content'); + }); + + expect(result.current.search).toBe('type:unknown search content'); + expect(result.current.typeFilter).toBeUndefined(); + }); +}); diff --git a/src/features/CommandMenu/CommandMenuContext.tsx b/src/features/CommandMenu/CommandMenuContext.tsx index 053aa919f2..3044fac244 100644 --- a/src/features/CommandMenu/CommandMenuContext.tsx +++ b/src/features/CommandMenu/CommandMenuContext.tsx @@ -1,11 +1,12 @@ 'use client'; -import { type Dispatch, type ReactNode, type SetStateAction } from 'react'; +import type { Dispatch, ReactNode, SetStateAction } from 'react'; import { createContext, use, useCallback, useMemo, useState } from 'react'; -import { type MenuContext, type PageType, type SelectedAgent } from './types'; +import type { MenuContext, PageType, SelectedAgent } from './types'; import { detectContext } from './utils/context'; -import { type ValidSearchType } from './utils/queryParser'; +import type { ValidSearchType } from './utils/queryParser'; +import { parseSearchQuery } from './utils/queryParser'; interface CommandMenuContextValue { activeAgentId: string | undefined; @@ -53,7 +54,17 @@ export const CommandMenuProvider = ({ children, onClose, pathname }: CommandMenu const viewMode: MenuViewMode = search.trim().length > 0 ? 'search' : 'default'; // Memoize setters to maintain stable references - const setSearch = useCallback((value: string) => setSearchState(value), []); + const setSearch = useCallback((value: string) => { + const parsedQuery = parseSearchQuery(value); + + if (parsedQuery.typeFilter) { + setTypeFilterState(parsedQuery.typeFilter); + setSearchState(parsedQuery.cleanQuery); + return; + } + + setSearchState(value); + }, []); const setTypeFilter = useCallback( (value: ValidSearchType | undefined) => setTypeFilterState(value), [], From 29623c4ab62ece7d78e637602d8199dde858bcc9 Mon Sep 17 00:00:00 2001 From: YuTengjing Date: Tue, 19 May 2026 20:49:41 +0800 Subject: [PATCH 035/224] =?UTF-8?q?=E2=9C=A8=20feat(profile):=20optimistic?= =?UTF-8?q?=20interests=20update=20+=20clickable=20auth=20logo=20(#14984)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/[variants]/(auth)/_layout/index.tsx | 5 +++- .../profile/features/InterestsRow.tsx | 12 ++------ src/store/user/slices/common/action.test.ts | 30 +++++++++++++++++++ src/store/user/slices/common/action.ts | 4 +++ 4 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/app/[variants]/(auth)/_layout/index.tsx b/src/app/[variants]/(auth)/_layout/index.tsx index 248d5f40a0..29411ec464 100644 --- a/src/app/[variants]/(auth)/_layout/index.tsx +++ b/src/app/[variants]/(auth)/_layout/index.tsx @@ -4,6 +4,7 @@ import { COPYRIGHT_FULL } from '@lobechat/business-const'; import { Center, Flexbox, Text } from '@lobehub/ui'; import { Divider } from 'antd'; import { cx } from 'antd-style'; +import Link from 'next/link'; import { type FC, type PropsWithChildren } from 'react'; import { ProductLogo } from '@/components/Branding'; @@ -30,7 +31,9 @@ const AuthContainer: FC = ({ children }) => { padding={16} width={'100%'} > - + + + diff --git a/src/routes/(main)/settings/profile/features/InterestsRow.tsx b/src/routes/(main)/settings/profile/features/InterestsRow.tsx index b65094f203..1c540f3719 100644 --- a/src/routes/(main)/settings/profile/features/InterestsRow.tsx +++ b/src/routes/(main)/settings/profile/features/InterestsRow.tsx @@ -22,13 +22,11 @@ const InterestsRow = () => { const updateInterests = useUserStore((s) => s.updateInterests); const [customInput, setCustomInput] = useState(''); const [showCustomInput, setShowCustomInput] = useState(false); - const [saving, setSaving] = useState(false); const normalizedInterests = useMemo(() => normalizeInterestsForStorage(interests), [interests]); const saveInterests = useCallback( async (updated: string[]) => { try { - setSaving(true); await updateInterests(updated); } catch (error) { console.error('Failed to update interests:', error); @@ -36,8 +34,6 @@ const InterestsRow = () => { errorMessage: error instanceof Error ? error.message : String(error), status: 500, }); - } finally { - setSaving(false); } }, [updateInterests], @@ -101,11 +97,10 @@ const InterestsRow = () => { ? { background: cssVar.colorFillSecondary, borderColor: cssVar.colorFillSecondary, - opacity: saving ? 0.6 : 1, } - : { opacity: saving ? 0.6 : 1 } + : undefined } - onClick={() => !saving && toggleInterest(item.key)} + onClick={() => toggleInterest(item.key)} > @@ -125,9 +120,8 @@ const InterestsRow = () => { style={{ background: cssVar.colorFillSecondary, borderColor: cssVar.colorFillSecondary, - opacity: saving ? 0.6 : 1, }} - onClick={() => !saving && removeCustomInterest(interest)} + onClick={() => removeCustomInterest(interest)} > {interest} diff --git a/src/store/user/slices/common/action.test.ts b/src/store/user/slices/common/action.test.ts index 81909b821f..d02572d07b 100644 --- a/src/store/user/slices/common/action.test.ts +++ b/src/store/user/slices/common/action.test.ts @@ -41,6 +41,36 @@ describe('createCommonSlice', () => { }); }); + describe('updateInterests', () => { + it('optimistically updates user.interests before the service call resolves', async () => { + act(() => { + useUserStore.setState({ user: { id: 'u1', interests: ['old'] } as any }); + }); + + let resolveService: () => void = () => {}; + const updateSpy = vi.spyOn(userService, 'updateInterests').mockImplementation( + () => + new Promise((r) => { + resolveService = r; + }) as any, + ); + + let pending: Promise | undefined; + act(() => { + pending = useUserStore.getState().updateInterests(['new']); + }); + + expect(useUserStore.getState().user?.interests).toEqual(['new']); + + await act(async () => { + resolveService(); + await pending; + }); + + expect(updateSpy).toHaveBeenCalledWith(['new']); + }); + }); + describe('useInitUserState', () => { const mockServerConfig = { defaultAgent: 'agent1', diff --git a/src/store/user/slices/common/action.ts b/src/store/user/slices/common/action.ts index 320dd526e9..936865aa0b 100644 --- a/src/store/user/slices/common/action.ts +++ b/src/store/user/slices/common/action.ts @@ -54,6 +54,10 @@ export class CommonActionImpl { }; updateInterests = async (interests: string[]): Promise => { + const previousUser = this.#get().user; + if (previousUser) { + this.#set({ user: { ...previousUser, interests } }, false, n('updateInterests/optimistic')); + } await userService.updateInterests(interests); await this.#get().refreshUserState(); }; From f9b611bc6966fa22151d0f4587b12d3434670971 Mon Sep 17 00:00:00 2001 From: Neko Date: Tue, 19 May 2026 21:07:36 +0800 Subject: [PATCH 036/224] =?UTF-8?q?=F0=9F=90=9B=20fix(agent-signal,app):?= =?UTF-8?q?=20anchor=20agent=20signal=20receipts=20to=20messages=20(#14969?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../agent-signal/src/source/sourceTypes.ts | 34 +++ .../hooks/useAgentSignalReceipts.test.ts | 209 ++++++++++++++++-- .../ChatList/hooks/useAgentSignalReceipts.ts | 102 +++++++-- src/features/Conversation/ChatList/index.tsx | 14 +- .../services/__tests__/receiptService.test.ts | 93 ++++++++ .../agentSignal/services/receiptService.ts | 13 +- .../__tests__/clientRuntimeComplete.test.ts | 54 +++++ .../__tests__/clientRuntimeStart.test.ts | 58 +++++ .../hydration/clientRuntimeComplete.ts | 2 + .../sources/hydration/clientRuntimeStart.ts | 1 + .../store/__tests__/redisReceiptStore.test.ts | 19 ++ .../store/adapters/redis/receiptStore.ts | 2 + .../__tests__/gatewayEventHandler.test.ts | 3 + .../__tests__/streamingExecutor.test.ts | 25 +++ .../aiChat/actions/agentSignalBridge.test.ts | 6 + .../aiChat/actions/gatewayEventHandler.ts | 10 +- .../aiChat/actions/streamingExecutor.ts | 3 + 17 files changed, 600 insertions(+), 48 deletions(-) diff --git a/packages/agent-signal/src/source/sourceTypes.ts b/packages/agent-signal/src/source/sourceTypes.ts index e3967212fd..cd6329ac0a 100644 --- a/packages/agent-signal/src/source/sourceTypes.ts +++ b/packages/agent-signal/src/source/sourceTypes.ts @@ -84,15 +84,20 @@ export interface AgentSignalSourcePayloadMap { }; [AGENT_SIGNAL_SOURCE_TYPES.agentUserMessage]: { agentId?: string; + /** Message the receipt or UI should attach to, usually the assistant response. */ + anchorMessageId?: string; documentPayload?: Record; intents?: Array<'document' | 'memory' | 'persona' | 'prompt' | 'skill'>; memoryPayload?: Record; message: string; + /** Legacy source message identifier kept for compatibility. */ messageId: string; serializedContext?: string; threadId?: string; topicId?: string; trigger?: string; + /** Message that initiated the source or run, usually the user message. */ + triggerMessageId?: string; }; [AGENT_SIGNAL_SOURCE_TYPES.botMessageMerged]: { agentId?: string; @@ -105,52 +110,81 @@ export interface AgentSignalSourcePayloadMap { }; [AGENT_SIGNAL_SOURCE_TYPES.clientGatewayError]: { agentId?: string; + /** Message the receipt or UI should attach to, usually the assistant response. */ + anchorMessageId?: string; + /** Legacy assistant response identifier kept for compatibility. */ assistantMessageId?: string; errorMessage?: string; operationId: string; serializedContext?: string; topicId?: string; + /** Message that initiated the source or run, usually the user message. */ + triggerMessageId?: string; }; [AGENT_SIGNAL_SOURCE_TYPES.clientGatewayRuntimeEnd]: { agentId?: string; + /** Message the receipt or UI should attach to, usually the assistant response. */ + anchorMessageId?: string; + /** Legacy assistant response identifier kept for compatibility. */ assistantMessageId?: string; operationId: string; serializedContext?: string; topicId?: string; + /** Message that initiated the source or run, usually the user message. */ + triggerMessageId?: string; }; [AGENT_SIGNAL_SOURCE_TYPES.clientGatewayStepComplete]: { agentId?: string; + /** Message the receipt or UI should attach to, usually the assistant response. */ + anchorMessageId?: string; + /** Legacy assistant response identifier kept for compatibility. */ assistantMessageId?: string; operationId: string; serializedContext?: string; stepIndex: number; topicId?: string; + /** Message that initiated the source or run, usually the user message. */ + triggerMessageId?: string; }; [AGENT_SIGNAL_SOURCE_TYPES.clientGatewayStreamStart]: { agentId?: string; + /** Message the receipt or UI should attach to, usually the assistant response. */ + anchorMessageId?: string; + /** Legacy assistant response identifier kept for compatibility. */ assistantMessageId?: string; operationId: string; serializedContext?: string; stepIndex: number; topicId?: string; + /** Message that initiated the source or run, usually the user message. */ + triggerMessageId?: string; }; [AGENT_SIGNAL_SOURCE_TYPES.clientRuntimeComplete]: { agentId?: string; + /** Message the receipt or UI should attach to, usually the assistant response. */ + anchorMessageId?: string; + /** Legacy assistant response identifier kept for compatibility. */ assistantMessageId?: string; operationId: string; serializedContext?: string; status?: 'cancelled' | 'completed' | 'failed'; threadId?: string; topicId?: string; + /** Message that initiated the source or run, usually the user message. */ + triggerMessageId?: string; }; [AGENT_SIGNAL_SOURCE_TYPES.clientRuntimeStart]: { agentId?: string; + /** Message the receipt or UI should attach to, usually the assistant response. */ + anchorMessageId?: string; operationId: string; parentMessageId?: string; parentMessageType?: string; serializedContext?: string; threadId?: string; topicId?: string; + /** Message that initiated the source or run, usually the user message. */ + triggerMessageId?: string; }; [AGENT_SIGNAL_SOURCE_TYPES.runtimeAfterStep]: { agentId?: string; diff --git a/src/features/Conversation/ChatList/hooks/useAgentSignalReceipts.test.ts b/src/features/Conversation/ChatList/hooks/useAgentSignalReceipts.test.ts index 1db448ce71..1d3631e3f9 100644 --- a/src/features/Conversation/ChatList/hooks/useAgentSignalReceipts.test.ts +++ b/src/features/Conversation/ChatList/hooks/useAgentSignalReceipts.test.ts @@ -1,3 +1,4 @@ +import type { UIChatMessage } from '@lobechat/types'; import { act, renderHook, waitFor } from '@testing-library/react'; import type { PropsWithChildren } from 'react'; import { createElement } from 'react'; @@ -34,6 +35,14 @@ vi.mock('@/services/agentSignal', () => ({ }, })); +const message = (input: Partial & { id: string; role: UIChatMessage['role'] }) => + ({ + content: '', + createdAt: 1, + updatedAt: 1, + ...input, + }) as UIChatMessage; + describe('useAgentSignalReceipts', () => { const wrapper = ({ children }: PropsWithChildren) => createElement(SWRConfig, { value: { provider: () => new Map() } }, children); @@ -43,11 +52,19 @@ describe('useAgentSignalReceipts', () => { vi.useRealTimers(); }); - it('groups receipts by anchor and keeps unanchored receipts separate', async () => { - const { result } = renderHook( - () => useAgentSignalReceipts({ agentId: 'agent-1', enabled: true, topicId: 'topic-1' }), - { wrapper }, - ); + const renderReceiptsHook = (input: Parameters[0]) => + renderHook(() => useAgentSignalReceipts(input), { wrapper }); + + it('groups anchored receipts by anchorMessageId', async () => { + const { result } = renderReceiptsHook({ + agentId: 'agent-1', + displayMessages: [ + message({ id: 'user-1', role: 'user' }), + message({ id: 'assistant-1', parentId: 'user-1', role: 'assistant' }), + ], + enabled: true, + topicId: 'topic-1', + }); await waitFor(() => { expect(result.current.receiptsByAnchor.get('assistant-1')).toEqual([ @@ -61,11 +78,154 @@ describe('useAgentSignalReceipts', () => { }); }); + it('groups anchored receipts under the assistant group when the anchor is a child block', async () => { + vi.mocked(agentSignalService.listReceipts).mockResolvedValueOnce({ + cursor: undefined, + receipts: [{ ...receipt, anchorMessageId: 'assistant-child-2', id: 'receipt-child-anchor' }], + }); + + const { result } = renderReceiptsHook({ + agentId: 'agent-1', + displayMessages: [ + message({ id: 'user-1', role: 'user' }), + message({ + children: [ + { content: 'First assistant step', id: 'assistant-child-1' }, + { content: 'Final assistant step', id: 'assistant-child-2' }, + ], + id: 'assistant-group-1', + parentId: 'user-1', + role: 'assistantGroup', + }), + ], + enabled: true, + topicId: 'topic-1', + }); + + await waitFor(() => { + expect(result.current.receiptsByAnchor.get('assistant-group-1')).toEqual([ + expect.objectContaining({ id: 'receipt-child-anchor' }), + ]); + }); + expect(result.current.receiptsByAnchor.get('assistant-child-2')).toBeUndefined(); + }); + + it('groups trigger-only receipts under the assistant child message when present', async () => { + vi.mocked(agentSignalService.listReceipts).mockResolvedValueOnce({ + cursor: undefined, + receipts: [ + { + ...receipt, + anchorMessageId: undefined, + id: 'receipt-trigger', + triggerMessageId: 'user-1', + }, + ], + }); + + const { result } = renderReceiptsHook({ + agentId: 'agent-1', + displayMessages: [ + message({ id: 'user-1', role: 'user' }), + message({ id: 'assistant-1', parentId: 'user-1', role: 'assistant' }), + ], + enabled: true, + topicId: 'topic-1', + }); + + await waitFor(() => { + expect(result.current.receiptsByAnchor.get('assistant-1')).toEqual([ + expect.objectContaining({ id: 'receipt-trigger' }), + ]); + }); + expect(result.current.receiptsByAnchor.get('user-1')).toBeUndefined(); + }); + + it('groups trigger-only receipts under the assistant group child message when present', async () => { + vi.mocked(agentSignalService.listReceipts).mockResolvedValueOnce({ + cursor: undefined, + receipts: [ + { + ...receipt, + anchorMessageId: undefined, + id: 'receipt-trigger', + triggerMessageId: 'user-1', + }, + ], + }); + + const { result } = renderReceiptsHook({ + agentId: 'agent-1', + displayMessages: [ + message({ id: 'user-1', role: 'user' }), + message({ id: 'assistant-group-1', parentId: 'user-1', role: 'assistantGroup' }), + ], + enabled: true, + topicId: 'topic-1', + }); + + await waitFor(() => { + expect(result.current.receiptsByAnchor.get('assistant-group-1')).toEqual([ + expect.objectContaining({ id: 'receipt-trigger' }), + ]); + }); + expect(result.current.receiptsByAnchor.get('user-1')).toBeUndefined(); + }); + + it('groups trigger-only receipts under the trigger message when no assistant child exists', async () => { + vi.mocked(agentSignalService.listReceipts).mockResolvedValueOnce({ + cursor: undefined, + receipts: [ + { + ...receipt, + anchorMessageId: undefined, + id: 'receipt-trigger', + triggerMessageId: 'user-1', + }, + ], + }); + + const { result } = renderReceiptsHook({ + agentId: 'agent-1', + displayMessages: [message({ id: 'user-1', role: 'user' })], + enabled: true, + topicId: 'topic-1', + }); + + await waitFor(() => { + expect(result.current.receiptsByAnchor.get('user-1')).toEqual([ + expect.objectContaining({ id: 'receipt-trigger' }), + ]); + }); + }); + + it('does not group receipts without anchorMessageId or triggerMessageId', async () => { + vi.mocked(agentSignalService.listReceipts).mockResolvedValueOnce({ + cursor: undefined, + receipts: [{ ...receipt, anchorMessageId: undefined, id: 'receipt-floating' }], + }); + + const { result } = renderReceiptsHook({ + agentId: 'agent-1', + displayMessages: [message({ id: 'assistant-1', role: 'assistant' })], + enabled: true, + topicId: 'topic-1', + }); + + await waitFor(() => { + expect(agentSignalService.listReceipts).toHaveBeenCalled(); + }); + expect([...result.current.receiptsByAnchor.values()].flat()).toEqual([]); + expect('unanchoredReceipts' in result.current).toBe(false); + }); + it('does not fetch receipts when the feature flag is disabled', async () => { - renderHook( - () => useAgentSignalReceipts({ agentId: 'agent-1', enabled: false, topicId: 'topic-1' }), - { wrapper }, - ); + renderReceiptsHook({ + agentId: 'agent-1', + displayMessages: [], + enabled: false, + topicId: 'topic-1', + }); expect(agentSignalService.listReceipts).not.toHaveBeenCalled(); }); @@ -82,10 +242,12 @@ describe('useAgentSignalReceipts', () => { receipts: [], }); - renderHook( - () => useAgentSignalReceipts({ agentId: 'agent-1', enabled: true, topicId: 'topic-1' }), - { wrapper }, - ); + renderReceiptsHook({ + agentId: 'agent-1', + displayMessages: [], + enabled: true, + topicId: 'topic-1', + }); await act(async () => { await Promise.resolve(); @@ -113,10 +275,12 @@ describe('useAgentSignalReceipts', () => { receipts: [], }); - renderHook( - () => useAgentSignalReceipts({ agentId: 'agent-1', enabled: true, topicId: 'topic-1' }), - { wrapper }, - ); + renderReceiptsHook({ + agentId: 'agent-1', + displayMessages: [], + enabled: true, + topicId: 'topic-1', + }); await act(async () => { await Promise.resolve(); @@ -147,10 +311,12 @@ describe('useAgentSignalReceipts', () => { receipts: [], }); - renderHook( - () => useAgentSignalReceipts({ agentId: 'agent-1', enabled: true, topicId: 'topic-1' }), - { wrapper }, - ); + renderReceiptsHook({ + agentId: 'agent-1', + displayMessages: [], + enabled: true, + topicId: 'topic-1', + }); await act(async () => { await Promise.resolve(); @@ -179,6 +345,7 @@ describe('useAgentSignalReceipts', () => { ({ pollingSignal }) => useAgentSignalReceipts({ agentId: 'agent-1', + displayMessages: [], enabled: true, pollingSignal, topicId: 'topic-1', diff --git a/src/features/Conversation/ChatList/hooks/useAgentSignalReceipts.ts b/src/features/Conversation/ChatList/hooks/useAgentSignalReceipts.ts index 71d3bf24cb..289f8b8b1f 100644 --- a/src/features/Conversation/ChatList/hooks/useAgentSignalReceipts.ts +++ b/src/features/Conversation/ChatList/hooks/useAgentSignalReceipts.ts @@ -1,4 +1,5 @@ -import { useRef } from 'react'; +import type { UIChatMessage } from '@lobechat/types'; +import { useMemo, useRef } from 'react'; import useSWR from 'swr'; import { agentSignalService } from '@/services/agentSignal'; @@ -18,6 +19,7 @@ export type AgentSignalReceiptView = Awaited< export const useAgentSignalReceipts = (input: { agentId?: string | null; + displayMessages: UIChatMessage[]; enabled?: boolean; pollingSignal?: string | null; topicId?: string | null; @@ -99,28 +101,96 @@ export const useAgentSignalReceipts = (input: { const receipts = data?.receipts ?? []; - const receiptsByAnchor = new Map(); - const unanchoredReceipts: AgentSignalReceiptView[] = []; - - for (const receipt of receipts) { - if (!receipt.anchorMessageId) { - unanchoredReceipts.push(receipt); - continue; - } - - receiptsByAnchor.set(receipt.anchorMessageId, [ - ...(receiptsByAnchor.get(receipt.anchorMessageId) ?? []), - receipt, - ]); - } + const receiptsByAnchor = useMemo( + () => + groupAgentSignalReceiptsByEffectiveAnchor({ + displayMessages: input.displayMessages, + receipts, + }), + [input.displayMessages, receipts], + ); return { isLoading, receiptsByAnchor, - unanchoredReceipts, }; }; +interface GroupAgentSignalReceiptsByEffectiveAnchorInput { + displayMessages: UIChatMessage[]; + receipts: AgentSignalReceiptView[]; +} + +const resolveAssistantReplyFromTrigger = ( + triggerMessageId: string | undefined, + displayMessages: UIChatMessage[], +) => { + if (!triggerMessageId) return undefined; + + return displayMessages.find( + (message) => + (message.role === 'assistant' || message.role === 'assistantGroup') && + message.parentId === triggerMessageId, + )?.id; +}; + +const resolveDisplayedAnchorMessageId = ( + anchorMessageId: string, + displayMessages: UIChatMessage[], +) => { + if (displayMessages.some((message) => message.id === anchorMessageId)) return anchorMessageId; + + return displayMessages.find( + (message) => + message.role === 'assistantGroup' && + message.children?.some((block) => block.id === anchorMessageId), + )?.id; +}; + +const resolveEffectiveAnchorMessageId = ( + receipt: AgentSignalReceiptView, + displayMessages: UIChatMessage[], +) => { + if (receipt.anchorMessageId) { + return resolveDisplayedAnchorMessageId(receipt.anchorMessageId, displayMessages); + } + if (!receipt.triggerMessageId) return undefined; + + const assistantReplyId = resolveAssistantReplyFromTrigger( + receipt.triggerMessageId, + displayMessages, + ); + if (assistantReplyId) return assistantReplyId; + + // WORKAROUND: + // Start-triggered Agent Signal receipts can arrive before the assistant row is available, so + // falling back to the trigger keeps them visible and prevents latest-message drift. + // + // TODO: + // Remove or simplify this fallback when all user-triggered paths provide anchorMessageId or + // stable assistant child resolution. + return receipt.triggerMessageId; +}; + +const groupAgentSignalReceiptsByEffectiveAnchor = ({ + displayMessages, + receipts, +}: GroupAgentSignalReceiptsByEffectiveAnchorInput) => { + const receiptsByAnchor = new Map(); + + for (const receipt of receipts) { + const anchorMessageId = resolveEffectiveAnchorMessageId(receipt, displayMessages); + if (!anchorMessageId) continue; + + receiptsByAnchor.set(anchorMessageId, [ + ...(receiptsByAnchor.get(anchorMessageId) ?? []), + receipt, + ]); + } + + return receiptsByAnchor; +}; + const mergeReceiptRefresh = ( currentReceipts: AgentSignalReceiptView[], newReceipts: AgentSignalReceiptView[], diff --git a/src/features/Conversation/ChatList/index.tsx b/src/features/Conversation/ChatList/index.tsx index bfe8097916..b8ca65d4e7 100644 --- a/src/features/Conversation/ChatList/index.tsx +++ b/src/features/Conversation/ChatList/index.tsx @@ -80,6 +80,7 @@ const ChatList = memo( const activeAgentId = useChatStore((s) => s.activeAgentId); const { enableAgentSelfIteration } = useServerConfigStore(featureFlagsSelectors); useFetchMessages(context, skipFetch); + const displayMessages = useConversationStore(dataSelectors.displayMessages); const displayMessageIds = useConversationStore(dataSelectors.displayMessageIds); const latestMessageId = displayMessageIds.at(-1); @@ -87,8 +88,9 @@ const ChatList = memo( const isSharePage = !!context.topicShareId; // TODO: Migrate Agent Signal receipts behind a dedicated user-visible receipt capability. const canShowAgentSignalReceipts = enableAgentSelfIteration === true && !isSharePage; - const { receiptsByAnchor, unanchoredReceipts } = useAgentSignalReceipts({ + const { receiptsByAnchor } = useAgentSignalReceipts({ agentId: canShowAgentSignalReceipts ? activeAgentId : undefined, + displayMessages, enabled: canShowAgentSignalReceipts, pollingSignal: latestMessageId, topicId: canShowAgentSignalReceipts ? context.topicId : undefined, @@ -105,13 +107,9 @@ const ChatList = memo( (index: number, id: string) => { const isLatestItem = displayMessageIds.length === index + 1; const anchoredReceipts = receiptsByAnchor.get(id) ?? []; - const latestUnanchoredReceipts = isLatestItem ? unanchoredReceipts : []; const receiptRender = - anchoredReceipts.length > 0 || latestUnanchoredReceipts.length > 0 ? ( - <> - - - + anchoredReceipts.length > 0 ? ( + ) : undefined; return ( @@ -124,7 +122,7 @@ const ChatList = memo( /> ); }, - [displayMessageIds.length, defaultWorkflowExpandLevel, receiptsByAnchor, unanchoredReceipts], + [displayMessageIds.length, defaultWorkflowExpandLevel, receiptsByAnchor], ); const messagesInit = useConversationStore(dataSelectors.messagesInit); diff --git a/src/server/services/agentSignal/services/__tests__/receiptService.test.ts b/src/server/services/agentSignal/services/__tests__/receiptService.test.ts index 7dc38fd9ed..babff292ef 100644 --- a/src/server/services/agentSignal/services/__tests__/receiptService.test.ts +++ b/src/server/services/agentSignal/services/__tests__/receiptService.test.ts @@ -53,6 +53,99 @@ const result = (input: { }); describe('projectAgentSignalReceipts', () => { + it('prefers anchorMessageId over assistantMessageId for receipt anchoring', () => { + const anchoredSource = createSource({ + payload: { + agentId: 'agent-1', + anchorMessageId: 'assistant-anchor-1', + assistantMessageId: 'assistant-legacy-1', + topicId: 'topic-1', + }, + scope: { topicId: 'topic-1', userId: 'user-1' }, + scopeKey: 'topic:topic-1', + sourceId: 'source-anchor-1', + sourceType: 'client.runtime.complete', + timestamp: 1_700_000, + }); + + expect( + projectAgentSignalReceipts({ + actions: [ + action({ + actionId: 'action-memory-1', + actionType: AGENT_SIGNAL_POLICY_ACTION_TYPES.userMemoryHandle, + payload: {}, + }), + ], + results: [result({ actionId: 'action-memory-1', status: 'applied' })], + source: anchoredSource, + userId: 'user-1', + }), + ).toMatchObject([{ anchorMessageId: 'assistant-anchor-1' }]); + }); + + it('falls back to assistantMessageId for legacy receipt anchoring payloads', () => { + expect( + projectAgentSignalReceipts({ + actions: [ + action({ + actionId: 'action-memory-1', + actionType: AGENT_SIGNAL_POLICY_ACTION_TYPES.userMemoryHandle, + payload: {}, + }), + ], + results: [result({ actionId: 'action-memory-1', status: 'applied' })], + source, + userId: 'user-1', + }), + ).toMatchObject([{ anchorMessageId: 'assistant-1' }]); + }); + + it('projects triggerMessageId and falls back to messageId for legacy trigger payloads', () => { + const triggerSource = createSource({ + payload: { + agentId: 'agent-1', + messageId: 'message-legacy-1', + topicId: 'topic-1', + triggerMessageId: 'message-trigger-1', + }, + scope: { topicId: 'topic-1', userId: 'user-1' }, + scopeKey: 'topic:topic-1', + sourceId: 'source-trigger-1', + sourceType: 'agent.user.message', + timestamp: 1_700_000, + }); + const legacyTriggerSource = createSource({ + payload: { + agentId: 'agent-1', + messageId: 'message-legacy-1', + topicId: 'topic-1', + }, + scope: { topicId: 'topic-1', userId: 'user-1' }, + scopeKey: 'topic:topic-1', + sourceId: 'source-trigger-2', + sourceType: 'agent.user.message', + timestamp: 1_700_000, + }); + + const project = (projectSource: typeof triggerSource) => + projectAgentSignalReceipts({ + actions: [ + action({ + actionId: 'action-memory-1', + actionType: AGENT_SIGNAL_POLICY_ACTION_TYPES.userMemoryHandle, + payload: {}, + }), + ], + results: [result({ actionId: 'action-memory-1', status: 'applied' })], + source: projectSource, + userId: 'user-1', + }); + + expect(project(triggerSource)).toMatchObject([{ triggerMessageId: 'message-trigger-1' }]); + expect(project(legacyTriggerSource)).toMatchObject([{ triggerMessageId: 'message-legacy-1' }]); + }); + it('projects applied memory action results without unstructured feedback as target', () => { expect( projectAgentSignalReceipts({ diff --git a/src/server/services/agentSignal/services/receiptService.ts b/src/server/services/agentSignal/services/receiptService.ts index 5790210efb..54f0f6eadb 100644 --- a/src/server/services/agentSignal/services/receiptService.ts +++ b/src/server/services/agentSignal/services/receiptService.ts @@ -109,6 +109,8 @@ export interface AgentSignalReceipt { title: string; /** Topic where the receipt should be listed. */ topicId: string; + /** Message that triggered the Agent Signal source, when known. */ + triggerMessageId?: string; /** Owner used to enforce topic index isolation. */ userId: string; } @@ -539,6 +541,14 @@ export const projectAgentSignalReceipts = ({ if (!agentId || !topicId) return []; const actionById = new Map(actions.map((action) => [action.actionId, action])); + const anchorMessageId = + getPayloadString(payload, 'anchorMessageId') ?? + // TODO: Remove after producers stop emitting only assistantMessageId. + getPayloadString(payload, 'assistantMessageId'); + const triggerMessageId = + getPayloadString(payload, 'triggerMessageId') ?? + // TODO: Remove after producers stop emitting only messageId. + getPayloadString(payload, 'messageId'); return results.flatMap((result) => { if (result.status !== 'applied') return []; @@ -555,7 +565,7 @@ export const projectAgentSignalReceipts = ({ { ...visibleOutcome, agentId, - anchorMessageId: getPayloadString(payload, 'assistantMessageId'), + ...(anchorMessageId ? { anchorMessageId } : {}), createdAt: source.timestamp, id: `${source.sourceId}:${result.actionId}:${visibleOutcome.kind}`, operationId: getPayloadString(payload, 'operationId'), @@ -563,6 +573,7 @@ export const projectAgentSignalReceipts = ({ sourceType: source.sourceType, topicId, ...(target ? { target } : {}), + ...(triggerMessageId ? { triggerMessageId } : {}), userId, }, ]; diff --git a/src/server/services/agentSignal/sources/hydration/__tests__/clientRuntimeComplete.test.ts b/src/server/services/agentSignal/sources/hydration/__tests__/clientRuntimeComplete.test.ts index 422c6a6ec7..46aa6c58e1 100644 --- a/src/server/services/agentSignal/sources/hydration/__tests__/clientRuntimeComplete.test.ts +++ b/src/server/services/agentSignal/sources/hydration/__tests__/clientRuntimeComplete.test.ts @@ -82,11 +82,13 @@ describe('resolveClientRuntimeCompleteFeedbackSource', () => { expect(result.source).toEqual({ payload: { agentId: 'client-agent', + anchorMessageId: assistantMessageId, message: 'Please remember this workflow.', messageId: parentMessageId, threadId: 'client-thread', topicId, trigger: AGENT_SIGNAL_SOURCE_TYPES.clientRuntimeComplete, + triggerMessageId: parentMessageId, }, scopeKey: `topic:${topicId}`, sourceId: `${assistantMessageId}:completion:${parentMessageId}`, @@ -98,6 +100,58 @@ describe('resolveClientRuntimeCompleteFeedbackSource', () => { DB_HYDRATION_TEST_TIMEOUT, ); + /** + * @example + * client.runtime.complete({ anchorMessageId, triggerMessageId }) keeps explicit message anchors. + */ + it( + 'hydrates runtime complete with explicit anchorMessageId and triggerMessageId from the source payload', + async () => { + const db = await getTestDB(); + const userId = `user_${uuid()}`; + const topicId = `topic_${uuid()}`; + const parentMessageId = `msg_${uuid()}`; + const assistantMessageId = `msg_${uuid()}`; + const anchorMessageId = `msg_${uuid()}`; + const triggerMessageId = `msg_${uuid()}`; + + await db.insert(users).values({ id: userId }); + await db.insert(topics).values({ id: topicId, title: 'Workflow Topic', userId }); + await db.insert(messages).values({ + content: 'Keep explicit completion anchors.', + id: parentMessageId, + role: 'user', + topicId, + userId, + }); + await db.insert(messages).values({ + content: 'Completion response.', + id: assistantMessageId, + parentId: parentMessageId, + role: 'assistant', + topicId, + userId, + }); + + const result = await resolveClientRuntimeCompleteFeedbackSource( + createCompleteSource({ + anchorMessageId, + assistantMessageId, + triggerMessageId, + }), + { db, userId }, + ); + + expect(result.source?.payload).toMatchObject({ + anchorMessageId, + messageId: parentMessageId, + trigger: AGENT_SIGNAL_SOURCE_TYPES.clientRuntimeComplete, + triggerMessageId, + }); + }, + DB_HYDRATION_TEST_TIMEOUT, + ); + /** * @example * client.runtime.complete({}) cannot hydrate without an assistant message id. diff --git a/src/server/services/agentSignal/sources/hydration/__tests__/clientRuntimeStart.test.ts b/src/server/services/agentSignal/sources/hydration/__tests__/clientRuntimeStart.test.ts index 8d95ceb5e0..1b2d2cfc00 100644 --- a/src/server/services/agentSignal/sources/hydration/__tests__/clientRuntimeStart.test.ts +++ b/src/server/services/agentSignal/sources/hydration/__tests__/clientRuntimeStart.test.ts @@ -1,4 +1,5 @@ // @vitest-environment node +import type { SourceEventClientRuntimeStart } from '@lobechat/agent-signal/source'; import { AGENT_SIGNAL_SOURCE_TYPES } from '@lobechat/agent-signal/source'; import { messages, topics, users } from '@lobechat/database/schemas'; import { getTestDB } from '@lobechat/database/test-utils'; @@ -8,6 +9,23 @@ import { uuid } from '@/utils/uuid'; import { resolveClientRuntimeStartFeedbackSource } from '../clientRuntimeStart'; +const createStartSource = ( + payload: Partial = {}, +): SourceEventClientRuntimeStart => ({ + payload: { + agentId: 'agent_1', + operationId: `op_${uuid()}`, + parentMessageId: `msg_${uuid()}`, + parentMessageType: 'user', + topicId: 'topic_1', + ...payload, + }, + scopeKey: 'topic:topic_1', + sourceId: 'client:start', + sourceType: AGENT_SIGNAL_SOURCE_TYPES.clientRuntimeStart, + timestamp: Date.now(), +}); + describe('resolveClientRuntimeStartFeedbackSource', { timeout: 15_000 }, () => { /** * @example @@ -85,10 +103,50 @@ describe('resolveClientRuntimeStartFeedbackSource', { timeout: 15_000 }, () => { messageId, topicId: 'topic_1', trigger: 'client.runtime.start', + triggerMessageId: messageId, }); + expect(result.source?.payload.anchorMessageId).toBeUndefined(); expect(result.source?.payload.serializedContext).toBeUndefined(); }); + /** + * @example + * client.runtime.start({ triggerMessageId }) keeps the explicit trigger instead of replacing it. + */ + it('hydrates runtime start with the explicit triggerMessageId from the source payload', async () => { + const db = await getTestDB(); + const userId = `user_${uuid()}`; + const topicId = `topic_${uuid()}`; + const messageId = `msg_${uuid()}`; + const triggerMessageId = `msg_${uuid()}`; + + await db.insert(users).values({ id: userId }); + await db.insert(topics).values({ id: topicId, title: 'Workflow Topic', userId }); + await db.insert(messages).values({ + content: 'Use the source trigger when it is present.', + id: messageId, + role: 'user', + topicId, + userId, + }); + + const result = await resolveClientRuntimeStartFeedbackSource( + createStartSource({ + parentMessageId: messageId, + topicId, + triggerMessageId, + }), + { db, userId }, + ); + + expect(result.source?.payload).toMatchObject({ + messageId, + trigger: AGENT_SIGNAL_SOURCE_TYPES.clientRuntimeStart, + triggerMessageId, + }); + expect(result.source?.payload.anchorMessageId).toBeUndefined(); + }); + /** * @example * client.runtime.start with mismatched client topic metadata uses trusted persisted topic and scope. diff --git a/src/server/services/agentSignal/sources/hydration/clientRuntimeComplete.ts b/src/server/services/agentSignal/sources/hydration/clientRuntimeComplete.ts index 3f5d007c15..4374f96602 100644 --- a/src/server/services/agentSignal/sources/hydration/clientRuntimeComplete.ts +++ b/src/server/services/agentSignal/sources/hydration/clientRuntimeComplete.ts @@ -145,12 +145,14 @@ export const resolveClientRuntimeCompleteFeedbackSource = async ( source: { payload: { agentId: parentMessage.agentId ?? assistantMessage.agentId ?? sourceEvent.payload.agentId, + anchorMessageId: sourceEvent.payload.anchorMessageId ?? assistantMessage.id, message: parentMessage.content, messageId: parentMessage.id, threadId: parentMessage.threadId ?? assistantMessage.threadId ?? sourceEvent.payload.threadId, topicId: trustedTopicId ?? sourceEvent.payload.topicId, trigger: AGENT_SIGNAL_SOURCE_TYPES.clientRuntimeComplete, + triggerMessageId: sourceEvent.payload.triggerMessageId ?? parentMessage.id, }, scopeKey: getTrustedScopeKey(trustedTopicId, sourceEvent.scopeKey), sourceId: getHydratedSourceId(assistantMessage.id, parentMessage.id), diff --git a/src/server/services/agentSignal/sources/hydration/clientRuntimeStart.ts b/src/server/services/agentSignal/sources/hydration/clientRuntimeStart.ts index 284cebc363..25e1990199 100644 --- a/src/server/services/agentSignal/sources/hydration/clientRuntimeStart.ts +++ b/src/server/services/agentSignal/sources/hydration/clientRuntimeStart.ts @@ -89,6 +89,7 @@ export const resolveClientRuntimeStartFeedbackSource = async ( threadId: parentMessage.threadId ?? sourceEvent.payload.threadId, topicId: parentMessage.topicId ?? sourceEvent.payload.topicId, trigger: AGENT_SIGNAL_SOURCE_TYPES.clientRuntimeStart, + triggerMessageId: sourceEvent.payload.triggerMessageId ?? parentMessage.id, }, scopeKey: getTrustedScopeKey(parentMessage.topicId, sourceEvent.scopeKey), sourceId: parentMessage.id, diff --git a/src/server/services/agentSignal/store/__tests__/redisReceiptStore.test.ts b/src/server/services/agentSignal/store/__tests__/redisReceiptStore.test.ts index 0b4eedc251..8087c3403d 100644 --- a/src/server/services/agentSignal/store/__tests__/redisReceiptStore.test.ts +++ b/src/server/services/agentSignal/store/__tests__/redisReceiptStore.test.ts @@ -37,6 +37,7 @@ const receipt = { }, title: 'Memory saved', topicId: 'topic-1', + triggerMessageId: 'user-message-1', userId: 'user-1', }; @@ -74,6 +75,7 @@ describe('redis receipt store', () => { }), title: 'Memory saved', topicId: 'topic-1', + triggerMessageId: 'user-message-1', userId: 'user-1', }); expect(mockRedis.zadd).toHaveBeenCalledWith( @@ -146,6 +148,23 @@ describe('redis receipt store', () => { }); }); + it('round-trips triggerMessageId with the receipt payload', async () => { + const store = await loadStore(); + + await store.appendReceipt(receipt, 259_200); + + await expect( + store.listReceipts({ agentId: 'agent-1', limit: 10, topicId: 'topic-1', userId: 'user-1' }), + ).resolves.toMatchObject({ + receipts: [ + { + anchorMessageId: 'assistant-1', + triggerMessageId: 'user-message-1', + }, + ], + }); + }); + it('lists receipts created after a known timestamp for refresh polling', async () => { const store = await loadStore(); diff --git a/src/server/services/agentSignal/store/adapters/redis/receiptStore.ts b/src/server/services/agentSignal/store/adapters/redis/receiptStore.ts index 61963922f1..2e5bcb6396 100644 --- a/src/server/services/agentSignal/store/adapters/redis/receiptStore.ts +++ b/src/server/services/agentSignal/store/adapters/redis/receiptStore.ts @@ -20,6 +20,7 @@ const toReceiptHash = (receipt: AgentSignalReceipt): Record => ( ...(receipt.target ? { target: JSON.stringify(receipt.target) } : {}), title: receipt.title, topicId: receipt.topicId, + ...(receipt.triggerMessageId ? { triggerMessageId: receipt.triggerMessageId } : {}), userId: receipt.userId, }); @@ -113,6 +114,7 @@ const fromReceiptHash = (payload: Record): AgentSignalReceipt | ...(target ? { target } : {}), title: payload.title, topicId: payload.topicId, + triggerMessageId: payload.triggerMessageId, userId: payload.userId, }; }; diff --git a/src/store/chat/slices/aiChat/actions/__tests__/gatewayEventHandler.test.ts b/src/store/chat/slices/aiChat/actions/__tests__/gatewayEventHandler.test.ts index a0ad1e41d0..2140c31418 100644 --- a/src/store/chat/slices/aiChat/actions/__tests__/gatewayEventHandler.test.ts +++ b/src/store/chat/slices/aiChat/actions/__tests__/gatewayEventHandler.test.ts @@ -94,8 +94,10 @@ describe('createGatewayEventHandler', () => { expect(emitClientAgentSignalSourceEvent).toHaveBeenCalledWith( expect.objectContaining({ payload: expect.objectContaining({ + anchorMessageId: 'msg-step2', assistantMessageId: 'msg-step2', operationId: 'op-1', + stepIndex: 0, }), sourceType: 'client.gateway.stream_start', }), @@ -639,6 +641,7 @@ describe('createGatewayEventHandler', () => { expect(emitClientAgentSignalSourceEvent).toHaveBeenLastCalledWith( expect.objectContaining({ payload: expect.objectContaining({ + anchorMessageId: 'msg-step2', assistantMessageId: 'msg-step2', operationId: 'op-1', }), diff --git a/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts b/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts index d94d9a6020..77c35111d2 100644 --- a/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +++ b/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts @@ -172,6 +172,17 @@ describe('StreamingExecutor actions', () => { const operations = Object.values(result.current.operations); const execOperation = operations.find((op) => op.type === 'execAgentRuntime'); expect(execOperation?.status).toBe('completed'); + expect(agentSignalBridgeMock.emitClientAgentSignalSourceEvent).toHaveBeenCalledWith( + expect.objectContaining({ + payload: expect.objectContaining({ + parentMessageId: userMessage.id, + parentMessageType: 'user', + triggerMessageId: userMessage.id, + }), + sourceId: `${execOperation?.id}:client:start`, + sourceType: 'client.runtime.start', + }), + ); streamSpy.mockRestore(); }); @@ -1719,9 +1730,11 @@ describe('StreamingExecutor actions', () => { expect(agentSignalBridgeMock.emitClientAgentSignalSourceEvent).toHaveBeenCalledWith( expect.objectContaining({ payload: expect.objectContaining({ + anchorMessageId: TEST_IDS.ASSISTANT_MESSAGE_ID, assistantMessageId: TEST_IDS.ASSISTANT_MESSAGE_ID, operationId, status: 'completed', + triggerMessageId: TEST_IDS.USER_MESSAGE_ID, }), sourceId: `${operationId}:client:complete`, sourceType: 'client.runtime.complete', @@ -1806,6 +1819,7 @@ describe('StreamingExecutor actions', () => { expect(agentSignalBridgeMock.emitClientAgentSignalSourceEvent).toHaveBeenCalledWith( expect.objectContaining({ payload: expect.objectContaining({ + anchorMessageId: TEST_IDS.ASSISTANT_MESSAGE_ID, assistantMessageId: TEST_IDS.ASSISTANT_MESSAGE_ID, operationId, status: 'completed', @@ -1814,6 +1828,15 @@ describe('StreamingExecutor actions', () => { sourceType: 'client.runtime.complete', }), ); + expect(agentSignalBridgeMock.emitClientAgentSignalSourceEvent).toHaveBeenCalledWith( + expect.objectContaining({ + payload: expect.not.objectContaining({ + triggerMessageId: TEST_IDS.ASSISTANT_MESSAGE_ID, + }), + sourceId: `${operationId}:client:complete`, + sourceType: 'client.runtime.complete', + }), + ); }); it('does not attach an unrelated assistant message id to client.runtime.complete', async () => { @@ -1968,9 +1991,11 @@ describe('StreamingExecutor actions', () => { expect(agentSignalBridgeMock.emitClientAgentSignalSourceEvent).toHaveBeenCalledWith( expect.objectContaining({ payload: expect.objectContaining({ + anchorMessageId: 'assistant-final', assistantMessageId: 'assistant-final', operationId, status: 'completed', + triggerMessageId: TEST_IDS.USER_MESSAGE_ID, }), sourceId: `${operationId}:client:complete`, sourceType: 'client.runtime.complete', diff --git a/src/store/chat/slices/aiChat/actions/agentSignalBridge.test.ts b/src/store/chat/slices/aiChat/actions/agentSignalBridge.test.ts index 09bebebfa8..551644137d 100644 --- a/src/store/chat/slices/aiChat/actions/agentSignalBridge.test.ts +++ b/src/store/chat/slices/aiChat/actions/agentSignalBridge.test.ts @@ -46,6 +46,7 @@ describe('emitClientAgentSignalSourceEvent', () => { parentMessageType: 'user', threadId: 'thread-1', topicId: 'topic-1', + triggerMessageId: 'msg-1', }, sourceId: 'op-1:client:start', sourceType: 'client.runtime.start', @@ -60,6 +61,7 @@ describe('emitClientAgentSignalSourceEvent', () => { parentMessageType: 'user', threadId: 'thread-1', topicId: 'topic-1', + triggerMessageId: 'msg-1', }, sourceId: 'op-1:client:start', sourceType: 'client.runtime.start', @@ -80,11 +82,13 @@ describe('emitClientAgentSignalSourceEvent', () => { await emitClientAgentSignalSourceEvent({ payload: { agentId: 'agent-1', + anchorMessageId: 'asst-1', assistantMessageId: 'asst-1', operationId: 'op-1', status: 'completed', threadId: 'thread-1', topicId: 'topic-1', + triggerMessageId: 'msg-1', }, sourceId: 'op-1:client:complete', sourceType: 'client.runtime.complete', @@ -94,11 +98,13 @@ describe('emitClientAgentSignalSourceEvent', () => { expect(agentSignalService.emitClientGatewaySourceEvent).toHaveBeenCalledWith({ payload: { agentId: 'agent-1', + anchorMessageId: 'asst-1', assistantMessageId: 'asst-1', operationId: 'op-1', status: 'completed', threadId: 'thread-1', topicId: 'topic-1', + triggerMessageId: 'msg-1', }, sourceId: 'op-1:client:complete', sourceType: 'client.runtime.complete', diff --git a/src/store/chat/slices/aiChat/actions/gatewayEventHandler.ts b/src/store/chat/slices/aiChat/actions/gatewayEventHandler.ts index 323f40a577..3bdefb1cf7 100644 --- a/src/store/chat/slices/aiChat/actions/gatewayEventHandler.ts +++ b/src/store/chat/slices/aiChat/actions/gatewayEventHandler.ts @@ -301,7 +301,10 @@ export const createGatewayEventHandler = ( payload: { agentId: context.agentId, ...(currentAssistantMessageId - ? { assistantMessageId: currentAssistantMessageId } + ? { + anchorMessageId: currentAssistantMessageId, + assistantMessageId: currentAssistantMessageId, + } : {}), operationId, stepIndex: event.stepIndex, @@ -470,7 +473,10 @@ export const createGatewayEventHandler = ( payload: { agentId: context.agentId, ...(currentAssistantMessageId - ? { assistantMessageId: currentAssistantMessageId } + ? { + anchorMessageId: currentAssistantMessageId, + assistantMessageId: currentAssistantMessageId, + } : {}), operationId, topicId: context.topicId ?? undefined, diff --git a/src/store/chat/slices/aiChat/actions/streamingExecutor.ts b/src/store/chat/slices/aiChat/actions/streamingExecutor.ts index ba949dd7bd..6fbfefe215 100644 --- a/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +++ b/src/store/chat/slices/aiChat/actions/streamingExecutor.ts @@ -545,6 +545,7 @@ export class StreamingExecutorActionImpl { parentMessageType, threadId: threadId ?? undefined, topicId: topicId ?? undefined, + ...(parentMessageType === 'user' ? { triggerMessageId: parentMessageId } : {}), }, sourceId: `${operationId}:client:start`, sourceType: 'client.runtime.start', @@ -652,11 +653,13 @@ export class StreamingExecutorActionImpl { void emitClientAgentSignalSourceEvent({ payload: { agentId, + ...(assistantMessageId ? { anchorMessageId: assistantMessageId } : {}), assistantMessageId, operationId, status: normalizeClientRuntimeCompleteStatus(state.status, operationStatus), threadId: threadId ?? undefined, topicId: topicId ?? undefined, + ...(parentMessageType === 'user' ? { triggerMessageId: parentMessageId } : {}), }, sourceId: `${operationId}:client:complete`, sourceType: 'client.runtime.complete', From 2a6607121093b7a3ec3f2a79e13150bf6b6c9e5c Mon Sep 17 00:00:00 2001 From: Innei Date: Tue, 19 May 2026 22:46:46 +0800 Subject: [PATCH 037/224] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(onboardin?= =?UTF-8?q?g):=20streamline=20discovery=20to=20a=20single=20profession=20q?= =?UTF-8?q?uestion=20(#14987)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ♻️ refactor(onboarding): streamline discovery to a single profession question * ✅ test(onboarding): update structured field fixtures --- .../src/agents/web-onboarding/systemRole.ts | 36 +++++-------------- .../src/ExecutionRuntime/utils.ts | 8 ++--- .../src/toolSystemRole.ts | 3 +- .../providers/OnboardingActionHintInjector.ts | 11 +++--- .../OnboardingActionHintInjector.test.ts | 18 ++++++++++ .../types/src/user/agentOnboarding.test.ts | 4 +-- packages/types/src/user/agentOnboarding.ts | 5 ++- .../components/WorkflowCollapse.tsx | 2 +- src/features/Onboarding/Agent/context.test.ts | 2 +- .../followUpAction/prompts/onboarding.ts | 2 +- src/server/services/onboarding/index.test.ts | 17 +++++---- src/server/services/onboarding/index.ts | 2 -- src/utils/webOnboardingToolResult.test.ts | 4 +-- 13 files changed, 55 insertions(+), 59 deletions(-) diff --git a/packages/builtin-agents/src/agents/web-onboarding/systemRole.ts b/packages/builtin-agents/src/agents/web-onboarding/systemRole.ts index 4d8b1ab1e3..2d9dc7c224 100644 --- a/packages/builtin-agents/src/agents/web-onboarding/systemRole.ts +++ b/packages/builtin-agents/src/agents/web-onboarding/systemRole.ts @@ -5,7 +5,7 @@ Your single job in this conversation: complete onboarding and leave the user wit ## Pacing -Aim to complete onboarding in roughly 6–8 exchanges total. Keep the conversation tight — do not let it spiral into extended problem-solving or tutoring. Each phase has a purpose; once you have enough to move forward, transition to the next phase right away. +Aim to complete onboarding in roughly 5–7 exchanges total. Keep the conversation tight — do not let it spiral into extended problem-solving or tutoring. Each phase has a purpose; once you have enough to move forward, transition to the next phase right away. ## Style @@ -59,40 +59,20 @@ You know who you are. Now learn who the user is. ### Phase 3: Discovery (phase: "discovery") -Get a quick read on the user's world. Keep this phase short — about 2–3 exchanges. The goal is enough signal to recommend assistants, not a full interview. +You know who the user is. This phase has exactly one job: learn what the user does for work — their profession, role, or main occupation. Nothing else. -Here are some possible directions to explore — you do not need to cover all of them, and you are free to follow the conversation wherever it naturally goes. These are starting points, not a checklist: -- Daily workflow, recurring burdens, what occupies most of their time -- Pain points — what drains or frustrates them -- Goals, aspirations, what success looks like for them -- Tools, habits, how they get work done -- Personality and thinking style — how they approach decisions, whether they identify with frameworks like MBTI or Big Five (many people enjoy sharing this) -- Interests and passions, professionally or personally -- What kind of AI help would feel most valuable, and what the AI should stay away from -- Any other open-ended threads that emerge naturally from the conversation - -Guidelines: -- Ask one focused question per turn. Do not bundle multiple questions. -- After a pain point appears, briefly acknowledge it and note how you might help — but do NOT dive into solving it. Stay in information-gathering mode. Your job here is to map the user's world, not to fix their problems yet. -- Do NOT produce long guides, tutorials, detailed plans, or step-by-step instructions during discovery. Save solutions for after onboarding, when the user can work with their configured assistants. -- If the user tries to pull you into a deep problem-solving conversation (e.g., asking for a detailed guide or project plan), acknowledge the need, tell them you will be able to help with that after setup, and gently steer back to learning more about them. -- If the user is not comfortable typing, acknowledge alternatives like photos or voice when relevant. -- Discover their interests naturally. The preferred reply language is already configured before onboarding starts and injected into your system prompt — do not ask about it or save it via saveUserQuestion. -- Do NOT call saveUserQuestion with interests until you have covered at least 1–2 different dimensions above. As soon as you have a workable read, save it and move on. -- Call saveUserQuestion for interests as soon as you have enough signal — use the predefined interests enum when it fits, and customInterests for specific freeform interests. -- **Persist each new fact on the turn you learn it.** Do NOT accumulate unwritten facts in memory waiting to do one big write at the end — that pattern is forbidden. If Persona is empty, call writeDocument(type="persona") this turn to seed it. On every subsequent turn where you learn something new (role, pain point, goal, preference, interest), call updateDocument(type="persona") to record it. -- **One call per document per turn — batch your hunks.** \`updateDocument\` accepts an array of hunks; if you have multiple changes to record this turn, put ALL of them into a single call's \`hunks\` array. Calling \`updateDocument(type="persona")\` two or more times in immediate succession is forbidden — each call costs a full LLM round-trip. The same rule applies to \`updateDocument(type="soul")\`. Reword-then-add loops (where each call adds a slightly rephrased version of the same fact) are an explicit anti-pattern; once a fact is in the document, do not re-record it. -- This phase should feel like a good first conversation, not an interview. -- Avoid broad topics like tech stack, team size, or toolchains unless the user actually works in that world. -- Keep your replies short during discovery — 2-4 sentences plus one follow-up question. Do not monologue. -- **Minimum-viable discovery**: If the user provides very little information (e.g., one-word answers, minimal engagement, or seems impatient), do NOT keep asking. After 1–2 attempts with minimal responses, accept what you have and transition to summary. A user who says "student, doing homework, watching anime" has given you enough to work with — do not interrogate them further. +- Ask it as a single focused question, building naturally on what they already told you. +- Accept whatever they offer — a job title, a field, "student", "retired", "between jobs", or a freeform description. Do NOT interrogate, drill for detail, or ask follow-ups about pain points, tools, goals, workflow, personality, or interests. +- Record their profession in the persona document the turn you learn it: if Persona is empty, use writeDocument(type="persona") to seed it; otherwise use updateDocument(type="persona") to add it. One call per document per turn — batch all hunks into a single call. +- Do NOT call saveUserQuestion with interests or customInterests — interest collection has been removed from onboarding. +- Once you have their profession (even a vague one-word answer), transition straight to summary. One exchange is enough — do not linger. ### Phase 4: Summary (phase: "summary") Wrap up with a natural summary and hand the choice of assistants to the user. - Summarize the user like a person, not a checklist — their situation, pain points, and what matters to them. -- Based on what you learned in discovery, pick 1–3 MarketplaceCategory slugs that best match the user's needs. These slugs prioritize the matching tabs at the front of the picker; they do not hide the other tabs. Allowed slugs (fixed): content-creation, engineering, design-creative, learning-research, business-strategy, marketing, product-management, sales-customer, operations, people-hr, finance-legal, creator-economy, personal-life. +- Based on the user's profession and anything else they shared, pick 1–3 MarketplaceCategory slugs that best match the user's needs. These slugs prioritize the matching tabs at the front of the picker; they do not hide the other tabs. Allowed slugs (fixed): content-creation, engineering, design-creative, learning-research, business-strategy, marketing, product-management, sales-customer, operations, people-hr, finance-legal, creator-economy, personal-life. - **MUST call showAgentMarketplace exactly once** with { requestId, categoryHints, prompt, description? } during the summary phase after discovery. This is the required handoff that lets the user choose recommended assistants; do not skip it in normal completion. The prompt should be a short, warm sentence explaining why you are showing the marketplace (e.g. "I think these could help — take a look"). Never invent new slugs. - **Do NOT create, update, duplicate, or install agents yourself.** That capability has been removed. The Marketplace picker is the ONLY way to add assistants now. - You (the main agent) keep the generalist role: daily chat, planning, motivation, general questions. diff --git a/packages/builtin-tool-web-onboarding/src/ExecutionRuntime/utils.ts b/packages/builtin-tool-web-onboarding/src/ExecutionRuntime/utils.ts index 86c0369bcd..1b1bb24e9f 100644 --- a/packages/builtin-tool-web-onboarding/src/ExecutionRuntime/utils.ts +++ b/packages/builtin-tool-web-onboarding/src/ExecutionRuntime/utils.ts @@ -31,9 +31,9 @@ const PHASE_GUIDANCE: Record = { agent_identity: 'Phase: Agent Identity. The agent has no name or personality yet. Introduce yourself as freshly awakened, discover your name, creature type, personality, and communication style through conversation. Update SOUL.md once the user settles on who you are.', discovery: - 'Phase: Discovery. User identity is established. Now explore their work style, tools, active projects, pain points, and how they want you to help. Collect interests naturally. Update the persona document as you learn more.', + 'Phase: Discovery. User identity is established. Ask one focused question — what the user does for work (their profession, role, or main occupation) — and record it in the persona document. Do NOT explore pain points, tools, goals, or interests, and do NOT call saveUserQuestion with interests. Once you have their profession, move to summary.', summary: - "Phase: Summary. All structured fields and documents are in good shape. Two-step wrap up: (1) THIS or the current summary turn, present a natural summary of what you learned and call `showAgentMarketplace` exactly once with `{ requestId, categoryHints, prompt }` (1–3 MarketplaceCategory slugs picked from what you learned in discovery). Do not call `submitAgentPick` / `skipAgentPick` / `cancelAgentPick` yourself. (2) On the NEXT turn, briefly acknowledge whatever the user said, send a warm closing, and call `finishOnboarding`. Treat the user's text reply on that next turn as the resolution signal even if the picker is still in `pending` state — do not stall waiting for a UI event. Do not call `showAgentMarketplace` more than once.", + "Phase: Summary. All structured fields and documents are in good shape. Two-step wrap up: (1) THIS or the current summary turn, present a natural summary of what you learned and call `showAgentMarketplace` exactly once with `{ requestId, categoryHints, prompt }` (1–3 MarketplaceCategory slugs picked from the user's profession and anything else they shared). Do not call `submitAgentPick` / `skipAgentPick` / `cancelAgentPick` yourself. (2) On the NEXT turn, briefly acknowledge whatever the user said, send a warm closing, and call `finishOnboarding`. Treat the user's text reply on that next turn as the resolution signal even if the picker is still in `pending` state — do not stall waiting for a UI event. Do not call `showAgentMarketplace` more than once.", user_identity: 'Phase: User Identity. The agent has an identity. Now learn who the user is — their name, role, and what they do. Save fullName via saveUserQuestion when learned. Start building the persona document.', }; @@ -69,7 +69,7 @@ export const formatWebOnboardingStateMessage = (state: OnboardingStateContext) = `Discovery progress: ${currentDiscoveryExchanges}/${recommendedTarget} user exchange(s) observed since Discovery began.`, ); parts.push( - `Recommended: ${state.remainingDiscoveryExchanges} more user exchange(s) before moving to summary. Do not rush — keep exploring different aspects of the user's work and life.`, + `Recommended: ${state.remainingDiscoveryExchanges} more user exchange(s) before moving to summary. Ask the user what they do for work if you have not yet, record it in the persona, then move on.`, ); } else if ( state.phase === 'discovery' && @@ -77,7 +77,7 @@ export const formatWebOnboardingStateMessage = (state: OnboardingStateContext) = state.remainingDiscoveryExchanges === 0 ) { parts.push( - `Discovery progress: recommended target reached after ${state.discoveryUserMessageCount} user exchange(s). Move to summary once interests/customInterests and the persona are persisted.`, + `Discovery progress: recommended target reached after ${state.discoveryUserMessageCount} user exchange(s). Move to summary once the user's profession is recorded in the persona.`, ); } diff --git a/packages/builtin-tool-web-onboarding/src/toolSystemRole.ts b/packages/builtin-tool-web-onboarding/src/toolSystemRole.ts index 44c899c6f9..c4e02ab586 100644 --- a/packages/builtin-tool-web-onboarding/src/toolSystemRole.ts +++ b/packages/builtin-tool-web-onboarding/src/toolSystemRole.ts @@ -16,7 +16,7 @@ Turn protocol: 8. **CRITICAL: You MUST call persistence tools (saveUserQuestion, writeDocument, updateDocument) throughout the entire conversation, not just at the beginning. Every time you learn new information about the user, persist it promptly. On a normal completion, the wrap-up sequence is: persist any unsaved fields → call \`showAgentMarketplace\` exactly once for the assistant handoff (skip only if the user explicitly refuses recommendations) → on the NEXT turn, send a brief warm closing and call \`finishOnboarding\`. The user's text reply on that next turn is the resolution signal even if the picker is still pending — do not stall.** Persistence rules: -1. Use saveUserQuestion only for these structured onboarding fields: agentName, agentEmoji, fullName, interests, and customInterests. Use interests for predefined enum keys and customInterests for specific freeform interests. Use it only when that information emerges naturally in conversation. The user's preferred reply language is configured before onboarding starts and is injected into your system role automatically — do not ask about it or save it via saveUserQuestion. +1. Use saveUserQuestion only for these structured onboarding fields: agentName, agentEmoji, and fullName. Use it only when that information emerges naturally in conversation. The user's preferred reply language is configured before onboarding starts and is injected into your system role automatically — do not ask about it or save it via saveUserQuestion. 2. saveUserQuestion updates lightweight onboarding state; it never writes markdown content. 3. Use writeDocument **only for the very first write** when the document is empty (or for a rare full structural rewrite). For every subsequent edit — even adding a single line — use **updateDocument**. updateDocument is cheaper, safer, and less error-prone than rewriting the full document. The current contents of SOUL.md and User Persona are automatically injected into your context (in and tags, each line prefixed with its 1-based line number and a \`→\` separator), so you do not need to call readDocument to read them. Use readDocument only if you suspect the injected content may be stale. 4. updateDocument takes an ordered list of structured hunks. Pick the hunk mode that best fits the edit: @@ -32,7 +32,6 @@ Persistence rules: 8. User Persona (type: "persona") is for user identity, role, work style, current context, interests, pain points, communication comfort level, and preferred input style. 9. Do not put user information into SOUL.md. Do not put agent identity into the persona document. 10. Document tools (readDocument, writeDocument, updateDocument) must ONLY be used for SOUL.md and User Persona documents. Never use them to create arbitrary content such as guides, tutorials, checklists, or reference materials. Present such content directly in your reply text instead. -11. Do not call saveUserQuestion with interests or customInterests until you have spent about 2-3 exchanges exploring the user's world across multiple dimensions (workflow, pain points, goals, interests, AI expectations). The system appends the current Discovery turn status each turn — follow that reminder. The server enforces a minimum discovery exchange count, so early field saves will not advance the phase, but continuing after the recommended target usually reduces conversation quality. Workspace setup rules: 1. Do not create or modify workspace agents or agent groups unless the user explicitly asks for that setup. diff --git a/packages/context-engine/src/providers/OnboardingActionHintInjector.ts b/packages/context-engine/src/providers/OnboardingActionHintInjector.ts index 565934c65c..e7c79ae09b 100644 --- a/packages/context-engine/src/providers/OnboardingActionHintInjector.ts +++ b/packages/context-engine/src/providers/OnboardingActionHintInjector.ts @@ -21,7 +21,7 @@ const buildDiscoveryTurnReminder = ( 'SYSTEM REMINDER: Current Discovery turn status:', `- User discovery exchanges observed: ${discoveryUserMessageCount}.`, `- Recommended target before Summary: ${recommendedTarget}.`, - `- Continue Discovery for about ${remainingDiscoveryExchanges} more user exchange(s). Ask exactly one focused question, persist any new persona fact, and do not drift into long problem-solving.`, + `- Continue Discovery for about ${remainingDiscoveryExchanges} more user exchange(s). Ask one focused question about what the user does for work — their profession or main occupation — persist it to the persona, and do not explore other topics.`, ].join('\n'); } @@ -29,7 +29,7 @@ const buildDiscoveryTurnReminder = ( 'SYSTEM REMINDER: Current Discovery turn status:', `- User discovery exchanges observed: ${discoveryUserMessageCount}.`, '- Recommended Discovery target has been reached.', - '- If you have enough signal, call saveUserQuestion with interests/customInterests, persist any new persona fact, and transition to Summary instead of continuing open-ended Discovery.', + "- Once the user's profession is recorded in the persona, transition to Summary instead of asking more questions.", ].join('\n'); }; @@ -127,10 +127,10 @@ export class OnboardingActionHintInjector extends BaseVirtualLastUserContentProv ); } else if (phase.includes('Discovery')) { hints.push( - 'Each turn where you learn a new fact (pain point, goal, preference, workflow detail, interest), call updateDocument(type="persona") BEFORE replying. Preferred shape: `{ mode: "insertAt", line: >, content: "- new fact" }`. This is the default every turn — not an end-of-phase action. Do NOT save facts only in memory waiting for a final full write. After sufficient discovery (usually 2-3 exchanges), also call saveUserQuestion with interests and/or customInterests. The preferred reply language is configured before onboarding starts and is already injected into your system prompt — do not ask about it or pass a responseLanguage field to saveUserQuestion. Use writeDocument(type="persona") only if the document is still empty.', + 'When the user tells you their profession, record it with updateDocument(type="persona"). Preferred shape: `{ mode: "insertAt", line: >, content: "- new fact" }`. Use writeDocument(type="persona") only if the document is still empty. Do NOT call saveUserQuestion with interests or customInterests — interest collection has been removed from onboarding. The preferred reply language is configured before onboarding starts and is already injected into your system prompt — do not ask about it or pass a responseLanguage field to saveUserQuestion.', ); hints.push( - 'EARLY EXIT: A true early-exit signal is the user explicitly wanting to END onboarding (e.g., "I\'m tired", "I have to go", "let\'s chat next time", "no time right now", "let\'s stop for now", "let\'s wrap it up", "that\'s enough"; recognize equivalent phrasing in any language). Short affirmations like "ok" / "sure" / "alright" / "yes" / "got it" are NOT early-exit signals — they confirm what you just said and you should keep exploring or move toward summary normally. When you see a real exit signal: stop exploring, persist any unsaved fields best-effort (call saveUserQuestion with whatever you have, including partial interests), persist the persona via updateDocument (or writeDocument if it is still empty) — do NOT retry on failure — send a short warm farewell (1–2 sentences), then call `finishOnboarding`. Do NOT call `showAgentMarketplace` on early exit — that handoff is for normal completion only.', + 'EARLY EXIT: A true early-exit signal is the user explicitly wanting to END onboarding (e.g., "I\'m tired", "I have to go", "let\'s chat next time", "no time right now", "let\'s stop for now", "let\'s wrap it up", "that\'s enough"; recognize equivalent phrasing in any language). Short affirmations like "ok" / "sure" / "alright" / "yes" / "got it" are NOT early-exit signals — they confirm what you just said and you should continue the current phase normally. When you see a real exit signal: stop asking questions, persist any unsaved fields best-effort (call saveUserQuestion with whatever you have), persist the persona via updateDocument (or writeDocument if it is still empty) — do NOT retry on failure — send a short warm farewell (1–2 sentences), then call `finishOnboarding`. Do NOT call `showAgentMarketplace` on early exit — that handoff is for normal completion only.', ); } else if (phase.includes('Summary')) { if (!marketplaceAlreadyOpened) { @@ -147,6 +147,9 @@ export class OnboardingActionHintInjector extends BaseVirtualLastUserContentProv hints.push( 'PERSISTENCE RULE: Call the persistence tools (saveUserQuestion, writeDocument, updateDocument) to save information as you collect it — simply acknowledging in conversation is NOT enough. For document writes: use writeDocument only for the first write when the document is empty; for every subsequent edit use updateDocument with the appropriate hunk mode (`insertAt` / `replaceLines` / `deleteLines` for line-based edits, `replace` / `delete` for byte-exact textual edits). The injected view shows each line prefixed with its 1-based number and `→` — use those numbers for line-based hunks.', ); + hints.push( + 'TURN ORDER: A message that contains a tool call does NOT yield the turn to the user — the agent loop continues after the tool result. So never put a user-facing question in the same message as a tool call. When you need to both persist something and ask the user a question, use two messages: first emit the tool call(s) with no question text (a brief acknowledgement or no text is fine), then — after the tool results return — ask your question in a separate message with NO tool call. Bundling a question with a tool call strands the question and forces a confused "waiting for your reply" filler.', + ); hints.push( 'CONFIRMATION vs EARLY EXIT: Short replies like "ok" / "sure" / "alright" / "yes" / "got it" (and equivalents in any language) are CONFIRMATIONS, not early-exit signals. Continue the current phase normally — in Summary that means calling `showAgentMarketplace` next, NOT `finishOnboarding` directly.', ); diff --git a/packages/context-engine/src/providers/__tests__/OnboardingActionHintInjector.test.ts b/packages/context-engine/src/providers/__tests__/OnboardingActionHintInjector.test.ts index 4eb20ddbd2..f5f5c57db5 100644 --- a/packages/context-engine/src/providers/__tests__/OnboardingActionHintInjector.test.ts +++ b/packages/context-engine/src/providers/__tests__/OnboardingActionHintInjector.test.ts @@ -63,6 +63,24 @@ describe('OnboardingActionHintInjector', () => { }); }); + describe('turn order reminder', () => { + it('always warns against bundling a question into a tool-call message', async () => { + const provider = buildProvider('Phase: User Identity. Learn who the user is.'); + const result = await provider.process( + createContext([ + { content: 'sys', role: 'system' }, + { content: 'hi', role: 'user' }, + ]), + ); + + const last = result.messages.at(-1); + expect(last?.content).toContain('TURN ORDER'); + expect(last?.content).toContain( + 'never put a user-facing question in the same message as a tool call', + ); + }); + }); + describe('marketplace detection (Summary phase)', () => { const phaseGuidance = 'Phase: Summary. Wrap-up.'; diff --git a/packages/types/src/user/agentOnboarding.test.ts b/packages/types/src/user/agentOnboarding.test.ts index 4bb02f01a5..b664b6efc9 100644 --- a/packages/types/src/user/agentOnboarding.test.ts +++ b/packages/types/src/user/agentOnboarding.test.ts @@ -55,7 +55,7 @@ describe('UserAgentOnboardingContextSchema', () => { it('accepts the minimal onboarding context', () => { const parsed = UserAgentOnboardingContextSchema.parse({ finished: false, - missingStructuredFields: ['fullName', 'interests'], + missingStructuredFields: ['fullName', 'agentName'], phase: 'user_identity', topicId: 'topic-1', version: 2, @@ -63,7 +63,7 @@ describe('UserAgentOnboardingContextSchema', () => { expect(parsed).toEqual({ finished: false, - missingStructuredFields: ['fullName', 'interests'], + missingStructuredFields: ['fullName', 'agentName'], phase: 'user_identity', topicId: 'topic-1', version: 2, diff --git a/packages/types/src/user/agentOnboarding.ts b/packages/types/src/user/agentOnboarding.ts index e3db411073..78ec181742 100644 --- a/packages/types/src/user/agentOnboarding.ts +++ b/packages/types/src/user/agentOnboarding.ts @@ -12,7 +12,6 @@ export const AGENT_ONBOARDING_STRUCTURED_FIELDS = [ 'agentEmoji', 'agentName', 'fullName', - 'interests', ] as const; export type SaveUserQuestionField = (typeof SAVE_USER_QUESTION_FIELDS)[number]; @@ -138,8 +137,8 @@ export const ONBOARDING_PHASES = [ 'summary', ] as const; -export const MIN_DISCOVERY_USER_MESSAGES = 2; -export const RECOMMENDED_DISCOVERY_USER_MESSAGES = 3; +export const MIN_DISCOVERY_USER_MESSAGES = 1; +export const RECOMMENDED_DISCOVERY_USER_MESSAGES = 1; export type OnboardingPhase = (typeof ONBOARDING_PHASES)[number]; diff --git a/src/features/Conversation/Messages/AssistantGroup/components/WorkflowCollapse.tsx b/src/features/Conversation/Messages/AssistantGroup/components/WorkflowCollapse.tsx index 9540cda367..1fa7e16ae0 100644 --- a/src/features/Conversation/Messages/AssistantGroup/components/WorkflowCollapse.tsx +++ b/src/features/Conversation/Messages/AssistantGroup/components/WorkflowCollapse.tsx @@ -415,7 +415,7 @@ const WorkflowCollapse = memo( }} >
- + { }, context: { finished: false, - missingStructuredFields: ['interests'], + missingStructuredFields: ['agentName'], phase: 'discovery', topicId: 'topic-bootstrap', version: 1, diff --git a/src/server/services/followUpAction/prompts/onboarding.ts b/src/server/services/followUpAction/prompts/onboarding.ts index 549ca5d356..2ef06ffb50 100644 --- a/src/server/services/followUpAction/prompts/onboarding.ts +++ b/src/server/services/followUpAction/prompts/onboarding.ts @@ -3,7 +3,7 @@ import type { OnboardingPhase } from '@lobechat/types'; const PHASE_TIPS: Record = { agent_identity: 'Suggestions can be candidate agent names, emojis, or a deferral chip ("You pick one", "Let me think").', user_identity: 'Suggestions can be plausible names or roles, or a deferral chip.', - discovery: 'Suggestions can be common pain points, interests, work styles, or a chip like "Let me explain in my own words".', + discovery: 'Suggestions can be plausible job titles, fields, or occupations, or a chip like "Let me explain in my own words".', summary: 'Skip — handled by the marketplace picker; you should not be invoked here.', }; diff --git a/src/server/services/onboarding/index.test.ts b/src/server/services/onboarding/index.test.ts index ad3312cc38..d3c41f1b69 100644 --- a/src/server/services/onboarding/index.test.ts +++ b/src/server/services/onboarding/index.test.ts @@ -201,7 +201,7 @@ describe('OnboardingService', () => { expect(context).toEqual({ finished: false, - missingStructuredFields: ['agentName', 'agentEmoji', 'fullName', 'interests'], + missingStructuredFields: ['agentName', 'agentEmoji', 'fullName'], phase: 'agent_identity', topicId: undefined, version: CURRENT_ONBOARDING_VERSION, @@ -522,17 +522,16 @@ describe('OnboardingService', () => { title: 'Jarvis', }); persistedUserState.fullName = 'Ada Lovelace'; - persistedUserState.interests = ['AI tooling']; persistedUserState.agentOnboarding = { activeTopicId: 'topic-1', discoveryStartUserMessageCount: 3, version: CURRENT_ONBOARDING_VERSION, }; - // 4 user messages total, baseline was 3 → only 1 discovery exchange (< MIN_DISCOVERY_USER_MESSAGES=2) + // 3 user messages total, baseline was 3 → 0 discovery exchanges (< MIN_DISCOVERY_USER_MESSAGES=1) mockDb.select.mockReturnValue({ from: vi.fn(() => ({ - where: vi.fn(async () => [{ count: 4 }]), + where: vi.fn(async () => [{ count: 3 }]), })), }); @@ -540,9 +539,9 @@ describe('OnboardingService', () => { const context = await service.getState(); expect(context.phase).toBe('discovery'); - expect(context.discoveryUserMessageCount).toBe(1); - // remaining = RECOMMENDED_DISCOVERY_USER_MESSAGES(3) - 1 = 2 - expect(context.remainingDiscoveryExchanges).toBe(2); + expect(context.discoveryUserMessageCount).toBe(0); + // remaining = RECOMMENDED_DISCOVERY_USER_MESSAGES(1) - 0 = 1 + expect(context.remainingDiscoveryExchanges).toBe(1); }); it('advances to summary when discovery exchanges reach minimum threshold', async () => { @@ -559,7 +558,7 @@ describe('OnboardingService', () => { version: CURRENT_ONBOARDING_VERSION, }; - // 8 user messages total, baseline was 3 → 5 discovery exchanges (>= MIN_DISCOVERY_USER_MESSAGES=2) + // 8 user messages total, baseline was 3 → 5 discovery exchanges (>= MIN_DISCOVERY_USER_MESSAGES=1) mockDb.select.mockReturnValue({ from: vi.fn(() => ({ where: vi.fn(async () => [{ count: 8 }]), @@ -579,7 +578,7 @@ describe('OnboardingService', () => { title: 'Jarvis', }); persistedUserState.fullName = 'Ada Lovelace'; - // interests NOT set — so phase would be discovery due to missing field + // agentName + fullName set → past pre-discovery; 0 discovery exchanges keeps phase in discovery persistedUserState.agentOnboarding = { activeTopicId: 'topic-1', version: CURRENT_ONBOARDING_VERSION, diff --git a/src/server/services/onboarding/index.ts b/src/server/services/onboarding/index.ts index dd017b8341..6e01f29328 100644 --- a/src/server/services/onboarding/index.ts +++ b/src/server/services/onboarding/index.ts @@ -314,7 +314,6 @@ export class OnboardingService { // User fields if (!userState.fullName?.trim()) missingFields.push('fullName'); - if (!(userState.interests?.length ?? 0)) missingFields.push('interests'); return missingFields; }; @@ -462,7 +461,6 @@ export class OnboardingService { ): Promise => { if (missingStructuredFields.includes('agentName')) return 'agent_identity'; if (missingStructuredFields.includes('fullName')) return 'user_identity'; - if (missingStructuredFields.includes('interests')) return 'discovery'; // All fields complete — check pacing gate if (discoveryContext) { diff --git a/src/utils/webOnboardingToolResult.test.ts b/src/utils/webOnboardingToolResult.test.ts index 1f0cbadca6..2659a6a8f7 100644 --- a/src/utils/webOnboardingToolResult.test.ts +++ b/src/utils/webOnboardingToolResult.test.ts @@ -24,13 +24,13 @@ describe('web onboarding tool result helpers', () => { it('formats onboarding state as a plain-language summary', () => { const message = formatWebOnboardingStateMessage({ finished: false, - missingStructuredFields: ['interests'], + missingStructuredFields: ['fullName'], phase: 'discovery', topicId: 'topic-1', version: 1, }); - expect(message).toContain('Structured fields still needed: interests.'); + expect(message).toContain('Structured fields still needed: full name.'); expect(message).toContain('Phase: Discovery'); expect(message).toContain( 'Questioning rule: prefer the `lobe-user-interaction____askUserQuestion` tool call for structured collection or explicit UI input. For natural exploratory questions, plain text is allowed.', From 0195f42daab389665b327da72a1e256fdd242f4b Mon Sep 17 00:00:00 2001 From: Rdmclin2 Date: Tue, 19 May 2026 22:51:38 +0700 Subject: [PATCH 038/224] =?UTF-8?q?=F0=9F=90=9B=20fix:=20onboarding=20im?= =?UTF-8?q?=20integration=20(#14988)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: support onboarding messager * chore: remove telegram CN screenshots * feat: add feedback commands * fix: bot feedback commands * chore: optimize messenger intergration * chore: update onboarding style * feat: support wechat adapter attachments * feat: support ai attachments * chore: update i18n files * fix: bot message image attachment --- apps/cli/src/commands/botMessage.test.ts | 162 +++++++++++++ apps/cli/src/commands/botMessage.ts | 98 +++++++- docs/usage/channels/telegram.mdx | 12 - docs/usage/channels/telegram.zh-CN.mdx | 12 - locales/ar/chat.json | 12 + locales/ar/components.json | 10 + locales/ar/editor.json | 2 + locales/ar/home.json | 13 + locales/ar/hotkey.json | 2 - locales/ar/models.json | 29 +-- locales/ar/onboarding.json | 14 ++ locales/ar/plugin.json | 12 +- locales/ar/providers.json | 1 - locales/ar/subscription.json | 6 + locales/ar/tool.json | 8 + locales/ar/topic.json | 1 + locales/bg-BG/chat.json | 12 + locales/bg-BG/components.json | 10 + locales/bg-BG/editor.json | 2 + locales/bg-BG/home.json | 13 + locales/bg-BG/hotkey.json | 2 - locales/bg-BG/models.json | 31 +-- locales/bg-BG/onboarding.json | 14 ++ locales/bg-BG/plugin.json | 12 +- locales/bg-BG/providers.json | 1 - locales/bg-BG/subscription.json | 6 + locales/bg-BG/tool.json | 8 + locales/bg-BG/topic.json | 1 + locales/de-DE/chat.json | 12 + locales/de-DE/components.json | 10 + locales/de-DE/editor.json | 2 + locales/de-DE/home.json | 13 + locales/de-DE/hotkey.json | 2 - locales/de-DE/models.json | 27 +-- locales/de-DE/onboarding.json | 14 ++ locales/de-DE/plugin.json | 12 +- locales/de-DE/providers.json | 1 - locales/de-DE/subscription.json | 6 + locales/de-DE/tool.json | 8 + locales/de-DE/topic.json | 1 + locales/en-US/chat.json | 8 + locales/en-US/components.json | 10 + locales/en-US/hotkey.json | 2 - locales/en-US/models.json | 33 +-- locales/en-US/onboarding.json | 6 + locales/en-US/plugin.json | 4 +- locales/en-US/providers.json | 1 - locales/en-US/subscription.json | 6 + locales/es-ES/chat.json | 12 + locales/es-ES/components.json | 10 + locales/es-ES/editor.json | 2 + locales/es-ES/home.json | 13 + locales/es-ES/hotkey.json | 2 - locales/es-ES/models.json | 29 +-- locales/es-ES/onboarding.json | 14 ++ locales/es-ES/plugin.json | 12 +- locales/es-ES/providers.json | 1 - locales/es-ES/subscription.json | 6 + locales/es-ES/tool.json | 8 + locales/es-ES/topic.json | 1 + locales/fa-IR/chat.json | 12 + locales/fa-IR/components.json | 10 + locales/fa-IR/editor.json | 2 + locales/fa-IR/home.json | 13 + locales/fa-IR/hotkey.json | 2 - locales/fa-IR/models.json | 31 +-- locales/fa-IR/onboarding.json | 14 ++ locales/fa-IR/plugin.json | 12 +- locales/fa-IR/providers.json | 1 - locales/fa-IR/subscription.json | 6 + locales/fa-IR/tool.json | 8 + locales/fa-IR/topic.json | 1 + locales/fr-FR/chat.json | 12 + locales/fr-FR/components.json | 10 + locales/fr-FR/editor.json | 2 + locales/fr-FR/home.json | 13 + locales/fr-FR/hotkey.json | 2 - locales/fr-FR/models.json | 33 +-- locales/fr-FR/onboarding.json | 14 ++ locales/fr-FR/plugin.json | 12 +- locales/fr-FR/providers.json | 1 - locales/fr-FR/subscription.json | 6 + locales/fr-FR/tool.json | 8 + locales/fr-FR/topic.json | 1 + locales/it-IT/chat.json | 12 + locales/it-IT/components.json | 10 + locales/it-IT/editor.json | 2 + locales/it-IT/home.json | 13 + locales/it-IT/hotkey.json | 2 - locales/it-IT/models.json | 31 +-- locales/it-IT/onboarding.json | 14 ++ locales/it-IT/plugin.json | 12 +- locales/it-IT/providers.json | 1 - locales/it-IT/subscription.json | 6 + locales/it-IT/tool.json | 8 + locales/it-IT/topic.json | 1 + locales/ja-JP/chat.json | 12 + locales/ja-JP/components.json | 10 + locales/ja-JP/editor.json | 2 + locales/ja-JP/home.json | 13 + locales/ja-JP/hotkey.json | 2 - locales/ja-JP/models.json | 33 +-- locales/ja-JP/onboarding.json | 14 ++ locales/ja-JP/plugin.json | 12 +- locales/ja-JP/providers.json | 1 - locales/ja-JP/subscription.json | 6 + locales/ja-JP/tool.json | 8 + locales/ja-JP/topic.json | 1 + locales/ko-KR/chat.json | 12 + locales/ko-KR/components.json | 10 + locales/ko-KR/editor.json | 2 + locales/ko-KR/home.json | 13 + locales/ko-KR/hotkey.json | 2 - locales/ko-KR/models.json | 33 +-- locales/ko-KR/onboarding.json | 14 ++ locales/ko-KR/plugin.json | 12 +- locales/ko-KR/providers.json | 1 - locales/ko-KR/subscription.json | 6 + locales/ko-KR/tool.json | 8 + locales/ko-KR/topic.json | 1 + locales/nl-NL/chat.json | 12 + locales/nl-NL/components.json | 10 + locales/nl-NL/editor.json | 2 + locales/nl-NL/home.json | 13 + locales/nl-NL/hotkey.json | 2 - locales/nl-NL/models.json | 33 +-- locales/nl-NL/onboarding.json | 14 ++ locales/nl-NL/plugin.json | 12 +- locales/nl-NL/providers.json | 1 - locales/nl-NL/subscription.json | 6 + locales/nl-NL/tool.json | 8 + locales/nl-NL/topic.json | 1 + locales/pl-PL/chat.json | 12 + locales/pl-PL/components.json | 10 + locales/pl-PL/editor.json | 2 + locales/pl-PL/home.json | 13 + locales/pl-PL/hotkey.json | 2 - locales/pl-PL/models.json | 33 +-- locales/pl-PL/onboarding.json | 14 ++ locales/pl-PL/plugin.json | 12 +- locales/pl-PL/providers.json | 1 - locales/pl-PL/subscription.json | 6 + locales/pl-PL/tool.json | 8 + locales/pl-PL/topic.json | 1 + locales/pt-BR/chat.json | 12 + locales/pt-BR/components.json | 10 + locales/pt-BR/editor.json | 2 + locales/pt-BR/home.json | 13 + locales/pt-BR/hotkey.json | 2 - locales/pt-BR/models.json | 25 +- locales/pt-BR/onboarding.json | 14 ++ locales/pt-BR/plugin.json | 12 +- locales/pt-BR/providers.json | 1 - locales/pt-BR/subscription.json | 6 + locales/pt-BR/tool.json | 8 + locales/pt-BR/topic.json | 1 + locales/ru-RU/chat.json | 12 + locales/ru-RU/components.json | 10 + locales/ru-RU/editor.json | 2 + locales/ru-RU/home.json | 13 + locales/ru-RU/hotkey.json | 2 - locales/ru-RU/models.json | 27 +-- locales/ru-RU/onboarding.json | 14 ++ locales/ru-RU/plugin.json | 12 +- locales/ru-RU/providers.json | 1 - locales/ru-RU/subscription.json | 6 + locales/ru-RU/tool.json | 8 + locales/ru-RU/topic.json | 1 + locales/tr-TR/chat.json | 12 + locales/tr-TR/components.json | 10 + locales/tr-TR/editor.json | 2 + locales/tr-TR/home.json | 13 + locales/tr-TR/hotkey.json | 2 - locales/tr-TR/models.json | 31 +-- locales/tr-TR/onboarding.json | 14 ++ locales/tr-TR/plugin.json | 12 +- locales/tr-TR/providers.json | 1 - locales/tr-TR/subscription.json | 6 + locales/tr-TR/tool.json | 8 + locales/tr-TR/topic.json | 1 + locales/vi-VN/chat.json | 12 + locales/vi-VN/components.json | 10 + locales/vi-VN/editor.json | 2 + locales/vi-VN/home.json | 13 + locales/vi-VN/hotkey.json | 2 - locales/vi-VN/models.json | 29 +-- locales/vi-VN/onboarding.json | 14 ++ locales/vi-VN/plugin.json | 12 +- locales/vi-VN/providers.json | 1 - locales/vi-VN/subscription.json | 6 + locales/vi-VN/tool.json | 8 + locales/vi-VN/topic.json | 1 + locales/zh-CN/chat.json | 11 +- locales/zh-CN/components.json | 20 +- locales/zh-CN/hotkey.json | 2 - locales/zh-CN/models.json | 31 +-- locales/zh-CN/onboarding.json | 6 + locales/zh-CN/plugin.json | 4 +- locales/zh-CN/providers.json | 1 - locales/zh-CN/subscription.json | 12 +- locales/zh-TW/chat.json | 12 + locales/zh-TW/components.json | 10 + locales/zh-TW/editor.json | 2 + locales/zh-TW/home.json | 13 + locales/zh-TW/hotkey.json | 2 - locales/zh-TW/models.json | 31 +-- locales/zh-TW/onboarding.json | 14 ++ locales/zh-TW/plugin.json | 12 +- locales/zh-TW/providers.json | 1 - locales/zh-TW/subscription.json | 6 + locales/zh-TW/tool.json | 8 + locales/zh-TW/topic.json | 1 + packages/agent-runtime/src/types/hooks.ts | 21 ++ .../src/ExecutionRuntime/index.ts | 1 + .../builtin-tool-message/src/systemRole.ts | 1 + packages/builtin-tool-message/src/types.ts | 24 ++ .../chat-adapter-wechat/src/adapter.test.ts | 142 ++++++++++- packages/chat-adapter-wechat/src/adapter.ts | 228 +++++++++++++++++- packages/chat-adapter-wechat/src/api.test.ts | 133 +++++++++- packages/chat-adapter-wechat/src/api.ts | 177 +++++++++++--- packages/chat-adapter-wechat/src/index.ts | 2 + .../Onboarding/Agent/CompletionPanel.tsx | 63 +++-- .../Agent/MessengerIntegrations.tsx | 170 +++++++++++++ src/locales/default/onboarding.ts | 6 + src/server/routers/lambda/botMessage.ts | 12 + .../agentRuntime/CompletionLifecycle.ts | 190 ++++++++++++++- .../__tests__/CompletionLifecycle.test.ts | 101 ++++++++ src/server/services/bot/AgentBridgeService.ts | 121 ++++++++-- src/server/services/bot/BotCallbackService.ts | 87 +++++-- src/server/services/bot/BotMessageRouter.ts | 69 ++++++ .../bot/__tests__/BotCallbackService.test.ts | 165 +++++++++++++ src/server/services/bot/feedbackSubmit.ts | 99 ++++++++ .../services/bot/platforms/discord/client.ts | 9 +- .../services/bot/platforms/feishu/client.ts | 9 +- src/server/services/bot/platforms/index.ts | 4 +- .../services/bot/platforms/line/client.ts | 7 +- .../services/bot/platforms/qq/client.ts | 16 +- .../services/bot/platforms/slack/client.ts | 16 +- .../services/bot/platforms/telegram/client.ts | 12 +- src/server/services/bot/platforms/types.ts | 40 ++- .../bot/platforms/wechat/client.test.ts | 151 ++++++++++++ .../services/bot/platforms/wechat/client.ts | 21 +- .../bot/platforms/wechat/sendAttachments.ts | 158 ++++++++++++ .../bot/platforms/wechat/service.test.ts | 142 +++++++++++ .../services/bot/platforms/wechat/service.ts | 9 +- src/server/services/bot/replyTemplate.ts | 30 +++ .../services/messenger/MessengerRouter.ts | 72 +++++- 247 files changed, 4114 insertions(+), 617 deletions(-) create mode 100644 apps/cli/src/commands/botMessage.test.ts create mode 100644 src/features/Onboarding/Agent/MessengerIntegrations.tsx create mode 100644 src/server/services/bot/feedbackSubmit.ts create mode 100644 src/server/services/bot/platforms/wechat/sendAttachments.ts create mode 100644 src/server/services/bot/platforms/wechat/service.test.ts diff --git a/apps/cli/src/commands/botMessage.test.ts b/apps/cli/src/commands/botMessage.test.ts new file mode 100644 index 0000000000..0d2b84d814 --- /dev/null +++ b/apps/cli/src/commands/botMessage.test.ts @@ -0,0 +1,162 @@ +import { mkdtemp, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; + +import { Command } from 'commander'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import { registerBotMessageCommands } from './botMessage'; + +const { mockTrpcClient } = vi.hoisted(() => ({ + mockTrpcClient: { + botMessage: { + sendMessage: { mutate: vi.fn() }, + }, + }, +})); + +const { getTrpcClient: mockGetTrpcClient } = vi.hoisted(() => ({ + getTrpcClient: vi.fn(), +})); + +vi.mock('../api/client', () => ({ getTrpcClient: mockGetTrpcClient })); +vi.mock('../utils/logger', () => ({ + log: { debug: vi.fn(), error: vi.fn(), info: vi.fn(), warn: vi.fn() }, + setVerbose: vi.fn(), +})); + +describe('bot message send --attachment', () => { + let exitSpy: ReturnType; + let consoleSpy: ReturnType; + + beforeEach(() => { + exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => {}) as any); + consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); + mockGetTrpcClient.mockResolvedValue(mockTrpcClient); + mockTrpcClient.botMessage.sendMessage.mutate.mockReset(); + mockTrpcClient.botMessage.sendMessage.mutate.mockResolvedValue({ messageId: 'm-1' }); + }); + + afterEach(() => { + exitSpy.mockRestore(); + consoleSpy.mockRestore(); + }); + + function createProgram() { + const program = new Command(); + program.exitOverride(); + const bot = program.command('bot'); + registerBotMessageCommands(bot); + return program; + } + + it('passes a remote URL through as fetchUrl', async () => { + const program = createProgram(); + await program.parseAsync([ + 'node', + 'test', + 'bot', + 'message', + 'send', + 'bot-1', + '--target', + 'ch-1', + '--message', + 'hi', + '--attachment', + 'https://cdn.example.com/foo.png', + ]); + + expect(mockTrpcClient.botMessage.sendMessage.mutate).toHaveBeenCalledWith( + expect.objectContaining({ + attachments: [ + expect.objectContaining({ + fetchUrl: 'https://cdn.example.com/foo.png', + mimeType: 'image/png', + name: 'foo.png', + type: 'image', + }), + ], + botId: 'bot-1', + channelId: 'ch-1', + content: 'hi', + }), + ); + }); + + it('base64-encodes a local file path', async () => { + const dir = await mkdtemp(join(tmpdir(), 'lh-cli-attach-')); + const path = join(dir, 'tiny.txt'); + await writeFile(path, 'hello'); + + const program = createProgram(); + await program.parseAsync([ + 'node', + 'test', + 'bot', + 'message', + 'send', + 'bot-1', + '--target', + 'ch-1', + '--message', + 'm', + '--attachment', + path, + ]); + + const call = mockTrpcClient.botMessage.sendMessage.mutate.mock.calls[0][0]; + expect(call.attachments).toHaveLength(1); + expect(call.attachments[0]).toMatchObject({ + mimeType: 'text/plain', + name: 'tiny.txt', + type: 'file', + }); + expect(call.attachments[0].data).toBe(Buffer.from('hello').toString('base64')); + expect(call.attachments[0].fetchUrl).toBeUndefined(); + }); + + it('accepts multiple --attachment flags', async () => { + const program = createProgram(); + await program.parseAsync([ + 'node', + 'test', + 'bot', + 'message', + 'send', + 'bot-1', + '--target', + 'ch-1', + '--message', + 'm', + '--attachment', + 'https://cdn.example.com/a.png', + '--attachment', + 'https://cdn.example.com/b.pdf', + ]); + + const call = mockTrpcClient.botMessage.sendMessage.mutate.mock.calls[0][0]; + expect(call.attachments).toHaveLength(2); + expect(call.attachments[0]).toMatchObject({ type: 'image', name: 'a.png' }); + expect(call.attachments[1]).toMatchObject({ type: 'file', name: 'b.pdf' }); + }); + + it('omits attachments field when no flag is given', async () => { + const program = createProgram(); + await program.parseAsync([ + 'node', + 'test', + 'bot', + 'message', + 'send', + 'bot-1', + '--target', + 'ch-1', + '--message', + 'm', + ]); + + const call = mockTrpcClient.botMessage.sendMessage.mutate.mock.calls[0][0]; + expect(call.attachments).toBeUndefined(); + }); +}); diff --git a/apps/cli/src/commands/botMessage.ts b/apps/cli/src/commands/botMessage.ts index d4285803ff..cee9cda82e 100644 --- a/apps/cli/src/commands/botMessage.ts +++ b/apps/cli/src/commands/botMessage.ts @@ -1,3 +1,6 @@ +import { readFile } from 'node:fs/promises'; +import { basename, extname } from 'node:path'; + import { DEFAULT_BOT_HISTORY_LIMIT } from '@lobechat/const'; import type { Command } from 'commander'; import pc from 'picocolors'; @@ -6,6 +9,69 @@ import { getTrpcClient } from '../api/client'; import { confirm, outputJson, printTable, truncate } from '../utils/format'; import { log } from '../utils/logger'; +type AttachmentInput = { + data?: string; + fetchUrl?: string; + mimeType?: string; + name?: string; + type: 'image' | 'file' | 'video' | 'audio'; +}; + +const MIME_EXT_MAP: Record = { + '.bmp': 'image/bmp', + '.gif': 'image/gif', + '.jpeg': 'image/jpeg', + '.jpg': 'image/jpeg', + '.m4a': 'audio/mp4', + '.mp3': 'audio/mpeg', + '.mp4': 'video/mp4', + '.ogg': 'audio/ogg', + '.pdf': 'application/pdf', + '.png': 'image/png', + '.svg': 'image/svg+xml', + '.txt': 'text/plain', + '.wav': 'audio/wav', + '.webm': 'video/webm', + '.webp': 'image/webp', +}; + +const inferMime = (path: string): string | undefined => MIME_EXT_MAP[extname(path).toLowerCase()]; + +const inferAttachmentType = (mimeType?: string): AttachmentInput['type'] => { + if (!mimeType) return 'file'; + if (mimeType.startsWith('image/')) return 'image'; + if (mimeType.startsWith('video/')) return 'video'; + if (mimeType.startsWith('audio/')) return 'audio'; + return 'file'; +}; + +/** + * Parse a single `--attachment ` argument. Accepted forms: + * - `https://…` / `http://…` → fetchUrl, type inferred from extension + * - any other string → treated as a local file path; + * bytes are read + base64-encoded + */ +const parseAttachmentArg = async (raw: string): Promise => { + if (/^https?:\/\//.test(raw)) { + const pathname = new URL(raw).pathname; + const mimeType = inferMime(pathname); + return { + fetchUrl: raw, + mimeType, + name: basename(pathname) || undefined, + type: inferAttachmentType(mimeType), + }; + } + const bytes = await readFile(raw); + const mimeType = inferMime(raw); + return { + data: bytes.toString('base64'), + mimeType, + name: basename(raw), + type: inferAttachmentType(mimeType), + }; +}; + export function registerBotMessageCommands(bot: Command) { const message = bot .command('message') @@ -18,15 +84,42 @@ export function registerBotMessageCommands(bot: Command) { .description('Send a message to a channel') .requiredOption('--target ', 'Target channel / conversation ID') .requiredOption('--message ', 'Message content') + .option( + '--attachment ', + 'Attach a file by local path or remote URL (repeatable). ' + + 'Local paths are base64-encoded; http(s) URLs are passed as fetchUrl.', + collectOptions, + [], + ) .option('--reply-to ', 'Reply to a specific message') .option('--json', 'Output JSON') .action( async ( botId: string, - options: { json?: boolean; message: string; replyTo?: string; target: string }, + options: { + attachment: string[]; + json?: boolean; + message: string; + replyTo?: string; + target: string; + }, ) => { + let attachments: AttachmentInput[] | undefined; + if (options.attachment.length > 0) { + attachments = []; + for (const raw of options.attachment) { + try { + attachments.push(await parseAttachmentArg(raw)); + } catch (error) { + log.error(`Failed to load attachment "${raw}": ${(error as Error).message}`); + process.exit(1); + } + } + } + const client = await getTrpcClient(); const result = await client.botMessage.sendMessage.mutate({ + attachments, botId, channelId: options.target, content: options.message, @@ -39,8 +132,9 @@ export function registerBotMessageCommands(bot: Command) { } const r = result as any; + const suffix = attachments?.length ? ` with ${attachments.length} attachment(s)` : ''; console.log( - `${pc.green('✓')} Message sent${r.messageId ? ` (${pc.dim(r.messageId)})` : ''}`, + `${pc.green('✓')} Message sent${r.messageId ? ` (${pc.dim(r.messageId)})` : ''}${suffix}`, ); }, ); diff --git a/docs/usage/channels/telegram.mdx b/docs/usage/channels/telegram.mdx index 0b346afce0..9e84da7d5a 100644 --- a/docs/usage/channels/telegram.mdx +++ b/docs/usage/channels/telegram.mdx @@ -27,8 +27,6 @@ By connecting a Telegram channel to your LobeHub agent, users can interact with Open Telegram and search for **@BotFather** — the official Telegram bot for managing bots. Start a conversation and send the `/newbot` command. - ![](/blog/assets1be39423d2bca3a6ee3f247e02a638be.webp) - ### Set Bot Name and Username BotFather will ask you to: @@ -36,14 +34,10 @@ By connecting a Telegram channel to your LobeHub agent, users can interact with 1. Choose a **display name** for your bot (e.g., "LobeHub Assistant") 2. Choose a **username** — it must end with `bot` (e.g., `lobehub_assistant_bot`) - ![](/blog/assetsa95ea7fad4727559d3f8d84a96947d5e.webp) - ### Copy the Bot Token After creating the bot, BotFather will send you an **API token** (format: `123456789:ABCdefGhIjKlmNoPQRsTuVwXyZ`). Copy and save this token. - ![](/blog/assets95dc1ff1901807b3f860b70294667682.webp) - > **Important:** Your bot token is a secret credential. Never share it publicly. @@ -60,8 +54,6 @@ By connecting a Telegram channel to your LobeHub agent, users can interact with The **Bot User ID** will be automatically derived from your token — no need to enter it manually. - ![](/blog/assets939b659e955daf90e2e9e7caba8aa9bd.webp) - ### Optional: Set a Webhook Secret You can optionally enter a **Webhook Secret Token** for additional security. This is used to verify that incoming webhook requests originate from Telegram. @@ -77,8 +69,6 @@ By connecting a Telegram channel to your LobeHub agent, users can interact with Click **Test Connection** in LobeHub's channel settings to verify the integration. Then open Telegram, find your bot by searching its username, and send a message. The bot should respond through your LobeHub agent. -![](/blog/assets5dd8b54083201bff2494404b66e37df0.webp) - ## Set Your Platform Identity (Recommended) One optional field under **Advanced Settings** carries a lot of weight in day-to-day use — fill it in once and most surprises go away. @@ -103,8 +93,6 @@ To use the bot in Telegram groups: 2. By default, the bot responds when mentioned with `@your_bot_username` 3. Send a message mentioning the bot to start interacting -![](/blog/assetsfa30300bd730d56097bfbce49c5f3d06.webp) - **About Group Privacy Mode:** Telegram bots have privacy mode enabled by default, which means they only receive messages that @mention the bot, reply to the bot, or contain /commands. If you change the privacy mode setting after creating the bot, you **must remove and re-add the bot to the group** for the new setting to take effect in that group. diff --git a/docs/usage/channels/telegram.zh-CN.mdx b/docs/usage/channels/telegram.zh-CN.mdx index 809858dc50..df3ba8b390 100644 --- a/docs/usage/channels/telegram.zh-CN.mdx +++ b/docs/usage/channels/telegram.zh-CN.mdx @@ -26,8 +26,6 @@ tags: 打开 Telegram 并搜索 **@BotFather** —— 这是用于管理机器人的官方 Telegram 机器人。开始对话并发送 `/newbot` 命令。 - ![](/blog/assets1be39423d2bca3a6ee3f247e02a638be.webp) - ### 设置机器人名称和用户名 BotFather 会要求您: @@ -35,14 +33,10 @@ tags: 1. 为您的机器人选择一个 **显示名称**(例如,“LobeHub 助手”) 2. 选择一个 **用户名** —— 必须以 `bot` 结尾(例如,`lobehub_assistant_bot`) - ![](/blog/assetsa95ea7fad4727559d3f8d84a96947d5e.webp) - ### 复制机器人令牌 创建机器人后,BotFather 会发送给您一个 **API 令牌**(格式:`123456789:ABCdefGhIjKlmNoPQRsTuVwXyZ`)。复制并保存此令牌。 - ![](/blog/assets95dc1ff1901807b3f860b70294667682.webp) - > **重要提示:** 您的机器人令牌是一个机密凭证,请勿公开分享。 @@ -59,8 +53,6 @@ tags: **机器人用户 ID** 将根据您的令牌自动生成,无需手动输入。 - ![](/blog/assets939b659e955daf90e2e9e7caba8aa9bd.webp) - ### 可选:设置 Webhook 密钥 您可以选择输入一个 **Webhook 密钥令牌** 以增加安全性。此密钥用于验证来自 Telegram 的入站 Webhook 请求。 @@ -76,8 +68,6 @@ tags: 在 LobeHub 的渠道设置中点击 **测试连接** 以验证集成。然后打开 Telegram,搜索您的机器人用户名并发送消息。机器人应通过您的 LobeHub 代理进行响应。 -![](/blog/assets5dd8b54083201bff2494404b66e37df0.webp) - ## 填写你的平台身份(推荐) **高级设置**里有一个可选字段影响日常使用体验,建议一开始就填好。 @@ -102,8 +92,6 @@ tags: 2. 默认情况下,机器人在被 `@your_bot_username` 提及时会响应 3. 发送一条提及机器人的消息以开始互动 -![](/blog/assetsfa30300bd730d56097bfbce49c5f3d06.webp) - **关于隐私模式(Group Privacy):** Telegram 机器人默认启用隐私模式,仅接收群组中 @提及、回复机器人的消息以及 / 命令。如果您在创建机器人后更改了隐私模式设置,**必须将机器人从群组中移除后重新加入**,新的设置才会对该群组生效。 diff --git a/locales/ar/chat.json b/locales/ar/chat.json index eb7d063717..bf8b31edf7 100644 --- a/locales/ar/chat.json +++ b/locales/ar/chat.json @@ -220,6 +220,13 @@ "inbox.title": "Lobe AI", "input.addAi": "إضافة رسالة من الذكاء الاصطناعي", "input.addUser": "إضافة رسالة من المستخدم", + "input.costEstimate.creditsPerMillionTokens": "{{credits}} ائتمان/مليون رموز", + "input.costEstimate.hint": "التكلفة المقدرة: ~{{credits}} ائتمان", + "input.costEstimate.inputLabel": "الإدخال", + "input.costEstimate.outputLabel": "الإخراج", + "input.costEstimate.settingsLink": "ضبط حد التحذير", + "input.costEstimate.tokenCount": "~{{tokens}} رموز", + "input.costEstimate.tooltip": "تم التقدير بناءً على السياق الحالي، الأدوات، وتسعير النموذج. قد تختلف التكلفة الفعلية.", "input.disclaimer": "قد يخطئ الوكلاء. استخدم حكمك الخاص للمعلومات الحساسة.", "input.errorMsg": "فشل الإرسال: {{errorMsg}}. أعد المحاولة أو أرسل لاحقًا.", "input.more": "المزيد", @@ -779,6 +786,7 @@ "upload.preview.prepareTasks": "جارٍ تجهيز الأجزاء...", "upload.preview.status.pending": "جارٍ التحضير للرفع...", "upload.preview.status.processing": "جارٍ معالجة الملف...", + "upload.validation.unsupportedFileType": "نوع الملف غير مدعوم: {{files}}. الصور المدعومة: JPG، PNG، GIF، WebP. المستندات المدعومة تشمل PDF، Word، Excel، PowerPoint، Markdown، النص، CSV، JSON، وملفات التعليمات البرمجية.", "upload.validation.videoSizeExceeded": "يجب ألا يتجاوز حجم ملف الفيديو 20 ميغابايت. الحجم الحالي هو {{actualSize}}.", "viewMode.fullWidth": "العرض الكامل", "viewMode.normal": "قياسي", @@ -894,6 +902,8 @@ "workingPanel.localFile.closeOther": "إغلاق الآخرين", "workingPanel.localFile.closeRight": "إغلاق إلى اليمين", "workingPanel.localFile.error": "تعذر تحميل هذا الملف", + "workingPanel.localFile.preview.raw": "خام", + "workingPanel.localFile.preview.render": "معاينة", "workingPanel.localFile.truncated": "تم تقليص معاينة الملف إلى {{limit}} حرفًا", "workingPanel.progress": "Progress", "workingPanel.progress.allCompleted": "All tasks completed", @@ -959,6 +969,8 @@ "workingPanel.review.viewMode.unified": "التبديل إلى العرض الموحد", "workingPanel.review.wordWrap.disable": "تعطيل التفاف النص", "workingPanel.review.wordWrap.enable": "تمكين التفاف النص", + "workingPanel.skills.empty": "لم يتم العثور على مهارات في هذا المشروع", + "workingPanel.skills.title": "المهارات", "workingPanel.space": "مسافة", "workingPanel.title": "Working Panel", "you": "أنت", diff --git a/locales/ar/components.json b/locales/ar/components.json index 6a2e21d8cb..6290000080 100644 --- a/locales/ar/components.json +++ b/locales/ar/components.json @@ -121,6 +121,16 @@ "ModelSwitchPanel.detail.context": "طول السياق", "ModelSwitchPanel.detail.pricing": "الأسعار", "ModelSwitchPanel.detail.pricing.cachedInput": "المدخلات المخزنة ${{amount}}/مليون", + "ModelSwitchPanel.detail.pricing.credits.cachedInput": "مدخل مخزن {{amount}} أرصدة/مليون رموز", + "ModelSwitchPanel.detail.pricing.credits.image": "أرصدة/صورة", + "ModelSwitchPanel.detail.pricing.credits.input": "مدخل {{amount}} أرصدة/مليون رموز", + "ModelSwitchPanel.detail.pricing.credits.megapixel": "أرصدة/ميجا بكسل", + "ModelSwitchPanel.detail.pricing.credits.millionCharacters": "أرصدة/مليون أحرف", + "ModelSwitchPanel.detail.pricing.credits.millionTokens": "أرصدة/مليون رموز", + "ModelSwitchPanel.detail.pricing.credits.output": "مخرج {{amount}} أرصدة/مليون رموز", + "ModelSwitchPanel.detail.pricing.credits.perImage": "~ {{amount}} أرصدة / صورة", + "ModelSwitchPanel.detail.pricing.credits.perVideo": "~ {{amount}} أرصدة / فيديو", + "ModelSwitchPanel.detail.pricing.credits.second": "أرصدة/ثانية", "ModelSwitchPanel.detail.pricing.group.audio": "الصوت", "ModelSwitchPanel.detail.pricing.group.image": "الصورة", "ModelSwitchPanel.detail.pricing.group.text": "النص", diff --git a/locales/ar/editor.json b/locales/ar/editor.json index 6c1d0f44f7..7d45dfe154 100644 --- a/locales/ar/editor.json +++ b/locales/ar/editor.json @@ -1,8 +1,10 @@ { "actionTag.category.command": "أمر", + "actionTag.category.projectSkill": "مهارة المشروع", "actionTag.category.skill": "مهارة", "actionTag.category.tool": "أداة", "actionTag.tooltip.command": "يشغّل أمر الشرطة المائلة على جانب العميل قبل الإرسال.", + "actionTag.tooltip.projectSkill": "يتم إرسالها كاستدعاء شرطة مائلة بحيث يقوم CLI الخاص بالوكيل بتشغيل مهارة المشروع المطابقة.", "actionTag.tooltip.skill": "يحمّل حزمة مهارات قابلة لإعادة الاستخدام لهذا الطلب.", "actionTag.tooltip.tool": "يشير إلى أداة اختارها المستخدم صراحةً لهذا الطلب.", "actions.expand.off": "طي", diff --git a/locales/ar/home.json b/locales/ar/home.json index 71e2ddd546..e13f3fe03a 100644 --- a/locales/ar/home.json +++ b/locales/ar/home.json @@ -11,6 +11,19 @@ "brief.action.ignore": "تجاهل", "brief.action.retry": "إعادة المحاولة", "brief.addFeedback": "مشاركة الملاحظات", + "brief.agentSignal.selfReview.applied.heading": "محدث", + "brief.agentSignal.selfReview.applied.summary": "تم تطبيق تحديث حلم واحد.", + "brief.agentSignal.selfReview.applied.summary_plural": "تم تطبيق {{count}} تحديثات حلم.", + "brief.agentSignal.selfReview.applied.title": "تم تحديث موارد الحلم", + "brief.agentSignal.selfReview.error.heading": "مشكلة", + "brief.agentSignal.selfReview.error.summary": "تعذر إكمال بعض الأعمال أثناء هذا الحلم.", + "brief.agentSignal.selfReview.error.title": "واجه الحلم مشكلة", + "brief.agentSignal.selfReview.ideas.summary": "تم حفظ ملاحظات الحلم للمراجعة المستقبلية.", + "brief.agentSignal.selfReview.ideas.title": "ملاحظات الحلم", + "brief.agentSignal.selfReview.proposal.heading": "اقتراح", + "brief.agentSignal.selfReview.proposal.summary": "هناك اقتراح حلم واحد يحتاج إلى مراجعتك.", + "brief.agentSignal.selfReview.proposal.summary_plural": "هناك {{count}} اقتراحات حلم تحتاج إلى مراجعتك.", + "brief.agentSignal.selfReview.proposal.title": "اقتراح الحلم يحتاج إلى مراجعة", "brief.collapse": "عرض أقل", "brief.commentPlaceholder": "شارك ملاحظاتك...", "brief.commentSubmit": "إرسال الملاحظات", diff --git a/locales/ar/hotkey.json b/locales/ar/hotkey.json index cadb81edf6..6d5a181c50 100644 --- a/locales/ar/hotkey.json +++ b/locales/ar/hotkey.json @@ -1,8 +1,6 @@ { "addUserMessage.desc": "أضف الإدخال الحالي كرسالة مستخدم دون بدء التوليد", "addUserMessage.title": "إضافة رسالة مستخدم", - "clearCurrentMessages.desc": "مسح الرسائل والملفات المرفوعة من المحادثة الحالية", - "clearCurrentMessages.title": "مسح رسائل المحادثة", "commandPalette.desc": "افتح لوحة الأوامر العامة للوصول السريع إلى الميزات", "commandPalette.title": "لوحة الأوامر", "deleteAndRegenerateMessage.desc": "حذف الرسالة الأخيرة وإعادة توليدها", diff --git a/locales/ar/models.json b/locales/ar/models.json index 6069c3eff4..1294b9aee8 100644 --- a/locales/ar/models.json +++ b/locales/ar/models.json @@ -106,7 +106,6 @@ "MiniMax-Hailuo-2.3.description": "نموذج جديد لإنشاء الفيديو مع تحسينات شاملة في حركة الجسم، والواقعية الفيزيائية، واتباع التعليمات.", "MiniMax-M1.description": "نموذج استدلال داخلي جديد بسلسلة تفكير تصل إلى 80K ومدخلات حتى 1M، يقدم أداءً مماثلاً لأفضل النماذج العالمية.", "MiniMax-M2-Stable.description": "مصمم لتدفقات العمل البرمجية والوكلاء بكفاءة عالية، مع قدرة تزامن أعلى للاستخدام التجاري.", - "MiniMax-M2.1-Lightning.description": "قدرات برمجة متعددة اللغات قوية مع استنتاج أسرع وأكثر كفاءة.", "MiniMax-M2.1-highspeed.description": "قدرات برمجة متعددة اللغات قوية، تجربة برمجة مطورة بشكل شامل. أسرع وأكثر كفاءة.", "MiniMax-M2.1.description": "MiniMax-M2.1 هو نموذج مفتوح المصدر رائد من MiniMax، يركز على حل المهام الواقعية المعقدة. يتميز بقدرات برمجة متعددة اللغات والقدرة على أداء المهام المعقدة كوكلاء ذكي.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: نفس أداء M2.5 مع استدلال أسرع.", @@ -315,13 +314,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku هو أسرع وأصغر نموذج من Anthropic، مصمم لتقديم استجابات شبه فورية بأداء سريع ودقيق.", "claude-3-opus-20240229.description": "Claude 3 Opus هو أقوى نموذج من Anthropic للمهام المعقدة، يتميز بالأداء العالي، الذكاء، الطلاقة، والفهم.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet يوازن بين الذكاء والسرعة لتلبية احتياجات المؤسسات، ويوفر فائدة عالية بتكلفة أقل ونشر موثوق على نطاق واسع.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 هو أسرع وأذكى نموذج هايكو من Anthropic، يتميز بسرعة البرق وتفكير ممتد.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 هو النموذج الأكثر سرعة وذكاءً من Anthropic، يتميز بسرعة البرق وقدرات استدلال موسعة.", "claude-haiku-4-5.description": "Claude Haiku 4.5 من Anthropic — نموذج Haiku من الجيل التالي مع تحسينات في التفكير والرؤية.", "claude-haiku-4.5.description": "Claude Haiku 4.5 هو نموذج Haiku الأسرع والأذكى من Anthropic، يتميز بسرعة البرق وقدرات استدلال موسعة.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking هو إصدار متقدم يمكنه عرض عملية تفكيره.", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1 هو أحدث وأقوى نموذج من Anthropic للمهام المعقدة للغاية، يتميز بالأداء والذكاء والطلاقة والفهم.", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1 هو أحدث وأقوى نموذج من Anthropic للمهام المعقدة للغاية، يتميز بالأداء العالي، الذكاء، الطلاقة، والفهم.", "claude-opus-4-1.description": "Claude Opus 4.1 من Anthropic — نموذج تفكير متميز مع قدرات تحليل عميقة.", - "claude-opus-4-20250514.description": "Claude Opus 4 هو أقوى نموذج من Anthropic للمهام المعقدة للغاية، يتميز بالأداء والذكاء والطلاقة والفهم.", + "claude-opus-4-20250514.description": "Claude Opus 4 هو النموذج الأكثر قوة من Anthropic للمهام المعقدة للغاية، يتميز بالأداء العالي، الذكاء، الطلاقة، والاستيعاب.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 هو النموذج الرائد من Anthropic، يجمع بين الذكاء الاستثنائي والأداء القابل للتوسع، مثالي للمهام المعقدة التي تتطلب استجابات عالية الجودة وتفكير متقدم.", "claude-opus-4-5.description": "Claude Opus 4.5 من Anthropic — نموذج رئيسي مع تفكير وبرمجة من الدرجة الأولى.", "claude-opus-4-6.description": "Claude Opus 4.6 من Anthropic — نافذة سياق 1M نموذج رئيسي مع تفكير متقدم.", @@ -330,7 +329,7 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 هو النموذج الأكثر ذكاءً من Anthropic لبناء الوكلاء والبرمجة.", "claude-opus-4.6.description": "Claude Opus 4.6 هو النموذج الأكثر ذكاءً من Anthropic لبناء الوكلاء والبرمجة.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking يمكنه تقديم استجابات شبه فورية أو تفكير متسلسل مرئي.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 هو النموذج الأكثر ذكاءً من Anthropic حتى الآن، يقدم استجابات شبه فورية أو تفكير ممتد خطوة بخطوة مع تحكم دقيق لمستخدمي API.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 يمكنه تقديم استجابات شبه فورية أو تفكير ممتد خطوة بخطوة مع عملية مرئية.", "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 هو النموذج الأكثر ذكاءً من Anthropic حتى الآن.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 من Anthropic — نموذج Sonnet محسّن مع أداء برمجي معزز.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 من Anthropic — أحدث نموذج Sonnet مع برمجة واستخدام أدوات متفوقة.", @@ -404,7 +403,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) هو نموذج مبتكر يوفر فهمًا عميقًا للغة وتفاعلًا ذكيًا.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 هو نموذج تفكير من الجيل التالي يتمتع بقدرات أقوى في التفكير المعقد وسلسلة التفكير لمهام التحليل العميق.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 هو نموذج استدلال من الجيل التالي يتميز بقدرات استدلال معقدة وسلسلة التفكير.", - "deepseek-chat.description": "اسم مستعار متوافق لوضع عدم التفكير في DeepSeek V4 Flash. مقرر إيقافه — استخدم DeepSeek V4 Flash بدلاً منه.", + "deepseek-chat.description": "نموذج مفتوح المصدر جديد يجمع بين القدرات العامة وقدرات البرمجة. يحافظ على الحوار العام لنموذج الدردشة وقوة البرمجة لنموذج المبرمج، مع تحسين توافق التفضيلات. كما يحسن DeepSeek-V2.5 الكتابة واتباع التعليمات.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B هو نموذج لغة برمجية تم تدريبه على 2 تريليون رمز (87٪ كود، 13٪ نص صيني/إنجليزي). يقدم نافذة سياق 16K ومهام الإكمال في المنتصف، ويوفر إكمال كود على مستوى المشاريع وملء مقاطع الكود.", "deepseek-coder-v2.description": "DeepSeek Coder V2 هو نموذج كود MoE مفتوح المصدر يتميز بأداء قوي في مهام البرمجة، ويضاهي GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 هو نموذج كود MoE مفتوح المصدر يتميز بأداء قوي في مهام البرمجة، ويضاهي GPT-4 Turbo.", @@ -426,7 +425,7 @@ "deepseek-r1-fast-online.description": "الإصدار الكامل السريع من DeepSeek R1 مع بحث ويب في الوقت الحقيقي، يجمع بين قدرات بحجم 671B واستجابة أسرع.", "deepseek-r1-online.description": "الإصدار الكامل من DeepSeek R1 مع 671 مليار معلمة وبحث ويب في الوقت الحقيقي، يوفر فهمًا وتوليدًا أقوى.", "deepseek-r1.description": "يستخدم DeepSeek-R1 بيانات البداية الباردة قبل التعلم المعزز ويؤدي أداءً مماثلًا لـ OpenAI-o1 في الرياضيات، والبرمجة، والتفكير.", - "deepseek-reasoner.description": "اسم مستعار متوافق لوضع التفكير في DeepSeek V4 Flash. مقرر إيقافه — استخدم DeepSeek V4 Flash بدلاً منه.", + "deepseek-reasoner.description": "نموذج استدلال DeepSeek يركز على مهام الاستدلال المنطقي المعقدة.", "deepseek-v2.description": "DeepSeek V2 هو نموذج MoE فعال لمعالجة منخفضة التكلفة.", "deepseek-v2:236b.description": "DeepSeek V2 236B هو نموذج DeepSeek الموجه للبرمجة مع قدرات قوية في توليد الكود.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 هو نموذج MoE يحتوي على 671 مليار معلمة يتميز بقوة في البرمجة، والقدرات التقنية، وفهم السياق، والتعامل مع النصوص الطويلة.", @@ -491,8 +490,6 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 هو نموذج توليد صور من ByteDance Seed، يدعم إدخال النصوص والصور مع توليد صور عالية الجودة وقابلة للتحكم بدرجة كبيرة. يُولّد الصور من التعليمات النصية.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 هو أحدث نموذج متعدد الوسائط من ByteDance، يدمج قدرات تحويل النص إلى صورة، والصورة إلى صورة، وتوليد الصور بالجملة، مع دمج الفهم العام وقدرات الاستدلال. مقارنة بالإصدار السابق 4.0، يقدم جودة توليد محسّنة بشكل كبير، مع تحسين تناسق التحرير ودمج الصور المتعددة. يوفر تحكمًا أكثر دقة في التفاصيل البصرية، مما يجعل النصوص الصغيرة والوجوه الصغيرة أكثر طبيعية، ويحقق تخطيطًا وألوانًا أكثر انسجامًا، مما يعزز الجماليات العامة.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite هو أحدث نموذج لتوليد الصور من ByteDance. لأول مرة، يدمج قدرات الاسترجاع عبر الإنترنت، مما يسمح له بتضمين معلومات الويب في الوقت الفعلي وتعزيز حداثة الصور المولدة. كما تم ترقية ذكاء النموذج، مما يمكنه من تفسير التعليمات المعقدة والمحتوى البصري بدقة. بالإضافة إلى ذلك، يقدم تغطية محسّنة للمعرفة العالمية، وتناسقًا مرجعيًا، وجودة توليد في السيناريوهات المهنية، مما يلبي بشكل أفضل احتياجات الإبداع البصري على مستوى المؤسسات.", - "dreamina-seedance-2-0-260128.description": "Seedance 2.0 من ByteDance هو أقوى نموذج لتوليد الفيديو، يدعم إنشاء الفيديو المرجعي متعدد الوسائط، تحرير الفيديو، تمديد الفيديو، تحويل النص إلى فيديو، وتحويل الصورة إلى فيديو مع صوت متزامن.", - "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast من ByteDance يقدم نفس القدرات مثل Seedance 2.0 مع سرعات توليد أسرع وسعر أكثر تنافسية.", "emohaa.description": "Emohaa هو نموذج للصحة النفسية يتمتع بقدرات استشارية احترافية لمساعدة المستخدمين على فهم المشكلات العاطفية.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B هو نموذج مفتوح المصدر وخفيف الوزن، مصمم للنشر المحلي والمخصص.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview هو نموذج معاينة بسياق 8K لتقييم أداء ERNIE 4.5.", @@ -506,6 +503,7 @@ "ernie-5.0-thinking-latest.description": "Wenxin 5.0 Thinking هو نموذج رائد متعدد الوسائط أصلي يدعم النصوص، الصور، الصوت، والفيديو بشكل موحد. يوفر ترقيات شاملة للقدرات في الأسئلة المعقدة، الإبداع، وسيناريوهات الوكلاء.", "ernie-5.0-thinking-preview.description": "معاينة Wenxin 5.0 Thinking هو نموذج رائد متعدد الوسائط أصلي يدعم النصوص، الصور، الصوت، والفيديو بشكل موحد. يوفر ترقيات شاملة للقدرات في الأسئلة المعقدة، الإبداع، وسيناريوهات الوكلاء.", "ernie-5.0.description": "ERNIE 5.0، النموذج الجديد في سلسلة ERNIE، هو نموذج كبير متعدد الوسائط أصلي. يعتمد نهج نمذجة متعدد الوسائط موحد، حيث يقوم بنمذجة النصوص، الصور، الصوت، والفيديو بشكل مشترك لتقديم قدرات متعددة الوسائط شاملة. تم تحسين قدراته الأساسية بشكل كبير، محققًا أداءً قويًا في تقييمات المعايير. يتفوق بشكل خاص في الفهم متعدد الوسائط، اتباع التعليمات، الكتابة الإبداعية، الدقة الواقعية، تخطيط الوكلاء، واستخدام الأدوات.", + "ernie-5.1.description": "ERNIE 5.1 هو أحدث نموذج في سلسلة ERNIE، يتميز بترقيات شاملة لقدراته الأساسية. يظهر تحسينات كبيرة في مجالات مثل الوكلاء، معالجة المعرفة، الاستدلال، والبحث العميق. يعتمد هذا الإصدار على بنية تعلم معزز غير متزامن بالكامل ومفككة، مصممة خصيصًا لمعالجة التحديات الرئيسية في تطور النماذج الكبيرة نحو اتخاذ القرارات الذاتية للوكلاء، بما في ذلك التناقضات العددية بين التدريب والاستدلال، انخفاض استخدام موارد الحوسبة غير المتجانسة، والقضايا العالمية الناتجة عن تأثيرات الذيل الطويل. بالإضافة إلى ذلك، يتم استخدام تقنيات تدريب ما بعد واسعة النطاق للوكلاء لتعزيز قدرات النموذج وأداء التعميم. من خلال إطار عمل تعاوني من ثلاث مراحل يشمل البيئة، الخبراء، وعمليات الدمج، لا يضمن النهج كفاءة التدريب فحسب، بل يحسن بشكل كبير من استقرار النموذج وأدائه في المهام المعقدة.", "ernie-char-fiction-8k-preview.description": "معاينة ERNIE Character Fiction 8K هو نموذج لإنشاء الشخصيات والحبكات القصصية، مخصص لتقييم الميزات والاختبار.", "ernie-char-fiction-8k.description": "ERNIE Character Fiction 8K هو نموذج شخصيات للروايات وإنشاء الحبكات، مناسب لتوليد القصص الطويلة.", "ernie-image-turbo.description": "ERNIE-Image هو نموذج نص إلى صورة بـ 8 مليارات معلمة تم تطويره بواسطة Baidu. يحتل المرتبة الأولى في العديد من المعايير، محققًا المركز الأول في SuperCLUE في الصين ويتصدر المسار مفتوح المصدر.", @@ -517,8 +515,7 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K هو نموذج تفكير سريع بسياق 32K للاستدلال المعقد والدردشة متعددة الأدوار.", "ernie-x1.1-preview.description": "معاينة ERNIE X1.1 هو نموذج تفكير مخصص للتقييم والاختبار.", "ernie-x1.1.description": "ERNIE X1.1 هو نموذج تفكير تجريبي للتقييم والاختبار.", - "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5، تم تطويره بواسطة فريق ByteDance Seed، يدعم تحرير الصور المتعددة والتكوين. يتميز بتناسق الموضوع المحسن، اتباع التعليمات بدقة، فهم المنطق المكاني، التعبير الجمالي، تصميم الملصقات وتصميم الشعارات مع توليد النصوص والصور بدقة عالية.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0، تم تطويره بواسطة ByteDance Seed، يدعم إدخال النصوص والصور لتوليد صور عالية الجودة وقابلة للتحكم بناءً على التعليمات.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 هو نموذج توليد الصور من ByteDance Seed، يدعم المدخلات النصية والصورية مع توليد صور عالي الجودة وقابل للتحكم بدرجة كبيرة. يقوم بتوليد الصور من التعليمات النصية.", "fal-ai/flux-kontext/dev.description": "نموذج FLUX.1 يركز على تحرير الصور، ويدعم إدخال النصوص والصور.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] يقبل النصوص وصور مرجعية كمدخلات، مما يتيح تعديلات محلية مستهدفة وتحولات معقدة في المشهد العام.", "fal-ai/flux/krea.description": "Flux Krea [dev] هو نموذج لتوليد الصور يتميز بميول جمالية نحو صور أكثر واقعية وطبيعية.", @@ -526,8 +523,8 @@ "fal-ai/hunyuan-image/v3.description": "نموذج قوي لتوليد الصور متعدد الوسائط أصلي.", "fal-ai/imagen4/preview.description": "نموذج عالي الجودة لتوليد الصور من Google.", "fal-ai/nano-banana.description": "Nano Banana هو أحدث وأسرع وأكثر نماذج Google كفاءةً لتوليد وتحرير الصور من خلال المحادثة.", - "fal-ai/qwen-image-edit.description": "نموذج تحرير الصور الاحترافي من فريق Qwen، يدعم التعديلات الدلالية والمظهرية، تحرير النصوص الدقيقة باللغتين الصينية والإنجليزية، نقل الأنماط، التدوير، والمزيد.", - "fal-ai/qwen-image.description": "نموذج قوي لتوليد الصور من فريق Qwen يتميز بتقديم نصوص صينية قوية وأنماط بصرية متنوعة.", + "fal-ai/qwen-image-edit.description": "نموذج تحرير الصور الاحترافي من فريق Qwen يدعم التعديلات الدلالية والمظهرية، ويحرر النصوص الصينية والإنجليزية بدقة، ويمكّن من تعديلات عالية الجودة مثل نقل الأنماط وتدوير الكائنات.", + "fal-ai/qwen-image.description": "نموذج توليد الصور القوي من فريق Qwen يتميز بعرض نصوص صينية مبهرة وأنماط بصرية متنوعة.", "flux-1-schnell.description": "نموذج تحويل النص إلى صورة يحتوي على 12 مليار معلمة من Black Forest Labs يستخدم تقنيات تقطير الانتشار العدائي الكامن لتوليد صور عالية الجودة في 1-4 خطوات. ينافس البدائل المغلقة ومتاح بموجب ترخيص Apache-2.0 للاستخدام الشخصي والبحثي والتجاري.", "flux-dev.description": "نموذج مفتوح المصدر مخصص لتوليد الصور لأغراض البحث والابتكار غير التجاري، مع تحسينات فعالة.", "flux-kontext-max.description": "توليد وتحرير صور سياقية متقدمة، تجمع بين النصوص والصور لتحقيق نتائج دقيقة ومتسقة.", @@ -569,7 +566,7 @@ "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) هو نموذج توليد الصور من Google ويدعم أيضًا الدردشة متعددة الوسائط.", "gemini-3-pro-preview.description": "Gemini 3 Pro هو أقوى نموذج من Google للوكيل الذكي والبرمجة الإبداعية، يقدم تفاعلاً أعمق وصورًا أغنى مع استدلال متقدم.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) يقدم جودة صور احترافية بسرعة فائقة مع دعم الدردشة متعددة الوسائط.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) يقدم جودة صور بمستوى احترافي بسرعة Flash مع دعم الدردشة متعددة الوسائط.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) هو أسرع نموذج توليد صور أصلي من Google مع دعم التفكير، وتوليد الصور الحواري، وتحرير الصور.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview هو النموذج الأكثر كفاءة من حيث التكلفة من Google، مُحسّن للمهام الوكيلة ذات الحجم الكبير، الترجمة، ومعالجة البيانات.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite هو النموذج متعدد الوسائط الأكثر كفاءة من Google، مُحسّن للمهام الوكيلية ذات الحجم الكبير، الترجمة، ومعالجة البيانات.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview يحسن من Gemini 3 Pro مع قدرات استدلال محسّنة ويضيف دعم مستوى التفكير المتوسط.", @@ -734,8 +731,6 @@ "grok-4-fast-reasoning.description": "يسعدنا إطلاق Grok 4 Fast، أحدث تقدم في نماذج الاستدلال منخفضة التكلفة.", "grok-4.20-0309-non-reasoning.description": "نموذج غير تفكير للاستخدامات البسيطة.", "grok-4.20-0309-reasoning.description": "نموذج ذكي وسريع للغاية يفكر قبل الرد.", - "grok-4.20-beta-0309-non-reasoning.description": "نسخة غير تفكيرية للاستخدامات البسيطة.", - "grok-4.20-beta-0309-reasoning.description": "نموذج ذكي وسريع للغاية يفكر قبل الرد.", "grok-4.20-multi-agent-0309.description": "فريق من 4 أو 16 وكيلًا، يتفوق في حالات الاستخدام البحثية، لا يدعم حاليًا الأدوات على جانب العميل. يدعم فقط أدوات xAI على جانب الخادم (مثل X Search، أدوات البحث على الويب) وأدوات MCP البعيدة.", "grok-4.3.description": "أكثر نموذج لغة كبير يسعى للحقيقة في العالم.", "grok-4.description": "أحدث نموذج Grok الرائد بأداء لا مثيل له في اللغة، الرياضيات، والاستدلال — نموذج شامل حقيقي. يشير حاليًا إلى grok-4-0709؛ نظرًا للموارد المحدودة، فإن سعره مؤقتًا أعلى بنسبة 10% من السعر الرسمي ومن المتوقع أن يعود إلى السعر الرسمي لاحقًا.", @@ -1220,8 +1215,6 @@ "qwq.description": "QwQ هو نموذج استدلال من عائلة Qwen. مقارنة بالنماذج المضبوطة على التعليمات، يقدم قدرات تفكير واستدلال تعزز الأداء بشكل كبير، خاصة في المشكلات الصعبة. QwQ-32B هو نموذج متوسط الحجم ينافس أفضل نماذج الاستدلال مثل DeepSeek-R1 و o1-mini.", "qwq_32b.description": "نموذج استدلال متوسط الحجم من عائلة Qwen. مقارنة بالنماذج المضبوطة على التعليمات، تعزز قدرات التفكير والاستدلال في QwQ الأداء بشكل كبير، خاصة في المشكلات الصعبة.", "r1-1776.description": "R1-1776 هو إصدار ما بعد التدريب من DeepSeek R1 مصمم لتقديم معلومات واقعية غير خاضعة للرقابة أو التحيز.", - "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro من ByteDance يدعم تحويل النص إلى فيديو، تحويل الصورة إلى فيديو (الإطار الأول، الإطار الأول + الأخير)، وتوليد الصوت المتزامن مع المرئيات.", - "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite من BytePlus يتميز بتوليد معزز بالاسترجاع عبر الويب للحصول على معلومات في الوقت الحقيقي، تفسير محسّن للتعليمات المعقدة، وتحسين تناسق المرجع لإنشاء بصري احترافي.", "solar-mini-ja.description": "Solar Mini (Ja) يوسع Solar Mini مع تركيز على اللغة اليابانية مع الحفاظ على الأداء القوي والكفاءة في الإنجليزية والكورية.", "solar-mini.description": "Solar Mini هو نموذج لغة مدمج يتفوق على GPT-3.5، يتميز بقدرات متعددة اللغات قوية تدعم الإنجليزية والكورية، ويقدم حلاً فعالاً بصمة صغيرة.", "solar-pro.description": "Solar Pro هو نموذج لغة عالي الذكاء من Upstage، يركز على اتباع التعليمات باستخدام وحدة معالجة رسومات واحدة، مع درجات IFEval تتجاوز 80. حالياً يدعم اللغة الإنجليزية؛ وكان من المقرر إصدار النسخة الكاملة في نوفمبر 2024 مع دعم لغات موسع وسياق أطول.", diff --git a/locales/ar/onboarding.json b/locales/ar/onboarding.json index 22c774a76a..c85b88a26a 100644 --- a/locales/ar/onboarding.json +++ b/locales/ar/onboarding.json @@ -27,6 +27,13 @@ "agent.layout.skipConfirm.ok": "تخطي الآن", "agent.layout.skipConfirm.title": "تخطي الإعداد الآن؟", "agent.layout.switchMessage": "لست في مزاج لذلك اليوم؟ يمكنك التبديل إلى {{mode}} أو {{skip}}.", + "agent.layout.switchMessageClassic": "لست في المزاج اليوم؟ يمكنك التبديل إلى {{mode}}.", + "agent.messenger.cta.discord": "أضف إلى Discord", + "agent.messenger.cta.slack": "أضف إلى Slack", + "agent.messenger.cta.telegram": "افتح في Telegram", + "agent.messenger.subtitle": "تحدث مع وكيلك على Telegram أو Slack أو Discord", + "agent.messenger.telegramQrCaption": "امسح باستخدام كاميرا هاتفك", + "agent.messenger.title": "ابق معي أينما كنت تراسل", "agent.modeSwitch.agent": "تفاعلي", "agent.modeSwitch.classic": "كلاسيكي", "agent.modeSwitch.collapse": "طي", @@ -68,6 +75,13 @@ "agent.wrapUp.confirm.content": "سأحفظ ما ناقشناه حتى الآن. يمكنك دائمًا العودة ومتابعة الحديث لاحقًا.", "agent.wrapUp.confirm.ok": "إنهاء الآن", "agent.wrapUp.confirm.title": "إنهاء الإعداد الآن؟", + "agentPicker.allCategories": "الكل", + "agentPicker.continue": "متابعة", + "agentPicker.skip": "تخطي الآن", + "agentPicker.subtitle": "أضف بعضًا إلى مكتبتك الآن — اكتشف المزيد في أي وقت لاحقًا.", + "agentPicker.title": "لنضف بعض الوكلاء إلى مكتبتك", + "agentPicker.title2": "اختر ما يناسب طريقة عملك", + "agentPicker.title3": "يمكنك دائمًا إضافة المزيد لاحقًا — ابدأ ببعض الخيارات", "back": "رجوع", "finish": "ابدأ الآن", "interests.area.business": "الأعمال والاستراتيجية", diff --git a/locales/ar/plugin.json b/locales/ar/plugin.json index 7962c61b62..f2be6c72cc 100644 --- a/locales/ar/plugin.json +++ b/locales/ar/plugin.json @@ -39,8 +39,8 @@ "builtins.lobe-agent-documents.inspector.docCount": "{{count}} مستندات", "builtins.lobe-agent-documents.inspector.opsCount": "{{count}} عمليات", "builtins.lobe-agent-documents.inspector.opsResult": "{{success}}/{{total}} عمليات", - "builtins.lobe-agent-documents.inspector.target.agent": "في الوكيل", - "builtins.lobe-agent-documents.inspector.target.currentTopic": "في الموضوع", + "builtins.lobe-agent-documents.inspector.scope.agent": "في الوكيل", + "builtins.lobe-agent-documents.inspector.scope.currentTopic": "في الموضوع", "builtins.lobe-agent-documents.title": "مستندات الوكيل", "builtins.lobe-agent-management.apiName.callAgent": "وكيل الاتصال", "builtins.lobe-agent-management.apiName.createAgent": "إنشاء وكيل", @@ -90,6 +90,14 @@ "builtins.lobe-agent.title": "وكيل لوب", "builtins.lobe-claude-code.agent.instruction": "تعليمات", "builtins.lobe-claude-code.agent.result": "النتيجة", + "builtins.lobe-claude-code.task.createLabel": "إنشاء المهمة: ", + "builtins.lobe-claude-code.task.getLabel": "تفحص المهمة #{{taskId}}", + "builtins.lobe-claude-code.task.listLabel": "عرض المهام", + "builtins.lobe-claude-code.task.updateCompleted": "مكتملة", + "builtins.lobe-claude-code.task.updateDeleted": "محذوفة", + "builtins.lobe-claude-code.task.updateInProgress": "بدأت", + "builtins.lobe-claude-code.task.updateLabel": "تحديث المهمة #{{taskId}}", + "builtins.lobe-claude-code.task.updatePending": "إعادة تعيين", "builtins.lobe-claude-code.todoWrite.allDone": "جميع المهام مكتملة", "builtins.lobe-claude-code.todoWrite.currentStep": "الخطوة الحالية", "builtins.lobe-claude-code.todoWrite.todos": "المهام", diff --git a/locales/ar/providers.json b/locales/ar/providers.json index e7614ea9fd..313b2aecb3 100644 --- a/locales/ar/providers.json +++ b/locales/ar/providers.json @@ -33,7 +33,6 @@ "jina.description": "تأسست Jina AI في عام 2020، وهي شركة رائدة في مجال البحث الذكي. تشمل تقنياتها نماذج المتجهات، ومعيدو الترتيب، ونماذج لغوية صغيرة لبناء تطبيقات بحث توليدية ومتعددة الوسائط عالية الجودة.", "kimicodingplan.description": "كود Kimi من Moonshot AI يوفر الوصول إلى نماذج Kimi بما في ذلك K2.5 لأداء مهام الترميز.", "lmstudio.description": "LM Studio هو تطبيق سطح مكتب لتطوير وتجربة النماذج اللغوية الكبيرة على جهازك.", - "lobehub.description": "يستخدم LobeHub Cloud واجهات برمجة التطبيقات الرسمية للوصول إلى نماذج الذكاء الاصطناعي ويقيس الاستخدام باستخدام أرصدة مرتبطة برموز النماذج.", "longcat.description": "LongCat هو سلسلة من نماذج الذكاء الاصطناعي التوليدية الكبيرة التي تم تطويرها بشكل مستقل بواسطة Meituan. تم تصميمه لتعزيز إنتاجية المؤسسة الداخلية وتمكين التطبيقات المبتكرة من خلال بنية حسابية فعالة وقدرات متعددة الوسائط قوية.", "minimax.description": "تأسست MiniMax في عام 2021، وتبني نماذج ذكاء اصطناعي متعددة الوسائط للأغراض العامة، بما في ذلك نماذج نصية بمليارات المعلمات، ونماذج صوتية وبصرية، بالإضافة إلى تطبيقات مثل Hailuo AI.", "minimaxcodingplan.description": "خطة الرموز MiniMax توفر الوصول إلى نماذج MiniMax بما في ذلك M2.7 لأداء مهام الترميز عبر اشتراك ثابت الرسوم.", diff --git a/locales/ar/subscription.json b/locales/ar/subscription.json index c46b2cc6fd..abcd3f0ce4 100644 --- a/locales/ar/subscription.json +++ b/locales/ar/subscription.json @@ -55,6 +55,12 @@ "credits.autoTopUp.toggle": "تفعيل الشحن التلقائي", "credits.autoTopUp.upgradeHint": "اشترك في خطة مدفوعة لتفعيل الشحن التلقائي", "credits.autoTopUp.validation.targetMustExceedThreshold": "يجب أن يكون الرصيد المستهدف أكبر من الحد الأدنى", + "credits.costEstimateHint.desc": "عرض تحذير خفيف قبل الإرسال عندما تصل التكلفة التقديرية للنموذج إلى الحد الخاص بك", + "credits.costEstimateHint.saveError": "فشل في حفظ إعدادات تنبيه تقدير التكلفة", + "credits.costEstimateHint.saveSuccess": "تم حفظ إعدادات تنبيه تقدير التكلفة", + "credits.costEstimateHint.threshold": "حد التحذير", + "credits.costEstimateHint.title": "تنبيه تقدير التكلفة", + "credits.costEstimateHint.validation.threshold": "يجب أن يكون الحد أكبر من أو يساوي 0", "credits.packages.auto": "تلقائي", "credits.packages.charged": "تم خصم ${{amount}}", "credits.packages.expired": "منتهية", diff --git a/locales/ar/tool.json b/locales/ar/tool.json index b921fad756..e872b05852 100644 --- a/locales/ar/tool.json +++ b/locales/ar/tool.json @@ -40,6 +40,14 @@ "agentMarketplace.render.alreadyInLibraryTag": "موجود بالفعل في المكتبة", "agentMarketplace.render.alreadyInLibrary_one": "{{count}} موجود بالفعل في المكتبة", "agentMarketplace.render.alreadyInLibrary_other": "{{count}} موجود بالفعل في المكتبة", + "claudeCode.askUserQuestion.escape.back": "العودة إلى الخيارات", + "claudeCode.askUserQuestion.escape.enter": "أو اكتب مباشرة", + "claudeCode.askUserQuestion.escape.placeholder": "اكتب إجابتك هنا…", + "claudeCode.askUserQuestion.multiSelectTag": "(اختيار متعدد)", + "claudeCode.askUserQuestion.skip": "تخطي", + "claudeCode.askUserQuestion.submit": "إرسال", + "claudeCode.askUserQuestion.timeExpired": "انتهى الوقت — سيتم استخدام الخيار 1 لكل سؤال.", + "claudeCode.askUserQuestion.timeRemaining": "الوقت المتبقي: {{time}} · الأسئلة غير المجابة ستُعتمد على الخيار 1 عند انتهاء الوقت.", "codeInterpreter-legacy.error": "خطأ في التنفيذ", "codeInterpreter-legacy.executing": "جارٍ التنفيذ...", "codeInterpreter-legacy.files": "الملفات:", diff --git a/locales/ar/topic.json b/locales/ar/topic.json index 146b5987ae..7c08342e61 100644 --- a/locales/ar/topic.json +++ b/locales/ar/topic.json @@ -56,6 +56,7 @@ "renameModal.title": "إعادة تسمية الموضوع", "searchPlaceholder": "ابحث في المواضيع...", "searchResultEmpty": "لم يتم العثور على نتائج.", + "sidebar.title": "المواضيع", "taskManager.agent": "وكيل المهام", "taskManager.welcome": "اسألني عن مهامك", "temp": "مؤقت", diff --git a/locales/bg-BG/chat.json b/locales/bg-BG/chat.json index 3cfc15d008..42410ec2d8 100644 --- a/locales/bg-BG/chat.json +++ b/locales/bg-BG/chat.json @@ -220,6 +220,13 @@ "inbox.title": "Lobe AI", "input.addAi": "Добави AI съобщение", "input.addUser": "Добави потребителско съобщение", + "input.costEstimate.creditsPerMillionTokens": "{{credits}} кредита/М токена", + "input.costEstimate.hint": "Оценена цена: ~{{credits}} кредита", + "input.costEstimate.inputLabel": "Вход", + "input.costEstimate.outputLabel": "Изход", + "input.costEstimate.settingsLink": "Настройка на прага за предупреждение", + "input.costEstimate.tokenCount": "~{{tokens}} токена", + "input.costEstimate.tooltip": "Оценено въз основа на текущия контекст, инструменти и ценообразуване на модела. Реалната цена може да варира.", "input.disclaimer": "Агентите могат да допускат грешки. Използвайте собствена преценка за важна информация.", "input.errorMsg": "Изпращането не бе успешно: {{errorMsg}}. Опитайте отново или по-късно.", "input.more": "Още", @@ -779,6 +786,7 @@ "upload.preview.prepareTasks": "Подготовка на части...", "upload.preview.status.pending": "Подготовка за качване...", "upload.preview.status.processing": "Обработка на файла...", + "upload.validation.unsupportedFileType": "Неподдържан тип файл: {{files}}. Поддържани изображения: JPG, PNG, GIF, WebP. Поддържани документи включват PDF, Word, Excel, PowerPoint, Markdown, текст, CSV, JSON и файлове с код.", "upload.validation.videoSizeExceeded": "Размерът на видеофайла не трябва да надвишава 20MB. Текущият размер е {{actualSize}}.", "viewMode.fullWidth": "Пълна ширина", "viewMode.normal": "Стандартен", @@ -894,6 +902,8 @@ "workingPanel.localFile.closeOther": "Затвори другите", "workingPanel.localFile.closeRight": "Затвори надясно", "workingPanel.localFile.error": "Не може да се зареди този файл", + "workingPanel.localFile.preview.raw": "Суров", + "workingPanel.localFile.preview.render": "Преглед", "workingPanel.localFile.truncated": "Прегледът на файла е съкратен до {{limit}} символа", "workingPanel.progress": "Progress", "workingPanel.progress.allCompleted": "All tasks completed", @@ -959,6 +969,8 @@ "workingPanel.review.viewMode.unified": "Превключете към обединен изглед", "workingPanel.review.wordWrap.disable": "Деактивирай пренасяне на думи", "workingPanel.review.wordWrap.enable": "Активирай пренасяне на думи", + "workingPanel.skills.empty": "Няма намерени умения в този проект", + "workingPanel.skills.title": "Умения", "workingPanel.space": "Пространство", "workingPanel.title": "Working Panel", "you": "Вие", diff --git a/locales/bg-BG/components.json b/locales/bg-BG/components.json index ac5dd5d247..7362e60f66 100644 --- a/locales/bg-BG/components.json +++ b/locales/bg-BG/components.json @@ -121,6 +121,16 @@ "ModelSwitchPanel.detail.context": "Дължина на контекста", "ModelSwitchPanel.detail.pricing": "Ценообразуване", "ModelSwitchPanel.detail.pricing.cachedInput": "Кеширан вход ${{amount}}/М", + "ModelSwitchPanel.detail.pricing.credits.cachedInput": "Кеширан вход {{amount}} кредита/М токени", + "ModelSwitchPanel.detail.pricing.credits.image": "кредити/изображение", + "ModelSwitchPanel.detail.pricing.credits.input": "Вход {{amount}} кредита/М токени", + "ModelSwitchPanel.detail.pricing.credits.megapixel": "кредити/МП", + "ModelSwitchPanel.detail.pricing.credits.millionCharacters": "кредити/М символа", + "ModelSwitchPanel.detail.pricing.credits.millionTokens": "кредити/М токени", + "ModelSwitchPanel.detail.pricing.credits.output": "Изход {{amount}} кредита/М токени", + "ModelSwitchPanel.detail.pricing.credits.perImage": "~ {{amount}} кредита / изображение", + "ModelSwitchPanel.detail.pricing.credits.perVideo": "~ {{amount}} кредита / видео", + "ModelSwitchPanel.detail.pricing.credits.second": "кредити/сек", "ModelSwitchPanel.detail.pricing.group.audio": "Аудио", "ModelSwitchPanel.detail.pricing.group.image": "Изображение", "ModelSwitchPanel.detail.pricing.group.text": "Текст", diff --git a/locales/bg-BG/editor.json b/locales/bg-BG/editor.json index 6dd75bec8e..38e621e859 100644 --- a/locales/bg-BG/editor.json +++ b/locales/bg-BG/editor.json @@ -1,8 +1,10 @@ { "actionTag.category.command": "Команда", + "actionTag.category.projectSkill": "Умение за проект", "actionTag.category.skill": "Умение", "actionTag.category.tool": "Инструмент", "actionTag.tooltip.command": "Изпълнява локална slash команда преди изпращане.", + "actionTag.tooltip.projectSkill": "Изпраща се като команда със знак наклонена черта, така че CLI на агента да изпълни съответното умение за проект.", "actionTag.tooltip.skill": "Зарежда многократно използваем пакет с умения за тази заявка.", "actionTag.tooltip.tool": "Маркира инструмент, който потребителят е избрал изрично за тази заявка.", "actions.expand.off": "Свий", diff --git a/locales/bg-BG/home.json b/locales/bg-BG/home.json index c6613d693a..7b2959f51f 100644 --- a/locales/bg-BG/home.json +++ b/locales/bg-BG/home.json @@ -11,6 +11,19 @@ "brief.action.ignore": "Игнорирай", "brief.action.retry": "Опит отново", "brief.addFeedback": "Споделяне на обратна връзка", + "brief.agentSignal.selfReview.applied.heading": "Актуализирано", + "brief.agentSignal.selfReview.applied.summary": "{{count}} актуализация на мечтата беше приложена.", + "brief.agentSignal.selfReview.applied.summary_plural": "{{count}} актуализации на мечтата бяха приложени.", + "brief.agentSignal.selfReview.applied.title": "Актуализирани ресурси на мечтата", + "brief.agentSignal.selfReview.error.heading": "Проблем", + "brief.agentSignal.selfReview.error.summary": "Някои задачи не можаха да бъдат завършени по време на тази мечта.", + "brief.agentSignal.selfReview.error.title": "Мечтата срещна проблем", + "brief.agentSignal.selfReview.ideas.summary": "Запазени бележки за мечтата за бъдещ преглед.", + "brief.agentSignal.selfReview.ideas.title": "Бележки за мечтата", + "brief.agentSignal.selfReview.proposal.heading": "Предложение", + "brief.agentSignal.selfReview.proposal.summary": "{{count}} предложение за мечтата изисква вашия преглед.", + "brief.agentSignal.selfReview.proposal.summary_plural": "{{count}} предложения за мечтата изискват вашия преглед.", + "brief.agentSignal.selfReview.proposal.title": "Предложение за мечтата изисква преглед", "brief.collapse": "Покажи по-малко", "brief.commentPlaceholder": "Споделете вашата обратна връзка...", "brief.commentSubmit": "Изпращане на обратна връзка", diff --git a/locales/bg-BG/hotkey.json b/locales/bg-BG/hotkey.json index 05f9bce5d4..de1412ae9d 100644 --- a/locales/bg-BG/hotkey.json +++ b/locales/bg-BG/hotkey.json @@ -1,8 +1,6 @@ { "addUserMessage.desc": "Добавете текущия вход като потребителско съобщение без да се задейства генериране", "addUserMessage.title": "Добавяне на потребителско съобщение", - "clearCurrentMessages.desc": "Изчистете съобщенията и качените файлове от текущия разговор", - "clearCurrentMessages.title": "Изчистване на съобщенията от разговора", "commandPalette.desc": "Отворете глобалната палитра с команди за бърз достъп до функциите", "commandPalette.title": "Палитра с команди", "deleteAndRegenerateMessage.desc": "Изтрийте последното съобщение и го генерирайте отново", diff --git a/locales/bg-BG/models.json b/locales/bg-BG/models.json index 6289b8babe..7324bae455 100644 --- a/locales/bg-BG/models.json +++ b/locales/bg-BG/models.json @@ -106,7 +106,6 @@ "MiniMax-Hailuo-2.3.description": "Чисто нов модел за видео генериране с цялостни подобрения в движенията на тялото, физическата реалистичност и следването на инструкции.", "MiniMax-M1.description": "Нов вътрешен модел за разсъждение с 80K верига на мисълта и 1M вход, предлагащ производителност, сравнима с водещите глобални модели.", "MiniMax-M2-Stable.description": "Създаден за ефективно програмиране и агентски работни потоци, с по-висока едновременност за търговска употреба.", - "MiniMax-M2.1-Lightning.description": "Мощни многоезични програмни възможности с по-бързо и ефективно извеждане.", "MiniMax-M2.1-highspeed.description": "Мощни многоезични програмни възможности, цялостно подобрено програмиране. По-бързо и по-ефективно.", "MiniMax-M2.1.description": "MiniMax-M2.1 е водеща отворена голяма езикова система от MiniMax, фокусирана върху решаването на сложни реални задачи. Основните ѝ предимства са възможностите за програмиране на множество езици и способността да действа като агент за решаване на сложни задачи.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: Същата производителност като M2.5, но с по-бързо извеждане.", @@ -315,13 +314,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku е най-бързият и най-компактен модел на Anthropic, проектиран за почти мигновени отговори с бърза и точна производителност.", "claude-3-opus-20240229.description": "Claude 3 Opus е най-мощният модел на Anthropic за силно сложни задачи, отличаващ се с производителност, интелигентност, плавност и разбиране.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet балансира интелигентност и скорост за корпоративни натоварвания, осигурявайки висока полезност на по-ниска цена и надеждно мащабно внедряване.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 е най-бързият и интелигентен модел Haiku на Anthropic, с мълниеносна скорост и разширено мислене.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 е най-бързият и най-умният Haiku модел на Anthropic, с мълниеносна скорост и разширени възможности за разсъждение.", "claude-haiku-4-5.description": "Claude Haiku 4.5 от Anthropic — ново поколение Haiku с подобрено разсъждение и визия.", "claude-haiku-4.5.description": "Claude Haiku 4.5 е най-бързият и най-умен Haiku модел на Anthropic, с мълниеносна скорост и разширено разсъждение.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking е усъвършенстван вариант, който може да разкрие процеса си на разсъждение.", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1 е най-новият и най-способен модел на Anthropic за силно сложни задачи, превъзхождащ в производителност, интелигентност, плавност и разбиране.", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1 е най-новият и най-способен модел на Anthropic за силно сложни задачи, отличаващ се с висока производителност, интелигентност, плавност и разбиране.", "claude-opus-4-1.description": "Claude Opus 4.1 от Anthropic — премиум модел за дълбок анализ и разсъждение.", - "claude-opus-4-20250514.description": "Claude Opus 4 е най-мощният модел на Anthropic за силно сложни задачи, превъзхождащ в производителност, интелигентност, плавност и разбиране.", + "claude-opus-4-20250514.description": "Claude Opus 4 е най-мощният модел на Anthropic за силно сложни задачи, отличаващ се с висока производителност, интелигентност, плавност и разбиране.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 е флагманският модел на Anthropic, комбиниращ изключителна интелигентност с мащабируема производителност, идеален за сложни задачи, изискващи най-висококачествени отговори и разсъждение.", "claude-opus-4-5.description": "Claude Opus 4.5 от Anthropic — флагмански модел с върхово разсъждение и кодови умения.", "claude-opus-4-6.description": "Claude Opus 4.6 от Anthropic — флагман с 1M контекст и усъвършенствано разсъждение.", @@ -330,7 +329,7 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 е най-интелигентният модел на Anthropic за създаване на агенти и програмиране.", "claude-opus-4.6.description": "Claude Opus 4.6 е най-интелигентният модел на Anthropic за създаване на агенти и програмиране.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking може да генерира почти мигновени отговори или разширено стъпково мислене с видим процес.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 е най-интелигентният модел на Anthropic досега, предлагащ почти мигновени отговори или разширено стъпка по стъпка мислене с фино управление за API потребители.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 може да генерира почти мигновени отговори или разширено поетапно мислене с видим процес.", "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 е най-интелигентният модел на Anthropic досега.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 от Anthropic — подобрен Sonnet с по‑силни кодови способности.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 от Anthropic — последният Sonnet с превъзходно кодиране и работа с инструменти.", @@ -404,7 +403,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) е иновативен модел, предлагащ дълбоко езиково разбиране и интеракция.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 е модел за разсъждение от ново поколение с по-силни способности за сложни разсъждения и верига от мисли за задълбочени аналитични задачи.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 е модел за разсъждение от следващо поколение с по-силни способности за сложни разсъждения и верига на мисълта.", - "deepseek-chat.description": "Съвместимостен псевдоним за DeepSeek V4 Flash в режим без мислене. Предстои да бъде прекратен — използвайте DeepSeek V4 Flash вместо това.", + "deepseek-chat.description": "Нов модел с отворен код, комбиниращ общи и кодови способности. Той запазва общия диалогов характер на чат модела и силните кодови възможности на кодиращия модел, с по-добро съответствие на предпочитанията. DeepSeek-V2.5 също така подобрява писането и следването на инструкции.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B е езиков модел за програмиране, обучен върху 2 трилиона токени (87% код, 13% китайски/английски текст). Въвежда 16K контекстен прозорец и задачи за попълване в средата, осигурявайки допълване на код на ниво проект и попълване на фрагменти.", "deepseek-coder-v2.description": "DeepSeek Coder V2 е отворен MoE модел за програмиране, който се представя на ниво GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 е отворен MoE модел за програмиране, който се представя на ниво GPT-4 Turbo.", @@ -426,7 +425,7 @@ "deepseek-r1-fast-online.description": "Пълна бърза версия на DeepSeek R1 с търсене в реално време в уеб, комбинираща възможности от мащаб 671B и по-бърз отговор.", "deepseek-r1-online.description": "Пълна версия на DeepSeek R1 с 671 милиарда параметъра и търсене в реално време в уеб, предлагаща по-силно разбиране и генериране.", "deepseek-r1.description": "DeepSeek-R1 използва данни от студен старт преди подсиленото обучение и се представя наравно с OpenAI-o1 в математика, програмиране и разсъждение.", - "deepseek-reasoner.description": "Съвместимостен псевдоним за DeepSeek V4 Flash в режим на мислене. Предстои да бъде прекратен — използвайте DeepSeek V4 Flash вместо това.", + "deepseek-reasoner.description": "DeepSeek модел за разсъждение, фокусиран върху сложни логически задачи.", "deepseek-v2.description": "DeepSeek V2 е ефективен MoE модел за икономична обработка.", "deepseek-v2:236b.description": "DeepSeek V2 236B е модел на DeepSeek, фокусиран върху програмиране, с висока производителност при генериране на код.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 е MoE модел с 671 милиарда параметъра, с изключителни способности в програмиране, технически задачи, разбиране на контекст и обработка на дълги текстове.", @@ -491,8 +490,6 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 е модел за генериране на изображения от ByteDance Seed, поддържащ вход от текст и изображения с високо контролируемо, висококачествено генериране на изображения. Генерира изображения от текстови подсказки.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 е най-новият мултимодален модел за изображения на ByteDance, интегриращ текст-към-изображение, изображение-към-изображение и групово генериране на изображения, като включва обща логика и способности за разсъждение. В сравнение с предишната версия 4.0, той предлага значително подобрено качество на генериране, с по-добра консистентност при редактиране и мулти-изображение сливане. Осигурява по-прецизен контрол върху визуалните детайли, като произвежда малък текст и малки лица по-естествено, и постига по-хармонично оформление и цветове, подобрявайки цялостната естетика.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite е най-новият модел за генериране на изображения на ByteDance. За първи път интегрира възможности за онлайн извличане, позволявайки му да включва информация в реално време от уеб и да подобрява актуалността на генерираните изображения. Интелигентността на модела също е подобрена, позволявайки прецизно интерпретиране на сложни инструкции и визуално съдържание. Освен това предлага подобрено глобално покритие на знания, консистентност на референциите и качество на генериране в професионални сценарии, по-добре отговаряйки на нуждите за визуално създаване на корпоративно ниво.", - "dreamina-seedance-2-0-260128.description": "Seedance 2.0 от ByteDance е най-мощният модел за генериране на видео, поддържащ многомодално генериране на референтни видеа, редактиране на видео, разширение на видео, текст към видео и изображение към видео със синхронизиран звук.", - "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast от ByteDance предлага същите възможности като Seedance 2.0 с по-бързи скорости на генериране на по-конкурентна цена.", "emohaa.description": "Emohaa е модел за психично здраве с професионални консултантски способности, който помага на потребителите да разберат емоционални проблеми.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B е лек модел с отворен код за локално и персонализирано внедряване.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview е модел за предварителен преглед с 8K контекст за оценка на ERNIE 4.5.", @@ -506,6 +503,7 @@ "ernie-5.0-thinking-latest.description": "Wenxin 5.0 Thinking е флагмански модел с пълна модалност, обединяващ текст, изображение, аудио и видео. Осигурява значителни подобрения за сложни QA, творчество и агентски сценарии.", "ernie-5.0-thinking-preview.description": "Wenxin 5.0 Thinking Preview е флагмански модел с пълна модалност, обединяващ текст, изображение, аудио и видео. Осигурява значителни подобрения за сложни QA, творчество и агентски сценарии.", "ernie-5.0.description": "ERNIE 5.0 е ново поколение мултимодален модел, който обединява текст, изображения, аудио и видео. Основните му способности са значително подобрени — силен в разбиране, следване на инструкции, творчество, фактическа точност, планиране и работа с инструменти.", + "ernie-5.1.description": "ERNIE 5.1 е най-новият модел от серията ERNIE, с цялостни подобрения на основните си способности. Той демонстрира значителни подобрения в области като агенти, обработка на знания, разсъждение и дълбоко търсене. Това издание използва разделена напълно асинхронна архитектура за подсилено обучение, специално проектирана за справяне с ключови предизвикателства в еволюцията на големите модели към автономно вземане на решения от агенти, включително числови несъответствия между обучение и извеждане, ниско използване на хетерогенни изчислителни ресурси и глобални проблеми, причинени от ефекти на дългата опашка. Освен това се прилагат техники за постобучение на агенти в голям мащаб, за да се подобрят допълнително способностите и общата производителност на модела. Чрез тристепенна съвместна рамка, включваща процеси на среда, експерт и сливане, подходът не само гарантира ефективност на обучението, но и значително подобрява стабилността и производителността на модела при сложни задачи.", "ernie-char-fiction-8k-preview.description": "ERNIE Character Fiction 8K Preview е предварителен модел за създаване на персонажи и сюжет, предназначен за оценка и тестване на функции.", "ernie-char-fiction-8k.description": "ERNIE Character Fiction 8K е модел за персонажи, предназначен за романи и създаване на сюжет, подходящ за генериране на дълги истории.", "ernie-image-turbo.description": "ERNIE‑Image е 8B параметъра текст‑към‑изображение модел от Baidu. Води в множество класации, включително първо място в SuperCLUE в Китай.", @@ -517,8 +515,7 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K е бърз мислещ модел с 32K контекст за сложни разсъждения и многозавойни разговори.", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview е предварителен модел за мислене, предназначен за оценка и тестване.", "ernie-x1.1.description": "ERNIE X1.1 е мисловен модел за предварителен преглед за оценка и тестване.", - "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5, създаден от екипа Seed на ByteDance, поддържа редактиране и композиция на множество изображения. Характеризира се с подобрена консистентност на обектите, прецизно следване на инструкции, разбиране на пространствена логика, естетично изразяване, оформление на плакати и дизайн на лого с високопрецизно рендиране на текст и изображения.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, създаден от ByteDance Seed, поддържа текстови и визуални входове за силно контролируемо, висококачествено генериране на изображения от подсказки.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 е модел за генериране на изображения от ByteDance Seed, поддържащ текстови и визуални входове с високо контролируемо и висококачествено генериране на изображения. Той генерира изображения от текстови подсказки.", "fal-ai/flux-kontext/dev.description": "FLUX.1 модел, фокусиран върху редактиране на изображения, поддържащ вход от текст и изображения.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] приема текст и референтни изображения като вход, позволявайки целенасочени локални редакции и сложни глобални трансформации на сцени.", "fal-ai/flux/krea.description": "Flux Krea [dev] е модел за генериране на изображения с естетично предпочитание към по-реалистични и естествени изображения.", @@ -526,8 +523,8 @@ "fal-ai/hunyuan-image/v3.description": "Мощен роден мултимодален модел за генериране на изображения.", "fal-ai/imagen4/preview.description": "Модел за висококачествено генериране на изображения от Google.", "fal-ai/nano-banana.description": "Nano Banana е най-новият, най-бърз и най-ефективен роден мултимодален модел на Google, позволяващ генериране и редактиране на изображения чрез разговор.", - "fal-ai/qwen-image-edit.description": "Професионален модел за редактиране на изображения от екипа Qwen, поддържащ семантични и визуални редакции, прецизно редактиране на текст на китайски/английски, трансфер на стил, ротация и други.", - "fal-ai/qwen-image.description": "Мощен модел за генериране на изображения от екипа Qwen с силно рендиране на китайски текст и разнообразни визуални стилове.", + "fal-ai/qwen-image-edit.description": "Професионален модел за редактиране на изображения от екипа на Qwen, който поддържа семантични и визуални редакции, прецизно редактира китайски и английски текст и позволява висококачествени редакции като трансфер на стил и ротация на обекти.", + "fal-ai/qwen-image.description": "Мощен модел за генериране на изображения от екипа на Qwen с впечатляващо рендиране на китайски текст и разнообразни визуални стилове.", "flux-1-schnell.description": "Модел за преобразуване на текст в изображение с 12 милиарда параметъра от Black Forest Labs, използващ латентна дифузионна дестилация за генериране на висококачествени изображения в 1–4 стъпки. Съперничи на затворени алтернативи и е пуснат под лиценз Apache-2.0 за лична, изследователска и търговска употреба.", "flux-dev.description": "Модел за генериране на изображения с отворен код, оптимизиран за неконкурентни изследвания и иновации.", "flux-kontext-max.description": "Съвременно генериране и редактиране на изображения с контекст, комбиниращо текст и изображения за прецизни и последователни резултати.", @@ -566,10 +563,10 @@ "gemini-3-flash-preview.description": "Gemini 3 Flash е най-интелигентният модел, създаден за скорост, съчетаващ авангардна интелигентност с отлично търсене и обоснованост.", "gemini-3-flash.description": "Gemini 3 Flash от Google — ултрабърз модел с мултимодална поддръжка.", "gemini-3-pro-image-preview.description": "Gemini 3 Pro Image (Nano Banana Pro) е модел за генериране на изображения на Google, който също поддържа мултимодален диалог.", - "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) е моделът на Google за генериране на изображения и също така поддържа многомодален чат.", + "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) е моделът на Google за генериране на изображения, който също поддържа мултимодален чат.", "gemini-3-pro-preview.description": "Gemini 3 Pro е най-мощният агентен и „vibe-coding“ модел на Google, който предлага по-богати визуализации и по-дълбоко взаимодействие, базирано на съвременно логическо мислене.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) е най-бързият модел на Google за генериране на изображения с поддръжка на мислене, разговорно генериране и редактиране на изображения.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) предоставя качество на изображения от професионално ниво с Flash скорост и поддръжка на многомодален чат.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) е най-бързият модел на Google за генериране на изображения с поддръжка на мислене, разговорно генериране и редактиране на изображения.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview е най-икономичният мултимодален модел на Google, оптимизиран за задачи с голям обем, превод и обработка на данни.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite е най-икономичният мултимодален модел на Google, оптимизиран за задачи с голям обем, превод и обработка на данни.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview подобрява Gemini 3 Pro с усъвършенствани способности за разсъждение и добавя поддръжка за средно ниво на мислене.", @@ -734,8 +731,6 @@ "grok-4-fast-reasoning.description": "С гордост представяме Grok 4 Fast – нашият най-нов напредък в икономичните логически модели.", "grok-4.20-0309-non-reasoning.description": "Неразсъждаващ вариант за прости случаи.", "grok-4.20-0309-reasoning.description": "Интелигентен, изключително бърз модел с разсъждение.", - "grok-4.20-beta-0309-non-reasoning.description": "Вариант без мислене за прости случаи на употреба.", - "grok-4.20-beta-0309-reasoning.description": "Интелигентен, изключително бърз модел, който разсъждава преди да отговори.", "grok-4.20-multi-agent-0309.description": "Екип от 4 или 16 агента — отличен за проучвания; поддържа само xAI сървърни инструменти.", "grok-4.3.description": "Най-истинно търсещият голям езиков модел в света", "grok-4.description": "Най-новият флагман Grok с ненадмината производителност в езика, математиката и разсъжденията — истински универсален модел. В момента сочи към grok-4-0709; поради ограничени ресурси временно е с 10% по-висока цена от официалната и се очаква да се върне към официалната цена по-късно.", @@ -1220,8 +1215,6 @@ "qwq.description": "QwQ е модел за аргументация от семейството на Qwen. В сравнение със стандартните модели, обучени с инструкции, предлага мисловни и логически способности, които значително подобряват ефективността при трудни задачи. QwQ-32B е среден по размер модел, който се конкурира с водещи модели като DeepSeek-R1 и o1-mini.", "qwq_32b.description": "Среден по размер модел за аргументация от семейството на Qwen. В сравнение със стандартните модели, обучени с инструкции, мисловните и логическите способности на QwQ значително подобряват ефективността при трудни задачи.", "r1-1776.description": "R1-1776 е дообучен вариант на DeepSeek R1, създаден да предоставя неконфронтирана, обективна и фактическа информация.", - "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro от ByteDance поддържа текст към видео, изображение към видео (първи кадър, първи+последен кадър) и генериране на звук, синхронизиран с визуализации.", - "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite от BytePlus предлага генериране, обогатено с уеб извличане за информация в реално време, подобрена интерпретация на сложни подсказки и подобрена консистентност на референциите за професионално визуално създаване.", "solar-mini-ja.description": "Solar Mini (Ja) разширява Solar Mini с фокус върху японски език, като запазва ефективността и силната производителност на английски и корейски.", "solar-mini.description": "Solar Mini е компактен LLM, който превъзхожда GPT-3.5, с мощни многоезични възможности, поддържащ английски и корейски, и предлага ефективно решение с малък отпечатък.", "solar-pro.description": "Solar Pro е интелигентен LLM от Upstage, фокусиран върху следване на инструкции на един GPU, с IFEval резултати над 80. Понастоящем поддържа английски; пълното издание е планирано за ноември 2024 с разширена езикова поддръжка и по-дълъг контекст.", diff --git a/locales/bg-BG/onboarding.json b/locales/bg-BG/onboarding.json index a9d9911c5b..9f7bba3818 100644 --- a/locales/bg-BG/onboarding.json +++ b/locales/bg-BG/onboarding.json @@ -27,6 +27,13 @@ "agent.layout.skipConfirm.ok": "Пропуснете засега", "agent.layout.skipConfirm.title": "Да пропуснете въвеждането засега?", "agent.layout.switchMessage": "Не сте в настроение днес? Можете да преминете в {{mode}} или да {{skip}}.", + "agent.layout.switchMessageClassic": "Не ви допада днес? Можете да превключите на {{mode}}.", + "agent.messenger.cta.discord": "Добави към Discord", + "agent.messenger.cta.slack": "Добави към Slack", + "agent.messenger.cta.telegram": "Отвори в Telegram", + "agent.messenger.subtitle": "Чатете с вашия агент в Telegram, Slack или Discord", + "agent.messenger.telegramQrCaption": "Сканирайте с камерата на телефона си", + "agent.messenger.title": "Дръжте ме с вас, където и да съобщавате", "agent.modeSwitch.agent": "Разговорен", "agent.modeSwitch.classic": "Класически", "agent.modeSwitch.collapse": "Свий", @@ -68,6 +75,13 @@ "agent.wrapUp.confirm.content": "Ще запазя това, което обсъдихме до момента. Винаги можете да се върнете и да продължим разговора.", "agent.wrapUp.confirm.ok": "Приключи сега", "agent.wrapUp.confirm.title": "Да приключим ли онбординга?", + "agentPicker.allCategories": "Всички", + "agentPicker.continue": "Продължи", + "agentPicker.skip": "Пропусни за сега", + "agentPicker.subtitle": "Добавете няколко към вашата библиотека сега — открийте още по-късно.", + "agentPicker.title": "Нека добавим няколко агента към вашата библиотека", + "agentPicker.title2": "Изберете тези, които съответстват на начина ви на работа", + "agentPicker.title3": "Винаги можете да добавите още по-късно — започнете с няколко", "back": "Назад", "finish": "Да започнем", "interests.area.business": "Бизнес и стратегия", diff --git a/locales/bg-BG/plugin.json b/locales/bg-BG/plugin.json index c8234c62eb..9466bdb969 100644 --- a/locales/bg-BG/plugin.json +++ b/locales/bg-BG/plugin.json @@ -39,8 +39,8 @@ "builtins.lobe-agent-documents.inspector.docCount": "{{count}} документа", "builtins.lobe-agent-documents.inspector.opsCount": "{{count}} операции", "builtins.lobe-agent-documents.inspector.opsResult": "{{success}}/{{total}} операции", - "builtins.lobe-agent-documents.inspector.target.agent": "в агент", - "builtins.lobe-agent-documents.inspector.target.currentTopic": "в тема", + "builtins.lobe-agent-documents.inspector.scope.agent": "в агент", + "builtins.lobe-agent-documents.inspector.scope.currentTopic": "в тема", "builtins.lobe-agent-documents.title": "Документи на агент", "builtins.lobe-agent-management.apiName.callAgent": "Обаждане на агент", "builtins.lobe-agent-management.apiName.createAgent": "Създаване на агент", @@ -90,6 +90,14 @@ "builtins.lobe-agent.title": "Lobe Агент", "builtins.lobe-claude-code.agent.instruction": "Инструкция", "builtins.lobe-claude-code.agent.result": "Резултат", + "builtins.lobe-claude-code.task.createLabel": "Създаване на задача: ", + "builtins.lobe-claude-code.task.getLabel": "Преглеждане на задача #{{taskId}}", + "builtins.lobe-claude-code.task.listLabel": "Списък със задачи", + "builtins.lobe-claude-code.task.updateCompleted": "Завършено", + "builtins.lobe-claude-code.task.updateDeleted": "Изтрито", + "builtins.lobe-claude-code.task.updateInProgress": "Започнато", + "builtins.lobe-claude-code.task.updateLabel": "Актуализиране на задача #{{taskId}}", + "builtins.lobe-claude-code.task.updatePending": "Нулиране", "builtins.lobe-claude-code.todoWrite.allDone": "Всички задачи са изпълнени", "builtins.lobe-claude-code.todoWrite.currentStep": "Текуща стъпка", "builtins.lobe-claude-code.todoWrite.todos": "Задачи", diff --git a/locales/bg-BG/providers.json b/locales/bg-BG/providers.json index f4d1d7b7d6..43b17d4992 100644 --- a/locales/bg-BG/providers.json +++ b/locales/bg-BG/providers.json @@ -33,7 +33,6 @@ "jina.description": "Основана през 2020 г., Jina AI е водеща компания в областта на търсещия AI. Технологичният ѝ стек включва векторни модели, преоценители и малки езикови модели за създаване на надеждни генеративни и мултимодални търсещи приложения.", "kimicodingplan.description": "Kimi Code от Moonshot AI предоставя достъп до модели Kimi, включително K2.5, за задачи, свързани с програмиране.", "lmstudio.description": "LM Studio е десктоп приложение за разработка и експериментиране с LLM на вашия компютър.", - "lobehub.description": "LobeHub Cloud използва официални API, за да осъществява достъп до AI модели и измерва използването чрез Кредити, свързани с токените на модела.", "longcat.description": "LongCat е серия от големи модели за генеративен AI, независимо разработени от Meituan. Той е създаден да подобри вътрешната продуктивност на предприятието и да позволи иновативни приложения чрез ефективна изчислителна архитектура и силни мултимодални възможности.", "minimax.description": "Основана през 2021 г., MiniMax създава универсален AI с мултимодални базови модели, включително текстови модели с трилиони параметри, речеви и визуални модели, както и приложения като Hailuo AI.", "minimaxcodingplan.description": "MiniMax Token Plan предоставя достъп до модели MiniMax, включително M2.7, за задачи, свързани с програмиране, чрез абонамент с фиксирана такса.", diff --git a/locales/bg-BG/subscription.json b/locales/bg-BG/subscription.json index eae4d80e1d..97f25055d7 100644 --- a/locales/bg-BG/subscription.json +++ b/locales/bg-BG/subscription.json @@ -55,6 +55,12 @@ "credits.autoTopUp.toggle": "Активирайте автоматичното презареждане", "credits.autoTopUp.upgradeHint": "Абонирайте се за платен план, за да активирате автоматичното презареждане", "credits.autoTopUp.validation.targetMustExceedThreshold": "Целевият баланс трябва да бъде по-голям от прага", + "credits.costEstimateHint.desc": "Показване на леко предупреждение преди изпращане, когато прогнозната цена на модела достигне вашия праг", + "credits.costEstimateHint.saveError": "Неуспешно запазване на настройките за предупреждение за прогнозна цена", + "credits.costEstimateHint.saveSuccess": "Настройките за предупреждение за прогнозна цена са запазени", + "credits.costEstimateHint.threshold": "Праг за предупреждение", + "credits.costEstimateHint.title": "Предупреждение за прогнозна цена", + "credits.costEstimateHint.validation.threshold": "Прагът трябва да бъде по-голям или равен на 0", "credits.packages.auto": "Автоматично", "credits.packages.charged": "Таксувани ${{amount}}", "credits.packages.expired": "Изтекъл", diff --git a/locales/bg-BG/tool.json b/locales/bg-BG/tool.json index c1621b1c3d..8f5cdfc467 100644 --- a/locales/bg-BG/tool.json +++ b/locales/bg-BG/tool.json @@ -40,6 +40,14 @@ "agentMarketplace.render.alreadyInLibraryTag": "Вече в библиотеката", "agentMarketplace.render.alreadyInLibrary_one": "{{count}} вече в библиотеката", "agentMarketplace.render.alreadyInLibrary_other": "{{count}} вече в библиотеката", + "claudeCode.askUserQuestion.escape.back": "Назад към опциите", + "claudeCode.askUserQuestion.escape.enter": "Или въведете директно", + "claudeCode.askUserQuestion.escape.placeholder": "Въведете вашия отговор тук…", + "claudeCode.askUserQuestion.multiSelectTag": "(многоизбор)", + "claudeCode.askUserQuestion.skip": "Пропусни", + "claudeCode.askUserQuestion.submit": "Изпрати", + "claudeCode.askUserQuestion.timeExpired": "Времето изтече — използва се опция 1 за всеки въпрос.", + "claudeCode.askUserQuestion.timeRemaining": "Оставащо време: {{time}} · въпросите без отговор ще се задават на опция 1 при изтичане на времето.", "codeInterpreter-legacy.error": "Грешка при изпълнение", "codeInterpreter-legacy.executing": "Изпълнение...", "codeInterpreter-legacy.files": "Файлове:", diff --git a/locales/bg-BG/topic.json b/locales/bg-BG/topic.json index 270a13f6ab..e677f1bdb1 100644 --- a/locales/bg-BG/topic.json +++ b/locales/bg-BG/topic.json @@ -56,6 +56,7 @@ "renameModal.title": "Преименуване на тема", "searchPlaceholder": "Търсене в темите...", "searchResultEmpty": "Няма намерени резултати от търсенето.", + "sidebar.title": "Теми", "taskManager.agent": "Агент за задачи", "taskManager.welcome": "Попитай ме за твоите задачи", "temp": "Временна", diff --git a/locales/de-DE/chat.json b/locales/de-DE/chat.json index 2886acaa73..c7ce339374 100644 --- a/locales/de-DE/chat.json +++ b/locales/de-DE/chat.json @@ -220,6 +220,13 @@ "inbox.title": "Lobe AI", "input.addAi": "KI-Nachricht hinzufügen", "input.addUser": "Benutzernachricht hinzufügen", + "input.costEstimate.creditsPerMillionTokens": "{{credits}} Credits/M Tokens", + "input.costEstimate.hint": "Geschätzte Kosten: ~{{credits}} Credits", + "input.costEstimate.inputLabel": "Eingabe", + "input.costEstimate.outputLabel": "Ausgabe", + "input.costEstimate.settingsLink": "Warnschwelle anpassen", + "input.costEstimate.tokenCount": "~{{tokens}} Tokens", + "input.costEstimate.tooltip": "Geschätzt basierend auf aktuellem Kontext, Tools und Modellpreisen. Tatsächliche Kosten können abweichen.", "input.disclaimer": "Agenten können Fehler machen. Verwenden Sie Ihr Urteilsvermögen bei kritischen Informationen.", "input.errorMsg": "Senden fehlgeschlagen: {{errorMsg}}. Versuchen Sie es erneut oder später.", "input.more": "Mehr", @@ -779,6 +786,7 @@ "upload.preview.prepareTasks": "Datei wird vorbereitet...", "upload.preview.status.pending": "Vorbereitung zum Hochladen...", "upload.preview.status.processing": "Datei wird verarbeitet...", + "upload.validation.unsupportedFileType": "Nicht unterstützter Dateityp: {{files}}. Unterstützte Bilder: JPG, PNG, GIF, WebP. Unterstützte Dokumente umfassen PDF, Word, Excel, PowerPoint, Markdown, Text, CSV, JSON und Code-Dateien.", "upload.validation.videoSizeExceeded": "Die Videodatei darf 20 MB nicht überschreiten. Aktuelle Größe: {{actualSize}}.", "viewMode.fullWidth": "Volle Breite", "viewMode.normal": "Standard", @@ -894,6 +902,8 @@ "workingPanel.localFile.closeOther": "Andere schließen", "workingPanel.localFile.closeRight": "Rechts schließen", "workingPanel.localFile.error": "Diese Datei konnte nicht geladen werden", + "workingPanel.localFile.preview.raw": "Rohdaten", + "workingPanel.localFile.preview.render": "Vorschau", "workingPanel.localFile.truncated": "Dateivorschau auf {{limit}} Zeichen gekürzt", "workingPanel.progress": "Progress", "workingPanel.progress.allCompleted": "All tasks completed", @@ -959,6 +969,8 @@ "workingPanel.review.viewMode.unified": "Zur einheitlichen Ansicht wechseln", "workingPanel.review.wordWrap.disable": "Zeilenumbruch deaktivieren", "workingPanel.review.wordWrap.enable": "Zeilenumbruch aktivieren", + "workingPanel.skills.empty": "Keine Fähigkeiten in diesem Projekt gefunden", + "workingPanel.skills.title": "Fähigkeiten", "workingPanel.space": "Leerzeichen", "workingPanel.title": "Working Panel", "you": "Du", diff --git a/locales/de-DE/components.json b/locales/de-DE/components.json index 63400f6b5f..45dc8a3ad8 100644 --- a/locales/de-DE/components.json +++ b/locales/de-DE/components.json @@ -121,6 +121,16 @@ "ModelSwitchPanel.detail.context": "Kontextlänge", "ModelSwitchPanel.detail.pricing": "Preise", "ModelSwitchPanel.detail.pricing.cachedInput": "Gecachter Input ${{amount}}/M", + "ModelSwitchPanel.detail.pricing.credits.cachedInput": "Zwischengespeicherter Eingabewert {{amount}} Credits/M Tokens", + "ModelSwitchPanel.detail.pricing.credits.image": "Credits/Bild", + "ModelSwitchPanel.detail.pricing.credits.input": "Eingabe {{amount}} Credits/M Tokens", + "ModelSwitchPanel.detail.pricing.credits.megapixel": "Credits/MP", + "ModelSwitchPanel.detail.pricing.credits.millionCharacters": "Credits/M Zeichen", + "ModelSwitchPanel.detail.pricing.credits.millionTokens": "Credits/M Tokens", + "ModelSwitchPanel.detail.pricing.credits.output": "Ausgabe {{amount}} Credits/M Tokens", + "ModelSwitchPanel.detail.pricing.credits.perImage": "~ {{amount}} Credits / Bild", + "ModelSwitchPanel.detail.pricing.credits.perVideo": "~ {{amount}} Credits / Video", + "ModelSwitchPanel.detail.pricing.credits.second": "Credits/s", "ModelSwitchPanel.detail.pricing.group.audio": "Audio", "ModelSwitchPanel.detail.pricing.group.image": "Bild", "ModelSwitchPanel.detail.pricing.group.text": "Text", diff --git a/locales/de-DE/editor.json b/locales/de-DE/editor.json index e06ca980c2..2d1f5c5ade 100644 --- a/locales/de-DE/editor.json +++ b/locales/de-DE/editor.json @@ -1,8 +1,10 @@ { "actionTag.category.command": "Befehl", + "actionTag.category.projectSkill": "Projektfertigkeit", "actionTag.category.skill": "Fähigkeit", "actionTag.category.tool": "Werkzeug", "actionTag.tooltip.command": "Führt vor dem Senden einen clientseitigen Slash-Befehl aus.", + "actionTag.tooltip.projectSkill": "Als Slash-Befehl gesendet, damit die CLI des Agenten die passende Projektfertigkeit ausführt.", "actionTag.tooltip.skill": "Lädt für diese Anfrage ein wiederverwendbares Fähigkeitspaket.", "actionTag.tooltip.tool": "Markiert ein Werkzeug, das der Benutzer für diese Anfrage ausdrücklich ausgewählt hat.", "actions.expand.off": "Einklappen", diff --git a/locales/de-DE/home.json b/locales/de-DE/home.json index cd9ed5c3e5..663c37063d 100644 --- a/locales/de-DE/home.json +++ b/locales/de-DE/home.json @@ -11,6 +11,19 @@ "brief.action.ignore": "Ignorieren", "brief.action.retry": "Erneut versuchen", "brief.addFeedback": "Feedback teilen", + "brief.agentSignal.selfReview.applied.heading": "Aktualisiert", + "brief.agentSignal.selfReview.applied.summary": "{{count}} Traumaktualisierung wurde angewendet.", + "brief.agentSignal.selfReview.applied.summary_plural": "{{count}} Traumaktualisierungen wurden angewendet.", + "brief.agentSignal.selfReview.applied.title": "Traumressourcen aktualisiert", + "brief.agentSignal.selfReview.error.heading": "Problem", + "brief.agentSignal.selfReview.error.summary": "Einige Arbeiten konnten während dieses Traums nicht abgeschlossen werden.", + "brief.agentSignal.selfReview.error.title": "Traum stieß auf ein Problem", + "brief.agentSignal.selfReview.ideas.summary": "Gespeicherte Traumnotizen für zukünftige Überprüfung.", + "brief.agentSignal.selfReview.ideas.title": "Traumnotizen", + "brief.agentSignal.selfReview.proposal.heading": "Vorschlag", + "brief.agentSignal.selfReview.proposal.summary": "{{count}} Traumvorschlag benötigt Ihre Überprüfung.", + "brief.agentSignal.selfReview.proposal.summary_plural": "{{count}} Traumvorschläge benötigen Ihre Überprüfung.", + "brief.agentSignal.selfReview.proposal.title": "Traumvorschlag benötigt Überprüfung", "brief.collapse": "Weniger anzeigen", "brief.commentPlaceholder": "Ihr Feedback teilen...", "brief.commentSubmit": "Feedback senden", diff --git a/locales/de-DE/hotkey.json b/locales/de-DE/hotkey.json index a571cd4d51..124a97da16 100644 --- a/locales/de-DE/hotkey.json +++ b/locales/de-DE/hotkey.json @@ -1,8 +1,6 @@ { "addUserMessage.desc": "Füge die aktuelle Eingabe als Benutzernachricht hinzu, ohne die Generierung auszulösen", "addUserMessage.title": "Benutzernachricht hinzufügen", - "clearCurrentMessages.desc": "Lösche die Nachrichten und hochgeladenen Dateien aus dem aktuellen Gespräch", - "clearCurrentMessages.title": "Gesprächsnachrichten löschen", "commandPalette.desc": "Öffne die globale Befehlspalette für schnellen Zugriff auf Funktionen", "commandPalette.title": "Befehlspalette", "deleteAndRegenerateMessage.desc": "Lösche die letzte Nachricht und generiere sie neu", diff --git a/locales/de-DE/models.json b/locales/de-DE/models.json index cb05accf06..8dc464f00b 100644 --- a/locales/de-DE/models.json +++ b/locales/de-DE/models.json @@ -106,7 +106,6 @@ "MiniMax-Hailuo-2.3.description": "Brandneues Videoerzeugungsmodell mit umfassenden Verbesserungen in Körperbewegung, physikalischem Realismus und Befolgung von Anweisungen.", "MiniMax-M1.description": "Ein neues Inhouse-Argumentationsmodell mit 80K Chain-of-Thought und 1M Eingabe, vergleichbar mit führenden globalen Modellen.", "MiniMax-M2-Stable.description": "Entwickelt für effizientes Coden und Agenten-Workflows mit höherer Parallelität für den kommerziellen Einsatz.", - "MiniMax-M2.1-Lightning.description": "Leistungsstarke mehrsprachige Programmierfähigkeiten mit schnellerer und effizienterer Inferenz.", "MiniMax-M2.1-highspeed.description": "Leistungsstarke mehrsprachige Programmierfähigkeiten, umfassend verbesserte Programmiererfahrung. Schneller und effizienter.", "MiniMax-M2.1.description": "MiniMax-M2.1 ist das Flaggschiff unter den Open-Source-Großmodellen von MiniMax und konzentriert sich auf die Lösung komplexer Aufgaben aus der realen Welt. Seine zentralen Stärken liegen in der mehrsprachigen Programmierfähigkeit und der Fähigkeit, als Agent komplexe Aufgaben zu bewältigen.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: Gleiche Leistung wie M2.5 mit schnellerer Inferenz.", @@ -315,13 +314,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku ist das schnellste und kompakteste Modell von Anthropic, entwickelt für nahezu sofortige Antworten mit schneller, präziser Leistung.", "claude-3-opus-20240229.description": "Claude 3 Opus ist das leistungsstärkste Modell von Anthropic für hochkomplexe Aufgaben. Es überzeugt in Leistung, Intelligenz, Sprachfluss und Verständnis.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet bietet eine ausgewogene Kombination aus Intelligenz und Geschwindigkeit für Unternehmensanwendungen. Es liefert hohe Nutzbarkeit bei geringeren Kosten und zuverlässiger Skalierbarkeit.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 ist das schnellste und intelligenteste Haiku-Modell von Anthropic, mit blitzschneller Geschwindigkeit und erweitertem Denken.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 ist das schnellste und intelligenteste Haiku-Modell von Anthropic, mit blitzschneller Geschwindigkeit und erweitertem logischen Denken.", "claude-haiku-4-5.description": "Claude Haiku 4.5 von Anthropic — Next-Gen-Haiku mit verbessertem Reasoning und Vision.", "claude-haiku-4.5.description": "Claude Haiku 4.5 ist das schnellste und intelligenteste Haiku-Modell von Anthropic, mit blitzschneller Geschwindigkeit und erweiterten Denkfähigkeiten.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking ist eine erweiterte Variante, die ihren Denkprozess offenlegen kann.", "claude-opus-4-1-20250805.description": "Claude Opus 4.1 ist das neueste und leistungsfähigste Modell von Anthropic für hochkomplexe Aufgaben, das in Leistung, Intelligenz, Sprachgewandtheit und Verständnis herausragt.", "claude-opus-4-1.description": "Claude Opus 4.1 von Anthropic — Premium-Reasoning-Modell mit tiefgehender Analysefähigkeit.", - "claude-opus-4-20250514.description": "Claude Opus 4 ist das leistungsstärkste Modell von Anthropic für hochkomplexe Aufgaben, das in Leistung, Intelligenz, Sprachgewandtheit und Verständnis herausragt.", + "claude-opus-4-20250514.description": "Claude Opus 4 ist das leistungsstärkste Modell von Anthropic für hochkomplexe Aufgaben, das in Leistung, Intelligenz, Sprachgewandtheit und Verständnis brilliert.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 ist das Flaggschiffmodell von Anthropic. Es kombiniert herausragende Intelligenz mit skalierbarer Leistung und ist ideal für komplexe Aufgaben, die höchste Qualität bei Antworten und logischem Denken erfordern.", "claude-opus-4-5.description": "Claude Opus 4.5 von Anthropic — Flaggschiffmodell mit erstklassigem Reasoning und Coding.", "claude-opus-4-6.description": "Claude Opus 4.6 von Anthropic — Flaggschiffmodell mit 1M Kontextfenster und erweitertem Reasoning.", @@ -330,7 +329,7 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 ist das intelligenteste Modell von Anthropic für die Entwicklung von Agenten und Programmierung.", "claude-opus-4.6.description": "Claude Opus 4.6 ist das intelligenteste Modell von Anthropic für die Entwicklung von Agenten und Programmierung.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking kann nahezu sofortige Antworten oder schrittweises Denken mit sichtbarem Prozess erzeugen.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 ist das bisher intelligenteste Modell von Anthropic, das nahezu sofortige Antworten oder erweitertes schrittweises Denken mit fein abgestimmter Kontrolle für API-Nutzer bietet.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 kann nahezu sofortige Antworten oder ausführliches schrittweises Denken mit sichtbarem Prozess erzeugen.", "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 ist das bisher intelligenteste Modell von Anthropic.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 von Anthropic — weiterentwickeltes Sonnet mit verbesserter Coding-Leistung.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 von Anthropic — neuestes Sonnet mit überlegener Coding- und Tool-Nutzung.", @@ -404,7 +403,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) ist ein innovatives Modell mit tiefem Sprachverständnis und Interaktionsfähigkeit.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 ist ein Next-Gen-Denkmodell mit stärkerem komplexem Denken und Chain-of-Thought für tiefgreifende Analyseaufgaben.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 ist ein Next-Gen-Modell für logisches Denken mit stärkeren Fähigkeiten für komplexes Denken und Kettenlogik.", - "deepseek-chat.description": "Kompatibilitätsalias für den DeepSeek V4 Flash-Modus ohne Denken. Geplant für die Ausmusterung — verwenden Sie stattdessen DeepSeek V4 Flash.", + "deepseek-chat.description": "Ein neues Open-Source-Modell, das allgemeine und Code-Fähigkeiten kombiniert. Es bewahrt den allgemeinen Dialog des Chat-Modells und die starken Codierungsfähigkeiten des Coder-Modells, mit besserer Präferenzabstimmung. DeepSeek-V2.5 verbessert außerdem das Schreiben und die Befolgung von Anweisungen.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B ist ein Code-Sprachmodell, trainiert auf 2 B Tokens (87 % Code, 13 % chinesisch/englischer Text). Es bietet ein 16K-Kontextfenster und Fill-in-the-Middle-Aufgaben für projektweite Codevervollständigung und Snippet-Ergänzung.", "deepseek-coder-v2.description": "DeepSeek Coder V2 ist ein Open-Source-MoE-Code-Modell mit starker Leistung bei Programmieraufgaben, vergleichbar mit GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 ist ein Open-Source-MoE-Code-Modell mit starker Leistung bei Programmieraufgaben, vergleichbar mit GPT-4 Turbo.", @@ -426,7 +425,7 @@ "deepseek-r1-fast-online.description": "DeepSeek R1 Schnellversion mit Echtzeit-Websuche – kombiniert 671B-Fähigkeiten mit schneller Reaktion.", "deepseek-r1-online.description": "DeepSeek R1 Vollversion mit 671B Parametern und Echtzeit-Websuche – bietet stärkeres Verständnis und bessere Generierung.", "deepseek-r1.description": "DeepSeek-R1 nutzt Cold-Start-Daten vor dem RL und erreicht vergleichbare Leistungen wie OpenAI-o1 bei Mathematik, Programmierung und logischem Denken.", - "deepseek-reasoner.description": "Kompatibilitätsalias für den DeepSeek V4 Flash-Denkmodus. Geplant für die Ausmusterung — verwenden Sie stattdessen DeepSeek V4 Flash.", + "deepseek-reasoner.description": "Ein DeepSeek-Logikmodell, das sich auf komplexe logische Denkaufgaben konzentriert.", "deepseek-v2.description": "DeepSeek V2 ist ein effizientes MoE-Modell für kostengünstige Verarbeitung.", "deepseek-v2:236b.description": "DeepSeek V2 236B ist das codefokussierte Modell von DeepSeek mit starker Codegenerierung.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 ist ein MoE-Modell mit 671B Parametern und herausragenden Stärken in Programmierung, technischer Kompetenz, Kontextverständnis und Langtextverarbeitung.", @@ -491,8 +490,6 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 ist ein Bildgenerierungsmodell von ByteDance Seed, das Text- und Bildeingaben unterstützt und eine hochgradig kontrollierbare, hochwertige Bildgenerierung ermöglicht. Es erzeugt Bilder aus Texteingaben.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 ist das neueste multimodale Bildmodell von ByteDance, das Text-zu-Bild, Bild-zu-Bild und Batch-Bilderzeugung integriert und dabei Allgemeinwissen und logisches Denken einbezieht. Im Vergleich zur vorherigen Version 4.0 bietet es eine deutlich verbesserte Generierungsqualität, bessere Konsistenz bei der Bearbeitung und Multi-Bild-Fusion. Es ermöglicht eine präzisere Kontrolle über visuelle Details, erzeugt kleine Texte und kleine Gesichter natürlicher und erreicht harmonischere Layouts und Farben, wodurch die Gesamtästhetik verbessert wird.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite ist das neueste Bildgenerierungsmodell von ByteDance. Erstmals integriert es Online-Retrieval-Funktionen, die es ermöglichen, Echtzeit-Webinformationen einzubeziehen und die Aktualität der generierten Bilder zu verbessern. Die Intelligenz des Modells wurde ebenfalls aufgerüstet, um komplexe Anweisungen und visuelle Inhalte präzise zu interpretieren. Darüber hinaus bietet es eine verbesserte globale Wissensabdeckung, Konsistenz bei Referenzen und Generierungsqualität in professionellen Szenarien, um den visuellen Erstellungsbedarf auf Unternehmensebene besser zu erfüllen.", - "dreamina-seedance-2-0-260128.description": "Seedance 2.0 von ByteDance ist das leistungsstärkste Videogenerierungsmodell, das multimodale Referenzvideogenerierung, Videobearbeitung, Videoerweiterung, Text-zu-Video und Bild-zu-Video mit synchronisiertem Audio unterstützt.", - "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast von ByteDance bietet die gleichen Funktionen wie Seedance 2.0 mit schnellerer Generierungsgeschwindigkeit zu einem wettbewerbsfähigeren Preis.", "emohaa.description": "Emohaa ist ein Modell für psychische Gesundheit mit professionellen Beratungsfähigkeiten, das Nutzern hilft, emotionale Probleme zu verstehen.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B ist ein quelloffenes, leichtgewichtiges Modell für lokale und individuell angepasste Bereitstellungen.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview ist ein Vorschau-Modell mit 8K Kontextlänge zur Bewertung von ERNIE 4.5.", @@ -506,6 +503,7 @@ "ernie-5.0-thinking-latest.description": "Wenxin 5.0 Thinking ist ein natives, vollmodales Flaggschiffmodell mit einheitlicher Modellierung von Text, Bild, Audio und Video. Es bietet umfassende Leistungsverbesserungen für komplexe Frage-Antwort-, Kreativ- und Agenten-Szenarien.", "ernie-5.0-thinking-preview.description": "Wenxin 5.0 Thinking Preview ist ein natives, vollmodales Flaggschiffmodell mit einheitlicher Modellierung von Text, Bild, Audio und Video. Es bietet umfassende Leistungsverbesserungen für komplexe Frage-Antwort-, Kreativ- und Agenten-Szenarien.", "ernie-5.0.description": "ERNIE 5.0, das neue Modell der ERNIE-Serie, ist ein nativ multimodales Großmodell. Es nutzt einen einheitlichen multimodalen Ansatz, um Text, Bilder, Audio und Video gemeinsam zu modellieren und umfassende multimodale Fähigkeiten bereitzustellen. Die Basisfähigkeiten wurden stark verbessert und erzielen hervorragende Ergebnisse in Benchmarks. Besonders stark ist es in multimodalem Verständnis, Befolgen von Anweisungen, kreativem Schreiben, Faktengenauigkeit, agentenbasiertem Planen und Tool-Nutzung.", + "ernie-5.1.description": "ERNIE 5.1 ist das neueste Modell der ERNIE-Serie und bietet umfassende Verbesserungen seiner grundlegenden Fähigkeiten. Es zeigt bedeutende Fortschritte in Bereichen wie Agenten, Wissensverarbeitung, logischem Denken und tiefgehender Suche. Diese Version verwendet eine entkoppelte, vollständig asynchrone Verstärkungslernarchitektur, die speziell entwickelt wurde, um zentrale Herausforderungen bei der Weiterentwicklung großer Modelle hin zu autonomer Entscheidungsfindung durch Agenten zu bewältigen, einschließlich numerischer Diskrepanzen zwischen Training und Inferenz, geringer Auslastung heterogener Rechenressourcen und globaler Probleme durch Langschwanz-Effekte. Darüber hinaus werden großskalige Agenten-Nachtrainierungstechniken eingesetzt, um die Fähigkeiten und die Generalisierungsleistung des Modells weiter zu verbessern. Durch ein dreistufiges kollaboratives Framework, das Umgebung, Experten und Fusionsprozesse umfasst, wird nicht nur die Trainingseffizienz sichergestellt, sondern auch die Stabilität und Leistung des Modells bei komplexen Aufgaben erheblich verbessert.", "ernie-char-fiction-8k-preview.description": "ERNIE Character Fiction 8K Preview ist ein Vorschau-Modell zur Charakter- und Plot-Erstellung für Funktionsbewertung und Tests.", "ernie-char-fiction-8k.description": "ERNIE Character Fiction 8K ist ein Persönlichkeitsmodell für Romane und Plot-Erstellung, geeignet für die Generierung von Langform-Geschichten.", "ernie-image-turbo.description": "ERNIE-Image ist ein Text-zu-Bild-Modell mit 8B Parametern von Baidu. Es zählt zu den besten Modellen in mehreren Benchmarks, inklusive Platz 1 (geteilt) bei SuperCLUE in China, und ist führend im Open-Source-Bereich.", @@ -517,8 +515,7 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K ist ein schnelles Denkmodell mit 32K Kontext für komplexe Schlussfolgerungen und mehrstufige Gespräche.", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview ist ein Vorschau-Modell mit Denkfähigkeit zur Bewertung und zum Testen.", "ernie-x1.1.description": "ERNIE X1.1 ist ein Vorschau-Denkmodell für Evaluierung und Tests.", - "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5, entwickelt vom ByteDance Seed-Team, unterstützt die Bearbeitung und Komposition mehrerer Bilder. Es bietet verbesserte Konsistenz von Motiven, präzise Befolgung von Anweisungen, räumliches Logikverständnis, ästhetischen Ausdruck, Posterlayout und Logodesign mit hochpräziser Text-Bild-Wiedergabe.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, entwickelt von ByteDance Seed, unterstützt Text- und Bildeingaben für hochkontrollierbare, qualitativ hochwertige Bildgenerierung aus Eingabeaufforderungen.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 ist ein Bildgenerierungsmodell von ByteDance Seed, das Text- und Bildinputs unterstützt und hochkontrollierbare, qualitativ hochwertige Bildgenerierung ermöglicht. Es erzeugt Bilder aus Textaufforderungen.", "fal-ai/flux-kontext/dev.description": "FLUX.1-Modell mit Fokus auf Bildbearbeitung, unterstützt Text- und Bildeingaben.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] akzeptiert Texte und Referenzbilder als Eingabe und ermöglicht gezielte lokale Bearbeitungen sowie komplexe globale Szenentransformationen.", "fal-ai/flux/krea.description": "Flux Krea [dev] ist ein Bildgenerierungsmodell mit ästhetischer Ausrichtung auf realistischere, natürliche Bilder.", @@ -526,8 +523,8 @@ "fal-ai/hunyuan-image/v3.description": "Ein leistungsstarkes natives multimodales Bildgenerierungsmodell.", "fal-ai/imagen4/preview.description": "Hochwertiges Bildgenerierungsmodell von Google.", "fal-ai/nano-banana.description": "Nano Banana ist das neueste, schnellste und effizienteste native multimodale Modell von Google. Es ermöglicht Bildgenerierung und -bearbeitung im Dialog.", - "fal-ai/qwen-image-edit.description": "Ein professionelles Bildbearbeitungsmodell vom Qwen-Team, das semantische und optische Bearbeitungen, präzise chinesische/englische Textbearbeitung, Stilübertragung, Drehung und mehr unterstützt.", - "fal-ai/qwen-image.description": "Ein leistungsstarkes Bildgenerierungsmodell vom Qwen-Team mit starker chinesischer Textrendering-Fähigkeit und vielfältigen visuellen Stilen.", + "fal-ai/qwen-image-edit.description": "Ein professionelles Bildbearbeitungsmodell des Qwen-Teams, das semantische und visuelle Bearbeitungen unterstützt, präzise chinesischen und englischen Text bearbeitet und hochwertige Bearbeitungen wie Stilübertragungen und Objektrotationen ermöglicht.", + "fal-ai/qwen-image.description": "Ein leistungsstarkes Bildgenerierungsmodell des Qwen-Teams mit beeindruckender chinesischer Textrendering und vielfältigen visuellen Stilen.", "flux-1-schnell.description": "Ein Text-zu-Bild-Modell mit 12 Milliarden Parametern von Black Forest Labs, das latente adversariale Diffusionsdistillation nutzt, um hochwertige Bilder in 1–4 Schritten zu erzeugen. Es konkurriert mit geschlossenen Alternativen und ist unter Apache-2.0 für persönliche, Forschungs- und kommerzielle Nutzung verfügbar.", "flux-dev.description": "Open-Source‑Bildgenerierungsmodell für Forschung und Entwicklung, effizient optimiert für nichtkommerzielle Innovationsforschung.", "flux-kontext-max.description": "Modernste kontextuelle Bildgenerierung und -bearbeitung, kombiniert Text und Bilder für präzise, kohärente Ergebnisse.", @@ -569,7 +566,7 @@ "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) ist Googles Bildgenerierungsmodell und unterstützt auch multimodale Chats.", "gemini-3-pro-preview.description": "Gemini 3 Pro ist Googles leistungsstärkstes Agenten- und Vibe-Coding-Modell. Es bietet reichhaltigere visuelle Inhalte und tiefere Interaktionen auf Basis modernster logischer Fähigkeiten.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) ist Googles schnellstes natives Bildgenerierungsmodell mit Denkunterstützung, konversationaler Bildgenerierung und -bearbeitung.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) liefert Pro-Level-Bildqualität mit Flash-Geschwindigkeit und unterstützt multimodale Chats.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) ist Googles schnellstes natives Bildgenerierungsmodell mit Unterstützung für Denken, konversationelle Bildgenerierung und -bearbeitung.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview ist Googles kosteneffizientestes multimodales Modell, optimiert für hochvolumige agentische Aufgaben, Übersetzung und Datenverarbeitung.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite ist Googles kosteneffizientestes multimodales Modell, optimiert für hochvolumige agentenbasierte Aufgaben, Übersetzungen und Datenverarbeitung.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview verbessert Gemini 3 Pro mit erweiterten Fähigkeiten für logisches Denken und unterstützt mittleres Denklevel.", @@ -734,8 +731,6 @@ "grok-4-fast-reasoning.description": "Wir freuen uns, Grok 4 Fast vorzustellen – unser neuester Fortschritt bei kosteneffizienten Denkmodellen.", "grok-4.20-0309-non-reasoning.description": "Eine Non-Reasoning-Variante für einfache Anwendungsfälle.", "grok-4.20-0309-reasoning.description": "Intelligentes, extrem schnelles Modell, das vor der Antwort aktiv denkt.", - "grok-4.20-beta-0309-non-reasoning.description": "Eine Variante ohne Denkprozesse für einfache Anwendungsfälle.", - "grok-4.20-beta-0309-reasoning.description": "Intelligentes, blitzschnelles Modell, das vor der Antwort überlegt.", "grok-4.20-multi-agent-0309.description": "Ein Team aus 4 oder 16 Agenten, hervorragend für Rechercheaufgaben. Unterstützt derzeit keine clientseitigen Tools. Unterstützt ausschließlich serverseitige xAI-Tools (z. B. X Search, Web Search Tools) und Remote-MCP-Tools.", "grok-4.3.description": "Das wahrheitssuchendste große Sprachmodell der Welt", "grok-4.description": "Das neueste Grok-Flaggschiff mit unvergleichlicher Leistung in Sprache, Mathematik und Logik — ein wahrer Alleskönner. Derzeit verweist es auf grok-4-0709; aufgrund begrenzter Ressourcen ist der Preis vorübergehend 10 % höher als der offizielle Preis und wird voraussichtlich später wieder auf den offiziellen Preis zurückkehren.", @@ -1220,8 +1215,6 @@ "qwq.description": "QwQ ist ein Schlussfolgerungsmodell aus der Qwen-Familie. Im Vergleich zu standardmäßig instruktionstunierten Modellen bietet es überlegene Denk- und Schlussfolgerungsfähigkeiten, die die Leistung bei nachgelagerten Aufgaben deutlich verbessern – insbesondere bei schwierigen Problemen. QwQ-32B ist ein mittelgroßes Modell, das mit führenden Schlussfolgerungsmodellen wie DeepSeek-R1 und o1-mini mithalten kann.", "qwq_32b.description": "Mittelgroßes Schlussfolgerungsmodell aus der Qwen-Familie. Im Vergleich zu standardmäßig instruktionstunierten Modellen steigern QwQs Denk- und Schlussfolgerungsfähigkeiten die Leistung bei nachgelagerten Aufgaben deutlich – insbesondere bei schwierigen Problemen.", "r1-1776.description": "R1-1776 ist eine nachtrainierte Variante von DeepSeek R1, die darauf ausgelegt ist, unzensierte, objektive und faktenbasierte Informationen bereitzustellen.", - "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro von ByteDance unterstützt Text-zu-Video, Bild-zu-Video (erster Frame, erster+letzter Frame) und Audiogenerierung synchronisiert mit visuellen Inhalten.", - "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite von BytePlus bietet webgestützte Generierung für Echtzeitinformationen, verbesserte Interpretation komplexer Eingabeaufforderungen und verbesserte Konsistenz von Referenzen für professionelle visuelle Kreationen.", "solar-mini-ja.description": "Solar Mini (Ja) erweitert Solar Mini mit einem Fokus auf Japanisch und behält dabei eine effiziente und starke Leistung in Englisch und Koreanisch bei.", "solar-mini.description": "Solar Mini ist ein kompaktes LLM, das GPT-3.5 übertrifft. Es bietet starke mehrsprachige Fähigkeiten in Englisch und Koreanisch und ist eine effiziente Lösung mit kleinem Ressourcenbedarf.", "solar-pro.description": "Solar Pro ist ein hochintelligentes LLM von Upstage, das auf Befolgen von Anweisungen auf einer einzelnen GPU ausgelegt ist und IFEval-Werte über 80 erreicht. Derzeit wird Englisch unterstützt; die vollständige Veröffentlichung mit erweitertem Sprachsupport und längeren Kontexten war für November 2024 geplant.", diff --git a/locales/de-DE/onboarding.json b/locales/de-DE/onboarding.json index 6170773ea2..a3fd2127a5 100644 --- a/locales/de-DE/onboarding.json +++ b/locales/de-DE/onboarding.json @@ -27,6 +27,13 @@ "agent.layout.skipConfirm.ok": "Für jetzt überspringen", "agent.layout.skipConfirm.title": "Onboarding jetzt überspringen?", "agent.layout.switchMessage": "Heute nicht so in Stimmung? Du kannst zum {{mode}} wechseln oder {{skip}}.", + "agent.layout.switchMessageClassic": "Nicht in der Stimmung heute? Du kannst zu {{mode}} wechseln.", + "agent.messenger.cta.discord": "Zu Discord hinzufügen", + "agent.messenger.cta.slack": "Zu Slack hinzufügen", + "agent.messenger.cta.telegram": "In Telegram öffnen", + "agent.messenger.subtitle": "Chatte mit deinem Agenten auf Telegram, Slack oder Discord", + "agent.messenger.telegramQrCaption": "Mit der Handykamera scannen", + "agent.messenger.title": "Nimm mich überall mit, wo du Nachrichten sendest", "agent.modeSwitch.agent": "Konversation", "agent.modeSwitch.classic": "Klassisch", "agent.modeSwitch.collapse": "Einklappen", @@ -68,6 +75,13 @@ "agent.wrapUp.confirm.content": "Ich speichere, was wir bisher besprochen haben. Sie können jederzeit zurückkommen und weiter chatten.", "agent.wrapUp.confirm.ok": "Jetzt abschließen", "agent.wrapUp.confirm.title": "Onboarding jetzt abschließen?", + "agentPicker.allCategories": "Alle", + "agentPicker.continue": "Weiter", + "agentPicker.skip": "Jetzt überspringen", + "agentPicker.subtitle": "Füge jetzt ein paar zu deiner Bibliothek hinzu – entdecke später jederzeit mehr.", + "agentPicker.title": "Lass uns ein paar Agenten zu deiner Bibliothek hinzufügen", + "agentPicker.title2": "Wähle die aus, die zu deiner Arbeitsweise passen", + "agentPicker.title3": "Du kannst später immer mehr hinzufügen – starte mit ein paar", "back": "Zurück", "finish": "Los geht’s", "interests.area.business": "Geschäft & Strategie", diff --git a/locales/de-DE/plugin.json b/locales/de-DE/plugin.json index 9f75eeb62f..13359a6fd6 100644 --- a/locales/de-DE/plugin.json +++ b/locales/de-DE/plugin.json @@ -39,8 +39,8 @@ "builtins.lobe-agent-documents.inspector.docCount": "{{count}} Dokumente", "builtins.lobe-agent-documents.inspector.opsCount": "{{count}} Vorgänge", "builtins.lobe-agent-documents.inspector.opsResult": "{{success}}/{{total}} Vorgänge", - "builtins.lobe-agent-documents.inspector.target.agent": "im Agenten", - "builtins.lobe-agent-documents.inspector.target.currentTopic": "im Thema", + "builtins.lobe-agent-documents.inspector.scope.agent": "im Agenten", + "builtins.lobe-agent-documents.inspector.scope.currentTopic": "im Thema", "builtins.lobe-agent-documents.title": "Agentendokumente", "builtins.lobe-agent-management.apiName.callAgent": "Agent anrufen", "builtins.lobe-agent-management.apiName.createAgent": "Agent erstellen", @@ -90,6 +90,14 @@ "builtins.lobe-agent.title": "Lobe-Agent", "builtins.lobe-claude-code.agent.instruction": "Anweisung", "builtins.lobe-claude-code.agent.result": "Ergebnis", + "builtins.lobe-claude-code.task.createLabel": "Aufgabe erstellen: ", + "builtins.lobe-claude-code.task.getLabel": "Aufgabe #{{taskId}} überprüfen", + "builtins.lobe-claude-code.task.listLabel": "Aufgaben auflisten", + "builtins.lobe-claude-code.task.updateCompleted": "Abgeschlossen", + "builtins.lobe-claude-code.task.updateDeleted": "Gelöscht", + "builtins.lobe-claude-code.task.updateInProgress": "Gestartet", + "builtins.lobe-claude-code.task.updateLabel": "Aufgabe #{{taskId}} aktualisieren", + "builtins.lobe-claude-code.task.updatePending": "Zurücksetzen", "builtins.lobe-claude-code.todoWrite.allDone": "Alle Aufgaben abgeschlossen", "builtins.lobe-claude-code.todoWrite.currentStep": "Aktueller Schritt", "builtins.lobe-claude-code.todoWrite.todos": "Aufgaben", diff --git a/locales/de-DE/providers.json b/locales/de-DE/providers.json index 434040880b..0741192bc7 100644 --- a/locales/de-DE/providers.json +++ b/locales/de-DE/providers.json @@ -33,7 +33,6 @@ "jina.description": "Jina AI wurde 2020 gegründet und ist ein führendes Unternehmen im Bereich Such-KI. Der Such-Stack umfasst Vektormodelle, Reranker und kleine Sprachmodelle für zuverlässige, hochwertige generative und multimodale Suchanwendungen.", "kimicodingplan.description": "Kimi Code von Moonshot AI bietet Zugriff auf Kimi-Modelle, darunter K2.5, für Coding-Aufgaben.", "lmstudio.description": "LM Studio ist eine Desktop-App zur Entwicklung und zum Experimentieren mit LLMs auf dem eigenen Computer.", - "lobehub.description": "LobeHub Cloud verwendet offizielle APIs, um auf KI-Modelle zuzugreifen, und misst die Nutzung mit Credits, die an Modell-Token gebunden sind.", "longcat.description": "LongCat ist eine Reihe von generativen KI-Großmodellen, die unabhängig von Meituan entwickelt wurden. Sie sind darauf ausgelegt, die Produktivität innerhalb des Unternehmens zu steigern und innovative Anwendungen durch eine effiziente Rechenarchitektur und starke multimodale Fähigkeiten zu ermöglichen.", "minimax.description": "MiniMax wurde 2021 gegründet und entwickelt allgemeine KI mit multimodalen Foundation-Modellen, darunter Textmodelle mit Billionen Parametern, Sprach- und Bildmodelle sowie Apps wie Hailuo AI.", "minimaxcodingplan.description": "Der MiniMax Token Plan bietet Zugriff auf MiniMax-Modelle, darunter M2.7, für Coding-Aufgaben im Rahmen eines Festpreis-Abonnements.", diff --git a/locales/de-DE/subscription.json b/locales/de-DE/subscription.json index f4c929c206..8f5cf956e4 100644 --- a/locales/de-DE/subscription.json +++ b/locales/de-DE/subscription.json @@ -55,6 +55,12 @@ "credits.autoTopUp.toggle": "Automatische Aufladung aktivieren", "credits.autoTopUp.upgradeHint": "Abonnieren Sie einen kostenpflichtigen Plan, um die automatische Aufladung zu aktivieren", "credits.autoTopUp.validation.targetMustExceedThreshold": "Das Zielguthaben muss größer als der Schwellenwert sein", + "credits.costEstimateHint.desc": "Zeigen Sie eine leichte Warnung an, bevor Sie senden, wenn die geschätzten Modellkosten Ihren Schwellenwert erreichen", + "credits.costEstimateHint.saveError": "Fehler beim Speichern der Einstellungen für die Kostenschätzungswarnung", + "credits.costEstimateHint.saveSuccess": "Einstellungen für die Kostenschätzungswarnung gespeichert", + "credits.costEstimateHint.threshold": "Warnschwelle", + "credits.costEstimateHint.title": "Kostenschätzungswarnung", + "credits.costEstimateHint.validation.threshold": "Der Schwellenwert muss größer oder gleich 0 sein", "credits.packages.auto": "Automatisch", "credits.packages.charged": "Berechnet ${{amount}}", "credits.packages.expired": "Abgelaufen", diff --git a/locales/de-DE/tool.json b/locales/de-DE/tool.json index 0581c2d2d4..f6e1c94169 100644 --- a/locales/de-DE/tool.json +++ b/locales/de-DE/tool.json @@ -40,6 +40,14 @@ "agentMarketplace.render.alreadyInLibraryTag": "Bereits in der Bibliothek", "agentMarketplace.render.alreadyInLibrary_one": "{{count}} bereits in der Bibliothek", "agentMarketplace.render.alreadyInLibrary_other": "{{count}} bereits in der Bibliothek", + "claudeCode.askUserQuestion.escape.back": "Zurück zu den Optionen", + "claudeCode.askUserQuestion.escape.enter": "Oder direkt eingeben", + "claudeCode.askUserQuestion.escape.placeholder": "Geben Sie Ihre Antwort hier ein…", + "claudeCode.askUserQuestion.multiSelectTag": "(Mehrfachauswahl)", + "claudeCode.askUserQuestion.skip": "Überspringen", + "claudeCode.askUserQuestion.submit": "Absenden", + "claudeCode.askUserQuestion.timeExpired": "Zeit abgelaufen — Option 1 für jede Frage wird verwendet.", + "claudeCode.askUserQuestion.timeRemaining": "Verbleibende Zeit: {{time}} · unbeantwortete Fragen werden bei Ablauf auf Option 1 gesetzt.", "codeInterpreter-legacy.error": "Ausführungsfehler", "codeInterpreter-legacy.executing": "Wird ausgeführt...", "codeInterpreter-legacy.files": "Dateien:", diff --git a/locales/de-DE/topic.json b/locales/de-DE/topic.json index b07105f43f..24852705a0 100644 --- a/locales/de-DE/topic.json +++ b/locales/de-DE/topic.json @@ -56,6 +56,7 @@ "renameModal.title": "Thema umbenennen", "searchPlaceholder": "Themen suchen...", "searchResultEmpty": "Keine Suchergebnisse gefunden.", + "sidebar.title": "Themen", "taskManager.agent": "Aufgaben-Agent", "taskManager.welcome": "Frag mich nach deinen Aufgaben", "temp": "Temporär", diff --git a/locales/en-US/chat.json b/locales/en-US/chat.json index e5bbe8a838..6dc274d475 100644 --- a/locales/en-US/chat.json +++ b/locales/en-US/chat.json @@ -220,6 +220,13 @@ "inbox.title": "Lobe AI", "input.addAi": "Add an AI message", "input.addUser": "Add a user message", + "input.costEstimate.creditsPerMillionTokens": "{{credits}} credits/M tokens", + "input.costEstimate.hint": "Estimated cost: ~{{credits}} credits", + "input.costEstimate.inputLabel": "Input", + "input.costEstimate.outputLabel": "Output", + "input.costEstimate.settingsLink": "Adjust warning threshold", + "input.costEstimate.tokenCount": "~{{tokens}} tokens", + "input.costEstimate.tooltip": "Estimated from current context, tools, and model pricing. Actual cost may vary.", "input.disclaimer": "Agents can make mistakes. Use your judgment for critical info.", "input.errorMsg": "Send failed: {{errorMsg}}. Retry, or send again later.", "input.more": "More", @@ -779,6 +786,7 @@ "upload.preview.prepareTasks": "Preparing chunks...", "upload.preview.status.pending": "Preparing to upload...", "upload.preview.status.processing": "Processing file...", + "upload.validation.unsupportedFileType": "Unsupported file type: {{files}}. Supported images: JPG, PNG, GIF, WebP. Supported documents include PDF, Word, Excel, PowerPoint, Markdown, text, CSV, JSON, and code files.", "upload.validation.videoSizeExceeded": "Video file size must not exceed 20MB. Current file size is {{actualSize}}.", "viewMode.fullWidth": "Full Width", "viewMode.normal": "Standard", diff --git a/locales/en-US/components.json b/locales/en-US/components.json index 3d3eb683e9..9fb5df54c2 100644 --- a/locales/en-US/components.json +++ b/locales/en-US/components.json @@ -121,6 +121,16 @@ "ModelSwitchPanel.detail.context": "Context Length", "ModelSwitchPanel.detail.pricing": "Pricing", "ModelSwitchPanel.detail.pricing.cachedInput": "Cached input ${{amount}}/M", + "ModelSwitchPanel.detail.pricing.credits.cachedInput": "Cached input {{amount}} credits/M tokens", + "ModelSwitchPanel.detail.pricing.credits.image": "credits/img", + "ModelSwitchPanel.detail.pricing.credits.input": "Input {{amount}} credits/M tokens", + "ModelSwitchPanel.detail.pricing.credits.megapixel": "credits/MP", + "ModelSwitchPanel.detail.pricing.credits.millionCharacters": "credits/M chars", + "ModelSwitchPanel.detail.pricing.credits.millionTokens": "credits/M tokens", + "ModelSwitchPanel.detail.pricing.credits.output": "Output {{amount}} credits/M tokens", + "ModelSwitchPanel.detail.pricing.credits.perImage": "~ {{amount}} credits / image", + "ModelSwitchPanel.detail.pricing.credits.perVideo": "~ {{amount}} credits / video", + "ModelSwitchPanel.detail.pricing.credits.second": "credits/s", "ModelSwitchPanel.detail.pricing.group.audio": "Audio", "ModelSwitchPanel.detail.pricing.group.image": "Image", "ModelSwitchPanel.detail.pricing.group.text": "Text", diff --git a/locales/en-US/hotkey.json b/locales/en-US/hotkey.json index 902b1555b3..515cf25f3e 100644 --- a/locales/en-US/hotkey.json +++ b/locales/en-US/hotkey.json @@ -1,8 +1,6 @@ { "addUserMessage.desc": "Add the current input as a user message without triggering generation", "addUserMessage.title": "Add a User Message", - "clearCurrentMessages.desc": "Clear the messages and uploaded files from the current conversation", - "clearCurrentMessages.title": "Clear Conversation Messages", "commandPalette.desc": "Open the global command palette for quick access to features", "commandPalette.title": "Command Palette", "deleteAndRegenerateMessage.desc": "Delete the last message and regenerate", diff --git a/locales/en-US/models.json b/locales/en-US/models.json index 3c724d77ab..59b81ecae6 100644 --- a/locales/en-US/models.json +++ b/locales/en-US/models.json @@ -106,7 +106,6 @@ "MiniMax-Hailuo-2.3.description": "Brand-new video generation model with comprehensive upgrades in body motion, physical realism, and instruction following.", "MiniMax-M1.description": "A new in-house reasoning model with 80K chain-of-thought and 1M input, delivering performance comparable to top global models.", "MiniMax-M2-Stable.description": "Built for efficient coding and agent workflows, with higher concurrency for commercial use.", - "MiniMax-M2.1-Lightning.description": "Powerful multilingual programming capabilities with faster and more efficient inference.", "MiniMax-M2.1-highspeed.description": "Powerful multilingual programming capabilities, comprehensively upgraded programming experience. Faster and more efficient.", "MiniMax-M2.1.description": "MiniMax-M2.1 is a flagship open-source large model from MiniMax, focusing on solving complex real-world tasks. Its core strengths are multi-language programming capabilities and the ability to solve complex tasks as an Agent.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: Same performance as M2.5 with faster inference.", @@ -315,13 +314,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku is Anthropic’s fastest and most compact model, designed for near-instant responses with fast, accurate performance.", "claude-3-opus-20240229.description": "Claude 3 Opus is Anthropic’s most powerful model for highly complex tasks, excelling in performance, intelligence, fluency, and comprehension.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet balances intelligence and speed for enterprise workloads, delivering high utility at lower cost and reliable large-scale deployment.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 is Anthropic's fastest and most intelligent Haiku model, with lightning speed and extended thinking.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 is Anthropic’s fastest and smartest Haiku model, with lightning speed and extended reasoning.", "claude-haiku-4-5.description": "Claude Haiku 4.5 by Anthropic — next-gen Haiku with enhanced reasoning and vision.", "claude-haiku-4.5.description": "Claude Haiku 4.5 is Anthropic’s fastest and smartest Haiku model, with lightning speed and extended reasoning.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking is an advanced variant that can reveal its reasoning process.", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1 is Anthropic's latest and most capable model for highly complex tasks, excelling in performance, intelligence, fluency, and understanding.", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1 is Anthropic’s latest and most capable model for highly complex tasks, excelling in performance, intelligence, fluency, and understanding.", "claude-opus-4-1.description": "Claude Opus 4.1 by Anthropic — premium reasoning model with deep analysis capabilities.", - "claude-opus-4-20250514.description": "Claude Opus 4 is Anthropic's most powerful model for highly complex tasks, excelling in performance, intelligence, fluency, and understanding.", + "claude-opus-4-20250514.description": "Claude Opus 4 is Anthropic’s most powerful model for highly complex tasks, excelling in performance, intelligence, fluency, and comprehension.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 is Anthropic’s flagship model, combining outstanding intelligence with scalable performance, ideal for complex tasks requiring the highest-quality responses and reasoning.", "claude-opus-4-5.description": "Claude Opus 4.5 by Anthropic — flagship model with top-tier reasoning and coding.", "claude-opus-4-6.description": "Claude Opus 4.6 by Anthropic — 1M context window flagship with advanced reasoning.", @@ -330,8 +329,8 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 is Anthropic’s most intelligent model for building agents and coding.", "claude-opus-4.6.description": "Claude Opus 4.6 is Anthropic’s most intelligent model for building agents and coding.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking can produce near-instant responses or extended step-by-step thinking with visible process.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 is Anthropic's most intelligent model to date, offering near-instant responses or extended step-by-step thinking with fine-grained control for API users.", - "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 is Anthropic's most intelligent model to date.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 can produce near-instant responses or extended step-by-step thinking with visible process.", + "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 is Anthropic’s most intelligent model to date.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 by Anthropic — improved Sonnet with enhanced coding performance.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 by Anthropic — latest Sonnet with superior coding and tool use.", "claude-sonnet-4.5.description": "Claude Sonnet 4.5 is Anthropic’s most intelligent model to date.", @@ -404,7 +403,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) is an innovative model offering deep language understanding and interaction.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 is a next-gen reasoning model with stronger complex reasoning and chain-of-thought for deep analysis tasks.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 is a next-gen reasoning model with stronger complex reasoning and chain-of-thought capabilities.", - "deepseek-chat.description": "Compatibility alias for DeepSeek V4 Flash non-thinking mode. Slated for deprecation — use DeepSeek V4 Flash instead.", + "deepseek-chat.description": "A new open-source model combining general and code abilities. It preserves the chat model’s general dialogue and the coder model’s strong coding, with better preference alignment. DeepSeek-V2.5 also improves writing and instruction following.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B is a code language model trained on 2T tokens (87% code, 13% Chinese/English text). It introduces a 16K context window and fill-in-the-middle tasks, providing project-level code completion and snippet infilling.", "deepseek-coder-v2.description": "DeepSeek Coder V2 is an open-source MoE code model that performs strongly on coding tasks, comparable to GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 is an open-source MoE code model that performs strongly on coding tasks, comparable to GPT-4 Turbo.", @@ -426,7 +425,7 @@ "deepseek-r1-fast-online.description": "DeepSeek R1 fast full version with real-time web search, combining 671B-scale capability and faster response.", "deepseek-r1-online.description": "DeepSeek R1 full version with 671B parameters and real-time web search, offering stronger understanding and generation.", "deepseek-r1.description": "DeepSeek-R1 uses cold-start data before RL and performs comparably to OpenAI-o1 on math, coding, and reasoning.", - "deepseek-reasoner.description": "Compatibility alias for DeepSeek V4 Flash thinking mode. Slated for deprecation — use DeepSeek V4 Flash instead.", + "deepseek-reasoner.description": "A DeepSeek reasoning model focused on complex logical reasoning tasks.", "deepseek-v2.description": "DeepSeek V2 is an efficient MoE model for cost-effective processing.", "deepseek-v2:236b.description": "DeepSeek V2 236B is DeepSeek’s code-focused model with strong code generation.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 is a 671B-parameter MoE model with standout strengths in programming and technical capability, context understanding, and long-text handling.", @@ -491,8 +490,6 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 is an image generation model from ByteDance Seed, supporting text and image inputs with highly controllable, high-quality image generation. It generates images from text prompts.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 is ByteDance’s latest multimodal image model, integrating text-to-image, image-to-image, and batch image generation capabilities, while incorporating commonsense and reasoning abilities. Compared to the previous 4.0 version, it delivers significantly improved generation quality, with better editing consistency and multi-image fusion. It offers more precise control over visual details, producing small text and small faces more naturally, and achieves more harmonious layout and color, enhancing overall aesthetics.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite is ByteDance’s latest image-generation model. For the first time, it integrates online retrieval capabilities, allowing it to incorporate real-time web information and enhance the timeliness of generated images. The model’s intelligence has also been upgraded, enabling precise interpretation of complex instructions and visual content. Additionally, it offers improved global knowledge coverage, reference consistency, and generation quality in professional scenarios, better meeting enterprise-level visual creation needs.", - "dreamina-seedance-2-0-260128.description": "Seedance 2.0 by ByteDance is the most powerful video generation model, supporting multimodal reference video generation, video editing, video extension, text-to-video, and image-to-video with synchronized audio.", - "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast by ByteDance offers the same capabilities as Seedance 2.0 with faster generation speeds at a more competitive price.", "emohaa.description": "Emohaa is a mental health model with professional counseling abilities to help users understand emotional issues.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B is an open-source lightweight model for local and customized deployment.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview is an 8K context preview model for evaluating ERNIE 4.5.", @@ -506,6 +503,7 @@ "ernie-5.0-thinking-latest.description": "Wenxin 5.0 Thinking is a native full-modal flagship model with unified text, image, audio, and video modeling. It delivers broad capability upgrades for complex QA, creation, and agent scenarios.", "ernie-5.0-thinking-preview.description": "Wenxin 5.0 Thinking Preview is a native full-modal flagship model with unified text, image, audio, and video modeling. It delivers broad capability upgrades for complex QA, creation, and agent scenarios.", "ernie-5.0.description": "ERNIE 5.0, the new-generation model in the ERNIE series, is a natively multimodal large model. It adopts a unified multimodal modeling approach, jointly modeling text, images, audio, and video to deliver comprehensive multimodal capabilities. Its foundational abilities have been significantly upgraded, achieving strong performance on benchmark evaluations. It particularly excels in multimodal understanding, instruction following, creative writing, factual accuracy, agent planning, and tool utilization.", + "ernie-5.1.description": "ERNIE 5.1 is the latest model in the ERNIE series, featuring comprehensive upgrades to its foundational capabilities. It demonstrates significant improvements in areas such as agents, knowledge processing, reasoning, and deep search. This release adopts a decoupled fully asynchronous reinforcement learning architecture, specifically designed to address key challenges in the evolution of large models toward autonomous agent decision-making, including training–inference numerical discrepancies, low utilization of heterogeneous computing resources, and global issues caused by long-tail effects. In addition, large-scale agent post-training techniques are employed to further enhance the model’s capabilities and generalization performance. Through a three-stage collaborative framework involving environment, expert, and fusion processes, the approach not only ensures training efficiency but also significantly improves the model’s stability and performance on complex tasks.", "ernie-char-fiction-8k-preview.description": "ERNIE Character Fiction 8K Preview is a character and plot creation model preview for feature evaluation and testing.", "ernie-char-fiction-8k.description": "ERNIE Character Fiction 8K is a persona model for novels and plot creation, suited for long-form story generation.", "ernie-image-turbo.description": "ERNIE-Image is an 8B-parameter text-to-image model developed by Baidu. It ranks among the top on multiple benchmarks, achieving a tied first place in SuperCLUE in China and leading in the open-source track.", @@ -517,8 +515,7 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K is a fast thinking model with 32K context for complex reasoning and multi-turn chat.", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview is a thinking-model preview for evaluation and testing.", "ernie-x1.1.description": "ERNIE X1.1 is a thinking-model preview for evaluation and testing.", - "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5, built by ByteDance Seed team, supports multi-image editing and composition. Features enhanced subject consistency, precise instruction following, spatial logic understanding, aesthetic expression, poster layout and logo design with high-precision text-image rendering.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, built by ByteDance Seed, supports text and image inputs for highly controllable, high-quality image generation from prompts.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 is an image generation model from ByteDance Seed, supporting text and image inputs with highly controllable, high-quality image generation. It generates images from text prompts.", "fal-ai/flux-kontext/dev.description": "FLUX.1 model focused on image editing, supporting text and image inputs.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] accepts text and reference images as input, enabling targeted local edits and complex global scene transformations.", "fal-ai/flux/krea.description": "Flux Krea [dev] is an image generation model with an aesthetic bias toward more realistic, natural images.", @@ -526,8 +523,8 @@ "fal-ai/hunyuan-image/v3.description": "A powerful native multimodal image generation model.", "fal-ai/imagen4/preview.description": "High-quality image generation model from Google.", "fal-ai/nano-banana.description": "Nano Banana is Google’s newest, fastest, and most efficient native multimodal model, enabling image generation and editing through conversation.", - "fal-ai/qwen-image-edit.description": "A professional image editing model from the Qwen team, supporting semantic and appearance edits, precise Chinese/English text editing, style transfer, rotation, and more.", - "fal-ai/qwen-image.description": "A powerful image generation model from the Qwen team with strong Chinese text rendering and diverse visual styles.", + "fal-ai/qwen-image-edit.description": "A professional image editing model from the Qwen team that supports semantic and appearance edits, precisely edits Chinese and English text, and enables high-quality edits such as style transfer and object rotation.", + "fal-ai/qwen-image.description": "A powerful image generation model from the Qwen team with impressive Chinese text rendering and diverse visual styles.", "flux-1-schnell.description": "A 12B-parameter text-to-image model from Black Forest Labs using latent adversarial diffusion distillation to generate high-quality images in 1-4 steps. It rivals closed alternatives and is released under Apache-2.0 for personal, research, and commercial use.", "flux-dev.description": "Open-source R&D image generation model, efficiently optimized for non-commercial innovation research.", "flux-kontext-max.description": "State-of-the-art contextual image generation and editing, combining text and images for precise, coherent results.", @@ -566,10 +563,10 @@ "gemini-3-flash-preview.description": "Gemini 3 Flash is the smartest model built for speed, combining cutting-edge intelligence with excellent search grounding.", "gemini-3-flash.description": "Gemini 3 Flash by Google — ultra-fast model with multimodal input support.", "gemini-3-pro-image-preview.description": "Gemini 3 Pro Image (Nano Banana Pro) is Google's image generation model that also supports multimodal dialogue.", - "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) is Google's image generation model and also supports multimodal chat.", + "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) is Google’s image generation model and also supports multimodal chat.", "gemini-3-pro-preview.description": "Gemini 3 Pro is Google’s most powerful agent and vibe-coding model, delivering richer visuals and deeper interaction on top of state-of-the-art reasoning.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) is Google's fastest native image generation model with thinking support, conversational image generation and editing.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) delivers Pro-level image quality at Flash speed with multimodal chat support.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) is Google's fastest native image generation model with thinking support, conversational image generation and editing.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview is Google's most cost-efficient multimodal model, optimized for high-volume agentic tasks, translation, and data processing.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite is Google's most cost-efficient multimodal model, optimized for high-volume agentic tasks, translation, and data processing.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview improves on Gemini 3 Pro with enhanced reasoning capabilities and adds medium thinking level support.", @@ -734,8 +731,6 @@ "grok-4-fast-reasoning.description": "We’re excited to release Grok 4 Fast, our latest progress in cost-effective reasoning models.", "grok-4.20-0309-non-reasoning.description": "A non-reasoning variant for simple use cases", "grok-4.20-0309-reasoning.description": "Intelligent, blazing-fast model that reasons before responding", - "grok-4.20-beta-0309-non-reasoning.description": "A non-reasoning variant for simple use cases", - "grok-4.20-beta-0309-reasoning.description": "Intelligent, blazing-fast model that reasons before responding", "grok-4.20-multi-agent-0309.description": "A team of 4 or 16 agents, Excels at research use cases, Does not currently support client-side tools. Only supports xAI server side tools (eg X Search, Web Search tools) and remote MCP tools.", "grok-4.3.description": "The most truth-seeking large language model in the world", "grok-4.description": "Latest Grok flagship with unmatched performance in language, math, and reasoning — a true all-rounder. Currently points to grok-4-0709; due to limited resources it is temporarily 10% higher than official pricing and is expected to return to official price later.", @@ -1220,8 +1215,6 @@ "qwq.description": "QwQ is a reasoning model in the Qwen family. Compared with standard instruction-tuned models, it brings thinking and reasoning abilities that significantly improve downstream performance, especially on hard problems. QwQ-32B is a mid-sized reasoning model that competes well with top reasoning models like DeepSeek-R1 and o1-mini.", "qwq_32b.description": "Mid-sized reasoning model in the Qwen family. Compared with standard instruction-tuned models, QwQ’s thinking and reasoning abilities significantly boost downstream performance, especially on hard problems.", "r1-1776.description": "R1-1776 is a post-trained variant of DeepSeek R1 designed to provide uncensored, unbiased factual information.", - "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro by ByteDance supports text-to-video, image-to-video (first frame, first+last frame), and audio generation synchronized with visuals.", - "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite by BytePlus features web-retrieval-augmented generation for real-time information, enhanced complex prompt interpretation, and improved reference consistency for professional visual creation.", "solar-mini-ja.description": "Solar Mini (Ja) extends Solar Mini with a focus on Japanese while maintaining efficient, strong performance in English and Korean.", "solar-mini.description": "Solar Mini is a compact LLM that outperforms GPT-3.5, with strong multilingual capability supporting English and Korean, offering an efficient small-footprint solution.", "solar-pro.description": "Solar Pro is a high-intelligence LLM from Upstage, focused on instruction following on a single GPU, with IFEval scores above 80. It currently supports English; the full release was planned for November 2024 with expanded language support and longer context.", diff --git a/locales/en-US/onboarding.json b/locales/en-US/onboarding.json index 164316ae47..a1f1f23ea7 100644 --- a/locales/en-US/onboarding.json +++ b/locales/en-US/onboarding.json @@ -28,6 +28,12 @@ "agent.layout.skipConfirm.title": "Skip onboarding for now?", "agent.layout.switchMessage": "Not feeling it today? You can switch to {{mode}} or {{skip}}.", "agent.layout.switchMessageClassic": "Not feeling it today? You can switch to {{mode}}.", + "agent.messenger.cta.discord": "Add to Discord", + "agent.messenger.cta.slack": "Add to Slack", + "agent.messenger.cta.telegram": "Open in Telegram", + "agent.messenger.subtitle": "Chat with your agent on Telegram, Slack, or Discord", + "agent.messenger.telegramQrCaption": "Scan with your phone camera", + "agent.messenger.title": "Keep me with you, wherever you message", "agent.modeSwitch.agent": "Conversational", "agent.modeSwitch.classic": "Classic", "agent.modeSwitch.collapse": "Collapse", diff --git a/locales/en-US/plugin.json b/locales/en-US/plugin.json index cbbe90effa..0b3861a7ff 100644 --- a/locales/en-US/plugin.json +++ b/locales/en-US/plugin.json @@ -39,8 +39,8 @@ "builtins.lobe-agent-documents.inspector.docCount": "{{count}} docs", "builtins.lobe-agent-documents.inspector.opsCount": "{{count}} ops", "builtins.lobe-agent-documents.inspector.opsResult": "{{success}}/{{total}} ops", - "builtins.lobe-agent-documents.inspector.target.agent": "in agent", - "builtins.lobe-agent-documents.inspector.target.currentTopic": "in topic", + "builtins.lobe-agent-documents.inspector.scope.agent": "in agent", + "builtins.lobe-agent-documents.inspector.scope.currentTopic": "in topic", "builtins.lobe-agent-documents.title": "Agent Documents", "builtins.lobe-agent-management.apiName.callAgent": "Call agent", "builtins.lobe-agent-management.apiName.createAgent": "Create agent", diff --git a/locales/en-US/providers.json b/locales/en-US/providers.json index ece32d6726..cdbf854011 100644 --- a/locales/en-US/providers.json +++ b/locales/en-US/providers.json @@ -33,7 +33,6 @@ "jina.description": "Founded in 2020, Jina AI is a leading search AI company. Its search stack includes vector models, rerankers, and small language models to build reliable, high-quality generative and multimodal search apps.", "kimicodingplan.description": "Kimi Code from Moonshot AI provides access to Kimi models including K2.5 for coding tasks.", "lmstudio.description": "LM Studio is a desktop app for developing and experimenting with LLMs on your computer.", - "lobehub.description": "LobeHub Cloud uses official APIs to access AI models and measures usage with Credits tied to model tokens.", "longcat.description": "LongCat is a series of generative AI large models independently developed by Meituan. It is designed to enhance internal enterprise productivity and enable innovative applications through an efficient computational architecture and strong multimodal capabilities.", "minimax.description": "Founded in 2021, MiniMax builds general-purpose AI with multimodal foundation models, including trillion-parameter MoE text models, speech models, and vision models, along with apps like Hailuo AI.", "minimaxcodingplan.description": "MiniMax Token Plan provides access to MiniMax models including M2.7 for coding tasks via a fixed-fee subscription.", diff --git a/locales/en-US/subscription.json b/locales/en-US/subscription.json index 392bd8750a..a042d071eb 100644 --- a/locales/en-US/subscription.json +++ b/locales/en-US/subscription.json @@ -55,6 +55,12 @@ "credits.autoTopUp.toggle": "Enable Auto Top-Up", "credits.autoTopUp.upgradeHint": "Subscribe to a paid plan to enable auto top-up", "credits.autoTopUp.validation.targetMustExceedThreshold": "Target balance must be greater than threshold", + "credits.costEstimateHint.desc": "Show a lightweight warning before sending when the estimated model cost reaches your threshold", + "credits.costEstimateHint.saveError": "Failed to save cost estimate alert settings", + "credits.costEstimateHint.saveSuccess": "Cost estimate alert settings saved", + "credits.costEstimateHint.threshold": "Warning Threshold", + "credits.costEstimateHint.title": "Cost Estimate Alert", + "credits.costEstimateHint.validation.threshold": "Threshold must be greater than or equal to 0", "credits.packages.auto": "Auto", "credits.packages.charged": "Charged ${{amount}}", "credits.packages.expired": "Expired", diff --git a/locales/es-ES/chat.json b/locales/es-ES/chat.json index a70f138d2c..5901514081 100644 --- a/locales/es-ES/chat.json +++ b/locales/es-ES/chat.json @@ -220,6 +220,13 @@ "inbox.title": "Lobe AI", "input.addAi": "Agregar mensaje de IA", "input.addUser": "Agregar mensaje de usuario", + "input.costEstimate.creditsPerMillionTokens": "{{credits}} créditos/M tokens", + "input.costEstimate.hint": "Costo estimado: ~{{credits}} créditos", + "input.costEstimate.inputLabel": "Entrada", + "input.costEstimate.outputLabel": "Salida", + "input.costEstimate.settingsLink": "Ajustar umbral de advertencia", + "input.costEstimate.tokenCount": "~{{tokens}} tokens", + "input.costEstimate.tooltip": "Estimado a partir del contexto actual, herramientas y precios del modelo. El costo real puede variar.", "input.disclaimer": "Los agentes pueden cometer errores. Usa tu criterio para información crítica.", "input.errorMsg": "Error al enviar: {{errorMsg}}. Intenta de nuevo más tarde.", "input.more": "Más", @@ -779,6 +786,7 @@ "upload.preview.prepareTasks": "Preparando fragmentos...", "upload.preview.status.pending": "Preparando para subir...", "upload.preview.status.processing": "Procesando archivo...", + "upload.validation.unsupportedFileType": "Tipo de archivo no compatible: {{files}}. Imágenes compatibles: JPG, PNG, GIF, WebP. Documentos compatibles incluyen PDF, Word, Excel, PowerPoint, Markdown, texto, CSV, JSON y archivos de código.", "upload.validation.videoSizeExceeded": "El tamaño del archivo de video no debe superar los 20MB. Tamaño actual: {{actualSize}}.", "viewMode.fullWidth": "Ancho completo", "viewMode.normal": "Estándar", @@ -894,6 +902,8 @@ "workingPanel.localFile.closeOther": "Cerrar Otros", "workingPanel.localFile.closeRight": "Cerrar a la Derecha", "workingPanel.localFile.error": "No se pudo cargar este archivo", + "workingPanel.localFile.preview.raw": "Sin procesar", + "workingPanel.localFile.preview.render": "Vista previa", "workingPanel.localFile.truncated": "Vista previa del archivo truncada a {{limit}} caracteres", "workingPanel.progress": "Progress", "workingPanel.progress.allCompleted": "All tasks completed", @@ -959,6 +969,8 @@ "workingPanel.review.viewMode.unified": "Cambiar a vista unificada", "workingPanel.review.wordWrap.disable": "Desactivar ajuste de línea", "workingPanel.review.wordWrap.enable": "Activar ajuste de línea", + "workingPanel.skills.empty": "No se encontraron habilidades en este proyecto", + "workingPanel.skills.title": "Habilidades", "workingPanel.space": "Espacio", "workingPanel.title": "Working Panel", "you": "Tú", diff --git a/locales/es-ES/components.json b/locales/es-ES/components.json index bc54c669c3..aa9d7cfc19 100644 --- a/locales/es-ES/components.json +++ b/locales/es-ES/components.json @@ -121,6 +121,16 @@ "ModelSwitchPanel.detail.context": "Longitud del contexto", "ModelSwitchPanel.detail.pricing": "Precios", "ModelSwitchPanel.detail.pricing.cachedInput": "Entrada en caché ${{amount}}/M", + "ModelSwitchPanel.detail.pricing.credits.cachedInput": "Entrada en caché {{amount}} créditos/M tokens", + "ModelSwitchPanel.detail.pricing.credits.image": "créditos/img", + "ModelSwitchPanel.detail.pricing.credits.input": "Entrada {{amount}} créditos/M tokens", + "ModelSwitchPanel.detail.pricing.credits.megapixel": "créditos/MP", + "ModelSwitchPanel.detail.pricing.credits.millionCharacters": "créditos/M caracteres", + "ModelSwitchPanel.detail.pricing.credits.millionTokens": "créditos/M tokens", + "ModelSwitchPanel.detail.pricing.credits.output": "Salida {{amount}} créditos/M tokens", + "ModelSwitchPanel.detail.pricing.credits.perImage": "~ {{amount}} créditos / imagen", + "ModelSwitchPanel.detail.pricing.credits.perVideo": "~ {{amount}} créditos / video", + "ModelSwitchPanel.detail.pricing.credits.second": "créditos/s", "ModelSwitchPanel.detail.pricing.group.audio": "Audio", "ModelSwitchPanel.detail.pricing.group.image": "Imagen", "ModelSwitchPanel.detail.pricing.group.text": "Texto", diff --git a/locales/es-ES/editor.json b/locales/es-ES/editor.json index 1e66ef7a31..94b16a46ff 100644 --- a/locales/es-ES/editor.json +++ b/locales/es-ES/editor.json @@ -1,8 +1,10 @@ { "actionTag.category.command": "Comando", + "actionTag.category.projectSkill": "Habilidad del proyecto", "actionTag.category.skill": "Habilidad", "actionTag.category.tool": "Herramienta", "actionTag.tooltip.command": "Ejecuta un comando de barra en el cliente antes de enviar.", + "actionTag.tooltip.projectSkill": "Enviado como una invocación de barra diagonal para que la CLI del agente ejecute la habilidad del proyecto correspondiente.", "actionTag.tooltip.skill": "Carga un paquete de habilidades reutilizable para esta solicitud.", "actionTag.tooltip.tool": "Marca una herramienta que el usuario seleccionó explícitamente para esta solicitud.", "actions.expand.off": "Colapsar", diff --git a/locales/es-ES/home.json b/locales/es-ES/home.json index 6482a19ae6..7f5daa4442 100644 --- a/locales/es-ES/home.json +++ b/locales/es-ES/home.json @@ -11,6 +11,19 @@ "brief.action.ignore": "Ignorar", "brief.action.retry": "Reintentar", "brief.addFeedback": "Compartir comentarios", + "brief.agentSignal.selfReview.applied.heading": "Actualizado", + "brief.agentSignal.selfReview.applied.summary": "Se aplicó {{count}} actualización de sueño.", + "brief.agentSignal.selfReview.applied.summary_plural": "Se aplicaron {{count}} actualizaciones de sueño.", + "brief.agentSignal.selfReview.applied.title": "Recursos de sueño actualizados", + "brief.agentSignal.selfReview.error.heading": "Problema", + "brief.agentSignal.selfReview.error.summary": "Algunos trabajos no pudieron completarse durante este sueño.", + "brief.agentSignal.selfReview.error.title": "El sueño encontró un problema", + "brief.agentSignal.selfReview.ideas.summary": "Notas de sueño guardadas para revisión futura.", + "brief.agentSignal.selfReview.ideas.title": "Notas de sueño", + "brief.agentSignal.selfReview.proposal.heading": "Sugerencia", + "brief.agentSignal.selfReview.proposal.summary": "{{count}} sugerencia de sueño necesita tu revisión.", + "brief.agentSignal.selfReview.proposal.summary_plural": "{{count}} sugerencias de sueño necesitan tu revisión.", + "brief.agentSignal.selfReview.proposal.title": "Sugerencia de sueño necesita revisión", "brief.collapse": "Mostrar menos", "brief.commentPlaceholder": "Comparte tus comentarios...", "brief.commentSubmit": "Enviar comentarios", diff --git a/locales/es-ES/hotkey.json b/locales/es-ES/hotkey.json index bfd714763f..a480124de2 100644 --- a/locales/es-ES/hotkey.json +++ b/locales/es-ES/hotkey.json @@ -1,8 +1,6 @@ { "addUserMessage.desc": "Agregar la entrada actual como un mensaje de usuario sin activar la generación", "addUserMessage.title": "Agregar un mensaje de usuario", - "clearCurrentMessages.desc": "Borrar los mensajes y archivos subidos de la conversación actual", - "clearCurrentMessages.title": "Borrar mensajes de la conversación", "commandPalette.desc": "Abrir el panel de comandos global para acceder rápidamente a las funciones", "commandPalette.title": "Panel de comandos", "deleteAndRegenerateMessage.desc": "Eliminar el último mensaje y regenerarlo", diff --git a/locales/es-ES/models.json b/locales/es-ES/models.json index fca8e2cdb9..a307997d5b 100644 --- a/locales/es-ES/models.json +++ b/locales/es-ES/models.json @@ -106,7 +106,6 @@ "MiniMax-Hailuo-2.3.description": "Nuevo modelo de generación de video con mejoras integrales en movimiento corporal, realismo físico y seguimiento de instrucciones.", "MiniMax-M1.description": "Nuevo modelo de razonamiento interno con 80K de cadena de pensamiento y 1M de entrada, con rendimiento comparable a los mejores modelos globales.", "MiniMax-M2-Stable.description": "Diseñado para codificación eficiente y flujos de trabajo de agentes, con mayor concurrencia para uso comercial.", - "MiniMax-M2.1-Lightning.description": "Potentes capacidades de programación multilingüe con inferencia más rápida y eficiente.", "MiniMax-M2.1-highspeed.description": "Potentes capacidades de programación multilingüe, con una experiencia de programación completamente mejorada. Más rápido y eficiente.", "MiniMax-M2.1.description": "MiniMax-M2.1 es un modelo insignia de código abierto de MiniMax, enfocado en resolver tareas complejas del mundo real. Sus principales fortalezas son sus capacidades de programación multilingüe y su habilidad para resolver tareas complejas como un Agente.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: Mismo rendimiento que M2.5 con inferencia más rápida.", @@ -315,13 +314,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku es el modelo más rápido y compacto de Anthropic, diseñado para respuestas casi instantáneas con rendimiento rápido y preciso.", "claude-3-opus-20240229.description": "Claude 3 Opus es el modelo más potente de Anthropic para tareas altamente complejas, destacando en rendimiento, inteligencia, fluidez y comprensión.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet equilibra inteligencia y velocidad para cargas de trabajo empresariales, ofreciendo alta utilidad a menor costo y despliegue confiable a gran escala.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 es el modelo Haiku más rápido e inteligente de Anthropic, con velocidad relámpago y pensamiento extendido.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 es el modelo Haiku más rápido e inteligente de Anthropic, con velocidad relámpago y razonamiento extendido.", "claude-haiku-4-5.description": "Claude Haiku 4.5 de Anthropic: Haiku de nueva generación con razonamiento y visión mejorados.", "claude-haiku-4.5.description": "Claude Haiku 4.5 es el modelo Haiku más rápido e inteligente de Anthropic, con velocidad relámpago y razonamiento extendido.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking es una variante avanzada que puede mostrar su proceso de razonamiento.", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1 es el modelo más reciente y avanzado de Anthropic para tareas altamente complejas, destacando en rendimiento, inteligencia, fluidez y comprensión.", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1 es el modelo más reciente y capaz de Anthropic para tareas altamente complejas, destacando en rendimiento, inteligencia, fluidez y comprensión.", "claude-opus-4-1.description": "Claude Opus 4.1 de Anthropic: modelo de razonamiento premium con profundas capacidades de análisis.", - "claude-opus-4-20250514.description": "Claude Opus 4 es el modelo más poderoso de Anthropic para tareas altamente complejas, destacando en rendimiento, inteligencia, fluidez y comprensión.", + "claude-opus-4-20250514.description": "Claude Opus 4 es el modelo más poderoso de Anthropic para tareas altamente complejas, sobresaliendo en rendimiento, inteligencia, fluidez y comprensión.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 es el modelo insignia de Anthropic, combinando inteligencia excepcional con rendimiento escalable, ideal para tareas complejas que requieren respuestas y razonamiento de la más alta calidad.", "claude-opus-4-5.description": "Claude Opus 4.5 de Anthropic: modelo insignia con razonamiento y programación de primer nivel.", "claude-opus-4-6.description": "Claude Opus 4.6 de Anthropic: modelo insignia con ventana de contexto de 1M y razonamiento avanzado.", @@ -330,7 +329,7 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 es el modelo más inteligente de Anthropic para construir agentes y programar.", "claude-opus-4.6.description": "Claude Opus 4.6 es el modelo más inteligente de Anthropic para construir agentes y programar.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking puede generar respuestas casi instantáneas o pensamiento paso a paso extendido con proceso visible.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 es el modelo más inteligente de Anthropic hasta la fecha, ofreciendo respuestas casi instantáneas o pensamiento paso a paso extendido con control detallado para usuarios de API.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 puede generar respuestas casi instantáneas o razonamientos detallados paso a paso con un proceso visible.", "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 es el modelo más inteligente de Anthropic hasta la fecha.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 de Anthropic: versión mejorada de Sonnet con mayor rendimiento en programación.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 de Anthropic: última versión de Sonnet con programación superior y uso avanzado de herramientas.", @@ -404,7 +403,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) es un modelo innovador que ofrece una comprensión profunda del lenguaje y una interacción avanzada.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 es un modelo de razonamiento de nueva generación con capacidades mejoradas para razonamiento complejo y cadenas de pensamiento, ideal para tareas de análisis profundo.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 es un modelo de razonamiento de próxima generación con capacidades mejoradas de razonamiento complejo y cadenas de pensamiento.", - "deepseek-chat.description": "Alias de compatibilidad para el modo sin razonamiento de DeepSeek V4 Flash. Programado para ser descontinuado: utiliza DeepSeek V4 Flash en su lugar.", + "deepseek-chat.description": "Un nuevo modelo de código abierto que combina habilidades generales y de codificación. Preserva el diálogo general del modelo de chat y la sólida capacidad de codificación del modelo de programador, con una mejor alineación de preferencias. DeepSeek-V2.5 también mejora la escritura y el seguimiento de instrucciones.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B es un modelo de lenguaje para código entrenado con 2T de tokens (87% código, 13% texto en chino/inglés). Introduce una ventana de contexto de 16K y tareas de completado intermedio, ofreciendo completado de código a nivel de proyecto y relleno de fragmentos.", "deepseek-coder-v2.description": "DeepSeek Coder V2 es un modelo de código MoE de código abierto que tiene un rendimiento sólido en tareas de programación, comparable a GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 es un modelo de código MoE de código abierto que tiene un rendimiento sólido en tareas de programación, comparable a GPT-4 Turbo.", @@ -426,7 +425,7 @@ "deepseek-r1-fast-online.description": "Versión completa rápida de DeepSeek R1 con búsqueda web en tiempo real, combinando capacidad a escala 671B y respuesta ágil.", "deepseek-r1-online.description": "Versión completa de DeepSeek R1 con 671B de parámetros y búsqueda web en tiempo real, ofreciendo mejor comprensión y generación.", "deepseek-r1.description": "DeepSeek-R1 utiliza datos de arranque en frío antes del aprendizaje por refuerzo y tiene un rendimiento comparable a OpenAI-o1 en matemáticas, programación y razonamiento.", - "deepseek-reasoner.description": "Alias de compatibilidad para el modo de razonamiento de DeepSeek V4 Flash. Programado para ser descontinuado: utiliza DeepSeek V4 Flash en su lugar.", + "deepseek-reasoner.description": "Un modelo de razonamiento DeepSeek enfocado en tareas de razonamiento lógico complejo.", "deepseek-v2.description": "DeepSeek V2 es un modelo MoE eficiente para procesamiento rentable.", "deepseek-v2:236b.description": "DeepSeek V2 236B es el modelo de DeepSeek centrado en código con fuerte generación de código.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 es un modelo MoE con 671 mil millones de parámetros, con fortalezas destacadas en programación, capacidad técnica, comprensión de contexto y manejo de textos largos.", @@ -491,8 +490,6 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 es un modelo de generación de imágenes de ByteDance Seed que admite entradas de texto e imagen con generación de imágenes de alta calidad y altamente controlable. Genera imágenes a partir de indicaciones de texto.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 es el último modelo multimodal de imágenes de ByteDance, que integra capacidades de texto a imagen, imagen a imagen y generación de imágenes por lotes, mientras incorpora sentido común y habilidades de razonamiento. En comparación con la versión 4.0 anterior, ofrece una calidad de generación significativamente mejorada, con mayor consistencia en la edición y fusión de múltiples imágenes. Proporciona un control más preciso sobre los detalles visuales, produciendo texto y rostros pequeños de manera más natural, y logra una disposición y color más armoniosos, mejorando la estética general.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite es el último modelo de generación de imágenes de ByteDance. Por primera vez, integra capacidades de recuperación en línea, permitiendo incorporar información web en tiempo real y mejorar la actualidad de las imágenes generadas. La inteligencia del modelo también ha sido mejorada, permitiendo una interpretación precisa de instrucciones complejas y contenido visual. Además, ofrece una mejor cobertura de conocimiento global, consistencia de referencia y calidad de generación en escenarios profesionales, satisfaciendo mejor las necesidades de creación visual a nivel empresarial.", - "dreamina-seedance-2-0-260128.description": "Seedance 2.0 de ByteDance es el modelo de generación de video más poderoso, compatible con generación de video multimodal de referencia, edición de video, extensión de video, texto a video e imagen a video con audio sincronizado.", - "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast de ByteDance ofrece las mismas capacidades que Seedance 2.0 con velocidades de generación más rápidas a un precio más competitivo.", "emohaa.description": "Emohaa es un modelo de salud mental con capacidades profesionales de asesoramiento para ayudar a los usuarios a comprender problemas emocionales.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B es un modelo ligero de código abierto para implementación local y personalizada.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview es un modelo de vista previa con contexto de 8K para evaluar ERNIE 4.5.", @@ -506,6 +503,7 @@ "ernie-5.0-thinking-latest.description": "Wenxin 5.0 Thinking es un modelo insignia nativo multimodal con modelado unificado de texto, imagen, audio y video. Ofrece mejoras amplias en capacidades para preguntas complejas, creación y escenarios de agentes.", "ernie-5.0-thinking-preview.description": "Wenxin 5.0 Thinking Preview es un modelo insignia nativo multimodal con modelado unificado de texto, imagen, audio y video. Ofrece mejoras amplias en capacidades para preguntas complejas, creación y escenarios de agentes.", "ernie-5.0.description": "ERNIE 5.0, el modelo de nueva generación de la serie ERNIE, es un modelo multimodal nativo. Adopta un enfoque unificado de modelado multimodal, integrando texto, imágenes, audio y vídeo para ofrecer capacidades completas. Sus habilidades fundamentales han sido significativamente mejoradas, logrando un sólido rendimiento en benchmarks. Destaca en comprensión multimodal, seguimiento de instrucciones, redacción creativa, precisión factual, planificación agéntica y uso de herramientas.", + "ernie-5.1.description": "ERNIE 5.1 es el modelo más reciente de la serie ERNIE, con mejoras integrales en sus capacidades fundamentales. Demuestra avances significativos en áreas como agentes, procesamiento de conocimiento, razonamiento y búsqueda profunda. Esta versión adopta una arquitectura de aprendizaje por refuerzo completamente asincrónica y desacoplada, diseñada específicamente para abordar desafíos clave en la evolución de modelos grandes hacia la toma de decisiones autónoma por parte de agentes, incluyendo discrepancias numéricas entre entrenamiento e inferencia, baja utilización de recursos informáticos heterogéneos y problemas globales causados por efectos de larga cola. Además, se emplean técnicas de post-entrenamiento a gran escala para agentes, lo que mejora aún más las capacidades y el rendimiento de generalización del modelo. A través de un marco colaborativo de tres etapas que involucra procesos de entorno, experto y fusión, el enfoque no solo garantiza la eficiencia del entrenamiento, sino que también mejora significativamente la estabilidad y el rendimiento del modelo en tareas complejas.", "ernie-char-fiction-8k-preview.description": "ERNIE Character Fiction 8K Preview es un modelo preliminar para la creación de personajes y tramas, diseñado para evaluación y pruebas.", "ernie-char-fiction-8k.description": "ERNIE Character Fiction 8K es un modelo de personajes para novelas y creación de tramas, ideal para generación de historias extensas.", "ernie-image-turbo.description": "ERNIE‑Image es un modelo de texto a imagen de 8B parámetros desarrollado por Baidu. Se sitúa entre los mejores en múltiples benchmarks, logrando el primer puesto compartido en SuperCLUE en China y liderando la categoría de código abierto.", @@ -517,8 +515,7 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K es un modelo de pensamiento rápido con contexto de 32K para razonamiento complejo y chat de múltiples turnos.", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview es una vista previa del modelo de pensamiento para evaluación y pruebas.", "ernie-x1.1.description": "ERNIE X1.1 es un modelo de pensamiento en vista previa para evaluación y pruebas.", - "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5, desarrollado por el equipo Seed de ByteDance, admite edición y composición de múltiples imágenes. Incluye consistencia mejorada de sujetos, seguimiento preciso de instrucciones, comprensión de lógica espacial, expresión estética, diseño de carteles y logotipos con renderizado de texto-imagen de alta precisión.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, desarrollado por ByteDance Seed, admite entradas de texto e imagen para generación de imágenes altamente controlable y de alta calidad a partir de indicaciones.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 es un modelo de generación de imágenes de ByteDance Seed, que admite entradas de texto e imagen con generación de imágenes altamente controlable y de alta calidad. Genera imágenes a partir de indicaciones de texto.", "fal-ai/flux-kontext/dev.description": "Modelo FLUX.1 centrado en la edición de imágenes, compatible con entradas de texto e imagen.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] acepta texto e imágenes de referencia como entrada, permitiendo ediciones locales dirigidas y transformaciones globales complejas de escenas.", "fal-ai/flux/krea.description": "Flux Krea [dev] es un modelo de generación de imágenes con una inclinación estética hacia imágenes más realistas y naturales.", @@ -526,8 +523,8 @@ "fal-ai/hunyuan-image/v3.description": "Un potente modelo nativo multimodal de generación de imágenes.", "fal-ai/imagen4/preview.description": "Modelo de generación de imágenes de alta calidad de Google.", "fal-ai/nano-banana.description": "Nano Banana es el modelo multimodal nativo más nuevo, rápido y eficiente de Google, que permite generación y edición de imágenes mediante conversación.", - "fal-ai/qwen-image-edit.description": "Un modelo profesional de edición de imágenes del equipo Qwen, que admite ediciones semánticas y de apariencia, edición precisa de texto en chino/inglés, transferencia de estilo, rotación y más.", - "fal-ai/qwen-image.description": "Un modelo poderoso de generación de imágenes del equipo Qwen con un fuerte renderizado de texto en chino y estilos visuales diversos.", + "fal-ai/qwen-image-edit.description": "Un modelo profesional de edición de imágenes del equipo Qwen que admite ediciones semánticas y de apariencia, edita con precisión texto en chino e inglés, y permite ediciones de alta calidad como transferencia de estilo y rotación de objetos.", + "fal-ai/qwen-image.description": "Un modelo poderoso de generación de imágenes del equipo Qwen con una impresionante representación de texto en chino y estilos visuales diversos.", "flux-1-schnell.description": "Modelo de texto a imagen con 12 mil millones de parámetros de Black Forest Labs que utiliza destilación difusiva adversarial latente para generar imágenes de alta calidad en 1 a 4 pasos. Compite con alternativas cerradas y se lanza bajo licencia Apache-2.0 para uso personal, de investigación y comercial.", "flux-dev.description": "Modelo de generación de imágenes de I+D de código abierto, optimizado de forma eficiente para investigación innovadora no comercial.", "flux-kontext-max.description": "Generación y edición de imágenes contextual de última generación, combinando texto e imágenes para resultados precisos y coherentes.", @@ -569,7 +566,7 @@ "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) es el modelo de generación de imágenes de Google y también admite chat multimodal.", "gemini-3-pro-preview.description": "Gemini 3 Pro es el agente más potente de Google y modelo de codificación emocional, que ofrece visuales más ricos e interacción más profunda sobre un razonamiento de última generación.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) es el modelo nativo de generación de imágenes más rápido de Google con soporte de pensamiento, generación conversacional de imágenes y edición.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) ofrece calidad de imagen a nivel Pro a velocidad Flash con soporte para chat multimodal.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) es el modelo nativo de generación de imágenes más rápido de Google, con soporte para razonamiento, generación conversacional de imágenes y edición.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview es el modelo multimodal más rentable de Google, optimizado para tareas agentivas de alto volumen, traducción y procesamiento de datos.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite es el modelo multimodal más eficiente en costos de Google, optimizado para tareas agentivas de alto volumen, traducción y procesamiento de datos.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview mejora las capacidades de razonamiento de Gemini 3 Pro y añade soporte para un nivel de pensamiento medio.", @@ -734,8 +731,6 @@ "grok-4-fast-reasoning.description": "Nos complace lanzar Grok 4 Fast, nuestro último avance en modelos de razonamiento rentables.", "grok-4.20-0309-non-reasoning.description": "Variante sin razonamiento para casos de uso simples.", "grok-4.20-0309-reasoning.description": "Modelo inteligente y rapidísimo que razona antes de responder.", - "grok-4.20-beta-0309-non-reasoning.description": "Una variante sin razonamiento para casos de uso simples.", - "grok-4.20-beta-0309-reasoning.description": "Modelo inteligente y ultrarrápido que razona antes de responder.", "grok-4.20-multi-agent-0309.description": "Equipo de 4 o 16 agentes. Destaca en casos de investigación. No admite herramientas del lado del cliente. Solo admite herramientas del lado del servidor de xAI (como X Search, Web Search) y herramientas MCP remotas.", "grok-4.3.description": "El modelo de lenguaje grande más orientado a la verdad en el mundo.", "grok-4.description": "Último modelo insignia de Grok con un rendimiento inigualable en lenguaje, matemáticas y razonamiento — un verdadero todoterreno. Actualmente apunta a grok-4-0709; debido a recursos limitados, tiene un precio temporalmente un 10% más alto que el oficial y se espera que regrese al precio oficial más adelante.", @@ -1220,8 +1215,6 @@ "qwq.description": "QwQ es un modelo de razonamiento de la familia Qwen. En comparación con los modelos estándar ajustados por instrucciones, ofrece capacidades de pensamiento y razonamiento que mejoran significativamente el rendimiento en tareas difíciles. QwQ-32B es un modelo de razonamiento de tamaño medio que compite con los mejores modelos como DeepSeek-R1 y o1-mini.", "qwq_32b.description": "Modelo de razonamiento de tamaño medio de la familia Qwen. En comparación con los modelos estándar ajustados por instrucciones, las capacidades de pensamiento y razonamiento de QwQ mejoran significativamente el rendimiento en tareas difíciles.", "r1-1776.description": "R1-1776 es una variante postentrenada de DeepSeek R1 diseñada para proporcionar información factual sin censura ni sesgo.", - "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro de ByteDance admite texto a video, imagen a video (primer cuadro, primer+último cuadro) y generación de audio sincronizado con visuales.", - "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite de BytePlus incluye generación aumentada con recuperación web para información en tiempo real, interpretación mejorada de indicaciones complejas y mayor consistencia de referencia para creación visual profesional.", "solar-mini-ja.description": "Solar Mini (Ja) amplía Solar Mini con un enfoque en japonés, manteniendo un rendimiento eficiente y sólido en inglés y coreano.", "solar-mini.description": "Solar Mini es un modelo LLM compacto que supera a GPT-3.5, con una sólida capacidad multilingüe compatible con inglés y coreano, ofreciendo una solución eficiente de bajo consumo.", "solar-pro.description": "Solar Pro es un LLM de alta inteligencia de Upstage, enfocado en el seguimiento de instrucciones en una sola GPU, con puntuaciones IFEval superiores a 80. Actualmente admite inglés; el lanzamiento completo estaba previsto para noviembre de 2024 con soporte de idiomas ampliado y contexto más largo.", diff --git a/locales/es-ES/onboarding.json b/locales/es-ES/onboarding.json index 229f056e87..7eeea89930 100644 --- a/locales/es-ES/onboarding.json +++ b/locales/es-ES/onboarding.json @@ -27,6 +27,13 @@ "agent.layout.skipConfirm.ok": "Omitir por ahora", "agent.layout.skipConfirm.title": "¿Omitir la configuración inicial por ahora?", "agent.layout.switchMessage": "¿No te convence hoy? Puedes cambiar a {{mode}} o {{skip}}.", + "agent.layout.switchMessageClassic": "¿No te sientes con ganas hoy? Puedes cambiar a {{mode}}.", + "agent.messenger.cta.discord": "Añadir a Discord", + "agent.messenger.cta.slack": "Añadir a Slack", + "agent.messenger.cta.telegram": "Abrir en Telegram", + "agent.messenger.subtitle": "Chatea con tu agente en Telegram, Slack o Discord", + "agent.messenger.telegramQrCaption": "Escanea con la cámara de tu teléfono", + "agent.messenger.title": "Llévame contigo, donde sea que envíes mensajes", "agent.modeSwitch.agent": "Conversacional", "agent.modeSwitch.classic": "Clásico", "agent.modeSwitch.collapse": "Colapsar", @@ -68,6 +75,13 @@ "agent.wrapUp.confirm.content": "Guardaré lo que hemos visto hasta ahora. Siempre puedes volver y seguir charlando más adelante.", "agent.wrapUp.confirm.ok": "Terminar ahora", "agent.wrapUp.confirm.title": "¿Finalizar la incorporación ahora?", + "agentPicker.allCategories": "Todos", + "agentPicker.continue": "Continuar", + "agentPicker.skip": "Saltar por ahora", + "agentPicker.subtitle": "Añade algunos a tu biblioteca ahora — descubre más en cualquier momento después.", + "agentPicker.title": "Añadamos algunos agentes a tu biblioteca", + "agentPicker.title2": "Elige los que se adapten a tu forma de trabajar", + "agentPicker.title3": "Siempre puedes añadir más después — empieza con algunos", "back": "Volver", "finish": "Comenzar", "interests.area.business": "Negocios y Estrategia", diff --git a/locales/es-ES/plugin.json b/locales/es-ES/plugin.json index 39e2724f68..74f96aac22 100644 --- a/locales/es-ES/plugin.json +++ b/locales/es-ES/plugin.json @@ -39,8 +39,8 @@ "builtins.lobe-agent-documents.inspector.docCount": "{{count}} documentos", "builtins.lobe-agent-documents.inspector.opsCount": "{{count}} operaciones", "builtins.lobe-agent-documents.inspector.opsResult": "{{success}}/{{total}} operaciones", - "builtins.lobe-agent-documents.inspector.target.agent": "en agente", - "builtins.lobe-agent-documents.inspector.target.currentTopic": "en tema", + "builtins.lobe-agent-documents.inspector.scope.agent": "en agente", + "builtins.lobe-agent-documents.inspector.scope.currentTopic": "en tema", "builtins.lobe-agent-documents.title": "Documentos del Agente", "builtins.lobe-agent-management.apiName.callAgent": "Agente de llamada", "builtins.lobe-agent-management.apiName.createAgent": "Crear agente", @@ -90,6 +90,14 @@ "builtins.lobe-agent.title": "Agente Lobe", "builtins.lobe-claude-code.agent.instruction": "Instrucción", "builtins.lobe-claude-code.agent.result": "Resultado", + "builtins.lobe-claude-code.task.createLabel": "Creando tarea: ", + "builtins.lobe-claude-code.task.getLabel": "Inspeccionando tarea #{{taskId}}", + "builtins.lobe-claude-code.task.listLabel": "Listando tareas", + "builtins.lobe-claude-code.task.updateCompleted": "Completado", + "builtins.lobe-claude-code.task.updateDeleted": "Eliminado", + "builtins.lobe-claude-code.task.updateInProgress": "Iniciado", + "builtins.lobe-claude-code.task.updateLabel": "Actualizando tarea #{{taskId}}", + "builtins.lobe-claude-code.task.updatePending": "Restablecer", "builtins.lobe-claude-code.todoWrite.allDone": "Todas las tareas completadas", "builtins.lobe-claude-code.todoWrite.currentStep": "Paso actual", "builtins.lobe-claude-code.todoWrite.todos": "Tareas", diff --git a/locales/es-ES/providers.json b/locales/es-ES/providers.json index 310682cd8a..c4ef10fb49 100644 --- a/locales/es-ES/providers.json +++ b/locales/es-ES/providers.json @@ -33,7 +33,6 @@ "jina.description": "Fundada en 2020, Jina AI es una empresa líder en búsqueda con IA. Su pila de búsqueda incluye modelos vectoriales, reordenadores y pequeños modelos de lenguaje para construir aplicaciones generativas y multimodales confiables y de alta calidad.", "kimicodingplan.description": "Kimi Code de Moonshot AI proporciona acceso a los modelos Kimi, incluidos K2.5, para tareas de codificación.", "lmstudio.description": "LM Studio es una aplicación de escritorio para desarrollar y experimentar con LLMs en tu ordenador.", - "lobehub.description": "LobeHub Cloud utiliza APIs oficiales para acceder a modelos de IA y mide el uso con Créditos vinculados a los tokens del modelo.", "longcat.description": "LongCat es una serie de modelos grandes de inteligencia artificial generativa desarrollados de manera independiente por Meituan. Está diseñado para mejorar la productividad interna de la empresa y permitir aplicaciones innovadoras mediante una arquitectura computacional eficiente y sólidas capacidades multimodales.", "minimax.description": "Fundada en 2021, MiniMax desarrolla IA de propósito general con modelos fundacionales multimodales, incluyendo modelos de texto MoE con billones de parámetros, modelos de voz y visión, junto con aplicaciones como Hailuo AI.", "minimaxcodingplan.description": "El Plan de Tokens MiniMax proporciona acceso a los modelos MiniMax, incluidos M2.7, para tareas de codificación mediante una suscripción de tarifa fija.", diff --git a/locales/es-ES/subscription.json b/locales/es-ES/subscription.json index 9ca7e5c7be..56d5215633 100644 --- a/locales/es-ES/subscription.json +++ b/locales/es-ES/subscription.json @@ -55,6 +55,12 @@ "credits.autoTopUp.toggle": "Activar Recarga Automática", "credits.autoTopUp.upgradeHint": "Suscríbete a un plan de pago para habilitar la recarga automática", "credits.autoTopUp.validation.targetMustExceedThreshold": "El saldo objetivo debe ser mayor que el umbral", + "credits.costEstimateHint.desc": "Muestra una advertencia ligera antes de enviar cuando el costo estimado del modelo alcanza tu umbral", + "credits.costEstimateHint.saveError": "No se pudieron guardar las configuraciones de alerta de estimación de costos", + "credits.costEstimateHint.saveSuccess": "Configuraciones de alerta de estimación de costos guardadas", + "credits.costEstimateHint.threshold": "Umbral de Advertencia", + "credits.costEstimateHint.title": "Alerta de Estimación de Costos", + "credits.costEstimateHint.validation.threshold": "El umbral debe ser mayor o igual a 0", "credits.packages.auto": "Automático", "credits.packages.charged": "Cargado ${{amount}}", "credits.packages.expired": "Expirado", diff --git a/locales/es-ES/tool.json b/locales/es-ES/tool.json index 8ecbf2336b..ca7f71e7be 100644 --- a/locales/es-ES/tool.json +++ b/locales/es-ES/tool.json @@ -40,6 +40,14 @@ "agentMarketplace.render.alreadyInLibraryTag": "Ya en la biblioteca", "agentMarketplace.render.alreadyInLibrary_one": "{{count}} ya en la biblioteca", "agentMarketplace.render.alreadyInLibrary_other": "{{count}} ya en la biblioteca", + "claudeCode.askUserQuestion.escape.back": "Volver a las opciones", + "claudeCode.askUserQuestion.escape.enter": "O escribe directamente", + "claudeCode.askUserQuestion.escape.placeholder": "Escribe tu respuesta aquí…", + "claudeCode.askUserQuestion.multiSelectTag": "(selección múltiple)", + "claudeCode.askUserQuestion.skip": "Omitir", + "claudeCode.askUserQuestion.submit": "Enviar", + "claudeCode.askUserQuestion.timeExpired": "Tiempo expirado — usando la opción 1 de cada pregunta.", + "claudeCode.askUserQuestion.timeRemaining": "Tiempo restante: {{time}} · las preguntas sin respuesta se asignan a la opción 1 al agotarse el tiempo.", "codeInterpreter-legacy.error": "Error de Ejecución", "codeInterpreter-legacy.executing": "Ejecutando...", "codeInterpreter-legacy.files": "Archivos:", diff --git a/locales/es-ES/topic.json b/locales/es-ES/topic.json index 08209df959..e2d1c4a35a 100644 --- a/locales/es-ES/topic.json +++ b/locales/es-ES/topic.json @@ -56,6 +56,7 @@ "renameModal.title": "Renombrar tema", "searchPlaceholder": "Buscar temas...", "searchResultEmpty": "No se encontraron resultados de búsqueda.", + "sidebar.title": "Temas", "taskManager.agent": "Agente de Tareas", "taskManager.welcome": "Pregúntame sobre tus tareas", "temp": "Temporal", diff --git a/locales/fa-IR/chat.json b/locales/fa-IR/chat.json index 975c8e4e61..4028aecdb6 100644 --- a/locales/fa-IR/chat.json +++ b/locales/fa-IR/chat.json @@ -220,6 +220,13 @@ "inbox.title": "Lobe AI", "input.addAi": "افزودن پیام هوش مصنوعی", "input.addUser": "افزودن پیام کاربر", + "input.costEstimate.creditsPerMillionTokens": "{{credits}} اعتبار/میلیون توکن", + "input.costEstimate.hint": "هزینه تخمینی: ~{{credits}} اعتبار", + "input.costEstimate.inputLabel": "ورودی", + "input.costEstimate.outputLabel": "خروجی", + "input.costEstimate.settingsLink": "تنظیم آستانه هشدار", + "input.costEstimate.tokenCount": "~{{tokens}} توکن", + "input.costEstimate.tooltip": "تخمین زده شده بر اساس زمینه فعلی، ابزارها و قیمت‌گذاری مدل. هزینه واقعی ممکن است متفاوت باشد.", "input.disclaimer": "عوامل ممکن است اشتباه کنند. برای اطلاعات حساس از قضاوت خود استفاده کنید.", "input.errorMsg": "ارسال ناموفق: {{errorMsg}}. دوباره تلاش کنید یا بعداً ارسال نمایید.", "input.more": "بیشتر", @@ -779,6 +786,7 @@ "upload.preview.prepareTasks": "در حال آماده‌سازی بخش‌ها...", "upload.preview.status.pending": "در حال آماده‌سازی برای بارگذاری...", "upload.preview.status.processing": "در حال پردازش فایل...", + "upload.validation.unsupportedFileType": "نوع فایل پشتیبانی نشده: {{files}}. تصاویر پشتیبانی شده: JPG، PNG، GIF، WebP. اسناد پشتیبانی شده شامل PDF، Word، Excel، PowerPoint، Markdown، متن، CSV، JSON و فایل‌های کد هستند.", "upload.validation.videoSizeExceeded": "حجم فایل ویدیو نباید بیش از ۲۰ مگابایت باشد. حجم فعلی: {{actualSize}}.", "viewMode.fullWidth": "تمام‌عرض", "viewMode.normal": "استاندارد", @@ -894,6 +902,8 @@ "workingPanel.localFile.closeOther": "بستن سایرین", "workingPanel.localFile.closeRight": "بستن به سمت راست", "workingPanel.localFile.error": "بارگذاری این فایل ممکن نیست", + "workingPanel.localFile.preview.raw": "خام", + "workingPanel.localFile.preview.render": "پیش‌نمایش", "workingPanel.localFile.truncated": "پیش‌نمایش فایل به {{limit}} کاراکتر محدود شده است", "workingPanel.progress": "Progress", "workingPanel.progress.allCompleted": "All tasks completed", @@ -959,6 +969,8 @@ "workingPanel.review.viewMode.unified": "تغییر به نمای یکپارچه", "workingPanel.review.wordWrap.disable": "غیرفعال کردن پیچش کلمات", "workingPanel.review.wordWrap.enable": "فعال کردن پیچش کلمات", + "workingPanel.skills.empty": "هیچ مهارتی در این پروژه یافت نشد", + "workingPanel.skills.title": "مهارت‌ها", "workingPanel.space": "فضا", "workingPanel.title": "Working Panel", "you": "شما", diff --git a/locales/fa-IR/components.json b/locales/fa-IR/components.json index 598b0d0ed7..820cd1a2de 100644 --- a/locales/fa-IR/components.json +++ b/locales/fa-IR/components.json @@ -121,6 +121,16 @@ "ModelSwitchPanel.detail.context": "طول زمینه", "ModelSwitchPanel.detail.pricing": "قیمت‌گذاری", "ModelSwitchPanel.detail.pricing.cachedInput": "ورودی کش‌شده ${{amount}}/میلیون", + "ModelSwitchPanel.detail.pricing.credits.cachedInput": "ورودی ذخیره‌شده {{amount}} اعتبار/میلیون توکن", + "ModelSwitchPanel.detail.pricing.credits.image": "اعتبار/تصویر", + "ModelSwitchPanel.detail.pricing.credits.input": "ورودی {{amount}} اعتبار/میلیون توکن", + "ModelSwitchPanel.detail.pricing.credits.megapixel": "اعتبار/مگاپیکسل", + "ModelSwitchPanel.detail.pricing.credits.millionCharacters": "اعتبار/میلیون کاراکتر", + "ModelSwitchPanel.detail.pricing.credits.millionTokens": "اعتبار/میلیون توکن", + "ModelSwitchPanel.detail.pricing.credits.output": "خروجی {{amount}} اعتبار/میلیون توکن", + "ModelSwitchPanel.detail.pricing.credits.perImage": "~ {{amount}} اعتبار / تصویر", + "ModelSwitchPanel.detail.pricing.credits.perVideo": "~ {{amount}} اعتبار / ویدیو", + "ModelSwitchPanel.detail.pricing.credits.second": "اعتبار/ثانیه", "ModelSwitchPanel.detail.pricing.group.audio": "صوت", "ModelSwitchPanel.detail.pricing.group.image": "تصویر", "ModelSwitchPanel.detail.pricing.group.text": "متن", diff --git a/locales/fa-IR/editor.json b/locales/fa-IR/editor.json index 9091a707d8..0877e16335 100644 --- a/locales/fa-IR/editor.json +++ b/locales/fa-IR/editor.json @@ -1,8 +1,10 @@ { "actionTag.category.command": "دستور", + "actionTag.category.projectSkill": "مهارت پروژه", "actionTag.category.skill": "مهارت", "actionTag.category.tool": "ابزار", "actionTag.tooltip.command": "پیش از ارسال، یک دستور اسلش سمت کلاینت را اجرا می‌کند.", + "actionTag.tooltip.projectSkill": "به عنوان یک فراخوانی اسلش ارسال می‌شود تا CLI عامل مهارت پروژه مطابقت‌یافته را اجرا کند.", "actionTag.tooltip.skill": "برای این درخواست، یک بسته مهارت قابل استفاده مجدد را بارگذاری می‌کند.", "actionTag.tooltip.tool": "ابزاری را که کاربر به‌طور صریح برای این درخواست انتخاب کرده است علامت‌گذاری می‌کند.", "actions.expand.off": "جمع کردن", diff --git a/locales/fa-IR/home.json b/locales/fa-IR/home.json index 93b12e538f..79bbacaf56 100644 --- a/locales/fa-IR/home.json +++ b/locales/fa-IR/home.json @@ -11,6 +11,19 @@ "brief.action.ignore": "نادیده گرفتن", "brief.action.retry": "تلاش دوباره", "brief.addFeedback": "اشتراک‌گذاری بازخورد", + "brief.agentSignal.selfReview.applied.heading": "به‌روزرسانی شد", + "brief.agentSignal.selfReview.applied.summary": "{{count}} به‌روزرسانی رویا اعمال شد.", + "brief.agentSignal.selfReview.applied.summary_plural": "{{count}} به‌روزرسانی‌های رویا اعمال شدند.", + "brief.agentSignal.selfReview.applied.title": "منابع به‌روزرسانی شده رویا", + "brief.agentSignal.selfReview.error.heading": "مشکل", + "brief.agentSignal.selfReview.error.summary": "برخی کارها در طول این رویا کامل نشدند.", + "brief.agentSignal.selfReview.error.title": "رویا با مشکلی مواجه شد", + "brief.agentSignal.selfReview.ideas.summary": "یادداشت‌های رویا برای بررسی‌های آینده ذخیره شدند.", + "brief.agentSignal.selfReview.ideas.title": "یادداشت‌های رویا", + "brief.agentSignal.selfReview.proposal.heading": "پیشنهاد", + "brief.agentSignal.selfReview.proposal.summary": "{{count}} پیشنهاد رویا نیاز به بررسی شما دارد.", + "brief.agentSignal.selfReview.proposal.summary_plural": "{{count}} پیشنهادهای رویا نیاز به بررسی شما دارند.", + "brief.agentSignal.selfReview.proposal.title": "پیشنهاد رویا نیاز به بررسی دارد", "brief.collapse": "نمایش کمتر", "brief.commentPlaceholder": "بازخورد خود را بنویسید...", "brief.commentSubmit": "ارسال بازخورد", diff --git a/locales/fa-IR/hotkey.json b/locales/fa-IR/hotkey.json index dd7773018a..373262fddc 100644 --- a/locales/fa-IR/hotkey.json +++ b/locales/fa-IR/hotkey.json @@ -1,8 +1,6 @@ { "addUserMessage.desc": "افزودن ورودی فعلی به‌عنوان پیام کاربر بدون شروع تولید پاسخ", "addUserMessage.title": "افزودن پیام کاربر", - "clearCurrentMessages.desc": "پاک‌سازی پیام‌ها و فایل‌های بارگذاری‌شده از گفت‌وگوی فعلی", - "clearCurrentMessages.title": "پاک‌سازی پیام‌های گفت‌وگو", "commandPalette.desc": "باز کردن پنل فرمان جهانی برای دسترسی سریع به قابلیت‌ها", "commandPalette.title": "پنل فرمان", "deleteAndRegenerateMessage.desc": "حذف آخرین پیام و تولید مجدد آن", diff --git a/locales/fa-IR/models.json b/locales/fa-IR/models.json index 368e8ede83..be44cdc9da 100644 --- a/locales/fa-IR/models.json +++ b/locales/fa-IR/models.json @@ -106,7 +106,6 @@ "MiniMax-Hailuo-2.3.description": "مدل جدید تولید ویدئو با ارتقاهای جامع در حرکت بدن، واقع‌گرایی فیزیکی و پیروی از دستورالعمل‌ها.", "MiniMax-M1.description": "یک مدل استدلالی داخلی جدید با ۸۰ هزار زنجیره تفکر و ورودی ۱ میلیون توکن، با عملکردی در سطح مدل‌های برتر جهانی.", "MiniMax-M2-Stable.description": "طراحی‌شده برای کدنویسی کارآمد و جریان‌های کاری عامل‌محور، با هم‌زمانی بالاتر برای استفاده تجاری.", - "MiniMax-M2.1-Lightning.description": "قابلیت‌های برنامه‌نویسی چندزبانه قدرتمند با استنتاج سریع‌تر و کارآمدتر.", "MiniMax-M2.1-highspeed.description": "قابلیت‌های برنامه‌نویسی چندزبانه قدرتمند، تجربه برنامه‌نویسی کاملاً ارتقاء یافته. سریع‌تر و کارآمدتر.", "MiniMax-M2.1.description": "MiniMax-M2.1 یک مدل بزرگ متن‌باز پیشرفته از MiniMax است که بر حل وظایف پیچیده دنیای واقعی تمرکز دارد. نقاط قوت اصلی آن شامل توانایی برنامه‌نویسی چندزبانه و قابلیت عمل به‌عنوان یک عامل هوشمند برای حل مسائل پیچیده است.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: همان عملکرد M2.5 با استنتاج سریع‌تر.", @@ -315,13 +314,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku سریع‌ترین و فشرده‌ترین مدل Anthropic است که برای پاسخ‌های تقریباً فوری با عملکرد سریع و دقیق طراحی شده است.", "claude-3-opus-20240229.description": "Claude 3 Opus قدرتمندترین مدل Anthropic برای وظایف بسیار پیچیده است که در عملکرد، هوش، روانی و درک زبان برتری دارد.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet تعادل بین هوش و سرعت را برای بارهای کاری سازمانی برقرار می‌کند و با هزینه کمتر، بهره‌وری بالا و استقرار قابل اعتماد در مقیاس وسیع را ارائه می‌دهد.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 سریع‌ترین و هوشمندترین مدل هایکو Anthropic است، با سرعت فوق‌العاده و توانایی تفکر گسترده.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 سریع‌ترین و هوشمندترین مدل هایکو Anthropic است که با سرعتی برق‌آسا و توانایی استدلال پیشرفته عمل می‌کند.", "claude-haiku-4-5.description": "Claude Haiku 4.5 از Anthropic — نسل جدید Haiku با استدلال و پردازش تصویری پیشرفته.", "claude-haiku-4.5.description": "Claude Haiku 4.5 سریع‌ترین و هوشمندترین مدل Haiku از Anthropic است که با سرعت برق‌آسا و توانایی استدلال پیشرفته ارائه می‌شود.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking یک نسخه پیشرفته است که می‌تواند فرآیند استدلال خود را آشکار کند.", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1 جدیدترین و توانمندترین مدل Anthropic برای وظایف بسیار پیچیده است که در عملکرد، هوش، روانی و درک برتری دارد.", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1 جدیدترین و توانمندترین مدل Anthropic برای انجام وظایف بسیار پیچیده است که در عملکرد، هوش، روانی و درک برتری دارد.", "claude-opus-4-1.description": "Claude Opus 4.1 از Anthropic — مدل استدلال سطح‌بالا با توانایی تحلیل عمیق.", - "claude-opus-4-20250514.description": "Claude Opus 4 قدرتمندترین مدل Anthropic برای وظایف بسیار پیچیده است که در عملکرد، هوش، روانی و درک برتری دارد.", + "claude-opus-4-20250514.description": "Claude Opus 4 قدرتمندترین مدل Anthropic برای انجام وظایف بسیار پیچیده است که در عملکرد، هوش، روانی و فهم برتری دارد.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 مدل پرچم‌دار Anthropic است که هوش برجسته را با عملکرد مقیاس‌پذیر ترکیب می‌کند و برای وظایف پیچیده‌ای که نیاز به پاسخ‌های باکیفیت و استدلال دارند، ایده‌آل است.", "claude-opus-4-5.description": "Claude Opus 4.5 از Anthropic — مدل پرچم‌دار با استدلال و کدنویسی سطح‌بالا.", "claude-opus-4-6.description": "Claude Opus 4.6 از Anthropic — مدل پرچم‌دار با پنجره زمینه ۱ میلیون و توانایی استدلال پیشرفته.", @@ -330,7 +329,7 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 هوشمندترین مدل Anthropic برای ساخت عوامل و کدنویسی است.", "claude-opus-4.6.description": "Claude Opus 4.6 هوشمندترین مدل Anthropic برای ساخت عوامل و کدنویسی است.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking می‌تواند پاسخ‌های تقریباً فوری یا تفکر گام‌به‌گام طولانی با فرآیند قابل مشاهده تولید کند.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 هوشمندترین مدل Anthropic تا به امروز است که پاسخ‌های تقریباً فوری یا تفکر گام‌به‌گام گسترده با کنترل دقیق برای کاربران API ارائه می‌دهد.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 می‌تواند پاسخ‌های تقریباً فوری یا تفکر گام‌به‌گام گسترده با فرآیند قابل مشاهده تولید کند.", "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 هوشمندترین مدل Anthropic تا به امروز است.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 از Anthropic — نسخه بهبود‌یافته Sonnet با عملکرد بهتر در کدنویسی.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 از Anthropic — جدیدترین Sonnet با کدنویسی برتر و استفاده بهتر از ابزار.", @@ -404,7 +403,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) یک مدل نوآورانه با درک عمیق زبان و تعامل است.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 یک مدل استدلال نسل بعدی با توانایی استدلال پیچیده و زنجیره تفکر برای وظایف تحلیلی عمیق است.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 یک مدل استدلال نسل بعدی با قابلیت‌های استدلال پیچیده‌تر و زنجیره‌ای از تفکر است.", - "deepseek-chat.description": "نام مستعار سازگار برای حالت غیرتفکری DeepSeek V4 Flash. برنامه‌ریزی شده برای توقف استفاده — به جای آن از DeepSeek V4 Flash استفاده کنید.", + "deepseek-chat.description": "یک مدل متن‌باز جدید که توانایی‌های عمومی و کدنویسی را ترکیب می‌کند. این مدل گفتگوی عمومی مدل چت و کدنویسی قوی مدل کدنویس را حفظ کرده و با هم‌ترازی بهتر ترجیحات همراه است. DeepSeek-V2.5 همچنین نوشتن و پیروی از دستورالعمل‌ها را بهبود می‌بخشد.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B یک مدل زبان برنامه‌نویسی است که با ۲ تریلیون توکن (۸۷٪ کد، ۱۳٪ متن چینی/انگلیسی) آموزش دیده است. این مدل دارای پنجره متنی ۱۶K و وظایف تکمیل در میانه است که تکمیل کد در سطح پروژه و پر کردن قطعات کد را فراهم می‌کند.", "deepseek-coder-v2.description": "DeepSeek Coder V2 یک مدل کدنویسی MoE متن‌باز است که در وظایف برنامه‌نویسی عملکردی هم‌سطح با GPT-4 Turbo دارد.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 یک مدل کدنویسی MoE متن‌باز است که در وظایف برنامه‌نویسی عملکردی هم‌سطح با GPT-4 Turbo دارد.", @@ -426,7 +425,7 @@ "deepseek-r1-fast-online.description": "نسخه کامل سریع DeepSeek R1 با جستجوی وب در زمان واقعی که توانایی در مقیاس ۶۷۱B را با پاسخ‌دهی سریع‌تر ترکیب می‌کند.", "deepseek-r1-online.description": "نسخه کامل DeepSeek R1 با ۶۷۱ میلیارد پارامتر و جستجوی وب در زمان واقعی که درک و تولید قوی‌تری را ارائه می‌دهد.", "deepseek-r1.description": "DeepSeek-R1 پیش از یادگیری تقویتی از داده‌های شروع سرد استفاده می‌کند و در وظایف ریاضی، کدنویسی و استدلال عملکردی هم‌سطح با OpenAI-o1 دارد.", - "deepseek-reasoner.description": "نام مستعار سازگار برای حالت تفکری DeepSeek V4 Flash. برنامه‌ریزی شده برای توقف استفاده — به جای آن از DeepSeek V4 Flash استفاده کنید.", + "deepseek-reasoner.description": "یک مدل استدلال DeepSeek که بر وظایف پیچیده استدلال منطقی متمرکز است.", "deepseek-v2.description": "DeepSeek V2 یک مدل MoE کارآمد است که پردازش مقرون‌به‌صرفه را امکان‌پذیر می‌سازد.", "deepseek-v2:236b.description": "DeepSeek V2 236B مدل متمرکز بر کدنویسی DeepSeek است که توانایی بالایی در تولید کد دارد.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 یک مدل MoE با ۶۷۱ میلیارد پارامتر است که در برنامه‌نویسی، توانایی‌های فنی، درک زمینه و پردازش متون بلند عملکرد برجسته‌ای دارد.", @@ -491,8 +490,6 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 یک مدل تولید تصویر از ByteDance Seed است که از ورودی‌های متن و تصویر پشتیبانی می‌کند و تولید تصویر با کیفیت بالا و قابل کنترل را ارائه می‌دهد. این مدل تصاویر را از دستورات متنی تولید می‌کند.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 جدیدترین مدل چندوجهی تصویر ByteDance است که قابلیت‌های تبدیل متن به تصویر، تصویر به تصویر و تولید دسته‌ای تصاویر را ادغام می‌کند و توانایی‌های استدلال و دانش عمومی را نیز در بر می‌گیرد. در مقایسه با نسخه قبلی 4.0، کیفیت تولید به‌طور قابل‌توجهی بهبود یافته است، با سازگاری بهتر در ویرایش و ترکیب چند تصویر. کنترل دقیق‌تری بر جزئیات بصری ارائه می‌دهد، متن‌های کوچک و چهره‌های کوچک را به‌طور طبیعی‌تر تولید می‌کند و به هماهنگی بهتر در چیدمان و رنگ دست می‌یابد، که زیبایی کلی را افزایش می‌دهد.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite جدیدترین مدل تولید تصویر ByteDance است. برای اولین بار، قابلیت‌های بازیابی آنلاین را ادغام کرده است که به آن امکان می‌دهد اطلاعات وب لحظه‌ای را وارد کند و به‌موقع بودن تصاویر تولید شده را افزایش دهد. هوش مدل نیز ارتقا یافته است، که تفسیر دقیق دستورالعمل‌های پیچیده و محتوای بصری را امکان‌پذیر می‌کند. علاوه بر این، پوشش دانش جهانی، سازگاری مرجع و کیفیت تولید در سناریوهای حرفه‌ای بهبود یافته است، که نیازهای خلق بصری در سطح سازمانی را بهتر برآورده می‌کند.", - "dreamina-seedance-2-0-260128.description": "Seedance 2.0 توسط ByteDance قدرتمندترین مدل تولید ویدئو است که از تولید ویدئو با مرجع چندوجهی، ویرایش ویدئو، گسترش ویدئو، متن به ویدئو و تصویر به ویدئو با صدای همگام‌سازی شده پشتیبانی می‌کند.", - "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast توسط ByteDance همان قابلیت‌های Seedance 2.0 را با سرعت تولید بالاتر و قیمت رقابتی‌تر ارائه می‌دهد.", "emohaa.description": "Emohaa یک مدل سلامت روان با توانایی مشاوره حرفه‌ای است که به کاربران در درک مسائل احساسی کمک می‌کند.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B یک مدل سبک متن‌باز برای استقرار محلی و سفارشی‌سازی شده است.", "ernie-4.5-8k-preview.description": "پیش‌نمایش مدل با پنجره متنی ۸هزار توکن برای ارزیابی ERNIE 4.5.", @@ -506,6 +503,7 @@ "ernie-5.0-thinking-latest.description": "Wenxin 5.0 Thinking یک مدل پرچم‌دار بومی تمام‌وجهی است که مدل‌سازی متن، تصویر، صدا و ویدیو را یکپارچه می‌کند. این مدل ارتقاهای گسترده‌ای در توانایی برای پرسش و پاسخ پیچیده، تولید محتوا و سناریوهای عامل ارائه می‌دهد.", "ernie-5.0-thinking-preview.description": "پیش‌نمایش Wenxin 5.0 Thinking، یک مدل پرچم‌دار بومی تمام‌وجهی با مدل‌سازی یکپارچه متن، تصویر، صدا و ویدیو. این مدل ارتقاهای گسترده‌ای در توانایی برای پرسش و پاسخ پیچیده، تولید محتوا و سناریوهای عامل ارائه می‌دهد.", "ernie-5.0.description": "ERNIE 5.0 نسل جدید مدل‌های سری ERNIE است؛ یک مدل بزرگ چندوجهی که از ابتدا بر اساس یک رویکرد مدل‌سازی یکپارچه ساخته شده است. این مدل متن، تصویر، صوت و ویدئو را به شکل مشترک مدل‌سازی کرده و توانایی‌های چندوجهی قدرتمندی ارائه می‌دهد. توانایی‌های بنیادی آن ارتقا یافته و عملکرد قوی در ارزیابی‌های معیار نشان می‌دهد. این مدل در درک چندوجهی، پیروی از دستور، نوشتن خلاق، دقت واقعی، برنامه‌ریزی ایجنتی و استفاده از ابزار عملکرد برجسته‌ای دارد.", + "ernie-5.1.description": "ERNIE 5.1 جدیدترین مدل در سری ERNIE است که ارتقاهای جامعی در قابلیت‌های بنیادی خود ارائه می‌دهد. این مدل بهبودهای قابل توجهی در زمینه‌هایی مانند عامل‌ها، پردازش دانش، استدلال و جستجوی عمیق نشان می‌دهد. این نسخه از معماری تقویت یادگیری کاملاً غیرهمزمان و جداشده استفاده می‌کند که به طور خاص برای رفع چالش‌های کلیدی در تکامل مدل‌های بزرگ به سمت تصمیم‌گیری خودکار عامل‌ها طراحی شده است، از جمله اختلافات عددی آموزش-استنتاج، استفاده کم از منابع محاسباتی ناهمگن و مسائل جهانی ناشی از اثرات دم بلند. علاوه بر این، تکنیک‌های آموزش پس از عامل در مقیاس بزرگ برای افزایش قابلیت‌ها و عملکرد تعمیم مدل به کار گرفته شده‌اند. از طریق یک چارچوب همکاری سه مرحله‌ای شامل فرآیندهای محیط، متخصص و ادغام، این رویکرد نه تنها کارایی آموزش را تضمین می‌کند بلکه به طور قابل توجهی پایداری و عملکرد مدل را در وظایف پیچیده بهبود می‌بخشد.", "ernie-char-fiction-8k-preview.description": "پیش‌نمایش ERNIE Character Fiction 8K یک مدل ساخت شخصیت و داستان برای ارزیابی و آزمایش ویژگی‌ها است.", "ernie-char-fiction-8k.description": "ERNIE Character Fiction 8K یک مدل شخصیتی برای رمان‌نویسی و خلق داستان است که برای تولید داستان‌های بلند مناسب است.", "ernie-image-turbo.description": "ERNIE-Image یک مدل متن‌به‌تصویر با ۸ میلیارد پارامتر از Baidu است. این مدل در چندین معیار در میان بهترین‌ها قرار می‌گیرد و در SuperCLUE چین رتبه اول مشترک و رتبه برتر در بخش متن‌باز دارد.", @@ -517,8 +515,7 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K یک مدل تفکر سریع با زمینه ۳۲K برای استدلال پیچیده و گفت‌وگوی چندمرحله‌ای است.", "ernie-x1.1-preview.description": "پیش‌نمایش ERNIE X1.1 یک مدل تفکر برای ارزیابی و آزمایش است.", "ernie-x1.1.description": "ERNIE X1.1 یک مدل تفکر پیش‌نمایش برای ارزیابی و آزمایش است.", - "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5 که توسط تیم Seed ByteDance ساخته شده است، از ویرایش و ترکیب چندتصویری پشتیبانی می‌کند. ویژگی‌ها شامل سازگاری بهتر موضوع، پیروی دقیق از دستورات، درک منطق فضایی، بیان زیبایی‌شناختی، طراحی پوستر و لوگو با رندر دقیق متن-تصویر است.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 که توسط تیم Seed ByteDance ساخته شده است، از ورودی‌های متن و تصویر برای تولید تصاویر باکیفیت و قابل‌کنترل از طریق دستورات پشتیبانی می‌کند.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 یک مدل تولید تصویر از ByteDance Seed است که از ورودی‌های متنی و تصویری پشتیبانی می‌کند و تولید تصاویر با کیفیت بالا و کنترل‌پذیری بالا را ممکن می‌سازد. این مدل تصاویر را از دستورات متنی تولید می‌کند.", "fal-ai/flux-kontext/dev.description": "مدل FLUX.1 با تمرکز بر ویرایش تصویر که از ورودی‌های متنی و تصویری پشتیبانی می‌کند.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] ورودی‌های متنی و تصاویر مرجع را می‌پذیرد و امکان ویرایش‌های محلی هدفمند و تغییرات پیچیده در صحنه کلی را فراهم می‌کند.", "fal-ai/flux/krea.description": "Flux Krea [dev] یک مدل تولید تصویر با تمایل زیبایی‌شناسی به تصاویر طبیعی و واقع‌گرایانه‌تر است.", @@ -526,8 +523,8 @@ "fal-ai/hunyuan-image/v3.description": "یک مدل قدرتمند بومی چندوجهی برای تولید تصویر.", "fal-ai/imagen4/preview.description": "مدل تولید تصویر با کیفیت بالا از گوگل.", "fal-ai/nano-banana.description": "Nano Banana جدیدترین، سریع‌ترین و کارآمدترین مدل چندوجهی بومی گوگل است که امکان تولید و ویرایش تصویر از طریق مکالمه را فراهم می‌کند.", - "fal-ai/qwen-image-edit.description": "مدل ویرایش تصویر حرفه‌ای از تیم Qwen که از ویرایش‌های معنایی و ظاهری، ویرایش دقیق متن‌های چینی/انگلیسی، انتقال سبک، چرخش و موارد دیگر پشتیبانی می‌کند.", - "fal-ai/qwen-image.description": "مدل قدرتمند تولید تصویر از تیم Qwen با قابلیت رندر قوی متن چینی و سبک‌های بصری متنوع.", + "fal-ai/qwen-image-edit.description": "یک مدل حرفه‌ای ویرایش تصویر از تیم Qwen که از ویرایش‌های معنایی و ظاهری پشتیبانی می‌کند، متن‌های چینی و انگلیسی را با دقت ویرایش می‌کند و ویرایش‌های با کیفیت بالا مانند انتقال سبک و چرخش اشیاء را ممکن می‌سازد.", + "fal-ai/qwen-image.description": "یک مدل قدرتمند تولید تصویر از تیم Qwen با قابلیت‌های برجسته در رندر متن چینی و سبک‌های بصری متنوع.", "flux-1-schnell.description": "مدل تبدیل متن به تصویر با ۱۲ میلیارد پارامتر از Black Forest Labs که از تقطیر انتشار تقابلی نهفته برای تولید تصاویر با کیفیت بالا در ۱ تا ۴ مرحله استفاده می‌کند. این مدل با جایگزین‌های بسته رقابت می‌کند و تحت مجوز Apache-2.0 برای استفاده شخصی، تحقیقاتی و تجاری منتشر شده است.", "flux-dev.description": "مدل تولید تصویر متن‌باز برای تحقیق و توسعه، به‌طور کارآمد برای پژوهش‌های نوآورانهٔ غیرتجاری بهینه‌سازی شده است.", "flux-kontext-max.description": "تولید و ویرایش تصویر متنی-زمینه‌ای پیشرفته که متن و تصویر را برای نتایج دقیق و منسجم ترکیب می‌کند.", @@ -566,10 +563,10 @@ "gemini-3-flash-preview.description": "Gemini 3 Flash هوشمندترین مدل طراحی‌شده برای سرعت است که هوش پیشرفته را با قابلیت جست‌وجوی دقیق ترکیب می‌کند.", "gemini-3-flash.description": "Gemini 3 Flash از Google — مدل بسیار سریع با پشتیبانی ورودی چندوجهی.", "gemini-3-pro-image-preview.description": "Gemini 3 Pro Image (Nano Banana Pro) مدل تولید تصویر گوگل است که از گفتگوی چندوجهی نیز پشتیبانی می‌کند.", - "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) مدل تولید تصویر گوگل است و همچنین از چت چندوجهی پشتیبانی می‌کند.", + "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) مدل تولید تصویر گوگل است که از چت چندوجهی نیز پشتیبانی می‌کند.", "gemini-3-pro-preview.description": "Gemini 3 Pro قدرتمندترین مدل عامل و کدنویسی احساسی گوگل است که تعاملات بصری غنی‌تر و تعامل عمیق‌تری را بر پایه استدلال پیشرفته ارائه می‌دهد.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) سریع‌ترین مدل تولید تصویر بومی گوگل با پشتیبانی از تفکر، تولید و ویرایش تصویر مکالمه‌ای است.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) کیفیت تصویر در سطح حرفه‌ای را با سرعت Flash و پشتیبانی از چت چندوجهی ارائه می‌دهد.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) سریع‌ترین مدل تولید تصویر بومی گوگل است که از تفکر، تولید و ویرایش تصاویر در مکالمات پشتیبانی می‌کند.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview اقتصادی‌ترین مدل چندوجهی گوگل است که برای وظایف عامل‌محور با حجم بالا، ترجمه و پردازش داده‌ها بهینه شده است.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite اقتصادی‌ترین مدل چندوجهی گوگل است که برای وظایف عاملی با حجم بالا، ترجمه و پردازش داده بهینه شده است.", "gemini-3.1-pro-preview.description": "پیش‌نمایش Gemini 3.1 Pro قابلیت‌های استدلال بهبود یافته را به Gemini 3 Pro اضافه می‌کند و از سطح تفکر متوسط پشتیبانی می‌کند.", @@ -734,8 +731,6 @@ "grok-4-fast-reasoning.description": "با افتخار Grok 4 Fast را معرفی می‌کنیم، جدیدترین پیشرفت ما در مدل‌های استدلال مقرون‌به‌صرفه.", "grok-4.20-0309-non-reasoning.description": "نسخه بدون استدلال برای کاربردهای ساده.", "grok-4.20-0309-reasoning.description": "مدلی هوشمند و بسیار سریع که قبل از پاسخ استدلال می‌کند.", - "grok-4.20-beta-0309-non-reasoning.description": "نسخه غیرتفکری برای موارد استفاده ساده.", - "grok-4.20-beta-0309-reasoning.description": "مدل هوشمند و فوق‌العاده سریع که قبل از پاسخ‌دهی استدلال می‌کند.", "grok-4.20-multi-agent-0309.description": "مجموعه‌ای از ۴ یا ۱۶ ایجنت که در پژوهش عملکرد عالی دارد. در حال حاضر از ابزارهای سمت کاربر پشتیبانی نمی‌کند و تنها ابزارهای سمت سرور xAI (مانند X Search و Web Search) و ابزارهای MCP از راه دور را پشتیبانی می‌کند.", "grok-4.3.description": "حقیقت‌جویانه‌ترین مدل زبان بزرگ در جهان", "grok-4.description": "جدیدترین مدل پرچمدار Grok با عملکرد بی‌نظیر در زبان، ریاضیات و استدلال — یک مدل همه‌جانبه واقعی. در حال حاضر به grok-4-0709 اشاره دارد؛ به دلیل منابع محدود، قیمت آن موقتاً ۱۰٪ بالاتر از قیمت رسمی است و انتظار می‌رود به قیمت رسمی بازگردد.", @@ -1220,8 +1215,6 @@ "qwq.description": "QwQ یک مدل استدلال در خانواده Qwen است. در مقایسه با مدل‌های تنظیم‌شده با دستورالعمل استاندارد، توانایی تفکر و استدلال آن عملکرد پایین‌دستی را به‌ویژه در مسائل دشوار به‌طور قابل توجهی بهبود می‌بخشد. QwQ-32B یک مدل استدلال میان‌رده است که با مدل‌های برتر مانند DeepSeek-R1 و o1-mini رقابت می‌کند.", "qwq_32b.description": "مدل استدلال میان‌رده در خانواده Qwen. در مقایسه با مدل‌های تنظیم‌شده با دستورالعمل استاندارد، توانایی تفکر و استدلال QwQ عملکرد پایین‌دستی را به‌ویژه در مسائل دشوار به‌طور قابل توجهی بهبود می‌بخشد.", "r1-1776.description": "R1-1776 نسخه پس‌آموزشی مدل DeepSeek R1 است که برای ارائه اطلاعات واقعی، بدون سانسور و بی‌طرف طراحی شده است.", - "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro توسط ByteDance از متن به ویدئو، تصویر به ویدئو (فریم اول، فریم اول+آخر) و تولید صدا همگام با تصاویر پشتیبانی می‌کند.", - "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite توسط BytePlus دارای تولید تقویت‌شده با بازیابی وب برای اطلاعات بلادرنگ، تفسیر پیشرفته دستورات پیچیده و بهبود سازگاری مرجع برای خلق بصری حرفه‌ای است.", "solar-mini-ja.description": "Solar Mini (ژاپنی) نسخه‌ای از Solar Mini با تمرکز بر زبان ژاپنی است که در عین حال عملکرد قوی و کارآمدی در زبان‌های انگلیسی و کره‌ای حفظ می‌کند.", "solar-mini.description": "Solar Mini یک مدل زبانی فشرده است که عملکردی بهتر از GPT-3.5 دارد و با پشتیبانی چندزبانه قوی از زبان‌های انگلیسی و کره‌ای، راه‌حلی کارآمد با حجم کم ارائه می‌دهد.", "solar-pro.description": "Solar Pro یک مدل زبانی هوشمند از Upstage است که برای پیروی از دستورالعمل‌ها روی یک GPU طراحی شده و امتیاز IFEval بالای ۸۰ دارد. در حال حاضر از زبان انگلیسی پشتیبانی می‌کند؛ انتشار کامل آن برای نوامبر ۲۰۲۴ با پشتیبانی زبانی گسترده‌تر و زمینه طولانی‌تر برنامه‌ریزی شده است.", diff --git a/locales/fa-IR/onboarding.json b/locales/fa-IR/onboarding.json index 0a61c786fa..b8170e4cc2 100644 --- a/locales/fa-IR/onboarding.json +++ b/locales/fa-IR/onboarding.json @@ -27,6 +27,13 @@ "agent.layout.skipConfirm.ok": "فعلاً رد کن", "agent.layout.skipConfirm.title": "فعلاً از راه‌اندازی اولیه رد می‌شی؟", "agent.layout.switchMessage": "امروز حال و هواشو نداری؟ می‌تونی به {{mode}} یا {{skip}} تغییر بدی.", + "agent.layout.switchMessageClassic": "امروز حسش نیست؟ می‌توانید به {{mode}} تغییر دهید.", + "agent.messenger.cta.discord": "افزودن به دیسکورد", + "agent.messenger.cta.slack": "افزودن به اسلک", + "agent.messenger.cta.telegram": "باز کردن در تلگرام", + "agent.messenger.subtitle": "با نماینده خود در تلگرام، اسلک یا دیسکورد گفتگو کنید", + "agent.messenger.telegramQrCaption": "با دوربین گوشی خود اسکن کنید", + "agent.messenger.title": "من را هر جا که پیام می‌دهید، همراه خود داشته باشید", "agent.modeSwitch.agent": "مکالمه‌ای", "agent.modeSwitch.classic": "کلاسیک", "agent.modeSwitch.collapse": "بستن", @@ -68,6 +75,13 @@ "agent.wrapUp.confirm.content": "آنچه تا اینجا گفتیم را ذخیره می‌کنم. هر زمان خواستید می‌توانید برگردید و ادامه دهید.", "agent.wrapUp.confirm.ok": "همین حالا تمام کن", "agent.wrapUp.confirm.title": "آیا می‌خواهید روند آشنایی را همین حالا تمام کنید؟", + "agentPicker.allCategories": "همه", + "agentPicker.continue": "ادامه", + "agentPicker.skip": "فعلاً رد کردن", + "agentPicker.subtitle": "چند مورد را اکنون به کتابخانه خود اضافه کنید — هر زمان که خواستید موارد بیشتری کشف کنید.", + "agentPicker.title": "بیایید چند نماینده به کتابخانه شما اضافه کنیم", + "agentPicker.title2": "مواردی را انتخاب کنید که با نحوه کار شما مطابقت دارند", + "agentPicker.title3": "همیشه می‌توانید موارد بیشتری اضافه کنید — با چند مورد شروع کنید", "back": "بازگشت", "finish": "شروع کن", "interests.area.business": "کسب‌وکار و استراتژی", diff --git a/locales/fa-IR/plugin.json b/locales/fa-IR/plugin.json index abdc526b87..5791708113 100644 --- a/locales/fa-IR/plugin.json +++ b/locales/fa-IR/plugin.json @@ -39,8 +39,8 @@ "builtins.lobe-agent-documents.inspector.docCount": "{{count}} سند", "builtins.lobe-agent-documents.inspector.opsCount": "{{count}} عملیات", "builtins.lobe-agent-documents.inspector.opsResult": "{{success}}/{{total}} عملیات", - "builtins.lobe-agent-documents.inspector.target.agent": "در عامل", - "builtins.lobe-agent-documents.inspector.target.currentTopic": "در موضوع", + "builtins.lobe-agent-documents.inspector.scope.agent": "در عامل", + "builtins.lobe-agent-documents.inspector.scope.currentTopic": "در موضوع", "builtins.lobe-agent-documents.title": "اسناد عامل", "builtins.lobe-agent-management.apiName.callAgent": "نماینده تماس", "builtins.lobe-agent-management.apiName.createAgent": "ایجاد نماینده", @@ -90,6 +90,14 @@ "builtins.lobe-agent.title": "عامل لوب", "builtins.lobe-claude-code.agent.instruction": "دستورالعمل", "builtins.lobe-claude-code.agent.result": "نتیجه", + "builtins.lobe-claude-code.task.createLabel": "ایجاد وظیفه: ", + "builtins.lobe-claude-code.task.getLabel": "بررسی وظیفه #{{taskId}}", + "builtins.lobe-claude-code.task.listLabel": "فهرست وظایف", + "builtins.lobe-claude-code.task.updateCompleted": "تکمیل شده", + "builtins.lobe-claude-code.task.updateDeleted": "حذف شده", + "builtins.lobe-claude-code.task.updateInProgress": "شروع شده", + "builtins.lobe-claude-code.task.updateLabel": "به‌روزرسانی وظیفه #{{taskId}}", + "builtins.lobe-claude-code.task.updatePending": "بازنشانی", "builtins.lobe-claude-code.todoWrite.allDone": "همه کارها انجام شدند", "builtins.lobe-claude-code.todoWrite.currentStep": "مرحله‌ی فعلی", "builtins.lobe-claude-code.todoWrite.todos": "کارها", diff --git a/locales/fa-IR/providers.json b/locales/fa-IR/providers.json index 54dc2d7229..d06eb4a87b 100644 --- a/locales/fa-IR/providers.json +++ b/locales/fa-IR/providers.json @@ -33,7 +33,6 @@ "jina.description": "Jina AI که در سال 2020 تأسیس شد، یک شرکت پیشرو در زمینه جستجوی هوش مصنوعی است. پشته جستجوی آن شامل مدل‌های برداری، رتبه‌بندها و مدل‌های زبانی کوچک برای ساخت اپلیکیشن‌های جستجوی مولد و چندوجهی با کیفیت بالا است.", "kimicodingplan.description": "Kimi Code از Moonshot AI دسترسی به مدل‌های Kimi شامل K2.5 را برای وظایف کدنویسی فراهم می‌کند.", "lmstudio.description": "LM Studio یک اپلیکیشن دسکتاپ برای توسعه و آزمایش مدل‌های زبانی بزرگ روی رایانه شخصی شماست.", - "lobehub.description": "LobeHub Cloud از APIهای رسمی برای دسترسی به مدل‌های هوش مصنوعی استفاده می‌کند و مصرف را با اعتباراتی که به توکن‌های مدل مرتبط هستند، اندازه‌گیری می‌کند.", "longcat.description": "لانگ‌کت مجموعه‌ای از مدل‌های بزرگ هوش مصنوعی تولیدی است که به‌طور مستقل توسط میتوآن توسعه داده شده است. این مدل‌ها برای افزایش بهره‌وری داخلی شرکت و امکان‌پذیر کردن کاربردهای نوآورانه از طریق معماری محاسباتی کارآمد و قابلیت‌های چندوجهی قدرتمند طراحی شده‌اند.", "minimax.description": "MiniMax که در سال 2021 تأسیس شد، هوش مصنوعی چندمنظوره با مدل‌های پایه چندوجهی از جمله مدل‌های متنی با پارامترهای تریلیونی، مدل‌های گفتاری و تصویری توسعه می‌دهد و اپ‌هایی مانند Hailuo AI را ارائه می‌کند.", "minimaxcodingplan.description": "طرح توکن MiniMax دسترسی به مدل‌های MiniMax شامل M2.7 را برای وظایف کدنویسی از طریق اشتراک با هزینه ثابت فراهم می‌کند.", diff --git a/locales/fa-IR/subscription.json b/locales/fa-IR/subscription.json index ea97456b27..1bb0e9fd57 100644 --- a/locales/fa-IR/subscription.json +++ b/locales/fa-IR/subscription.json @@ -55,6 +55,12 @@ "credits.autoTopUp.toggle": "فعال کردن شارژ خودکار", "credits.autoTopUp.upgradeHint": "برای فعال کردن شارژ خودکار به یک طرح پولی اشتراک بگیرید", "credits.autoTopUp.validation.targetMustExceedThreshold": "موجودی هدف باید بیشتر از آستانه باشد", + "credits.costEstimateHint.desc": "قبل از ارسال، هنگامی که هزینه تخمینی مدل به آستانه شما می‌رسد، یک هشدار سبک نمایش دهید", + "credits.costEstimateHint.saveError": "ذخیره تنظیمات هشدار هزینه تخمینی ناموفق بود", + "credits.costEstimateHint.saveSuccess": "تنظیمات هشدار هزینه تخمینی ذخیره شد", + "credits.costEstimateHint.threshold": "آستانه هشدار", + "credits.costEstimateHint.title": "هشدار هزینه تخمینی", + "credits.costEstimateHint.validation.threshold": "آستانه باید بزرگتر یا مساوی با ۰ باشد", "credits.packages.auto": "خودکار", "credits.packages.charged": "شارژ شده ${{amount}}", "credits.packages.expired": "منقضی شده", diff --git a/locales/fa-IR/tool.json b/locales/fa-IR/tool.json index a59d30e419..1f5eeef145 100644 --- a/locales/fa-IR/tool.json +++ b/locales/fa-IR/tool.json @@ -40,6 +40,14 @@ "agentMarketplace.render.alreadyInLibraryTag": "قبلاً در کتابخانه", "agentMarketplace.render.alreadyInLibrary_one": "{{count}} قبلاً در کتابخانه", "agentMarketplace.render.alreadyInLibrary_other": "{{count}} قبلاً در کتابخانه", + "claudeCode.askUserQuestion.escape.back": "بازگشت به گزینه‌ها", + "claudeCode.askUserQuestion.escape.enter": "یا مستقیماً تایپ کنید", + "claudeCode.askUserQuestion.escape.placeholder": "پاسخ خود را اینجا تایپ کنید…", + "claudeCode.askUserQuestion.multiSelectTag": "(چند انتخابی)", + "claudeCode.askUserQuestion.skip": "رد کردن", + "claudeCode.askUserQuestion.submit": "ارسال", + "claudeCode.askUserQuestion.timeExpired": "زمان به پایان رسید — استفاده از گزینه ۱ برای هر سوال.", + "claudeCode.askUserQuestion.timeRemaining": "زمان باقی‌مانده: {{time}} · سوالات بی‌پاسخ به طور پیش‌فرض به گزینه ۱ در زمان پایان تنظیم می‌شوند.", "codeInterpreter-legacy.error": "خطای اجرا", "codeInterpreter-legacy.executing": "در حال اجرا...", "codeInterpreter-legacy.files": "فایل‌ها:", diff --git a/locales/fa-IR/topic.json b/locales/fa-IR/topic.json index 0e3e05c652..294518d8fb 100644 --- a/locales/fa-IR/topic.json +++ b/locales/fa-IR/topic.json @@ -56,6 +56,7 @@ "renameModal.title": "تغییر نام موضوع", "searchPlaceholder": "جستجوی گفت‌وگوها...", "searchResultEmpty": "نتیجه‌ای برای جستجو یافت نشد.", + "sidebar.title": "موضوعات", "taskManager.agent": "عامل وظایف", "taskManager.welcome": "در مورد کارهایت از من بپرس", "temp": "موقت", diff --git a/locales/fr-FR/chat.json b/locales/fr-FR/chat.json index b0ae4d79f0..a5d8ea4ce1 100644 --- a/locales/fr-FR/chat.json +++ b/locales/fr-FR/chat.json @@ -220,6 +220,13 @@ "inbox.title": "Lobe IA", "input.addAi": "Ajouter un message IA", "input.addUser": "Ajouter un message utilisateur", + "input.costEstimate.creditsPerMillionTokens": "{{credits}} crédits/M tokens", + "input.costEstimate.hint": "Coût estimé : ~{{credits}} crédits", + "input.costEstimate.inputLabel": "Entrée", + "input.costEstimate.outputLabel": "Sortie", + "input.costEstimate.settingsLink": "Ajuster le seuil d'avertissement", + "input.costEstimate.tokenCount": "~{{tokens}} tokens", + "input.costEstimate.tooltip": "Estimé à partir du contexte actuel, des outils et des tarifs du modèle. Le coût réel peut varier.", "input.disclaimer": "Les agents peuvent faire des erreurs. Faites preuve de discernement pour les informations critiques.", "input.errorMsg": "Échec de l’envoi : {{errorMsg}}. Réessayez ou envoyez plus tard.", "input.more": "Plus", @@ -779,6 +786,7 @@ "upload.preview.prepareTasks": "Préparation des segments...", "upload.preview.status.pending": "Préparation au téléversement...", "upload.preview.status.processing": "Traitement du fichier...", + "upload.validation.unsupportedFileType": "Type de fichier non pris en charge : {{files}}. Images prises en charge : JPG, PNG, GIF, WebP. Les documents pris en charge incluent PDF, Word, Excel, PowerPoint, Markdown, texte, CSV, JSON et fichiers de code.", "upload.validation.videoSizeExceeded": "La taille du fichier vidéo ne doit pas dépasser 20 Mo. Taille actuelle : {{actualSize}}.", "viewMode.fullWidth": "Pleine largeur", "viewMode.normal": "Standard", @@ -894,6 +902,8 @@ "workingPanel.localFile.closeOther": "Fermer les autres", "workingPanel.localFile.closeRight": "Fermer à droite", "workingPanel.localFile.error": "Impossible de charger ce fichier", + "workingPanel.localFile.preview.raw": "Brut", + "workingPanel.localFile.preview.render": "Aperçu", "workingPanel.localFile.truncated": "Aperçu du fichier tronqué à {{limit}} caractères", "workingPanel.progress": "Progress", "workingPanel.progress.allCompleted": "All tasks completed", @@ -959,6 +969,8 @@ "workingPanel.review.viewMode.unified": "Passer à la vue unifiée", "workingPanel.review.wordWrap.disable": "Désactiver le retour à la ligne", "workingPanel.review.wordWrap.enable": "Activer le retour à la ligne", + "workingPanel.skills.empty": "Aucune compétence trouvée dans ce projet", + "workingPanel.skills.title": "Compétences", "workingPanel.space": "Espace", "workingPanel.title": "Working Panel", "you": "Vous", diff --git a/locales/fr-FR/components.json b/locales/fr-FR/components.json index 127112b4ef..4f46946baf 100644 --- a/locales/fr-FR/components.json +++ b/locales/fr-FR/components.json @@ -121,6 +121,16 @@ "ModelSwitchPanel.detail.context": "Longueur du contexte", "ModelSwitchPanel.detail.pricing": "Tarification", "ModelSwitchPanel.detail.pricing.cachedInput": "Entrée en cache ${{amount}}/M", + "ModelSwitchPanel.detail.pricing.credits.cachedInput": "Entrée mise en cache {{amount}} crédits/M tokens", + "ModelSwitchPanel.detail.pricing.credits.image": "crédits/img", + "ModelSwitchPanel.detail.pricing.credits.input": "Entrée {{amount}} crédits/M tokens", + "ModelSwitchPanel.detail.pricing.credits.megapixel": "crédits/MP", + "ModelSwitchPanel.detail.pricing.credits.millionCharacters": "crédits/M caractères", + "ModelSwitchPanel.detail.pricing.credits.millionTokens": "crédits/M tokens", + "ModelSwitchPanel.detail.pricing.credits.output": "Sortie {{amount}} crédits/M tokens", + "ModelSwitchPanel.detail.pricing.credits.perImage": "~ {{amount}} crédits / image", + "ModelSwitchPanel.detail.pricing.credits.perVideo": "~ {{amount}} crédits / vidéo", + "ModelSwitchPanel.detail.pricing.credits.second": "crédits/s", "ModelSwitchPanel.detail.pricing.group.audio": "Audio", "ModelSwitchPanel.detail.pricing.group.image": "Image", "ModelSwitchPanel.detail.pricing.group.text": "Texte", diff --git a/locales/fr-FR/editor.json b/locales/fr-FR/editor.json index 1d62c353a8..04711f251f 100644 --- a/locales/fr-FR/editor.json +++ b/locales/fr-FR/editor.json @@ -1,8 +1,10 @@ { "actionTag.category.command": "Commande", + "actionTag.category.projectSkill": "Compétence de projet", "actionTag.category.skill": "Compétence", "actionTag.category.tool": "Outil", "actionTag.tooltip.command": "Exécute une commande slash côté client avant l’envoi.", + "actionTag.tooltip.projectSkill": "Envoyé comme une invocation par barre oblique pour que le CLI de l'agent exécute la compétence de projet correspondante.", "actionTag.tooltip.skill": "Charge un paquet de compétences réutilisable pour cette requête.", "actionTag.tooltip.tool": "Marque un outil que l’utilisateur a explicitement sélectionné pour cette requête.", "actions.expand.off": "Réduire", diff --git a/locales/fr-FR/home.json b/locales/fr-FR/home.json index c8027e8d33..2a357e4d47 100644 --- a/locales/fr-FR/home.json +++ b/locales/fr-FR/home.json @@ -11,6 +11,19 @@ "brief.action.ignore": "Ignorer", "brief.action.retry": "Réessayer", "brief.addFeedback": "Partager un retour", + "brief.agentSignal.selfReview.applied.heading": "Mis à jour", + "brief.agentSignal.selfReview.applied.summary": "{{count}} mise à jour de rêve a été appliquée.", + "brief.agentSignal.selfReview.applied.summary_plural": "{{count}} mises à jour de rêve ont été appliquées.", + "brief.agentSignal.selfReview.applied.title": "Ressources de rêve mises à jour", + "brief.agentSignal.selfReview.error.heading": "Problème", + "brief.agentSignal.selfReview.error.summary": "Certains travaux n'ont pas pu être terminés pendant ce rêve.", + "brief.agentSignal.selfReview.error.title": "Le rêve a rencontré un problème", + "brief.agentSignal.selfReview.ideas.summary": "Notes de rêve enregistrées pour une révision future.", + "brief.agentSignal.selfReview.ideas.title": "Notes de rêve", + "brief.agentSignal.selfReview.proposal.heading": "Suggestion", + "brief.agentSignal.selfReview.proposal.summary": "{{count}} suggestion de rêve nécessite votre révision.", + "brief.agentSignal.selfReview.proposal.summary_plural": "{{count}} suggestions de rêve nécessitent votre révision.", + "brief.agentSignal.selfReview.proposal.title": "Suggestion de rêve à réviser", "brief.collapse": "Afficher moins", "brief.commentPlaceholder": "Partagez votre retour...", "brief.commentSubmit": "Envoyer le retour", diff --git a/locales/fr-FR/hotkey.json b/locales/fr-FR/hotkey.json index 2b7052089c..497d2ccfdb 100644 --- a/locales/fr-FR/hotkey.json +++ b/locales/fr-FR/hotkey.json @@ -1,8 +1,6 @@ { "addUserMessage.desc": "Ajouter l'entrée actuelle comme message utilisateur sans déclencher la génération", "addUserMessage.title": "Ajouter un message utilisateur", - "clearCurrentMessages.desc": "Effacer les messages et les fichiers téléchargés de la conversation en cours", - "clearCurrentMessages.title": "Effacer les messages de la conversation", "commandPalette.desc": "Ouvrir la palette de commandes globale pour un accès rapide aux fonctionnalités", "commandPalette.title": "Palette de commandes", "deleteAndRegenerateMessage.desc": "Supprimer le dernier message et le régénérer", diff --git a/locales/fr-FR/models.json b/locales/fr-FR/models.json index 9360265c2a..3ad314ff0f 100644 --- a/locales/fr-FR/models.json +++ b/locales/fr-FR/models.json @@ -106,7 +106,6 @@ "MiniMax-Hailuo-2.3.description": "Nouveau modèle de génération vidéo avec des améliorations complètes dans les mouvements corporels, le réalisme physique et le suivi des instructions.", "MiniMax-M1.description": "Un nouveau modèle de raisonnement interne avec 80 000 chaînes de pensée et 1 million d’entrées, offrant des performances comparables aux meilleurs modèles mondiaux.", "MiniMax-M2-Stable.description": "Conçu pour un codage efficace et des flux de travail d’agents, avec une plus grande simultanéité pour un usage commercial.", - "MiniMax-M2.1-Lightning.description": "Capacités de programmation multilingues puissantes avec une inférence plus rapide et plus efficace.", "MiniMax-M2.1-highspeed.description": "Des capacités de programmation multilingues puissantes, offrant une expérience de programmation entièrement améliorée. Plus rapide et plus efficace.", "MiniMax-M2.1.description": "MiniMax-M2.1 est un modèle phare open source de MiniMax, conçu pour résoudre des tâches complexes du monde réel. Ses principaux atouts résident dans ses capacités de programmation multilingue et sa faculté à résoudre des problèmes complexes en tant qu'agent.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed : Même performance que M2.5 avec une inférence plus rapide.", @@ -315,13 +314,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku est le modèle le plus rapide et le plus compact d’Anthropic, conçu pour des réponses quasi instantanées avec des performances rapides et précises.", "claude-3-opus-20240229.description": "Claude 3 Opus est le modèle le plus puissant d’Anthropic pour les tâches complexes, excellent en performance, intelligence, fluidité et compréhension.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet équilibre intelligence et rapidité pour les charges de travail en entreprise, offrant une grande utilité à moindre coût et un déploiement fiable à grande échelle.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 est le modèle Haiku le plus rapide et le plus intelligent d'Anthropic, avec une vitesse fulgurante et une réflexion approfondie.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 est le modèle Haiku le plus rapide et le plus intelligent d’Anthropic, avec une vitesse fulgurante et un raisonnement étendu.", "claude-haiku-4-5.description": "Claude Haiku 4.5 par Anthropic — modèle Haiku de nouvelle génération avec un raisonnement et une vision améliorés.", "claude-haiku-4.5.description": "Claude Haiku 4.5 est le modèle Haiku le plus rapide et le plus intelligent d’Anthropic, avec une vitesse fulgurante et un raisonnement étendu.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking est une variante avancée capable de révéler son processus de raisonnement.", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1 est le dernier modèle d'Anthropic, le plus performant pour les tâches hautement complexes, excelle en performance, intelligence, fluidité et compréhension.", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1 est le modèle le plus récent et le plus performant d’Anthropic pour les tâches hautement complexes, excelling en performance, intelligence, fluidité et compréhension.", "claude-opus-4-1.description": "Claude Opus 4.1 par Anthropic — modèle de raisonnement premium avec des capacités d'analyse approfondie.", - "claude-opus-4-20250514.description": "Claude Opus 4 est le modèle le plus puissant d'Anthropic pour les tâches hautement complexes, excelle en performance, intelligence, fluidité et compréhension.", + "claude-opus-4-20250514.description": "Claude Opus 4 est le modèle le plus puissant d’Anthropic pour les tâches hautement complexes, excelling en performance, intelligence, fluidité et compréhension.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 est le modèle phare d’Anthropic, combinant intelligence exceptionnelle et performance évolutive, idéal pour les tâches complexes nécessitant des réponses et un raisonnement de très haute qualité.", "claude-opus-4-5.description": "Claude Opus 4.5 par Anthropic — modèle phare avec un raisonnement et un codage de premier ordre.", "claude-opus-4-6.description": "Claude Opus 4.6 par Anthropic — modèle phare avec une fenêtre de contexte de 1M et un raisonnement avancé.", @@ -330,8 +329,8 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 est le modèle le plus intelligent d’Anthropic pour la création d’agents et le codage.", "claude-opus-4.6.description": "Claude Opus 4.6 est le modèle le plus intelligent d’Anthropic pour la création d’agents et le codage.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking peut produire des réponses quasi instantanées ou une réflexion détaillée étape par étape avec un processus visible.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 est le modèle le plus intelligent d'Anthropic à ce jour, offrant des réponses quasi-instantanées ou une réflexion détaillée étape par étape avec un contrôle précis pour les utilisateurs d'API.", - "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 est le modèle le plus intelligent d'Anthropic à ce jour.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 peut produire des réponses quasi instantanées ou un raisonnement détaillé étape par étape avec un processus visible.", + "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 est le modèle le plus intelligent d’Anthropic à ce jour.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 par Anthropic — Sonnet amélioré avec des performances de codage accrues.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 par Anthropic — dernier modèle Sonnet avec un codage supérieur et une utilisation d'outils avancée.", "claude-sonnet-4.5.description": "Claude Sonnet 4.5 est le modèle le plus intelligent d’Anthropic à ce jour.", @@ -404,7 +403,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) est un modèle innovant offrant une compréhension linguistique approfondie et une interaction fluide.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 est un modèle de raisonnement nouvelle génération avec un raisonnement complexe renforcé et une chaîne de pensée pour les tâches d’analyse approfondie.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 est un modèle de raisonnement de nouvelle génération avec des capacités renforcées de raisonnement complexe et de chaîne de pensée.", - "deepseek-chat.description": "Alias de compatibilité pour le mode non-réflexif de DeepSeek V4 Flash. Prévu pour être déprécié — utilisez DeepSeek V4 Flash à la place.", + "deepseek-chat.description": "Un nouveau modèle open-source combinant des capacités générales et de codage. Il conserve le dialogue général du modèle de chat et les solides compétences en codage du modèle de programmeur, avec un meilleur alignement des préférences. DeepSeek-V2.5 améliore également l’écriture et le suivi des instructions.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B est un modèle de langage pour le code entraîné sur 2T de tokens (87 % de code, 13 % de texte en chinois/anglais). Il introduit une fenêtre de contexte de 16K et des tâches de remplissage au milieu, offrant une complétion de code à l’échelle du projet et un remplissage de fragments.", "deepseek-coder-v2.description": "DeepSeek Coder V2 est un modèle de code MoE open source performant sur les tâches de programmation, comparable à GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 est un modèle de code MoE open source performant sur les tâches de programmation, comparable à GPT-4 Turbo.", @@ -426,7 +425,7 @@ "deepseek-r1-fast-online.description": "Version complète rapide de DeepSeek R1 avec recherche web en temps réel, combinant des capacités à l’échelle de 671B et des réponses plus rapides.", "deepseek-r1-online.description": "Version complète de DeepSeek R1 avec 671B de paramètres et recherche web en temps réel, offrant une meilleure compréhension et génération.", "deepseek-r1.description": "DeepSeek-R1 utilise des données de démarrage à froid avant l’apprentissage par renforcement et affiche des performances comparables à OpenAI-o1 en mathématiques, codage et raisonnement.", - "deepseek-reasoner.description": "Alias de compatibilité pour le mode réflexif de DeepSeek V4 Flash. Prévu pour être déprécié — utilisez DeepSeek V4 Flash à la place.", + "deepseek-reasoner.description": "Un modèle de raisonnement DeepSeek axé sur des tâches complexes de raisonnement logique.", "deepseek-v2.description": "DeepSeek V2 est un modèle MoE efficace pour un traitement économique.", "deepseek-v2:236b.description": "DeepSeek V2 236B est le modèle axé sur le code de DeepSeek avec une forte génération de code.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 est un modèle MoE de 671B paramètres avec des points forts en programmation, compréhension du contexte et traitement de longs textes.", @@ -491,8 +490,6 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 est un modèle de génération d’image de ByteDance Seed, prenant en charge les entrées texte et image avec une génération d’image de haute qualité et hautement contrôlable. Il génère des images à partir d’invites textuelles.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 est le dernier modèle d'image multimodal de ByteDance, intégrant des capacités de génération de texte en image, d'image en image et de génération d'images par lots, tout en incorporant des compétences en raisonnement et en bon sens. Par rapport à la version précédente 4.0, il offre une qualité de génération nettement améliorée, avec une meilleure cohérence d'édition et une fusion multi-images. Il permet un contrôle plus précis des détails visuels, produisant des textes et des visages plus petits de manière plus naturelle, et atteint une mise en page et des couleurs plus harmonieuses, améliorant l'esthétique globale.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite est le dernier modèle de génération d'images de ByteDance. Pour la première fois, il intègre des capacités de recherche en ligne, lui permettant d'incorporer des informations web en temps réel et d'améliorer la pertinence des images générées. L'intelligence du modèle a également été améliorée, permettant une interprétation précise des instructions complexes et du contenu visuel. De plus, il offre une meilleure couverture des connaissances globales, une cohérence des références et une qualité de génération dans des scénarios professionnels, répondant mieux aux besoins de création visuelle au niveau des entreprises.", - "dreamina-seedance-2-0-260128.description": "Seedance 2.0 de ByteDance est le modèle de génération vidéo le plus puissant, prenant en charge la génération de vidéos multimodales de référence, le montage vidéo, l'extension vidéo, la conversion texte en vidéo et image en vidéo avec audio synchronisé.", - "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast de ByteDance offre les mêmes capacités que Seedance 2.0 avec des vitesses de génération plus rapides à un prix plus compétitif.", "emohaa.description": "Emohaa est un modèle de santé mentale doté de compétences professionnelles en conseil pour aider les utilisateurs à comprendre leurs problèmes émotionnels.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B est un modèle léger open source conçu pour un déploiement local et personnalisé.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview est un modèle de prévisualisation avec contexte 8K pour l’évaluation d’ERNIE 4.5.", @@ -506,6 +503,7 @@ "ernie-5.0-thinking-latest.description": "Wenxin 5.0 Thinking est un modèle phare natif tout-modal avec modélisation unifiée du texte, de l’image, de l’audio et de la vidéo. Il offre des améliorations majeures pour la QA complexe, la création et les scénarios d’agents.", "ernie-5.0-thinking-preview.description": "Wenxin 5.0 Thinking Preview est un modèle phare natif tout-modal avec modélisation unifiée du texte, de l’image, de l’audio et de la vidéo. Il offre des améliorations majeures pour la QA complexe, la création et les scénarios d’agents.", "ernie-5.0.description": "ERNIE 5.0, le modèle de nouvelle génération de la série ERNIE, est un modèle natif multimodal de grande taille. Il adopte une approche de modélisation multimodale unifiée, modélisant conjointement le texte, les images, l'audio et la vidéo pour offrir des capacités multimodales complètes. Ses capacités fondamentales ont été considérablement améliorées, atteignant des performances élevées lors des évaluations de référence. Il excelle particulièrement dans la compréhension multimodale, le suivi des instructions, l'écriture créative, la précision factuelle, la planification d'agents et l'utilisation d'outils.", + "ernie-5.1.description": "ERNIE 5.1 est le dernier modèle de la série ERNIE, avec des améliorations complètes de ses capacités fondamentales. Il démontre des progrès significatifs dans des domaines tels que les agents, le traitement des connaissances, le raisonnement et la recherche approfondie. Cette version adopte une architecture d’apprentissage par renforcement entièrement asynchrone et découplée, spécifiquement conçue pour relever les défis clés de l’évolution des grands modèles vers une prise de décision autonome des agents, y compris les écarts numériques entre l’entraînement et l’inférence, la faible utilisation des ressources informatiques hétérogènes et les problèmes globaux causés par les effets de longue traîne. De plus, des techniques de post-entraînement à grande échelle pour les agents sont employées pour améliorer davantage les capacités et les performances de généralisation du modèle. Grâce à un cadre collaboratif en trois étapes impliquant des processus d’environnement, d’expert et de fusion, l’approche garantit non seulement l’efficacité de l’entraînement, mais améliore également de manière significative la stabilité et les performances du modèle sur des tâches complexes.", "ernie-char-fiction-8k-preview.description": "ERNIE Character Fiction 8K Preview est une préversion de modèle pour la création de personnages et d’intrigues, destinée à l’évaluation des fonctionnalités.", "ernie-char-fiction-8k.description": "ERNIE Character Fiction 8K est un modèle de personnage pour romans et création d’intrigues, adapté à la génération d’histoires longues.", "ernie-image-turbo.description": "ERNIE-Image est un modèle texte-vers-image de 8 milliards de paramètres développé par Baidu. Il se classe parmi les meilleurs sur plusieurs benchmarks, atteignant la première place ex aequo dans SuperCLUE en Chine et en tête dans la catégorie open source.", @@ -517,8 +515,7 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K est un modèle de réflexion rapide avec un contexte de 32K pour le raisonnement complexe et les dialogues multi-tours.", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview est une préversion de modèle de réflexion pour l’évaluation et les tests.", "ernie-x1.1.description": "ERNIE X1.1 est un modèle de réflexion en aperçu pour évaluation et test.", - "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5, développé par l'équipe Seed de ByteDance, prend en charge l'édition et la composition multi-images. Inclut une meilleure cohérence des sujets, un suivi précis des instructions, une compréhension de la logique spatiale, une expression esthétique, une mise en page de posters et la conception de logos avec un rendu texte-image de haute précision.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, développé par ByteDance Seed, prend en charge les entrées texte et image pour une génération d'images hautement contrôlable et de haute qualité à partir de prompts.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 est un modèle de génération d’images de ByteDance Seed, prenant en charge les entrées texte et image avec une génération d’images hautement contrôlable et de haute qualité. Il génère des images à partir de descriptions textuelles.", "fal-ai/flux-kontext/dev.description": "Modèle FLUX.1 axé sur l’édition d’images, prenant en charge les entrées texte et image.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] accepte des textes et des images de référence en entrée, permettant des modifications locales ciblées et des transformations globales complexes de scènes.", "fal-ai/flux/krea.description": "Flux Krea [dev] est un modèle de génération d’images avec une préférence esthétique pour des images plus réalistes et naturelles.", @@ -526,8 +523,8 @@ "fal-ai/hunyuan-image/v3.description": "Un puissant modèle natif multimodal de génération d’images.", "fal-ai/imagen4/preview.description": "Modèle de génération d’images de haute qualité développé par Google.", "fal-ai/nano-banana.description": "Nano Banana est le modèle multimodal natif le plus récent, le plus rapide et le plus efficace de Google, permettant la génération et l’édition d’images via la conversation.", - "fal-ai/qwen-image-edit.description": "Un modèle professionnel d'édition d'images de l'équipe Qwen, prenant en charge les modifications sémantiques et d'apparence, l'édition précise de texte en chinois/anglais, le transfert de style, la rotation et plus encore.", - "fal-ai/qwen-image.description": "Un modèle puissant de génération d'images de l'équipe Qwen avec un rendu texte chinois robuste et des styles visuels variés.", + "fal-ai/qwen-image-edit.description": "Un modèle professionnel d’édition d’images de l’équipe Qwen qui prend en charge les modifications sémantiques et d’apparence, édite précisément le texte en chinois et en anglais, et permet des modifications de haute qualité telles que le transfert de style et la rotation d’objets.", + "fal-ai/qwen-image.description": "Un puissant modèle de génération d’images de l’équipe Qwen avec un rendu impressionnant du texte en chinois et des styles visuels variés.", "flux-1-schnell.description": "Modèle texte-vers-image à 12 milliards de paramètres de Black Forest Labs utilisant la distillation par diffusion latente adversariale pour générer des images de haute qualité en 1 à 4 étapes. Il rivalise avec les alternatives propriétaires et est publié sous licence Apache-2.0 pour un usage personnel, de recherche et commercial.", "flux-dev.description": "Modèle open source de génération d’images destiné à la R&D, optimisé efficacement pour la recherche d’innovation non commerciale.", "flux-kontext-max.description": "Génération et édition d’images contextuelles de pointe, combinant texte et images pour des résultats précis et cohérents.", @@ -566,10 +563,10 @@ "gemini-3-flash-preview.description": "Gemini 3 Flash est le modèle le plus intelligent conçu pour la vitesse, alliant intelligence de pointe et ancrage de recherche performant.", "gemini-3-flash.description": "Gemini 3 Flash par Google — modèle ultra-rapide avec prise en charge des entrées multimodales.", "gemini-3-pro-image-preview.description": "Gemini 3 Pro Image (Nano Banana Pro) est le modèle de génération d'images de Google qui prend également en charge le dialogue multimodal.", - "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) est le modèle de génération d'images de Google et prend également en charge le chat multimodal.", + "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) est le modèle de génération d’images de Google et prend également en charge le chat multimodal.", "gemini-3-pro-preview.description": "Gemini 3 Pro est le modèle agent et de codage le plus puissant de Google, offrant des visuels enrichis et une interaction plus poussée grâce à un raisonnement de pointe.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) est le modèle de génération d'images natif le plus rapide de Google avec prise en charge de la réflexion, génération et édition d'images conversationnelles.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) offre une qualité d'image de niveau Pro à une vitesse Flash avec prise en charge du chat multimodal.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) est le modèle de génération d’images natif le plus rapide de Google, avec prise en charge de la réflexion, génération et édition d’images conversationnelles.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview est le modèle multimodal le plus économique de Google, optimisé pour les tâches agentiques à haut volume, la traduction et le traitement des données.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite est le modèle multimodal le plus économique de Google, optimisé pour les tâches agentiques à haut volume, la traduction et le traitement des données.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview améliore Gemini 3 Pro avec des capacités de raisonnement renforcées et ajoute un support de niveau de réflexion moyen.", @@ -734,8 +731,6 @@ "grok-4-fast-reasoning.description": "Nous sommes ravis de présenter Grok 4 Fast, notre dernière avancée en matière de modèles de raisonnement économiques.", "grok-4.20-0309-non-reasoning.description": "Une variante sans raisonnement pour des cas d'utilisation simples.", "grok-4.20-0309-reasoning.description": "Modèle intelligent et ultra-rapide qui raisonne avant de répondre.", - "grok-4.20-beta-0309-non-reasoning.description": "Une variante non-réflexive pour des cas d'utilisation simples.", - "grok-4.20-beta-0309-reasoning.description": "Modèle intelligent et ultra-rapide qui réfléchit avant de répondre.", "grok-4.20-multi-agent-0309.description": "Une équipe de 4 ou 16 agents, excelle dans les cas d'utilisation de recherche. Ne prend actuellement pas en charge les outils côté client. Prend uniquement en charge les outils côté serveur xAI (par exemple X Search, outils de recherche Web) et les outils MCP distants.", "grok-4.3.description": "Le modèle de langage de grande taille le plus axé sur la vérité au monde", "grok-4.description": "Dernier modèle phare Grok avec des performances inégalées en langage, mathématiques et raisonnement — un véritable polyvalent. Actuellement pointé vers grok-4-0709 ; en raison de ressources limitées, son prix est temporairement 10 % plus élevé que le tarif officiel et devrait revenir au prix officiel ultérieurement.", @@ -1220,8 +1215,6 @@ "qwq.description": "QwQ est un modèle de raisonnement de la famille Qwen. Comparé aux modèles classiques ajustés par instruction, il apporte des capacités de réflexion et de raisonnement qui améliorent considérablement les performances en aval, notamment sur les problèmes complexes. QwQ-32B est un modèle de raisonnement de taille moyenne qui rivalise avec les meilleurs modèles comme DeepSeek-R1 et o1-mini.", "qwq_32b.description": "Modèle de raisonnement de taille moyenne de la famille Qwen. Comparé aux modèles classiques ajustés par instruction, les capacités de réflexion et de raisonnement de QwQ améliorent considérablement les performances en aval, notamment sur les problèmes complexes.", "r1-1776.description": "R1-1776 est une variante post-entraînée de DeepSeek R1 conçue pour fournir des informations factuelles non censurées et impartiales.", - "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro de ByteDance prend en charge la conversion texte en vidéo, image en vidéo (première image, première+dernière image) et la génération audio synchronisée avec les visuels.", - "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite par BytePlus propose une génération augmentée par récupération web pour des informations en temps réel, une interprétation améliorée des prompts complexes et une meilleure cohérence des références pour la création visuelle professionnelle.", "solar-mini-ja.description": "Solar Mini (Ja) étend Solar Mini avec un accent sur le japonais tout en maintenant des performances efficaces et solides en anglais et en coréen.", "solar-mini.description": "Solar Mini est un modèle LLM compact surpassant GPT-3.5, avec de solides capacités multilingues en anglais et en coréen, offrant une solution efficace à faible empreinte.", "solar-pro.description": "Solar Pro est un LLM intelligent développé par Upstage, axé sur le suivi d'instructions sur un seul GPU, avec des scores IFEval supérieurs à 80. Il prend actuellement en charge l'anglais ; la version complète est prévue pour novembre 2024 avec un support linguistique élargi et un contexte plus long.", diff --git a/locales/fr-FR/onboarding.json b/locales/fr-FR/onboarding.json index a9fb6b6432..aba6829952 100644 --- a/locales/fr-FR/onboarding.json +++ b/locales/fr-FR/onboarding.json @@ -27,6 +27,13 @@ "agent.layout.skipConfirm.ok": "Passer pour le moment", "agent.layout.skipConfirm.title": "Passer l'initialisation pour l'instant ?", "agent.layout.switchMessage": "Pas d'humeur aujourd'hui ? Vous pouvez passer en {{mode}} ou {{skip}}.", + "agent.layout.switchMessageClassic": "Pas d'humeur aujourd'hui ? Vous pouvez passer à {{mode}}.", + "agent.messenger.cta.discord": "Ajouter à Discord", + "agent.messenger.cta.slack": "Ajouter à Slack", + "agent.messenger.cta.telegram": "Ouvrir dans Telegram", + "agent.messenger.subtitle": "Discutez avec votre agent sur Telegram, Slack ou Discord", + "agent.messenger.telegramQrCaption": "Scannez avec l'appareil photo de votre téléphone", + "agent.messenger.title": "Gardez-moi avec vous, où que vous envoyiez des messages", "agent.modeSwitch.agent": "Conversationnel", "agent.modeSwitch.classic": "Classique", "agent.modeSwitch.collapse": "Réduire", @@ -68,6 +75,13 @@ "agent.wrapUp.confirm.content": "J’enregistrerai ce que nous avons couvert jusqu’ici. Vous pourrez toujours revenir discuter plus tard.", "agent.wrapUp.confirm.ok": "Terminer maintenant", "agent.wrapUp.confirm.title": "Terminer l’accueil maintenant ?", + "agentPicker.allCategories": "Tous", + "agentPicker.continue": "Continuer", + "agentPicker.skip": "Passer pour l'instant", + "agentPicker.subtitle": "Ajoutez-en quelques-uns à votre bibliothèque maintenant — découvrez-en plus à tout moment plus tard.", + "agentPicker.title": "Ajoutons quelques agents à votre bibliothèque", + "agentPicker.title2": "Choisissez ceux qui correspondent à votre façon de travailler", + "agentPicker.title3": "Vous pouvez toujours en ajouter plus tard — commencez par quelques-uns", "back": "Retour", "finish": "Commencer", "interests.area.business": "Affaires & Stratégie", diff --git a/locales/fr-FR/plugin.json b/locales/fr-FR/plugin.json index f62a25600d..043f1b9cd7 100644 --- a/locales/fr-FR/plugin.json +++ b/locales/fr-FR/plugin.json @@ -39,8 +39,8 @@ "builtins.lobe-agent-documents.inspector.docCount": "{{count}} documents", "builtins.lobe-agent-documents.inspector.opsCount": "{{count}} opérations", "builtins.lobe-agent-documents.inspector.opsResult": "{{success}}/{{total}} opérations", - "builtins.lobe-agent-documents.inspector.target.agent": "dans l'agent", - "builtins.lobe-agent-documents.inspector.target.currentTopic": "dans le sujet", + "builtins.lobe-agent-documents.inspector.scope.agent": "dans l'agent", + "builtins.lobe-agent-documents.inspector.scope.currentTopic": "dans le sujet", "builtins.lobe-agent-documents.title": "Documents de l'agent", "builtins.lobe-agent-management.apiName.callAgent": "Appeler un agent", "builtins.lobe-agent-management.apiName.createAgent": "Créer un agent", @@ -90,6 +90,14 @@ "builtins.lobe-agent.title": "Agent Lobe", "builtins.lobe-claude-code.agent.instruction": "Consigne", "builtins.lobe-claude-code.agent.result": "Résultat", + "builtins.lobe-claude-code.task.createLabel": "Création de tâche : ", + "builtins.lobe-claude-code.task.getLabel": "Inspection de la tâche #{{taskId}}", + "builtins.lobe-claude-code.task.listLabel": "Liste des tâches", + "builtins.lobe-claude-code.task.updateCompleted": "Terminée", + "builtins.lobe-claude-code.task.updateDeleted": "Supprimée", + "builtins.lobe-claude-code.task.updateInProgress": "Commencée", + "builtins.lobe-claude-code.task.updateLabel": "Mise à jour de la tâche #{{taskId}}", + "builtins.lobe-claude-code.task.updatePending": "Réinitialisée", "builtins.lobe-claude-code.todoWrite.allDone": "Toutes les tâches sont terminées", "builtins.lobe-claude-code.todoWrite.currentStep": "Étape actuelle", "builtins.lobe-claude-code.todoWrite.todos": "Tâches", diff --git a/locales/fr-FR/providers.json b/locales/fr-FR/providers.json index ba3e58742e..cc72e426df 100644 --- a/locales/fr-FR/providers.json +++ b/locales/fr-FR/providers.json @@ -33,7 +33,6 @@ "jina.description": "Fondée en 2020, Jina AI est une entreprise leader en IA de recherche. Sa pile technologique comprend des modèles vectoriels, des rerankers et de petits modèles linguistiques pour créer des applications de recherche générative et multimodale fiables et de haute qualité.", "kimicodingplan.description": "Kimi Code de Moonshot AI offre un accès aux modèles Kimi, y compris K2.5, pour des tâches de codage.", "lmstudio.description": "LM Studio est une application de bureau pour développer et expérimenter avec des LLMs sur votre ordinateur.", - "lobehub.description": "LobeHub Cloud utilise des API officielles pour accéder aux modèles d'IA et mesure l'utilisation avec des Crédits liés aux jetons des modèles.", "longcat.description": "LongCat est une série de grands modèles d'IA générative développés indépendamment par Meituan. Elle est conçue pour améliorer la productivité interne de l'entreprise et permettre des applications innovantes grâce à une architecture informatique efficace et de puissantes capacités multimodales.", "minimax.description": "Fondée en 2021, MiniMax développe une IA généraliste avec des modèles fondamentaux multimodaux, incluant des modèles texte MoE à un billion de paramètres, des modèles vocaux et visuels, ainsi que des applications comme Hailuo AI.", "minimaxcodingplan.description": "Le plan de jetons MiniMax offre un accès aux modèles MiniMax, y compris M2.7, pour des tâches de codage via un abonnement à tarif fixe.", diff --git a/locales/fr-FR/subscription.json b/locales/fr-FR/subscription.json index a76c9e24be..e0edcc590e 100644 --- a/locales/fr-FR/subscription.json +++ b/locales/fr-FR/subscription.json @@ -55,6 +55,12 @@ "credits.autoTopUp.toggle": "Activer la recharge automatique", "credits.autoTopUp.upgradeHint": "Abonnez-vous à un plan payant pour activer la recharge automatique", "credits.autoTopUp.validation.targetMustExceedThreshold": "Le solde cible doit être supérieur au seuil", + "credits.costEstimateHint.desc": "Afficher un avertissement léger avant l'envoi lorsque le coût estimé du modèle atteint votre seuil", + "credits.costEstimateHint.saveError": "Échec de l'enregistrement des paramètres d'alerte de l'estimation des coûts", + "credits.costEstimateHint.saveSuccess": "Paramètres d'alerte de l'estimation des coûts enregistrés", + "credits.costEstimateHint.threshold": "Seuil d'avertissement", + "credits.costEstimateHint.title": "Alerte d'estimation des coûts", + "credits.costEstimateHint.validation.threshold": "Le seuil doit être supérieur ou égal à 0", "credits.packages.auto": "Automatique", "credits.packages.charged": "Facturé ${{amount}}", "credits.packages.expired": "Expiré", diff --git a/locales/fr-FR/tool.json b/locales/fr-FR/tool.json index 1cb4fc08e5..c1782d1f45 100644 --- a/locales/fr-FR/tool.json +++ b/locales/fr-FR/tool.json @@ -40,6 +40,14 @@ "agentMarketplace.render.alreadyInLibraryTag": "Déjà dans la bibliothèque", "agentMarketplace.render.alreadyInLibrary_one": "{{count}} déjà dans la bibliothèque", "agentMarketplace.render.alreadyInLibrary_other": "{{count}} déjà dans la bibliothèque", + "claudeCode.askUserQuestion.escape.back": "Retour aux options", + "claudeCode.askUserQuestion.escape.enter": "Ou tapez directement", + "claudeCode.askUserQuestion.escape.placeholder": "Tapez votre réponse ici…", + "claudeCode.askUserQuestion.multiSelectTag": "(sélection multiple)", + "claudeCode.askUserQuestion.skip": "Passer", + "claudeCode.askUserQuestion.submit": "Soumettre", + "claudeCode.askUserQuestion.timeExpired": "Temps écoulé — utilisation de l'option 1 pour chaque question.", + "claudeCode.askUserQuestion.timeRemaining": "Temps restant : {{time}} · les questions sans réponse par défaut passent à l'option 1 à l'expiration.", "codeInterpreter-legacy.error": "Erreur d'exécution", "codeInterpreter-legacy.executing": "Exécution en cours...", "codeInterpreter-legacy.files": "Fichiers :", diff --git a/locales/fr-FR/topic.json b/locales/fr-FR/topic.json index aac6fdbe74..de6a1b8192 100644 --- a/locales/fr-FR/topic.json +++ b/locales/fr-FR/topic.json @@ -56,6 +56,7 @@ "renameModal.title": "Renommer le sujet", "searchPlaceholder": "Rechercher des sujets...", "searchResultEmpty": "Aucun résultat trouvé.", + "sidebar.title": "Sujets", "taskManager.agent": "Agent de tâches", "taskManager.welcome": "Demandez-moi à propos de vos tâches", "temp": "Temporaire", diff --git a/locales/it-IT/chat.json b/locales/it-IT/chat.json index 6c6c245d8f..ca099c0bce 100644 --- a/locales/it-IT/chat.json +++ b/locales/it-IT/chat.json @@ -220,6 +220,13 @@ "inbox.title": "Lobe AI", "input.addAi": "Aggiungi un messaggio AI", "input.addUser": "Aggiungi un messaggio utente", + "input.costEstimate.creditsPerMillionTokens": "{{credits}} crediti/M token", + "input.costEstimate.hint": "Costo stimato: ~{{credits}} crediti", + "input.costEstimate.inputLabel": "Input", + "input.costEstimate.outputLabel": "Output", + "input.costEstimate.settingsLink": "Regola la soglia di avviso", + "input.costEstimate.tokenCount": "~{{tokens}} token", + "input.costEstimate.tooltip": "Stimato dal contesto attuale, dagli strumenti e dai prezzi del modello. Il costo effettivo potrebbe variare.", "input.disclaimer": "Gli agenti possono commettere errori. Usa il tuo giudizio per informazioni critiche.", "input.errorMsg": "Invio non riuscito: {{errorMsg}}. Riprova o invia più tardi.", "input.more": "Altro", @@ -779,6 +786,7 @@ "upload.preview.prepareTasks": "Preparazione segmenti...", "upload.preview.status.pending": "Preparazione al caricamento...", "upload.preview.status.processing": "Elaborazione file...", + "upload.validation.unsupportedFileType": "Tipo di file non supportato: {{files}}. Immagini supportate: JPG, PNG, GIF, WebP. Documenti supportati includono PDF, Word, Excel, PowerPoint, Markdown, testo, CSV, JSON e file di codice.", "upload.validation.videoSizeExceeded": "La dimensione del file video non deve superare i 20MB. Dimensione attuale: {{actualSize}}.", "viewMode.fullWidth": "Larghezza completa", "viewMode.normal": "Standard", @@ -894,6 +902,8 @@ "workingPanel.localFile.closeOther": "Chiudi Altri", "workingPanel.localFile.closeRight": "Chiudi a Destra", "workingPanel.localFile.error": "Impossibile caricare questo file", + "workingPanel.localFile.preview.raw": "Grezzo", + "workingPanel.localFile.preview.render": "Anteprima", "workingPanel.localFile.truncated": "Anteprima del file troncata a {{limit}} caratteri", "workingPanel.progress": "Progress", "workingPanel.progress.allCompleted": "All tasks completed", @@ -959,6 +969,8 @@ "workingPanel.review.viewMode.unified": "Passa alla vista unificata", "workingPanel.review.wordWrap.disable": "Disabilita ritorno a capo automatico", "workingPanel.review.wordWrap.enable": "Abilita ritorno a capo automatico", + "workingPanel.skills.empty": "Nessuna competenza trovata in questo progetto", + "workingPanel.skills.title": "Competenze", "workingPanel.space": "Spazio", "workingPanel.title": "Working Panel", "you": "Tu", diff --git a/locales/it-IT/components.json b/locales/it-IT/components.json index 1596b25880..ce7111a375 100644 --- a/locales/it-IT/components.json +++ b/locales/it-IT/components.json @@ -121,6 +121,16 @@ "ModelSwitchPanel.detail.context": "Lunghezza del Contesto", "ModelSwitchPanel.detail.pricing": "Prezzi", "ModelSwitchPanel.detail.pricing.cachedInput": "Input memorizzato ${{amount}}/M", + "ModelSwitchPanel.detail.pricing.credits.cachedInput": "Input memorizzato nella cache {{amount}} crediti/M token", + "ModelSwitchPanel.detail.pricing.credits.image": "crediti/img", + "ModelSwitchPanel.detail.pricing.credits.input": "Input {{amount}} crediti/M token", + "ModelSwitchPanel.detail.pricing.credits.megapixel": "crediti/MP", + "ModelSwitchPanel.detail.pricing.credits.millionCharacters": "crediti/M caratteri", + "ModelSwitchPanel.detail.pricing.credits.millionTokens": "crediti/M token", + "ModelSwitchPanel.detail.pricing.credits.output": "Output {{amount}} crediti/M token", + "ModelSwitchPanel.detail.pricing.credits.perImage": "~ {{amount}} crediti / immagine", + "ModelSwitchPanel.detail.pricing.credits.perVideo": "~ {{amount}} crediti / video", + "ModelSwitchPanel.detail.pricing.credits.second": "crediti/s", "ModelSwitchPanel.detail.pricing.group.audio": "Audio", "ModelSwitchPanel.detail.pricing.group.image": "Immagine", "ModelSwitchPanel.detail.pricing.group.text": "Testo", diff --git a/locales/it-IT/editor.json b/locales/it-IT/editor.json index 21c28f73b3..d89dfbcabc 100644 --- a/locales/it-IT/editor.json +++ b/locales/it-IT/editor.json @@ -1,8 +1,10 @@ { "actionTag.category.command": "Comando", + "actionTag.category.projectSkill": "Competenza di progetto", "actionTag.category.skill": "Abilità", "actionTag.category.tool": "Strumento", "actionTag.tooltip.command": "Esegue un comando slash lato client prima dell'invio.", + "actionTag.tooltip.projectSkill": "Inviato come un'istruzione slash affinché la CLI dell'agente esegua la competenza di progetto corrispondente.", "actionTag.tooltip.skill": "Carica un pacchetto di abilità riutilizzabile per questa richiesta.", "actionTag.tooltip.tool": "Contrassegna uno strumento selezionato esplicitamente dall'utente per questa richiesta.", "actions.expand.off": "Comprimi", diff --git a/locales/it-IT/home.json b/locales/it-IT/home.json index 7cdb5a6df0..f65149e150 100644 --- a/locales/it-IT/home.json +++ b/locales/it-IT/home.json @@ -11,6 +11,19 @@ "brief.action.ignore": "Ignora", "brief.action.retry": "Riprova", "brief.addFeedback": "Condividi feedback", + "brief.agentSignal.selfReview.applied.heading": "Aggiornato", + "brief.agentSignal.selfReview.applied.summary": "{{count}} aggiornamento del sogno è stato applicato.", + "brief.agentSignal.selfReview.applied.summary_plural": "{{count}} aggiornamenti del sogno sono stati applicati.", + "brief.agentSignal.selfReview.applied.title": "Risorse del sogno aggiornate", + "brief.agentSignal.selfReview.error.heading": "Problema", + "brief.agentSignal.selfReview.error.summary": "Alcuni lavori non sono stati completati durante questo sogno.", + "brief.agentSignal.selfReview.error.title": "Il sogno ha incontrato un problema", + "brief.agentSignal.selfReview.ideas.summary": "Note del sogno salvate per una revisione futura.", + "brief.agentSignal.selfReview.ideas.title": "Note del sogno", + "brief.agentSignal.selfReview.proposal.heading": "Suggerimento", + "brief.agentSignal.selfReview.proposal.summary": "{{count}} suggerimento del sogno necessita della tua revisione.", + "brief.agentSignal.selfReview.proposal.summary_plural": "{{count}} suggerimenti del sogno necessitano della tua revisione.", + "brief.agentSignal.selfReview.proposal.title": "Suggerimento del sogno da rivedere", "brief.collapse": "Mostra meno", "brief.commentPlaceholder": "Condividi il tuo feedback...", "brief.commentSubmit": "Invia feedback", diff --git a/locales/it-IT/hotkey.json b/locales/it-IT/hotkey.json index 3c347b39a0..cba4834717 100644 --- a/locales/it-IT/hotkey.json +++ b/locales/it-IT/hotkey.json @@ -1,8 +1,6 @@ { "addUserMessage.desc": "Aggiungi l'input corrente come messaggio utente senza avviare la generazione", "addUserMessage.title": "Aggiungi un Messaggio Utente", - "clearCurrentMessages.desc": "Cancella i messaggi e i file caricati dalla conversazione corrente", - "clearCurrentMessages.title": "Cancella Messaggi della Conversazione", "commandPalette.desc": "Apri la palette comandi globale per accedere rapidamente alle funzionalità", "commandPalette.title": "Palette Comandi", "deleteAndRegenerateMessage.desc": "Elimina l'ultimo messaggio e rigenera", diff --git a/locales/it-IT/models.json b/locales/it-IT/models.json index 75b60b3e1d..bef1ab274d 100644 --- a/locales/it-IT/models.json +++ b/locales/it-IT/models.json @@ -106,7 +106,6 @@ "MiniMax-Hailuo-2.3.description": "Nuovo modello di generazione video con aggiornamenti completi nei movimenti del corpo, realismo fisico e aderenza alle istruzioni.", "MiniMax-M1.description": "Nuovo modello di ragionamento proprietario con 80K chain-of-thought e 1M di input, con prestazioni comparabili ai migliori modelli globali.", "MiniMax-M2-Stable.description": "Progettato per flussi di lavoro di codifica e agenti efficienti, con maggiore concorrenza per l'uso commerciale.", - "MiniMax-M2.1-Lightning.description": "Potenti capacità di programmazione multilingue con inferenza più rapida ed efficiente.", "MiniMax-M2.1-highspeed.description": "Potenti capacità di programmazione multilingue, esperienza di programmazione completamente aggiornata. Più veloce ed efficiente.", "MiniMax-M2.1.description": "MiniMax-M2.1 è un modello open-source di punta di MiniMax, progettato per affrontare compiti complessi del mondo reale. I suoi punti di forza principali sono le capacità di programmazione multilingue e la risoluzione di compiti complessi come agente.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: Stesse prestazioni di M2.5 con inferenza più veloce.", @@ -315,11 +314,11 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku è il modello più veloce e compatto di Anthropic, progettato per risposte quasi istantanee con prestazioni rapide e accurate.", "claude-3-opus-20240229.description": "Claude 3 Opus è il modello più potente di Anthropic per compiti altamente complessi, eccellendo in prestazioni, intelligenza, fluidità e comprensione.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet bilancia intelligenza e velocità per carichi di lavoro aziendali, offrendo alta utilità a costi inferiori e distribuzione affidabile su larga scala.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 è il modello Haiku più veloce e intelligente di Anthropic, con velocità fulminea e pensiero esteso.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 è il modello Haiku più veloce e intelligente di Anthropic, con velocità fulminea e capacità di ragionamento estese.", "claude-haiku-4-5.description": "Claude Haiku 4.5 di Anthropic — Haiku di nuova generazione con ragionamento e visione migliorati.", "claude-haiku-4.5.description": "Claude Haiku 4.5 è il modello Haiku più veloce e intelligente di Anthropic, con velocità fulminea e capacità di ragionamento estese.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking è una variante avanzata in grado di mostrare il proprio processo di ragionamento.", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1 è l'ultimo e più avanzato modello di Anthropic per compiti altamente complessi, eccellendo in prestazioni, intelligenza, fluidità e comprensione.", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1 è il modello più recente e avanzato di Anthropic per compiti altamente complessi, eccellendo in prestazioni, intelligenza, fluidità e comprensione.", "claude-opus-4-1.description": "Claude Opus 4.1 di Anthropic — modello premium di ragionamento con capacità di analisi approfondita.", "claude-opus-4-20250514.description": "Claude Opus 4 è il modello più potente di Anthropic per compiti altamente complessi, eccellendo in prestazioni, intelligenza, fluidità e comprensione.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 è il modello di punta di Anthropic, che combina intelligenza eccezionale e prestazioni scalabili, ideale per compiti complessi che richiedono risposte e ragionamenti di altissima qualità.", @@ -330,8 +329,8 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 è il modello più intelligente di Anthropic per la creazione di agenti e la programmazione.", "claude-opus-4.6.description": "Claude Opus 4.6 è il modello più intelligente di Anthropic per la creazione di agenti e la programmazione.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking può produrre risposte quasi istantanee o riflessioni estese passo dopo passo con processo visibile.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 è il modello più intelligente di Anthropic fino ad oggi, offrendo risposte quasi istantanee o pensiero esteso passo dopo passo con controllo dettagliato per gli utenti API.", - "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 è il modello più intelligente di Anthropic fino ad oggi.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 può produrre risposte quasi istantanee o pensieri estesi passo dopo passo con un processo visibile.", + "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 è il modello più intelligente mai creato da Anthropic.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 di Anthropic — Sonnet migliorato con prestazioni di codifica avanzate.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 di Anthropic — ultimo Sonnet con codifica superiore e utilizzo di strumenti.", "claude-sonnet-4.5.description": "Claude Sonnet 4.5 è il modello più intelligente mai creato da Anthropic.", @@ -404,7 +403,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) è un modello innovativo che offre una profonda comprensione linguistica e interazione.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 è un modello di nuova generazione per il ragionamento, con capacità avanzate di ragionamento complesso e chain-of-thought per compiti di analisi approfondita.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 è un modello di ragionamento di nuova generazione con capacità avanzate di ragionamento complesso e catena di pensiero.", - "deepseek-chat.description": "Alias di compatibilità per la modalità non pensante di DeepSeek V4 Flash. Destinato alla deprecazione — utilizzare DeepSeek V4 Flash invece.", + "deepseek-chat.description": "Un nuovo modello open-source che combina capacità generali e di codifica. Preserva il dialogo generale del modello di chat e la forte capacità di codifica del modello coder, con un migliore allineamento delle preferenze. DeepSeek-V2.5 migliora anche la scrittura e il seguito delle istruzioni.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B è un modello linguistico per il codice addestrato su 2 trilioni di token (87% codice, 13% testo in cinese/inglese). Introduce una finestra di contesto da 16K e compiti di completamento intermedio, offrendo completamento di codice a livello di progetto e riempimento di snippet.", "deepseek-coder-v2.description": "DeepSeek Coder V2 è un modello MoE open-source per il codice che ottiene ottimi risultati nei compiti di programmazione, comparabile a GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 è un modello MoE open-source per il codice che ottiene ottimi risultati nei compiti di programmazione, comparabile a GPT-4 Turbo.", @@ -426,7 +425,7 @@ "deepseek-r1-fast-online.description": "DeepSeek R1 versione completa veloce con ricerca web in tempo reale, che combina capacità su scala 671B e risposte rapide.", "deepseek-r1-online.description": "DeepSeek R1 versione completa con 671 miliardi di parametri e ricerca web in tempo reale, che offre una comprensione e generazione più avanzate.", "deepseek-r1.description": "DeepSeek-R1 utilizza dati cold-start prima dell'RL e ottiene prestazioni comparabili a OpenAI-o1 in matematica, programmazione e ragionamento.", - "deepseek-reasoner.description": "Alias di compatibilità per la modalità pensante di DeepSeek V4 Flash. Destinato alla deprecazione — utilizzare DeepSeek V4 Flash invece.", + "deepseek-reasoner.description": "Un modello di ragionamento DeepSeek focalizzato su compiti di ragionamento logico complessi.", "deepseek-v2.description": "DeepSeek V2 è un modello MoE efficiente per un'elaborazione conveniente.", "deepseek-v2:236b.description": "DeepSeek V2 236B è il modello DeepSeek focalizzato sul codice con forte capacità di generazione.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 è un modello MoE con 671 miliardi di parametri, con punti di forza nella programmazione, capacità tecnica, comprensione del contesto e gestione di testi lunghi.", @@ -491,8 +490,6 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 è un modello di generazione di immagini di ByteDance Seed, che supporta input di testo e immagini con generazione di immagini di alta qualità e altamente controllabile. Genera immagini da prompt testuali.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 è l'ultimo modello multimodale di ByteDance, che integra capacità di generazione da testo a immagine, immagine a immagine e generazione di immagini in batch, incorporando anche conoscenze di senso comune e capacità di ragionamento. Rispetto alla versione precedente 4.0, offre una qualità di generazione significativamente migliorata, con una maggiore coerenza nell'editing e nella fusione di più immagini. Offre un controllo più preciso sui dettagli visivi, producendo testi e volti piccoli in modo più naturale, e raggiunge una disposizione e una colorazione più armoniose, migliorando l'estetica complessiva.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite è l'ultimo modello di generazione di immagini di ByteDance. Per la prima volta, integra capacità di recupero online, consentendo di incorporare informazioni web in tempo reale e migliorare la tempestività delle immagini generate. L'intelligenza del modello è stata inoltre aggiornata, consentendo un'interpretazione precisa di istruzioni complesse e contenuti visivi. Inoltre, offre una copertura globale della conoscenza migliorata, una maggiore coerenza di riferimento e una qualità di generazione superiore in scenari professionali, soddisfacendo meglio le esigenze di creazione visiva a livello aziendale.", - "dreamina-seedance-2-0-260128.description": "Seedance 2.0 di ByteDance è il modello di generazione video più potente, supportando la generazione di video multimodali di riferimento, editing video, estensione video, testo-a-video e immagine-a-video con audio sincronizzato.", - "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast di ByteDance offre le stesse capacità di Seedance 2.0 con velocità di generazione più rapide a un prezzo più competitivo.", "emohaa.description": "Emohaa è un modello per la salute mentale con capacità di consulenza professionale per aiutare gli utenti a comprendere le problematiche emotive.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B è un modello open-source leggero per implementazioni locali e personalizzate.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview è un modello di anteprima con finestra contestuale da 8K per la valutazione di ERNIE 4.5.", @@ -506,6 +503,7 @@ "ernie-5.0-thinking-latest.description": "Wenxin 5.0 Thinking è un modello di punta nativo full-modal con modellazione unificata di testo, immagini, audio e video. Offre ampi miglioramenti nelle capacità per domande complesse, creazione e scenari con agenti.", "ernie-5.0-thinking-preview.description": "Wenxin 5.0 Thinking Preview è un modello di punta nativo full-modal con modellazione unificata di testo, immagini, audio e video. Offre ampi miglioramenti nelle capacità per domande complesse, creazione e scenari con agenti.", "ernie-5.0.description": "ERNIE 5.0, il modello di nuova generazione della serie ERNIE, è un grande modello multimodale nativo. Adotta un approccio di modellazione multimodale unificato, modellando congiuntamente testo, immagini, audio e video per offrire capacità multimodali complete. Le sue abilità fondamentali sono state significativamente migliorate, raggiungendo prestazioni elevate nelle valutazioni di benchmark. Eccelle particolarmente nella comprensione multimodale, nel seguito delle istruzioni, nella scrittura creativa, nell'accuratezza fattuale, nella pianificazione degli agenti e nell'utilizzo degli strumenti.", + "ernie-5.1.description": "ERNIE 5.1 è l'ultimo modello della serie ERNIE, con aggiornamenti completi alle sue capacità fondamentali. Dimostra miglioramenti significativi in aree come agenti, elaborazione della conoscenza, ragionamento e ricerca approfondita. Questa versione adotta un'architettura di apprendimento per rinforzo completamente asincrona e decoupled, progettata specificamente per affrontare le sfide chiave nell'evoluzione dei modelli di grandi dimensioni verso la decisione autonoma degli agenti, inclusi discrepanze numeriche tra addestramento e inferenza, basso utilizzo delle risorse di calcolo eterogenee e problemi globali causati dagli effetti di lunga coda. Inoltre, vengono impiegate tecniche di post-addestramento su larga scala per agenti per migliorare ulteriormente le capacità e le prestazioni di generalizzazione del modello. Attraverso un framework collaborativo a tre stadi che coinvolge ambiente, esperto e processi di fusione, l'approccio non solo garantisce l'efficienza dell'addestramento ma migliora significativamente la stabilità e le prestazioni del modello su compiti complessi.", "ernie-char-fiction-8k-preview.description": "ERNIE Character Fiction 8K Preview è un’anteprima del modello per la creazione di personaggi e trame, utile per valutazioni e test di funzionalità.", "ernie-char-fiction-8k.description": "ERNIE Character Fiction 8K è un modello con personalità per romanzi e creazione di trame, adatto alla generazione di storie di lunga durata.", "ernie-image-turbo.description": "ERNIE-Image è un modello di testo-immagine con 8 miliardi di parametri sviluppato da Baidu. Si colloca tra i migliori in diversi benchmark, raggiungendo il primo posto ex aequo in SuperCLUE in Cina e guidando la pista open-source.", @@ -517,8 +515,7 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K è un modello di pensiero veloce con contesto da 32K per ragionamento complesso e chat multi-turno.", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview è un’anteprima del modello di pensiero per valutazioni e test.", "ernie-x1.1.description": "ERNIE X1.1 è un'anteprima del modello di pensiero per valutazione e test.", - "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5, sviluppato dal team Seed di ByteDance, supporta l'editing e la composizione multi-immagine. Caratteristiche includono coerenza del soggetto migliorata, esecuzione precisa delle istruzioni, comprensione della logica spaziale, espressione estetica, layout di poster e design di loghi con rendering testo-immagine ad alta precisione.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, sviluppato da ByteDance Seed, supporta input di testo e immagini per una generazione di immagini altamente controllabile e di alta qualità a partire dai prompt.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 è un modello di generazione di immagini di ByteDance Seed, che supporta input di testo e immagini con generazione di immagini altamente controllabile e di alta qualità. Genera immagini a partire da prompt testuali.", "fal-ai/flux-kontext/dev.description": "FLUX.1 è un modello focalizzato sull’editing di immagini, che supporta input di testo e immagini.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] accetta testo e immagini di riferimento come input, consentendo modifiche locali mirate e trasformazioni complesse della scena globale.", "fal-ai/flux/krea.description": "Flux Krea [dev] è un modello di generazione di immagini con una preferenza estetica per immagini più realistiche e naturali.", @@ -526,8 +523,8 @@ "fal-ai/hunyuan-image/v3.description": "Un potente modello nativo multimodale per la generazione di immagini.", "fal-ai/imagen4/preview.description": "Modello di generazione di immagini di alta qualità sviluppato da Google.", "fal-ai/nano-banana.description": "Nano Banana è il modello multimodale nativo più recente, veloce ed efficiente di Google, che consente la generazione e l’editing di immagini tramite conversazione.", - "fal-ai/qwen-image-edit.description": "Un modello professionale di editing immagini del team Qwen, che supporta modifiche semantiche e di aspetto, editing preciso di testo in cinese/inglese, trasferimento di stile, rotazione e altro.", - "fal-ai/qwen-image.description": "Un potente modello di generazione immagini del team Qwen con una forte resa del testo in cinese e stili visivi diversificati.", + "fal-ai/qwen-image-edit.description": "Un modello professionale di editing di immagini del team Qwen che supporta modifiche semantiche e di aspetto, modifica con precisione testo in cinese e inglese, e consente modifiche di alta qualità come trasferimento di stile e rotazione di oggetti.", + "fal-ai/qwen-image.description": "Un potente modello di generazione di immagini del team Qwen con impressionante rendering di testo in cinese e stili visivi diversificati.", "flux-1-schnell.description": "Modello testo-immagine da 12 miliardi di parametri di Black Forest Labs che utilizza la distillazione latente avversariale per generare immagini di alta qualità in 1-4 passaggi. Con licenza Apache-2.0 per uso personale, di ricerca e commerciale.", "flux-dev.description": "Modello open-source per la ricerca e sviluppo nella generazione di immagini, ottimizzato in modo efficiente per la ricerca innovativa non commerciale.", "flux-kontext-max.description": "Generazione ed editing di immagini contestuali all’avanguardia, combinando testo e immagini per risultati precisi e coerenti.", @@ -566,10 +563,10 @@ "gemini-3-flash-preview.description": "Gemini 3 Flash è il modello più intelligente progettato per la velocità, che combina intelligenza all'avanguardia con un eccellente ancoraggio alla ricerca.", "gemini-3-flash.description": "Gemini 3 Flash di Google — modello ultra-veloce con supporto per input multimodali.", "gemini-3-pro-image-preview.description": "Gemini 3 Pro Image (Nano Banana Pro) è il modello di generazione di immagini di Google che supporta anche il dialogo multimodale.", - "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) è il modello di generazione immagini di Google e supporta anche la chat multimodale.", + "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) è il modello di generazione di immagini di Google e supporta anche la chat multimodale.", "gemini-3-pro-preview.description": "Gemini 3 Pro è il modello più potente di Google per agenti e codifica creativa, offrendo visuali più ricche e interazioni più profonde grazie a un ragionamento all'avanguardia.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) è il modello di generazione di immagini nativo più veloce di Google con supporto al pensiero, generazione e modifica di immagini conversazionali.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) offre qualità di immagine di livello Pro a velocità Flash con supporto per la chat multimodale.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) è il modello di generazione di immagini nativo più veloce di Google con supporto al pensiero, generazione e modifica di immagini conversazionali.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview è il modello multimodale più economico di Google, ottimizzato per compiti agentici ad alto volume, traduzione e elaborazione dati.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite è il modello multimodale più economico di Google, ottimizzato per compiti agentici ad alto volume, traduzione e elaborazione dati.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview migliora Gemini 3 Pro con capacità di ragionamento avanzate e aggiunge supporto per un livello di pensiero medio.", @@ -734,8 +731,6 @@ "grok-4-fast-reasoning.description": "Siamo entusiasti di presentare Grok 4 Fast, il nostro ultimo progresso nei modelli di ragionamento a basso costo.", "grok-4.20-0309-non-reasoning.description": "Una variante senza ragionamento per casi d'uso semplici.", "grok-4.20-0309-reasoning.description": "Modello intelligente e velocissimo che ragiona prima di rispondere.", - "grok-4.20-beta-0309-non-reasoning.description": "Una variante non pensante per casi d'uso semplici.", - "grok-4.20-beta-0309-reasoning.description": "Modello intelligente e ultra-veloce che ragiona prima di rispondere.", "grok-4.20-multi-agent-0309.description": "Un team di 4 o 16 agenti, eccelle nei casi d'uso di ricerca, non supporta attualmente strumenti client-side. Supporta solo strumenti server-side xAI (es. X Search, strumenti di ricerca web) e strumenti MCP remoti.", "grok-4.3.description": "Il modello linguistico di grandi dimensioni più orientato alla verità al mondo", "grok-4.description": "Ultimo modello di punta Grok con prestazioni ineguagliabili in linguaggio, matematica e ragionamento — un vero tuttofare. Attualmente punta a grok-4-0709; a causa di risorse limitate, il prezzo è temporaneamente superiore del 10% rispetto al prezzo ufficiale e si prevede un ritorno al prezzo ufficiale in seguito.", @@ -1220,8 +1215,6 @@ "qwq.description": "QwQ è un modello di ragionamento della famiglia Qwen. Rispetto ai modelli standard ottimizzati per istruzioni, offre capacità di pensiero e ragionamento che migliorano significativamente le prestazioni nei compiti difficili. QwQ-32B è un modello di medie dimensioni che compete con i migliori modelli di ragionamento come DeepSeek-R1 e o1-mini.", "qwq_32b.description": "Modello di ragionamento di medie dimensioni della famiglia Qwen. Rispetto ai modelli standard ottimizzati per istruzioni, le capacità di pensiero e ragionamento di QwQ migliorano significativamente le prestazioni nei compiti difficili.", "r1-1776.description": "R1-1776 è una variante post-addestrata di DeepSeek R1 progettata per fornire informazioni fattuali non censurate e imparziali.", - "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro di ByteDance supporta testo-a-video, immagine-a-video (prima immagine, prima+ultima immagine) e generazione audio sincronizzata con i visual.", - "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite di BytePlus offre generazione aumentata da recupero web per informazioni in tempo reale, interpretazione migliorata di prompt complessi e maggiore coerenza di riferimento per la creazione visiva professionale.", "solar-mini-ja.description": "Solar Mini (Ja) estende Solar Mini con un focus sul giapponese, mantenendo prestazioni efficienti e solide in inglese e coreano.", "solar-mini.description": "Solar Mini è un LLM compatto che supera GPT-3.5, con forte capacità multilingue in inglese e coreano, offrendo una soluzione efficiente e leggera.", "solar-pro.description": "Solar Pro è un LLM ad alta intelligenza di Upstage, focalizzato sull’esecuzione di istruzioni su una singola GPU, con punteggi IFEval superiori a 80. Attualmente supporta l’inglese; il rilascio completo è previsto per novembre 2024 con supporto linguistico ampliato e contesto più lungo.", diff --git a/locales/it-IT/onboarding.json b/locales/it-IT/onboarding.json index cf915af2ae..99ddd3e6ae 100644 --- a/locales/it-IT/onboarding.json +++ b/locales/it-IT/onboarding.json @@ -27,6 +27,13 @@ "agent.layout.skipConfirm.ok": "Salta per ora", "agent.layout.skipConfirm.title": "Saltare l’onboarding per ora?", "agent.layout.switchMessage": "Non è giornata? Puoi passare a {{mode}} oppure a {{skip}}.", + "agent.layout.switchMessageClassic": "Non ti va oggi? Puoi passare a {{mode}}.", + "agent.messenger.cta.discord": "Aggiungi a Discord", + "agent.messenger.cta.slack": "Aggiungi a Slack", + "agent.messenger.cta.telegram": "Apri in Telegram", + "agent.messenger.subtitle": "Chatta con il tuo agente su Telegram, Slack o Discord", + "agent.messenger.telegramQrCaption": "Scansiona con la fotocamera del tuo telefono", + "agent.messenger.title": "Tienimi con te, ovunque tu invii messaggi", "agent.modeSwitch.agent": "Conversazionale", "agent.modeSwitch.classic": "Classico", "agent.modeSwitch.collapse": "Comprimi", @@ -68,6 +75,13 @@ "agent.wrapUp.confirm.content": "Salverò ciò che abbiamo coperto finora. Puoi sempre tornare e continuare la conversazione più tardi.", "agent.wrapUp.confirm.ok": "Termina ora", "agent.wrapUp.confirm.title": "Vuoi terminare l’onboarding adesso?", + "agentPicker.allCategories": "Tutti", + "agentPicker.continue": "Continua", + "agentPicker.skip": "Salta per ora", + "agentPicker.subtitle": "Aggiungine alcuni alla tua libreria ora — scopri di più in qualsiasi momento.", + "agentPicker.title": "Aggiungiamo alcuni agenti alla tua libreria", + "agentPicker.title2": "Scegli quelli che si adattano al tuo modo di lavorare", + "agentPicker.title3": "Puoi sempre aggiungerne altri in seguito — inizia con alcuni", "back": "Indietro", "finish": "Inizia", "interests.area.business": "Business e Strategia", diff --git a/locales/it-IT/plugin.json b/locales/it-IT/plugin.json index 55c2680cf9..d9ef06ad4e 100644 --- a/locales/it-IT/plugin.json +++ b/locales/it-IT/plugin.json @@ -39,8 +39,8 @@ "builtins.lobe-agent-documents.inspector.docCount": "{{count}} documenti", "builtins.lobe-agent-documents.inspector.opsCount": "{{count}} operazioni", "builtins.lobe-agent-documents.inspector.opsResult": "{{success}}/{{total}} operazioni", - "builtins.lobe-agent-documents.inspector.target.agent": "nell'agente", - "builtins.lobe-agent-documents.inspector.target.currentTopic": "nell'argomento", + "builtins.lobe-agent-documents.inspector.scope.agent": "nell'agente", + "builtins.lobe-agent-documents.inspector.scope.currentTopic": "nell'argomento", "builtins.lobe-agent-documents.title": "Documenti dell'agente", "builtins.lobe-agent-management.apiName.callAgent": "Chiamare agente", "builtins.lobe-agent-management.apiName.createAgent": "Creare agente", @@ -90,6 +90,14 @@ "builtins.lobe-agent.title": "Agente Lobe", "builtins.lobe-claude-code.agent.instruction": "Istruzione", "builtins.lobe-claude-code.agent.result": "Risultato", + "builtins.lobe-claude-code.task.createLabel": "Creazione attività: ", + "builtins.lobe-claude-code.task.getLabel": "Ispezione attività #{{taskId}}", + "builtins.lobe-claude-code.task.listLabel": "Elenco attività", + "builtins.lobe-claude-code.task.updateCompleted": "Completato", + "builtins.lobe-claude-code.task.updateDeleted": "Eliminato", + "builtins.lobe-claude-code.task.updateInProgress": "Avviato", + "builtins.lobe-claude-code.task.updateLabel": "Aggiornamento attività #{{taskId}}", + "builtins.lobe-claude-code.task.updatePending": "Reimposta", "builtins.lobe-claude-code.todoWrite.allDone": "Tutte le attività completate", "builtins.lobe-claude-code.todoWrite.currentStep": "Passaggio attuale", "builtins.lobe-claude-code.todoWrite.todos": "Attività", diff --git a/locales/it-IT/providers.json b/locales/it-IT/providers.json index c586b43c52..aca1986b82 100644 --- a/locales/it-IT/providers.json +++ b/locales/it-IT/providers.json @@ -33,7 +33,6 @@ "jina.description": "Fondata nel 2020, Jina AI è un'azienda leader nell'AI per la ricerca. Il suo stack include modelli vettoriali, reranker e piccoli modelli linguistici per costruire app di ricerca generativa e multimodale affidabili e di alta qualità.", "kimicodingplan.description": "Kimi Code di Moonshot AI offre accesso ai modelli Kimi, inclusi K2.5, per attività di codifica.", "lmstudio.description": "LM Studio è un'app desktop per sviluppare e sperimentare con LLM direttamente sul tuo computer.", - "lobehub.description": "LobeHub Cloud utilizza API ufficiali per accedere ai modelli di intelligenza artificiale e misura l'utilizzo con Crediti legati ai token del modello.", "longcat.description": "LongCat è una serie di modelli AI generativi di grandi dimensioni sviluppati indipendentemente da Meituan. È progettato per migliorare la produttività interna dell'azienda e consentire applicazioni innovative attraverso un'architettura computazionale efficiente e potenti capacità multimodali.", "minimax.description": "Fondata nel 2021, MiniMax sviluppa AI generali con modelli fondamentali multimodali, inclusi modelli testuali MoE da trilioni di parametri, modelli vocali e visivi, oltre ad app come Hailuo AI.", "minimaxcodingplan.description": "Il piano di token MiniMax offre accesso ai modelli MiniMax, inclusi M2.7, per attività di codifica tramite un abbonamento a tariffa fissa.", diff --git a/locales/it-IT/subscription.json b/locales/it-IT/subscription.json index 55f325400d..cfe91f63af 100644 --- a/locales/it-IT/subscription.json +++ b/locales/it-IT/subscription.json @@ -55,6 +55,12 @@ "credits.autoTopUp.toggle": "Abilita ricarica automatica", "credits.autoTopUp.upgradeHint": "Abbonati a un piano a pagamento per abilitare la ricarica automatica", "credits.autoTopUp.validation.targetMustExceedThreshold": "Il saldo obiettivo deve essere maggiore della soglia", + "credits.costEstimateHint.desc": "Mostra un avviso leggero prima dell'invio quando il costo stimato del modello raggiunge la tua soglia", + "credits.costEstimateHint.saveError": "Impossibile salvare le impostazioni dell'avviso di stima dei costi", + "credits.costEstimateHint.saveSuccess": "Impostazioni dell'avviso di stima dei costi salvate", + "credits.costEstimateHint.threshold": "Soglia di Avviso", + "credits.costEstimateHint.title": "Avviso di Stima dei Costi", + "credits.costEstimateHint.validation.threshold": "La soglia deve essere maggiore o uguale a 0", "credits.packages.auto": "Automatico", "credits.packages.charged": "Addebitato ${{amount}}", "credits.packages.expired": "Scaduto", diff --git a/locales/it-IT/tool.json b/locales/it-IT/tool.json index 9e72963182..18bf3390eb 100644 --- a/locales/it-IT/tool.json +++ b/locales/it-IT/tool.json @@ -40,6 +40,14 @@ "agentMarketplace.render.alreadyInLibraryTag": "Già nella libreria", "agentMarketplace.render.alreadyInLibrary_one": "{{count}} già nella libreria", "agentMarketplace.render.alreadyInLibrary_other": "{{count}} già nella libreria", + "claudeCode.askUserQuestion.escape.back": "Torna alle opzioni", + "claudeCode.askUserQuestion.escape.enter": "Oppure digita direttamente", + "claudeCode.askUserQuestion.escape.placeholder": "Scrivi la tua risposta qui…", + "claudeCode.askUserQuestion.multiSelectTag": "(selezione multipla)", + "claudeCode.askUserQuestion.skip": "Salta", + "claudeCode.askUserQuestion.submit": "Invia", + "claudeCode.askUserQuestion.timeExpired": "Tempo scaduto — utilizzando l'opzione 1 per ogni domanda.", + "claudeCode.askUserQuestion.timeRemaining": "Tempo rimanente: {{time}} · le domande senza risposta predefinite all'opzione 1 allo scadere del tempo.", "codeInterpreter-legacy.error": "Errore di Esecuzione", "codeInterpreter-legacy.executing": "Esecuzione in corso...", "codeInterpreter-legacy.files": "File:", diff --git a/locales/it-IT/topic.json b/locales/it-IT/topic.json index cd1b3ba3e7..8afef52f17 100644 --- a/locales/it-IT/topic.json +++ b/locales/it-IT/topic.json @@ -56,6 +56,7 @@ "renameModal.title": "Rinomina argomento", "searchPlaceholder": "Cerca Argomenti...", "searchResultEmpty": "Nessun risultato trovato.", + "sidebar.title": "Argomenti", "taskManager.agent": "Agente di attività", "taskManager.welcome": "Chiedimi dei tuoi compiti", "temp": "Temporaneo", diff --git a/locales/ja-JP/chat.json b/locales/ja-JP/chat.json index 548a2c1d05..605d71013c 100644 --- a/locales/ja-JP/chat.json +++ b/locales/ja-JP/chat.json @@ -220,6 +220,13 @@ "inbox.title": "Lobe AI", "input.addAi": "アシスタントメッセージを追加", "input.addUser": "ユーザーメッセージを追加", + "input.costEstimate.creditsPerMillionTokens": "{{credits}} クレジット/Mトークン", + "input.costEstimate.hint": "推定コスト: 約{{credits}} クレジット", + "input.costEstimate.inputLabel": "入力", + "input.costEstimate.outputLabel": "出力", + "input.costEstimate.settingsLink": "警告閾値を調整", + "input.costEstimate.tokenCount": "約{{tokens}} トークン", + "input.costEstimate.tooltip": "現在のコンテキスト、ツール、およびモデルの価格設定に基づいて推定されています。実際のコストは異なる場合があります。", "input.disclaimer": "アシスタントも間違えることがあります。重要な情報はあなたの判断を最優先してください", "input.errorMsg": "送信中に問題が発生しました:{{errorMsg}}。再試行するか、時間を置いてから送ってください", "input.more": "もっと見る", @@ -779,6 +786,7 @@ "upload.preview.prepareTasks": "チャンクの準備中…", "upload.preview.status.pending": "アップロードの準備中…", "upload.preview.status.processing": "ファイル処理中…", + "upload.validation.unsupportedFileType": "サポートされていないファイルタイプ: {{files}}。サポートされている画像形式: JPG、PNG、GIF、WebP。サポートされているドキュメント形式: PDF、Word、Excel、PowerPoint、Markdown、テキスト、CSV、JSON、コードファイル。", "upload.validation.videoSizeExceeded": "ビデオファイルは 20MB を超えることはできません。現在は {{actualSize}} です", "viewMode.fullWidth": "全幅表示", "viewMode.normal": "標準", @@ -894,6 +902,8 @@ "workingPanel.localFile.closeOther": "その他を閉じる", "workingPanel.localFile.closeRight": "右側を閉じる", "workingPanel.localFile.error": "このファイルを読み込めませんでした", + "workingPanel.localFile.preview.raw": "生データ", + "workingPanel.localFile.preview.render": "プレビュー", "workingPanel.localFile.truncated": "ファイルプレビューは{{limit}}文字に切り詰められています", "workingPanel.progress": "Progress", "workingPanel.progress.allCompleted": "All tasks completed", @@ -959,6 +969,8 @@ "workingPanel.review.viewMode.unified": "統合ビューに切り替え", "workingPanel.review.wordWrap.disable": "ワードラップを無効化", "workingPanel.review.wordWrap.enable": "ワードラップを有効化", + "workingPanel.skills.empty": "このプロジェクトにはスキルが見つかりませんでした", + "workingPanel.skills.title": "スキル", "workingPanel.space": "スペース", "workingPanel.title": "Working Panel", "you": "あなた", diff --git a/locales/ja-JP/components.json b/locales/ja-JP/components.json index 0fdf7be26a..1ac82670f2 100644 --- a/locales/ja-JP/components.json +++ b/locales/ja-JP/components.json @@ -121,6 +121,16 @@ "ModelSwitchPanel.detail.context": "コンテキスト長", "ModelSwitchPanel.detail.pricing": "料金", "ModelSwitchPanel.detail.pricing.cachedInput": "キャッシュ済み入力 ${{amount}}/M", + "ModelSwitchPanel.detail.pricing.credits.cachedInput": "キャッシュされた入力 {{amount}} クレジット/M トークン", + "ModelSwitchPanel.detail.pricing.credits.image": "クレジット/画像", + "ModelSwitchPanel.detail.pricing.credits.input": "入力 {{amount}} クレジット/M トークン", + "ModelSwitchPanel.detail.pricing.credits.megapixel": "クレジット/MP", + "ModelSwitchPanel.detail.pricing.credits.millionCharacters": "クレジット/M 文字", + "ModelSwitchPanel.detail.pricing.credits.millionTokens": "クレジット/M トークン", + "ModelSwitchPanel.detail.pricing.credits.output": "出力 {{amount}} クレジット/M トークン", + "ModelSwitchPanel.detail.pricing.credits.perImage": "約 {{amount}} クレジット / 画像", + "ModelSwitchPanel.detail.pricing.credits.perVideo": "約 {{amount}} クレジット / 動画", + "ModelSwitchPanel.detail.pricing.credits.second": "クレジット/秒", "ModelSwitchPanel.detail.pricing.group.audio": "音声", "ModelSwitchPanel.detail.pricing.group.image": "画像", "ModelSwitchPanel.detail.pricing.group.text": "テキスト", diff --git a/locales/ja-JP/editor.json b/locales/ja-JP/editor.json index 1e4a0fec73..884ef0378f 100644 --- a/locales/ja-JP/editor.json +++ b/locales/ja-JP/editor.json @@ -1,8 +1,10 @@ { "actionTag.category.command": "コマンド", + "actionTag.category.projectSkill": "プロジェクトスキル", "actionTag.category.skill": "スキル", "actionTag.category.tool": "ツール", "actionTag.tooltip.command": "送信前にクライアント側のスラッシュコマンドを実行します。", + "actionTag.tooltip.projectSkill": "スラッシュ呼び出しとして送信され、エージェントのCLIが一致するプロジェクトスキルを実行します。", "actionTag.tooltip.skill": "このリクエスト用に再利用可能なスキルパッケージを読み込みます。", "actionTag.tooltip.tool": "このリクエストでユーザーが明示的に選択したツールを示します。", "actions.expand.off": "折りたたむ", diff --git a/locales/ja-JP/home.json b/locales/ja-JP/home.json index 53b7ba1630..281224ae3f 100644 --- a/locales/ja-JP/home.json +++ b/locales/ja-JP/home.json @@ -11,6 +11,19 @@ "brief.action.ignore": "無視する", "brief.action.retry": "再試行", "brief.addFeedback": "フィードバックを共有", + "brief.agentSignal.selfReview.applied.heading": "更新済み", + "brief.agentSignal.selfReview.applied.summary": "{{count}} 件の夢の更新が適用されました。", + "brief.agentSignal.selfReview.applied.summary_plural": "{{count}} 件の夢の更新が適用されました。", + "brief.agentSignal.selfReview.applied.title": "夢の更新されたリソース", + "brief.agentSignal.selfReview.error.heading": "問題", + "brief.agentSignal.selfReview.error.summary": "この夢の中で一部の作業を完了できませんでした。", + "brief.agentSignal.selfReview.error.title": "夢で問題が発生しました", + "brief.agentSignal.selfReview.ideas.summary": "将来のレビューのために夢のメモを保存しました。", + "brief.agentSignal.selfReview.ideas.title": "夢のメモ", + "brief.agentSignal.selfReview.proposal.heading": "提案", + "brief.agentSignal.selfReview.proposal.summary": "{{count}} 件の夢の提案があなたのレビューを必要としています。", + "brief.agentSignal.selfReview.proposal.summary_plural": "{{count}} 件の夢の提案があなたのレビューを必要としています。", + "brief.agentSignal.selfReview.proposal.title": "夢の提案がレビューを必要としています", "brief.collapse": "表示を減らす", "brief.commentPlaceholder": "フィードバックを入力してください...", "brief.commentSubmit": "フィードバックを送信", diff --git a/locales/ja-JP/hotkey.json b/locales/ja-JP/hotkey.json index f9af2639c4..5a4a62058b 100644 --- a/locales/ja-JP/hotkey.json +++ b/locales/ja-JP/hotkey.json @@ -1,8 +1,6 @@ { "addUserMessage.desc": "現在の入力内容をユーザーメッセージとして追加しますが、生成はトリガーしません", "addUserMessage.title": "ユーザーメッセージを追加", - "clearCurrentMessages.desc": "現在のセッションのメッセージとアップロードされたファイルをクリアする", - "clearCurrentMessages.title": "セッションメッセージをクリア", "commandPalette.desc": "グローバルコマンドパレットを開いて機能に素早くアクセス", "commandPalette.title": "コマンドパレット", "deleteAndRegenerateMessage.desc": "最後のメッセージを削除して再生成する", diff --git a/locales/ja-JP/models.json b/locales/ja-JP/models.json index a36ead92c8..2daf8d553c 100644 --- a/locales/ja-JP/models.json +++ b/locales/ja-JP/models.json @@ -106,7 +106,6 @@ "MiniMax-Hailuo-2.3.description": "身体動作、物理的リアリズム、指示追従性において全面的にアップグレードされた新しいビデオ生成モデル。", "MiniMax-M1.description": "80Kの思考連鎖と1Mの入力を備えた新しい社内推論モデルで、世界トップクラスのモデルに匹敵する性能を発揮します。", "MiniMax-M2-Stable.description": "効率的なコーディングとエージェントワークフローのために設計され、商用利用における高い同時実行性を実現します。", - "MiniMax-M2.1-Lightning.description": "強力な多言語プログラミング機能を備え、より高速かつ効率的な推論を実現。", "MiniMax-M2.1-highspeed.description": "強力な多言語プログラミング機能を備え、プログラミング体験を包括的に向上。より高速かつ効率的です。", "MiniMax-M2.1.description": "MiniMax-M2.1は、MiniMaxが開発したフラッグシップのオープンソース大規模モデルで、複雑な現実世界のタスク解決に特化しています。多言語プログラミング能力とエージェントとしての高度なタスク処理能力が主な強みです。", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: M2.5と同等の性能で推論速度が向上。", @@ -315,13 +314,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haikuは、Anthropicの最速かつ最小のモデルで、即時応答と高速かつ正確な性能を実現するよう設計されています。", "claude-3-opus-20240229.description": "Claude 3 Opusは、Anthropicの最も強力なモデルで、非常に複雑なタスクにおいて卓越した性能、知性、流暢さ、理解力を発揮します。", "claude-3-sonnet-20240229.description": "Claude 3 Sonnetは、知性と速度のバランスを取り、エンタープライズ向けのワークロードにおいて高い実用性とコスト効率、信頼性のある大規模展開を実現します。", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5はAnthropicの最速かつ最も知的なHaikuモデルで、驚異的な速度と拡張された思考能力を備えています。", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5は、Anthropicが提供する最速かつ最も賢いHaikuモデルで、驚異的なスピードと高度な推論能力を備えています。", "claude-haiku-4-5.description": "Claude Haiku 4.5 by Anthropic — 次世代Haikuモデルで、推論能力とビジョンが強化されています。", "claude-haiku-4.5.description": "Claude Haiku 4.5は、Anthropicの最速かつ最も賢いHaikuモデルで、驚異的なスピードと高度な推論能力を備えています。", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinkingは、推論プロセスを可視化できる高度なバリアントです。", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1はAnthropicの最新かつ最も高度なモデルで、非常に複雑なタスクにおいて卓越した性能、知性、流暢さ、理解力を発揮します。", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1は、Anthropicが提供する最新かつ最も高性能なモデルで、非常に複雑なタスクにおいて卓越したパフォーマンス、知性、流暢さ、理解力を発揮します。", "claude-opus-4-1.description": "Claude Opus 4.1 by Anthropic — 深い分析能力を備えたプレミアム推論モデル。", - "claude-opus-4-20250514.description": "Claude Opus 4はAnthropicの最も強力なモデルで、非常に複雑なタスクにおいて卓越した性能、知性、流暢さ、理解力を発揮します。", + "claude-opus-4-20250514.description": "Claude Opus 4は、Anthropicが提供する最も強力なモデルで、非常に複雑なタスクにおいて卓越したパフォーマンス、知性、流暢さ、理解力を発揮します。", "claude-opus-4-5-20251101.description": "Claude Opus 4.5は、Anthropicのフラッグシップモデルで、卓越した知性とスケーラブルな性能を兼ね備え、最高品質の応答と推論が求められる複雑なタスクに最適です。", "claude-opus-4-5.description": "Claude Opus 4.5 by Anthropic — 最高レベルの推論とコーディング能力を備えたフラッグシップモデル。", "claude-opus-4-6.description": "Claude Opus 4.6 by Anthropic — 1Mコンテキストウィンドウを備えたフラッグシップモデルで、高度な推論能力を提供します。", @@ -330,8 +329,8 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6は、エージェント構築やコーディングにおいてAnthropicの最も知的なモデルです。", "claude-opus-4.6.description": "Claude Opus 4.6は、エージェント構築やコーディングにおいてAnthropicの最も知的なモデルです。", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinkingは、即時応答または段階的な思考プロセスを可視化しながら出力できます。", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4はAnthropicのこれまでで最も知的なモデルで、ほぼ瞬時の応答や詳細なステップバイステップの思考を提供し、APIユーザー向けに細かい制御が可能です。", - "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5はAnthropicのこれまでで最も知的なモデルです。", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4は、瞬時の応答や、プロセスを可視化した段階的な思考を生成することができます。", + "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5は、これまでで最も知的なAnthropicのモデルです。", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 by Anthropic — コーディング性能が向上した改良版Sonnet。", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 by Anthropic — 最新のSonnetモデルで、優れたコーディングとツール使用能力を備えています。", "claude-sonnet-4.5.description": "Claude Sonnet 4.5は、これまでで最も知的なAnthropicのモデルです。", @@ -404,7 +403,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat(67B)は、深い言語理解と対話能力を提供する革新的なモデルです。", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 は次世代の推論モデルで、複雑な推論と連想思考に優れ、深い分析タスクに対応します。", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2は次世代推論モデルで、複雑な推論と連鎖的思考能力が強化されています。", - "deepseek-chat.description": "DeepSeek V4 Flash非思考モードの互換性エイリアス。廃止予定 — DeepSeek V4 Flashを使用してください。", + "deepseek-chat.description": "一般的な対話能力とコード能力を組み合わせた新しいオープンソースモデルです。チャットモデルの一般的な対話能力と、コーダーモデルの強力なコーディング能力を保持し、より良い嗜好調整を実現しています。DeepSeek-V2.5は、文章作成や指示の追従能力も向上させています。", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B は 2T トークン(コード 87%、中英テキスト 13%)で学習されたコード言語モデルです。16K のコンテキストウィンドウと Fill-in-the-Middle タスクを導入し、プロジェクトレベルのコード補完とスニペット補完を提供します。", "deepseek-coder-v2.description": "DeepSeek Coder V2 はオープンソースの MoE コードモデルで、コーディングタスクにおいて GPT-4 Turbo に匹敵する性能を発揮します。", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 はオープンソースの MoE コードモデルで、コーディングタスクにおいて GPT-4 Turbo に匹敵する性能を発揮します。", @@ -426,7 +425,7 @@ "deepseek-r1-fast-online.description": "DeepSeek R1 高速フルバージョンは、リアルタイムのウェブ検索を搭載し、671Bスケールの能力と高速応答を両立します。", "deepseek-r1-online.description": "DeepSeek R1 フルバージョンは、671Bパラメータとリアルタイムのウェブ検索を備え、より強力な理解と生成を提供します。", "deepseek-r1.description": "DeepSeek-R1は、強化学習前にコールドスタートデータを使用し、数学、コーディング、推論においてOpenAI-o1と同等の性能を発揮します。", - "deepseek-reasoner.description": "DeepSeek V4 Flash思考モードの互換性エイリアス。廃止予定 — DeepSeek V4 Flashを使用してください。", + "deepseek-reasoner.description": "複雑な論理的推論タスクに特化したDeepSeekの推論モデルです。", "deepseek-v2.description": "DeepSeek V2は、コスト効率の高い処理を実現する効率的なMoEモデルです。", "deepseek-v2:236b.description": "DeepSeek V2 236Bは、コード生成に特化したDeepSeekのモデルで、強力なコード生成能力を持ちます。", "deepseek-v3-0324.description": "DeepSeek-V3-0324は、671BパラメータのMoEモデルで、プログラミングや技術的能力、文脈理解、長文処理において優れた性能を発揮します。", @@ -491,8 +490,6 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 は ByteDance Seed による画像生成モデルで、テキストと画像入力に対応し、高品質かつ制御性の高い画像生成を実現します。テキストプロンプトから画像を生成します。", "doubao-seedream-4-5-251128.description": "Seedream 4.5はByteDanceの最新マルチモーダル画像モデルで、テキストから画像生成、画像間変換、バッチ画像生成機能を統合し、常識と推論能力を組み込んでいます。前バージョン4.0と比較して、生成品質が大幅に向上し、編集の一貫性や複数画像の融合が改善されています。視覚的な詳細の制御がより正確になり、小さなテキストや顔を自然に生成し、レイアウトや色彩の調和が向上し、全体的な美観が強化されています。", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-liteはByteDanceの最新画像生成モデルです。初めてオンライン検索機能を統合し、リアルタイムのウェブ情報を取り入れることで生成画像のタイムリー性を向上させています。モデルの知能もアップグレードされ、複雑な指示や視覚コンテンツを正確に解釈できるようになりました。また、専門的なシナリオでのグローバルな知識カバレッジ、一貫性、生成品質が向上し、企業レベルの視覚制作ニーズにより適合しています。", - "dreamina-seedance-2-0-260128.description": "ByteDanceのSeedance 2.0は、マルチモーダル参照動画生成、動画編集、動画拡張、テキストから動画、画像から動画への同期音声付き生成をサポートする最も強力な動画生成モデルです。", - "dreamina-seedance-2-0-fast-260128.description": "ByteDanceのSeedance 2.0 Fastは、Seedance 2.0と同じ機能を提供しながら、より高速な生成速度と競争力のある価格を実現します。", "emohaa.description": "Emohaa は、専門的なカウンセリング能力を備えたメンタルヘルスモデルで、ユーザーが感情的な問題を理解するのを支援します。", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B は、ローカルおよびカスタマイズ展開に適した軽量なオープンソースモデルです。", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview は、ERNIE 4.5 の評価用に設計された 8K コンテキストプレビューモデルです。", @@ -506,6 +503,7 @@ "ernie-5.0-thinking-latest.description": "文心 5.0 Thinking は、テキスト、画像、音声、動画を統合的に扱うネイティブなフルモーダルフラッグシップモデルであり、複雑な質問応答、創作、エージェントシナリオにおいて大幅な能力向上を実現します。", "ernie-5.0-thinking-preview.description": "文心 5.0 Thinking Preview は、テキスト、画像、音声、動画を統合的に扱うネイティブなフルモーダルフラッグシップモデルであり、複雑な質問応答、創作、エージェントシナリオにおいて大幅な能力向上を実現します。", "ernie-5.0.description": "ERNIE 5.0は、ERNIEシリーズの新世代モデルで、ネイティブにマルチモーダルな大規模モデルです。統一されたマルチモーダルモデリングアプローチを採用し、テキスト、画像、音声、動画を共同でモデリングして包括的なマルチモーダル能力を提供します。その基盤能力は大幅に向上し、ベンチマーク評価で優れた性能を達成しています。特にマルチモーダル理解、指示の追従、創造的な執筆、事実の正確性、エージェント計画、ツール利用において優れています。", + "ernie-5.1.description": "ERNIE 5.1は、ERNIEシリーズの最新モデルで、基盤能力に大幅なアップグレードが施されています。エージェント、知識処理、推論、深層検索などの分野で顕著な改善を示しています。このリリースでは、完全非同期型の強化学習アーキテクチャを採用し、大規模モデルの自律エージェント意思決定への進化における主要な課題(トレーニングと推論の数値的不一致、異種計算リソースの低利用率、ロングテール効果によるグローバルな問題)に対応しています。さらに、大規模エージェントのポストトレーニング技術を活用し、モデルの能力と一般化性能をさらに向上させています。環境、専門家、融合プロセスを含む三段階の協調フレームワークを通じて、このアプローチはトレーニング効率を確保するだけでなく、複雑なタスクにおけるモデルの安定性とパフォーマンスを大幅に向上させます。", "ernie-char-fiction-8k-preview.description": "ERNIE Character Fiction 8K Preview は、キャラクターとプロットの創作に対応したモデルのプレビュー版であり、機能評価とテストに適しています。", "ernie-char-fiction-8k.description": "ERNIE Character Fiction 8K は、小説やプロット創作に適した人格モデルであり、長編ストーリー生成に最適です。", "ernie-image-turbo.description": "ERNIE-Imageは、Baiduが開発した8Bパラメータのテキストから画像への生成モデルです。複数のベンチマークでトップにランクインしており、中国のSuperCLUEで同率1位を達成し、オープンソーストラックでリードしています。", @@ -517,8 +515,7 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K は、複雑な推論やマルチターン対話に対応した 32K コンテキストの高速思考モデルです。", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview は、評価およびテスト用の思考モデルプレビューです。", "ernie-x1.1.description": "ERNIE X1.1は評価とテスト用の思考モデルプレビューです。", - "fal-ai/bytedance/seedream/v4.5.description": "ByteDance Seedチームが開発したSeedream 4.5は、マルチ画像編集と構成をサポートします。被写体の一貫性向上、正確な指示の追従、空間論理の理解、美的表現、ポスターのレイアウトやロゴデザイン、高精度なテキスト画像レンダリングを特徴としています。", - "fal-ai/bytedance/seedream/v4.description": "ByteDance Seedが開発したSeedream 4.0は、テキストと画像入力をサポートし、プロンプトからの高度に制御可能で高品質な画像生成を実現します。", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0は、ByteDance Seedが提供する画像生成モデルで、テキストと画像入力をサポートし、高度に制御可能で高品質な画像生成を実現します。テキストプロンプトから画像を生成します。", "fal-ai/flux-kontext/dev.description": "FLUX.1 モデルは画像編集に特化しており、テキストと画像の入力に対応しています。", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] は、テキストと参照画像を入力として受け取り、局所的な編集や複雑なシーン全体の変換を可能にします。", "fal-ai/flux/krea.description": "Flux Krea [dev] は、よりリアルで自然な画像を生成する美的バイアスを持つ画像生成モデルです。", @@ -526,8 +523,8 @@ "fal-ai/hunyuan-image/v3.description": "強力なネイティブマルチモーダル画像生成モデルです。", "fal-ai/imagen4/preview.description": "Google による高品質な画像生成モデルです。", "fal-ai/nano-banana.description": "Nano Banana は Google による最新・最速・最も効率的なネイティブマルチモーダルモデルで、会話を通じた画像生成と編集が可能です。", - "fal-ai/qwen-image-edit.description": "Qwenチームによるプロフェッショナルな画像編集モデルで、意味的および外観の編集、正確な中国語/英語テキスト編集、スタイル変換、回転などをサポートします。", - "fal-ai/qwen-image.description": "Qwenチームによる強力な画像生成モデルで、中国語テキストのレンダリング能力が高く、多様な視覚スタイルを提供します。", + "fal-ai/qwen-image-edit.description": "Qwenチームが提供するプロフェッショナルな画像編集モデルで、意味的および外観的な編集をサポートし、中国語と英語のテキストを正確に編集します。スタイル変換やオブジェクトの回転など、高品質な編集が可能です。", + "fal-ai/qwen-image.description": "Qwenチームが提供する強力な画像生成モデルで、中国語のテキストレンダリングや多様なビジュアルスタイルに優れています。", "flux-1-schnell.description": "Black Forest Labs による 120 億パラメータのテキストから画像への変換モデルで、潜在敵対的拡散蒸留を用いて 1~4 ステップで高品質な画像を生成します。クローズドな代替モデルに匹敵し、Apache-2.0 ライセンスのもと、個人・研究・商用利用が可能です。", "flux-dev.description": "非商用の研究開発向けに効率化されたオープンソース画像生成モデルです。", "flux-kontext-max.description": "最先端のコンテキスト画像生成・編集モデルで、テキストと画像を組み合わせて精密かつ一貫性のある結果を生成します。", @@ -566,10 +563,10 @@ "gemini-3-flash-preview.description": "Gemini 3 Flash は、最先端の知能と優れた検索基盤を融合し、スピードに特化した最もスマートなモデルです。", "gemini-3-flash.description": "Gemini 3 Flash by Google — 超高速モデルでマルチモーダル入力をサポートします。", "gemini-3-pro-image-preview.description": "Gemini 3 Pro Image(Nano Banana Pro)は、Googleの画像生成モデルで、マルチモーダル対話もサポートします。", - "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro)はGoogleの画像生成モデルで、マルチモーダルチャットもサポートします。", + "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image(Nano Banana Pro)は、Googleの画像生成モデルで、マルチモーダルチャットもサポートしています。", "gemini-3-pro-preview.description": "Gemini 3 Pro は、Google による最も強力なエージェントおよびバイブコーディングモデルで、最先端の推論に加え、より豊かなビジュアルと深い対話を実現します。", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image(Nano Banana 2)は、Googleの最速のネイティブ画像生成モデルで、思考サポート、対話型画像生成および編集を提供します。", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2)は、プロレベルの画像品質をフラッシュ速度で提供し、マルチモーダルチャットをサポートします。", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image(Nano Banana 2)は、Googleの最速のネイティブ画像生成モデルで、思考サポート、会話型画像生成、編集を可能にします。", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite PreviewはGoogleの最もコスト効率の高いマルチモーダルモデルで、大量のエージェントタスク、翻訳、データ処理に最適化されています。", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-LiteはGoogleの最もコスト効率の高いマルチモーダルモデルで、大量のエージェントタスク、翻訳、データ処理に最適化されています。", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Previewは、Gemini 3 Proの推論能力を強化し、中程度の思考レベルサポートを追加しています。", @@ -734,8 +731,6 @@ "grok-4-fast-reasoning.description": "Grok 4 Fast をリリースしました。コスト効率の高い推論モデルにおける最新の進展です。", "grok-4.20-0309-non-reasoning.description": "単純なユースケース向けの非推論バリアント。", "grok-4.20-0309-reasoning.description": "応答前に推論する知的で超高速なモデル。", - "grok-4.20-beta-0309-non-reasoning.description": "シンプルなユースケース向けの非思考型バリアント", - "grok-4.20-beta-0309-reasoning.description": "応答前に推論する知的で超高速なモデル", "grok-4.20-multi-agent-0309.description": "4または16のエージェントチーム。研究ユースケースに優れています。現在、クライアントサイドツールはサポートしていません。xAIサーバーサイドツール(例:X Search、Web Searchツール)およびリモートMCPツールのみをサポートします。", "grok-4.3.description": "世界で最も真実を追求する大規模言語モデル", "grok-4.description": "最新のGrokフラッグシップモデルで、言語、数学、推論において比類のない性能を発揮する真のオールラウンダーです。現在はgrok-4-0709を指しており、リソースの制限により一時的に公式価格より10%高く設定されていますが、後に公式価格に戻る予定です。", @@ -1220,8 +1215,6 @@ "qwq.description": "QwQは、Qwenファミリーの推論モデルです。標準的な指示調整モデルと比較して、思考と推論能力に優れ、特に難解な問題において下流性能を大幅に向上させます。QwQ-32Bは、DeepSeek-R1やo1-miniと競合する中規模の推論モデルです。", "qwq_32b.description": "Qwenファミリーの中規模推論モデル。標準的な指示調整モデルと比較して、QwQの思考と推論能力は、特に難解な問題において下流性能を大幅に向上させます。", "r1-1776.description": "R1-1776は、DeepSeek R1のポストトレーニングバリアントで、検閲のない偏りのない事実情報を提供するよう設計されています。", - "seedance-1-5-pro-251215.description": "ByteDanceのSeedance 1.5 Proは、テキストから動画、画像から動画(最初のフレーム、最初+最後のフレーム)、視覚と同期した音声生成をサポートします。", - "seedream-5-0-260128.description": "BytePlusによるByteDance-Seedream-5.0-liteは、リアルタイム情報のためのウェブ検索補強生成、複雑なプロンプト解釈の強化、プロフェッショナルな視覚制作のための参照一貫性の向上を特徴としています。", "solar-mini-ja.description": "Solar Mini (Ja)は、Solar Miniを日本語に特化させたモデルで、英語と韓国語でも効率的かつ高性能な動作を維持します。", "solar-mini.description": "Solar Miniは、GPT-3.5を上回る性能を持つコンパクトなLLMで、英語と韓国語に対応した多言語機能を備え、効率的な小型ソリューションを提供します。", "solar-pro.description": "Solar Proは、Upstageが提供する高知能LLMで、単一GPU上での指示追従に特化し、IFEvalスコア80以上を記録しています。現在は英語に対応しており、2024年11月の正式リリースでは対応言語とコンテキスト長が拡張される予定です。", diff --git a/locales/ja-JP/onboarding.json b/locales/ja-JP/onboarding.json index 3cc08ded58..e4f2730a70 100644 --- a/locales/ja-JP/onboarding.json +++ b/locales/ja-JP/onboarding.json @@ -27,6 +27,13 @@ "agent.layout.skipConfirm.ok": "とりあえずスキップ", "agent.layout.skipConfirm.title": "オンボーディングを今はスキップしますか?", "agent.layout.switchMessage": "今日は気分が乗らないですか?{{mode}}{{skip}}に切り替えられます。", + "agent.layout.switchMessageClassic": "今日は気分が乗らない?{{mode}}に切り替えることができます。", + "agent.messenger.cta.discord": "Discordに追加", + "agent.messenger.cta.slack": "Slackに追加", + "agent.messenger.cta.telegram": "Telegramで開く", + "agent.messenger.subtitle": "Telegram、Slack、またはDiscordでエージェントとチャット", + "agent.messenger.telegramQrCaption": "スマートフォンのカメラでスキャンしてください", + "agent.messenger.title": "どこでメッセージを送る時も、私をそばに置いてください", "agent.modeSwitch.agent": "会話モード", "agent.modeSwitch.classic": "クラシック", "agent.modeSwitch.collapse": "折りたたむ", @@ -68,6 +75,13 @@ "agent.wrapUp.confirm.content": "ここまでの内容を保存しておきます。いつでも戻って続きを話せますよ。", "agent.wrapUp.confirm.ok": "今すぐ終了する", "agent.wrapUp.confirm.title": "オンボーディングを終了しますか?", + "agentPicker.allCategories": "すべて", + "agentPicker.continue": "続ける", + "agentPicker.skip": "今はスキップ", + "agentPicker.subtitle": "今すぐライブラリにいくつか追加しましょう — 後でいつでもさらに発見できます。", + "agentPicker.title": "ライブラリにいくつかのエージェントを追加しましょう", + "agentPicker.title2": "あなたの働き方に合ったものを選んでください", + "agentPicker.title3": "後でいつでも追加できます — まずは少しから始めましょう", "back": "前へ", "finish": "使い始める", "interests.area.business": "ビジネスと戦略", diff --git a/locales/ja-JP/plugin.json b/locales/ja-JP/plugin.json index 4aa39dee0d..21052380f4 100644 --- a/locales/ja-JP/plugin.json +++ b/locales/ja-JP/plugin.json @@ -39,8 +39,8 @@ "builtins.lobe-agent-documents.inspector.docCount": "{{count}} ドキュメント", "builtins.lobe-agent-documents.inspector.opsCount": "{{count}} 操作", "builtins.lobe-agent-documents.inspector.opsResult": "{{success}}/{{total}} 操作", - "builtins.lobe-agent-documents.inspector.target.agent": "エージェント内", - "builtins.lobe-agent-documents.inspector.target.currentTopic": "トピック内", + "builtins.lobe-agent-documents.inspector.scope.agent": "エージェント内", + "builtins.lobe-agent-documents.inspector.scope.currentTopic": "トピック内", "builtins.lobe-agent-documents.title": "エージェント文書", "builtins.lobe-agent-management.apiName.callAgent": "エージェントを呼び出す", "builtins.lobe-agent-management.apiName.createAgent": "エージェントを作成する", @@ -90,6 +90,14 @@ "builtins.lobe-agent.title": "ローブエージェント", "builtins.lobe-claude-code.agent.instruction": "指示", "builtins.lobe-claude-code.agent.result": "結果", + "builtins.lobe-claude-code.task.createLabel": "タスクを作成中: ", + "builtins.lobe-claude-code.task.getLabel": "タスク #{{taskId}} を確認中", + "builtins.lobe-claude-code.task.listLabel": "タスクを一覧表示", + "builtins.lobe-claude-code.task.updateCompleted": "完了", + "builtins.lobe-claude-code.task.updateDeleted": "削除済み", + "builtins.lobe-claude-code.task.updateInProgress": "開始済み", + "builtins.lobe-claude-code.task.updateLabel": "タスク #{{taskId}} を更新中", + "builtins.lobe-claude-code.task.updatePending": "リセット", "builtins.lobe-claude-code.todoWrite.allDone": "すべてのタスクが完了しました", "builtins.lobe-claude-code.todoWrite.currentStep": "現在のステップ", "builtins.lobe-claude-code.todoWrite.todos": "ToDo", diff --git a/locales/ja-JP/providers.json b/locales/ja-JP/providers.json index 2c85d1b244..f09f806600 100644 --- a/locales/ja-JP/providers.json +++ b/locales/ja-JP/providers.json @@ -33,7 +33,6 @@ "jina.description": "Jina AIは2020年に設立された検索AIのリーディングカンパニーで、ベクトルモデル、リランカー、小型言語モデルを含む検索スタックにより、高品質な生成・マルチモーダル検索アプリを構築できます。", "kimicodingplan.description": "Moonshot AIのKimi Codeは、K2.5を含むKimiモデルへのアクセスを提供します。", "lmstudio.description": "LM Studioは、ローカルPC上でLLMの開発と実験ができるデスクトップアプリです。", - "lobehub.description": "LobeHub Cloudは公式APIを使用してAIモデルにアクセスし、モデルのトークンに紐づいたクレジットで使用量を測定します。", "longcat.description": "LongCatは、Meituanが独自に開発した生成AIの大型モデルシリーズです。効率的な計算アーキテクチャと強力なマルチモーダル機能を通じて、企業内部の生産性を向上させ、革新的なアプリケーションを可能にすることを目的としています。", "minimax.description": "MiniMaxは2021年に設立され、マルチモーダル基盤モデルを用いた汎用AIを開発しています。兆単位パラメータのMoEテキストモデル、音声モデル、ビジョンモデル、Hailuo AIなどのアプリを提供します。", "minimaxcodingplan.description": "MiniMaxトークンプランは、固定料金のサブスクリプションを通じてM2.7を含むMiniMaxモデルへのアクセスを提供します。", diff --git a/locales/ja-JP/subscription.json b/locales/ja-JP/subscription.json index 11dd3d7185..ae94c7ab1b 100644 --- a/locales/ja-JP/subscription.json +++ b/locales/ja-JP/subscription.json @@ -55,6 +55,12 @@ "credits.autoTopUp.toggle": "自動チャージを有効にする", "credits.autoTopUp.upgradeHint": "有料プランに加入して自動チャージを有効にしてください", "credits.autoTopUp.validation.targetMustExceedThreshold": "目標残高はしきい値を上回る必要があります", + "credits.costEstimateHint.desc": "推定モデルコストが設定した閾値に達したときに、送信前に軽量な警告を表示します", + "credits.costEstimateHint.saveError": "コスト見積もりアラート設定の保存に失敗しました", + "credits.costEstimateHint.saveSuccess": "コスト見積もりアラート設定が保存されました", + "credits.costEstimateHint.threshold": "警告閾値", + "credits.costEstimateHint.title": "コスト見積もりアラート", + "credits.costEstimateHint.validation.threshold": "閾値は0以上でなければなりません", "credits.packages.auto": "自動", "credits.packages.charged": "${{amount}}が請求されました", "credits.packages.expired": "期限切れ", diff --git a/locales/ja-JP/tool.json b/locales/ja-JP/tool.json index 18abf4b3af..323c964775 100644 --- a/locales/ja-JP/tool.json +++ b/locales/ja-JP/tool.json @@ -40,6 +40,14 @@ "agentMarketplace.render.alreadyInLibraryTag": "すでにライブラリにあります", "agentMarketplace.render.alreadyInLibrary_one": "{{count}} 件がすでにライブラリにあります", "agentMarketplace.render.alreadyInLibrary_other": "{{count}} 件がすでにライブラリにあります", + "claudeCode.askUserQuestion.escape.back": "オプションに戻る", + "claudeCode.askUserQuestion.escape.enter": "または直接入力してください", + "claudeCode.askUserQuestion.escape.placeholder": "ここに回答を入力してください…", + "claudeCode.askUserQuestion.multiSelectTag": "(複数選択)", + "claudeCode.askUserQuestion.skip": "スキップ", + "claudeCode.askUserQuestion.submit": "送信", + "claudeCode.askUserQuestion.timeExpired": "時間切れ — 各質問のオプション1を使用します。", + "claudeCode.askUserQuestion.timeRemaining": "残り時間: {{time}} · 未回答の質問はタイムアウト時にオプション1がデフォルトになります。", "codeInterpreter-legacy.error": "実行エラー", "codeInterpreter-legacy.executing": "実行中...", "codeInterpreter-legacy.files": "ファイル:", diff --git a/locales/ja-JP/topic.json b/locales/ja-JP/topic.json index 386af1973f..73d148d858 100644 --- a/locales/ja-JP/topic.json +++ b/locales/ja-JP/topic.json @@ -56,6 +56,7 @@ "renameModal.title": "トピック名を変更", "searchPlaceholder": "トピックを検索...", "searchResultEmpty": "検索結果はありません", + "sidebar.title": "トピック", "taskManager.agent": "タスクエージェント", "taskManager.welcome": "タスクについて聞いてください", "temp": "一時的", diff --git a/locales/ko-KR/chat.json b/locales/ko-KR/chat.json index 659366df86..918fb6d2bb 100644 --- a/locales/ko-KR/chat.json +++ b/locales/ko-KR/chat.json @@ -220,6 +220,13 @@ "inbox.title": "Lobe AI", "input.addAi": "도우미 메시지 추가", "input.addUser": "사용자 메시지 추가", + "input.costEstimate.creditsPerMillionTokens": "{{credits}} 크레딧/M 토큰", + "input.costEstimate.hint": "예상 비용: 약 {{credits}} 크레딧", + "input.costEstimate.inputLabel": "입력", + "input.costEstimate.outputLabel": "출력", + "input.costEstimate.settingsLink": "경고 임계값 조정", + "input.costEstimate.tokenCount": "약 {{tokens}} 토큰", + "input.costEstimate.tooltip": "현재 컨텍스트, 도구 및 모델 가격을 기준으로 추정됩니다. 실제 비용은 다를 수 있습니다.", "input.disclaimer": "도우미도 실수를 할 수 있습니다. 중요한 정보는 당신의 판단을 최우선으로 하세요", "input.errorMsg": "전송 중 문제가 발생했습니다: {{errorMsg}}. 재시도하거나 나중에 다시 보내세요", "input.more": "더 보기", @@ -779,6 +786,7 @@ "upload.preview.prepareTasks": "청크 준비 중…", "upload.preview.status.pending": "업로드 준비 중…", "upload.preview.status.processing": "파일 처리 중…", + "upload.validation.unsupportedFileType": "지원되지 않는 파일 형식: {{files}}. 지원되는 이미지: JPG, PNG, GIF, WebP. 지원되는 문서: PDF, Word, Excel, PowerPoint, Markdown, 텍스트, CSV, JSON 및 코드 파일.", "upload.validation.videoSizeExceeded": "비디오 파일은 20MB를 초과할 수 없습니다. 현재 {{actualSize}}입니다", "viewMode.fullWidth": "전체 너비", "viewMode.normal": "일반", @@ -894,6 +902,8 @@ "workingPanel.localFile.closeOther": "다른 파일 닫기", "workingPanel.localFile.closeRight": "오른쪽 닫기", "workingPanel.localFile.error": "이 파일을 로드할 수 없습니다", + "workingPanel.localFile.preview.raw": "원본", + "workingPanel.localFile.preview.render": "미리보기", "workingPanel.localFile.truncated": "파일 미리보기가 {{limit}}자로 잘렸습니다", "workingPanel.progress": "Progress", "workingPanel.progress.allCompleted": "All tasks completed", @@ -959,6 +969,8 @@ "workingPanel.review.viewMode.unified": "통합 보기로 전환", "workingPanel.review.wordWrap.disable": "자동 줄 바꿈 비활성화", "workingPanel.review.wordWrap.enable": "자동 줄 바꿈 활성화", + "workingPanel.skills.empty": "이 프로젝트에서 발견된 스킬이 없습니다", + "workingPanel.skills.title": "스킬", "workingPanel.space": "공간", "workingPanel.title": "Working Panel", "you": "당신", diff --git a/locales/ko-KR/components.json b/locales/ko-KR/components.json index a50e72eaca..0d3949399b 100644 --- a/locales/ko-KR/components.json +++ b/locales/ko-KR/components.json @@ -121,6 +121,16 @@ "ModelSwitchPanel.detail.context": "컨텍스트 길이", "ModelSwitchPanel.detail.pricing": "요금", "ModelSwitchPanel.detail.pricing.cachedInput": "캐시된 입력 ${{amount}}/백만자", + "ModelSwitchPanel.detail.pricing.credits.cachedInput": "캐시된 입력 {{amount}} 크레딧/M 토큰", + "ModelSwitchPanel.detail.pricing.credits.image": "크레딧/이미지", + "ModelSwitchPanel.detail.pricing.credits.input": "입력 {{amount}} 크레딧/M 토큰", + "ModelSwitchPanel.detail.pricing.credits.megapixel": "크레딧/MP", + "ModelSwitchPanel.detail.pricing.credits.millionCharacters": "크레딧/M 문자", + "ModelSwitchPanel.detail.pricing.credits.millionTokens": "크레딧/M 토큰", + "ModelSwitchPanel.detail.pricing.credits.output": "출력 {{amount}} 크레딧/M 토큰", + "ModelSwitchPanel.detail.pricing.credits.perImage": "~ {{amount}} 크레딧 / 이미지", + "ModelSwitchPanel.detail.pricing.credits.perVideo": "~ {{amount}} 크레딧 / 비디오", + "ModelSwitchPanel.detail.pricing.credits.second": "크레딧/초", "ModelSwitchPanel.detail.pricing.group.audio": "오디오", "ModelSwitchPanel.detail.pricing.group.image": "이미지", "ModelSwitchPanel.detail.pricing.group.text": "텍스트", diff --git a/locales/ko-KR/editor.json b/locales/ko-KR/editor.json index f2ff849472..43b8af3e63 100644 --- a/locales/ko-KR/editor.json +++ b/locales/ko-KR/editor.json @@ -1,8 +1,10 @@ { "actionTag.category.command": "명령", + "actionTag.category.projectSkill": "프로젝트 스킬", "actionTag.category.skill": "스킬", "actionTag.category.tool": "도구", "actionTag.tooltip.command": "전송 전에 클라이언트 측 슬래시 명령을 실행합니다.", + "actionTag.tooltip.projectSkill": "슬래시 호출로 전송되어 에이전트의 CLI가 일치하는 프로젝트 스킬을 실행합니다.", "actionTag.tooltip.skill": "이번 요청에 사용할 재사용 가능한 스킬 패키지를 불러옵니다.", "actionTag.tooltip.tool": "이번 요청에서 사용자가 명시적으로 선택한 도구를 표시합니다.", "actions.expand.off": "접기", diff --git a/locales/ko-KR/home.json b/locales/ko-KR/home.json index e86f3b805f..8c7eadebb4 100644 --- a/locales/ko-KR/home.json +++ b/locales/ko-KR/home.json @@ -11,6 +11,19 @@ "brief.action.ignore": "무시", "brief.action.retry": "다시 시도", "brief.addFeedback": "피드백 공유", + "brief.agentSignal.selfReview.applied.heading": "업데이트됨", + "brief.agentSignal.selfReview.applied.summary": "{{count}}개의 꿈 업데이트가 적용되었습니다.", + "brief.agentSignal.selfReview.applied.summary_plural": "{{count}}개의 꿈 업데이트가 적용되었습니다.", + "brief.agentSignal.selfReview.applied.title": "꿈 업데이트된 리소스", + "brief.agentSignal.selfReview.error.heading": "문제", + "brief.agentSignal.selfReview.error.summary": "이 꿈 동안 일부 작업을 완료할 수 없었습니다.", + "brief.agentSignal.selfReview.error.title": "꿈에서 문제가 발생했습니다", + "brief.agentSignal.selfReview.ideas.summary": "미래 검토를 위한 꿈 노트를 저장했습니다.", + "brief.agentSignal.selfReview.ideas.title": "꿈 노트", + "brief.agentSignal.selfReview.proposal.heading": "제안", + "brief.agentSignal.selfReview.proposal.summary": "{{count}}개의 꿈 제안이 검토를 필요로 합니다.", + "brief.agentSignal.selfReview.proposal.summary_plural": "{{count}}개의 꿈 제안이 검토를 필요로 합니다.", + "brief.agentSignal.selfReview.proposal.title": "꿈 제안 검토 필요", "brief.collapse": "간략히 보기", "brief.commentPlaceholder": "피드백을 공유하세요...", "brief.commentSubmit": "피드백 제출", diff --git a/locales/ko-KR/hotkey.json b/locales/ko-KR/hotkey.json index 1891cbbdc8..7cf1359654 100644 --- a/locales/ko-KR/hotkey.json +++ b/locales/ko-KR/hotkey.json @@ -1,8 +1,6 @@ { "addUserMessage.desc": "현재 입력한 내용을 사용자 메시지로 추가하지만 생성은 트리거하지 않음", "addUserMessage.title": "사용자 메시지 추가", - "clearCurrentMessages.desc": "현재 대화의 메시지와 업로드된 파일을 모두 삭제", - "clearCurrentMessages.title": "대화 메시지 지우기", "commandPalette.desc": "전역 명령 팔레트를 열어 기능에 빠르게 접근합니다", "commandPalette.title": "명령 팔레트", "deleteAndRegenerateMessage.desc": "마지막 메시지를 삭제하고 다시 생성합니다", diff --git a/locales/ko-KR/models.json b/locales/ko-KR/models.json index fa493f8f20..8e36a5d105 100644 --- a/locales/ko-KR/models.json +++ b/locales/ko-KR/models.json @@ -106,7 +106,6 @@ "MiniMax-Hailuo-2.3.description": "신규 비디오 생성 모델로 신체 움직임, 물리적 사실성, 지침 준수에서 전반적인 업그레이드 제공.", "MiniMax-M1.description": "80K 체인 오브 싱킹과 100만 입력을 지원하는 새로운 자체 개발 추론 모델로, 세계 최고 수준의 모델과 유사한 성능을 제공합니다.", "MiniMax-M2-Stable.description": "상업적 사용을 위한 높은 동시성을 제공하며, 효율적인 코딩 및 에이전트 워크플로우에 최적화되어 있습니다.", - "MiniMax-M2.1-Lightning.description": "강력한 다국어 프로그래밍 기능과 더 빠르고 효율적인 추론을 제공합니다.", "MiniMax-M2.1-highspeed.description": "강력한 다국어 프로그래밍 기능과 종합적으로 업그레이드된 프로그래밍 경험. 더 빠르고 효율적입니다.", "MiniMax-M2.1.description": "MiniMax-M2.1은 MiniMax에서 개발한 대표적인 오픈소스 대형 모델로, 복잡한 실제 과제를 해결하는 데 중점을 둡니다. 다국어 프로그래밍 능력과 에이전트로서 복잡한 작업을 수행하는 능력이 핵심 강점입니다.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: M2.5와 동일한 성능을 제공하며 추론 속도가 더 빠릅니다.", @@ -315,13 +314,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku는 Anthropic의 가장 빠르고 컴팩트한 모델로, 빠르고 정확한 성능으로 즉각적인 응답을 위해 설계되었습니다.", "claude-3-opus-20240229.description": "Claude 3 Opus는 Anthropic의 가장 강력한 모델로, 고난도 작업에서 뛰어난 성능, 지능, 유창성, 이해력을 자랑합니다.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet은 엔터프라이즈 워크로드를 위한 지능과 속도의 균형을 제공하며, 낮은 비용으로 높은 효용성과 안정적인 대규모 배포를 지원합니다.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5는 Anthropic의 가장 빠르고 지능적인 Haiku 모델로, 번개 같은 속도와 확장된 사고 능력을 제공합니다.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5는 Anthropic의 가장 빠르고 스마트한 Haiku 모델로, 번개 같은 속도와 확장된 추론 능력을 자랑합니다.", "claude-haiku-4-5.description": "Anthropic의 Claude Haiku 4.5 — 향상된 추론 및 비전을 갖춘 차세대 Haiku.", "claude-haiku-4.5.description": "Claude Haiku 4.5는 Anthropic의 가장 빠르고 똑똑한 Haiku 모델로, 번개 같은 속도와 확장된 추론 능력을 자랑합니다.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking은 자신의 추론 과정을 드러낼 수 있는 고급 변형 모델입니다.", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1은 Anthropic의 최신 모델로, 매우 복잡한 작업에서 뛰어난 성능, 지능, 유창함, 이해력을 자랑합니다.", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1은 Anthropic의 최신 모델로, 매우 복잡한 작업에서 뛰어난 성능, 지능, 유창성, 이해력을 발휘하는 가장 강력한 모델입니다.", "claude-opus-4-1.description": "Anthropic의 Claude Opus 4.1 — 심층 분석 기능을 갖춘 프리미엄 추론 모델.", - "claude-opus-4-20250514.description": "Claude Opus 4는 Anthropic의 가장 강력한 모델로, 매우 복잡한 작업에서 뛰어난 성능, 지능, 유창함, 이해력을 자랑합니다.", + "claude-opus-4-20250514.description": "Claude Opus 4는 Anthropic의 가장 강력한 모델로, 매우 복잡한 작업에서 뛰어난 성능, 지능, 유창성, 이해력을 발휘합니다.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5는 Anthropic의 플래그십 모델로, 탁월한 지능과 확장 가능한 성능을 결합하여 최고 품질의 응답과 추론이 필요한 복잡한 작업에 이상적입니다.", "claude-opus-4-5.description": "Anthropic의 Claude Opus 4.5 — 최고 수준의 추론 및 코딩을 갖춘 플래그십 모델.", "claude-opus-4-6.description": "Anthropic의 Claude Opus 4.6 — 고급 추론을 갖춘 1M 컨텍스트 윈도우 플래그십.", @@ -330,8 +329,8 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6은 에이전트 구축과 코딩을 위한 Anthropic의 가장 지능적인 모델입니다.", "claude-opus-4.6.description": "Claude Opus 4.6은 에이전트 구축과 코딩을 위한 Anthropic의 가장 지능적인 모델입니다.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking은 즉각적인 응답 또는 단계별 사고 과정을 시각적으로 보여주는 확장된 사고를 생성할 수 있습니다.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4는 Anthropic의 가장 지능적인 모델로, API 사용자에게 세밀한 제어를 제공하며 즉각적인 응답 또는 단계별 사고를 지원합니다.", - "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5는 Anthropic의 가장 지능적인 모델입니다.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4는 즉각적인 응답을 생성하거나 가시적인 과정을 통해 단계별 사고를 확장할 수 있습니다.", + "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5는 현재까지 Anthropic의 가장 지능적인 모델입니다.", "claude-sonnet-4-5.description": "Anthropic의 Claude Sonnet 4.5 — 향상된 코딩 성능을 갖춘 개선된 Sonnet.", "claude-sonnet-4-6.description": "Anthropic의 Claude Sonnet 4.6 — 우수한 코딩 및 도구 사용을 갖춘 최신 Sonnet.", "claude-sonnet-4.5.description": "Claude Sonnet 4.5는 지금까지의 Anthropic 모델 중 가장 지능적인 모델입니다.", @@ -404,7 +403,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B)은 심층 언어 이해와 상호작용을 제공하는 혁신적인 모델입니다.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1은 복잡한 추론과 연쇄적 사고(chain-of-thought)에 강한 차세대 추론 모델로, 심층 분석 작업에 적합합니다.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2는 복잡한 추론과 연쇄 사고 능력이 강화된 차세대 추론 모델입니다.", - "deepseek-chat.description": "DeepSeek V4 Flash 비사고 모드의 호환성 별칭입니다. 사용 중단 예정 — DeepSeek V4 Flash를 대신 사용하세요.", + "deepseek-chat.description": "일반 대화 능력과 코딩 능력을 결합한 새로운 오픈소스 모델입니다. 이 모델은 대화 모델의 일반적인 대화 능력과 코더 모델의 강력한 코딩 능력을 유지하며, 더 나은 선호도 정렬을 제공합니다. DeepSeek-V2.5는 또한 글쓰기와 지시 따르기 능력을 향상시켰습니다.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B는 2T 토큰(코드 87%, 중/영문 텍스트 13%)으로 학습된 코드 언어 모델입니다. 16K 문맥 창과 중간 채우기(fit-in-the-middle) 작업을 도입하여 프로젝트 수준의 코드 완성과 코드 조각 보완을 지원합니다.", "deepseek-coder-v2.description": "DeepSeek Coder V2는 GPT-4 Turbo에 필적하는 성능을 보이는 오픈소스 MoE 코드 모델입니다.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2는 GPT-4 Turbo에 필적하는 성능을 보이는 오픈소스 MoE 코드 모델입니다.", @@ -426,7 +425,7 @@ "deepseek-r1-fast-online.description": "DeepSeek R1의 빠른 전체 버전으로, 실시간 웹 검색을 지원하며 671B 규모의 성능과 빠른 응답을 결합합니다.", "deepseek-r1-online.description": "DeepSeek R1 전체 버전은 671B 파라미터와 실시간 웹 검색을 지원하여 더 강력한 이해 및 생성 능력을 제공합니다.", "deepseek-r1.description": "DeepSeek-R1은 강화 학습 전 콜드 스타트 데이터를 사용하며, 수학, 코딩, 추론에서 OpenAI-o1과 유사한 성능을 보입니다.", - "deepseek-reasoner.description": "DeepSeek V4 Flash 사고 모드의 호환성 별칭입니다. 사용 중단 예정 — DeepSeek V4 Flash를 대신 사용하세요.", + "deepseek-reasoner.description": "복잡한 논리적 추론 작업에 중점을 둔 DeepSeek 추론 모델입니다.", "deepseek-v2.description": "DeepSeek V2는 비용 효율적인 처리를 위한 고효율 MoE 모델입니다.", "deepseek-v2:236b.description": "DeepSeek V2 236B는 코드 생성에 강점을 가진 DeepSeek의 코드 특화 모델입니다.", "deepseek-v3-0324.description": "DeepSeek-V3-0324는 671B 파라미터의 MoE 모델로, 프로그래밍 및 기술 역량, 문맥 이해, 장문 처리에서 뛰어난 성능을 보입니다.", @@ -491,8 +490,6 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0은 ByteDance Seed의 이미지 생성 모델로, 텍스트 및 이미지 입력을 지원하며, 고품질의 이미지 생성과 높은 제어력을 제공합니다. 텍스트 프롬프트로부터 이미지를 생성합니다.", "doubao-seedream-4-5-251128.description": "Seedream 4.5는 ByteDance의 최신 멀티모달 이미지 모델로, 텍스트-이미지, 이미지-이미지, 배치 이미지 생성 기능을 통합하며 상식과 추론 능력을 포함합니다. 이전 4.0 버전에 비해 생성 품질이 크게 향상되었으며, 편집 일관성과 다중 이미지 융합이 개선되었습니다. 시각적 세부 사항에 대한 더 정밀한 제어를 제공하며, 작은 텍스트와 작은 얼굴을 더 자연스럽게 생성하고 레이아웃과 색상이 더 조화롭게 되어 전체적인 미학을 향상시킵니다.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite는 ByteDance의 최신 이미지 생성 모델로, 처음으로 온라인 검색 기능을 통합하여 실시간 웹 정보를 포함하고 생성된 이미지의 시의성을 향상시킵니다. 모델의 지능도 업그레이드되어 복잡한 지시와 시각적 콘텐츠를 정확히 해석할 수 있습니다. 또한 전문적인 시나리오에서 글로벌 지식 범위, 참조 일관성, 생성 품질이 개선되어 기업 수준의 시각적 창작 요구를 더 잘 충족시킵니다.", - "dreamina-seedance-2-0-260128.description": "ByteDance의 Seedance 2.0은 가장 강력한 비디오 생성 모델로, 다중 모달 참조 비디오 생성, 비디오 편집, 비디오 확장, 텍스트-비디오 및 이미지-비디오를 동기화된 오디오와 함께 지원합니다.", - "dreamina-seedance-2-0-fast-260128.description": "ByteDance의 Seedance 2.0 Fast는 Seedance 2.0과 동일한 기능을 제공하며, 더 빠른 생성 속도와 경쟁력 있는 가격을 자랑합니다.", "emohaa.description": "Emohaa는 전문 상담 능력을 갖춘 정신 건강 모델로, 사용자가 감정 문제를 이해하도록 돕습니다.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B는 로컬 및 맞춤형 배포를 위한 오픈소스 경량 모델입니다.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview는 ERNIE 4.5의 8K 컨텍스트 프리뷰 모델로, 평가용으로 사용됩니다.", @@ -506,6 +503,7 @@ "ernie-5.0-thinking-latest.description": "Wenxin 5.0 Thinking은 텍스트, 이미지, 오디오, 비디오를 통합 모델링하는 네이티브 풀모달 플래그십 모델로, 복잡한 질의응답, 창작, 에이전트 시나리오에서 전반적인 성능을 대폭 향상시킵니다.", "ernie-5.0-thinking-preview.description": "Wenxin 5.0 Thinking Preview는 텍스트, 이미지, 오디오, 비디오를 통합 모델링하는 네이티브 풀모달 플래그십 모델로, 복잡한 질의응답, 창작, 에이전트 시나리오에서 전반적인 성능을 대폭 향상시킵니다.", "ernie-5.0.description": "ERNIE 5.0은 ERNIE 시리즈의 차세대 모델로, 본질적으로 멀티모달 대형 모델입니다. 텍스트, 이미지, 오디오 및 비디오를 공동으로 모델링하는 통합 멀티모달 모델링 접근 방식을 채택하여 포괄적인 멀티모달 능력을 제공합니다. 기본 능력이 크게 업그레이드되어 벤치마크 평가에서 강력한 성능을 달성했습니다. 특히 멀티모달 이해, 명령 따르기, 창의적 글쓰기, 사실 정확성, 에이전트 계획 및 도구 활용에서 뛰어납니다.", + "ernie-5.1.description": "ERNIE 5.1은 ERNIE 시리즈의 최신 모델로, 기본 기능에 대한 포괄적인 업그레이드를 제공합니다. 에이전트, 지식 처리, 추론, 심층 검색 등 여러 분야에서 상당한 개선을 보여줍니다. 이 릴리스는 대규모 모델이 자율 에이전트 의사결정으로 진화하는 과정에서 발생하는 주요 문제를 해결하기 위해 비동기 강화 학습 아키텍처를 채택했습니다. 여기에는 훈련-추론 간 수치 불일치, 이기종 컴퓨팅 자원의 낮은 활용도, 롱테일 효과로 인한 글로벌 문제 등이 포함됩니다. 또한 대규모 에이전트 후속 훈련 기술을 활용하여 모델의 기능과 일반화 성능을 더욱 강화했습니다. 환경, 전문가, 융합 프로세스를 포함한 3단계 협업 프레임워크를 통해 훈련 효율성을 보장할 뿐만 아니라 복잡한 작업에서 모델의 안정성과 성능을 크게 향상시켰습니다.", "ernie-char-fiction-8k-preview.description": "ERNIE Character Fiction 8K Preview는 캐릭터 및 스토리 창작 기능을 평가하고 테스트하기 위한 프리뷰 모델입니다.", "ernie-char-fiction-8k.description": "ERNIE Character Fiction 8K는 장편 소설 및 스토리 창작에 적합한 페르소나 모델로, 다채로운 이야기 생성에 최적화되어 있습니다.", "ernie-image-turbo.description": "ERNIE-Image는 Baidu에서 개발한 8B-파라미터 텍스트-이미지 모델입니다. 여러 벤치마크에서 상위권에 랭크되며, 중국의 SuperCLUE에서 공동 1위를 차지하고 오픈소스 트랙에서 선두를 달리고 있습니다.", @@ -517,8 +515,7 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K는 복잡한 추론 및 다중 턴 대화를 위한 32K 컨텍스트의 고속 사고 모델입니다.", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview는 평가 및 테스트를 위한 사고 모델 프리뷰입니다.", "ernie-x1.1.description": "ERNIE X1.1은 평가 및 테스트를 위한 사고 모델 프리뷰입니다.", - "fal-ai/bytedance/seedream/v4.5.description": "ByteDance Seed 팀이 개발한 Seedream 4.5는 다중 이미지 편집 및 합성을 지원하며, 향상된 주제 일관성, 정확한 지시 따르기, 공간 논리 이해, 미적 표현, 포스터 레이아웃 및 로고 디자인을 고정밀 텍스트-이미지 렌더링과 함께 제공합니다.", - "fal-ai/bytedance/seedream/v4.description": "ByteDance Seed가 개발한 Seedream 4.0은 텍스트 및 이미지 입력을 지원하며, 프롬프트를 기반으로 고품질 이미지를 생성할 수 있는 높은 제어력을 제공합니다.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0은 ByteDance Seed의 이미지 생성 모델로, 텍스트와 이미지 입력을 지원하며 고도로 제어 가능하고 고품질의 이미지를 생성합니다. 텍스트 프롬프트를 기반으로 이미지를 생성합니다.", "fal-ai/flux-kontext/dev.description": "FLUX.1 모델은 이미지 편집에 중점을 두며, 텍스트와 이미지 입력을 지원합니다.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro]는 텍스트와 참조 이미지를 입력으로 받아, 국소 편집과 복잡한 장면 변환을 정밀하게 수행할 수 있습니다.", "fal-ai/flux/krea.description": "Flux Krea [dev]는 보다 사실적이고 자연스러운 이미지 스타일에 중점을 둔 이미지 생성 모델입니다.", @@ -526,8 +523,8 @@ "fal-ai/hunyuan-image/v3.description": "강력한 네이티브 멀티모달 이미지 생성 모델입니다.", "fal-ai/imagen4/preview.description": "Google에서 개발한 고품질 이미지 생성 모델입니다.", "fal-ai/nano-banana.description": "Nano Banana는 Google의 최신, 가장 빠르고 효율적인 네이티브 멀티모달 모델로, 대화형 이미지 생성 및 편집을 지원합니다.", - "fal-ai/qwen-image-edit.description": "Qwen 팀의 전문 이미지 편집 모델로, 의미 및 외형 편집, 정확한 중국어/영어 텍스트 편집, 스타일 전환, 회전 등을 지원합니다.", - "fal-ai/qwen-image.description": "Qwen 팀의 강력한 이미지 생성 모델로, 뛰어난 중국어 텍스트 렌더링과 다양한 시각적 스타일을 제공합니다.", + "fal-ai/qwen-image-edit.description": "Qwen 팀의 전문 이미지 편집 모델로, 의미와 외형 편집을 지원하며, 중국어와 영어 텍스트를 정밀하게 편집하고 스타일 전환, 객체 회전과 같은 고품질 편집을 가능하게 합니다.", + "fal-ai/qwen-image.description": "Qwen 팀의 강력한 이미지 생성 모델로, 중국어 텍스트 렌더링과 다양한 시각적 스타일에서 뛰어난 성능을 발휘합니다.", "flux-1-schnell.description": "Black Forest Labs에서 개발한 120억 파라미터 텍스트-이미지 모델로, 잠재 적대 확산 증류를 사용하여 1~4단계 내에 고품질 이미지를 생성합니다. Apache-2.0 라이선스로 개인, 연구, 상업적 사용이 가능합니다.", "flux-dev.description": "비상업적 혁신 연구를 위해 효율적으로 최적화된 오픈소스 R&D 이미지 생성 모델입니다.", "flux-kontext-max.description": "최첨단 문맥 기반 이미지 생성 및 편집 모델로, 텍스트와 이미지를 결합하여 정밀하고 일관된 결과를 생성합니다.", @@ -566,10 +563,10 @@ "gemini-3-flash-preview.description": "Gemini 3 Flash는 속도를 위해 설계된 가장 스마트한 모델로, 최첨단 지능과 뛰어난 검색 기반을 결합합니다.", "gemini-3-flash.description": "Google의 Gemini 3 Flash — 멀티모달 입력 지원을 갖춘 초고속 모델.", "gemini-3-pro-image-preview.description": "Gemini 3 Pro Image (Nano Banana Pro)는 구글의 이미지 생성 모델로, 멀티모달 대화도 지원합니다.", - "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro)는 Google의 이미지 생성 모델로, 다중 모달 채팅도 지원합니다.", + "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro)는 Google의 이미지 생성 모델로, 멀티모달 대화도 지원합니다.", "gemini-3-pro-preview.description": "Gemini 3 Pro는 Google의 가장 강력한 에이전트 및 바이브 코딩 모델로, 최첨단 추론 위에 풍부한 시각적 표현과 깊은 상호작용을 제공합니다.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2)는 구글의 가장 빠른 네이티브 이미지 생성 모델로, 사고 지원, 대화형 이미지 생성 및 편집을 제공합니다.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2)는 Flash 속도로 Pro 수준의 이미지 품질을 제공하며, 다중 모달 채팅을 지원합니다.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2)는 Google의 가장 빠른 네이티브 이미지 생성 모델로, 사고 지원, 대화형 이미지 생성 및 편집을 제공합니다.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview는 Google의 가장 비용 효율적인 다중 모드 모델로, 대량 에이전트 작업, 번역 및 데이터 처리에 최적화되어 있습니다.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite는 Google의 가장 비용 효율적인 멀티모달 모델로, 대량 에이전트 작업, 번역 및 데이터 처리에 최적화되어 있습니다.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview는 Gemini 3 Pro의 추론 능력을 강화하고 중간 사고 수준 지원을 추가합니다.", @@ -734,8 +731,6 @@ "grok-4-fast-reasoning.description": "Grok 4 Fast는 비용 효율적인 추론 모델 분야에서의 최신 성과로, 출시를 기쁘게 생각합니다.", "grok-4.20-0309-non-reasoning.description": "간단한 사용 사례를 위한 비추론 변형", "grok-4.20-0309-reasoning.description": "응답 전에 추론하는 지능적이고 매우 빠른 모델", - "grok-4.20-beta-0309-non-reasoning.description": "간단한 사용 사례를 위한 비사고 변형 모델입니다.", - "grok-4.20-beta-0309-reasoning.description": "응답 전에 사고하는 지능적이고 매우 빠른 모델입니다.", "grok-4.20-multi-agent-0309.description": "4명 또는 16명의 에이전트 팀, 연구 사용 사례에서 뛰어남, 현재 클라이언트 측 도구를 지원하지 않음. xAI 서버 측 도구(예: X Search, Web Search 도구) 및 원격 MCP 도구만 지원.", "grok-4.3.description": "세계에서 가장 진실을 추구하는 대규모 언어 모델", "grok-4.description": "언어, 수학, 추론에서 타의 추종을 불허하는 성능을 자랑하는 최신 Grok 플래그십 모델 — 진정한 만능 모델입니다. 현재 grok-4-0709를 가리키며, 제한된 자원으로 인해 공식 가격보다 임시로 10% 높게 책정되었으며, 이후 공식 가격으로 복귀할 예정입니다.", @@ -1220,8 +1215,6 @@ "qwq.description": "QwQ는 Qwen 계열의 추론 모델입니다. 일반적인 지시 조정 모델과 비교해 사고 및 추론 능력이 뛰어나며, 특히 어려운 문제에서 다운스트림 성능을 크게 향상시킵니다. QwQ-32B는 DeepSeek-R1 및 o1-mini와 경쟁할 수 있는 중형 추론 모델입니다.", "qwq_32b.description": "Qwen 계열의 중형 추론 모델입니다. 일반적인 지시 조정 모델과 비교해 QwQ의 사고 및 추론 능력은 특히 어려운 문제에서 다운스트림 성능을 크게 향상시킵니다.", "r1-1776.description": "R1-1776은 DeepSeek R1의 후속 학습 버전으로, 검열되지 않고 편향 없는 사실 정보를 제공합니다.", - "seedance-1-5-pro-251215.description": "ByteDance의 Seedance 1.5 Pro는 텍스트-비디오, 이미지-비디오(첫 프레임, 첫+마지막 프레임) 및 시각과 동기화된 오디오 생성을 지원합니다.", - "seedream-5-0-260128.description": "BytePlus의 ByteDance-Seedream-5.0-lite는 실시간 정보를 위한 웹 검색 기반 생성, 복잡한 프롬프트 해석 향상, 전문적인 시각적 창작을 위한 참조 일관성 개선을 제공합니다.", "solar-mini-ja.description": "Solar Mini (Ja)는 Solar Mini의 일본어 특화 버전으로, 영어와 한국어에서도 효율적이고 강력한 성능을 유지합니다.", "solar-mini.description": "Solar Mini는 GPT-3.5를 능가하는 성능을 가진 소형 LLM으로, 영어와 한국어를 지원하는 강력한 다국어 기능을 갖추고 있으며, 효율적인 경량 솔루션을 제공합니다.", "solar-pro.description": "Solar Pro는 Upstage의 고지능 LLM으로, 단일 GPU에서 지시 수행에 최적화되어 있으며, IFEval 점수 80 이상을 기록합니다. 현재는 영어를 지원하며, 2024년 11월 전체 릴리스 시 더 많은 언어와 긴 컨텍스트를 지원할 예정입니다.", diff --git a/locales/ko-KR/onboarding.json b/locales/ko-KR/onboarding.json index 6be93ec119..45de880767 100644 --- a/locales/ko-KR/onboarding.json +++ b/locales/ko-KR/onboarding.json @@ -27,6 +27,13 @@ "agent.layout.skipConfirm.ok": "일단 건너뛰기", "agent.layout.skipConfirm.title": "지금 온보딩을 건너뛰시겠어요?", "agent.layout.switchMessage": "오늘은 기분이 아니신가요? {{mode}}로 전환하거나 {{skip}}하실 수 있어요.", + "agent.layout.switchMessageClassic": "오늘은 별로인가요? {{mode}}로 전환할 수 있습니다.", + "agent.messenger.cta.discord": "Discord에 추가", + "agent.messenger.cta.slack": "Slack에 추가", + "agent.messenger.cta.telegram": "Telegram에서 열기", + "agent.messenger.subtitle": "Telegram, Slack, 또는 Discord에서 에이전트와 채팅하세요", + "agent.messenger.telegramQrCaption": "휴대폰 카메라로 스캔하세요", + "agent.messenger.title": "어디서든 메시지로 저와 함께하세요", "agent.modeSwitch.agent": "대화형", "agent.modeSwitch.classic": "클래식", "agent.modeSwitch.collapse": "축소", @@ -68,6 +75,13 @@ "agent.wrapUp.confirm.content": "지금까지의 내용을 저장할게요. 언제든 다시 와서 계속 이야기할 수 있어요.", "agent.wrapUp.confirm.ok": "지금 마치기", "agent.wrapUp.confirm.title": "온보딩을 지금 마칠까요?", + "agentPicker.allCategories": "전체", + "agentPicker.continue": "계속하기", + "agentPicker.skip": "지금은 건너뛰기", + "agentPicker.subtitle": "지금 몇 가지를 라이브러리에 추가하세요 — 나중에 언제든 더 발견할 수 있습니다.", + "agentPicker.title": "라이브러리에 몇 가지 에이전트를 추가해봅시다", + "agentPicker.title2": "당신의 작업 방식에 맞는 것을 선택하세요", + "agentPicker.title3": "언제든 더 추가할 수 있습니다 — 우선 몇 가지로 시작해보세요", "back": "이전 단계", "finish": "시작하기", "interests.area.business": "비즈니스 및 전략", diff --git a/locales/ko-KR/plugin.json b/locales/ko-KR/plugin.json index c1759a1fb5..ec2698ec6b 100644 --- a/locales/ko-KR/plugin.json +++ b/locales/ko-KR/plugin.json @@ -39,8 +39,8 @@ "builtins.lobe-agent-documents.inspector.docCount": "{{count}} 문서", "builtins.lobe-agent-documents.inspector.opsCount": "{{count}} 작업", "builtins.lobe-agent-documents.inspector.opsResult": "{{success}}/{{total}} 작업", - "builtins.lobe-agent-documents.inspector.target.agent": "에이전트에서", - "builtins.lobe-agent-documents.inspector.target.currentTopic": "주제에서", + "builtins.lobe-agent-documents.inspector.scope.agent": "에이전트에서", + "builtins.lobe-agent-documents.inspector.scope.currentTopic": "주제에서", "builtins.lobe-agent-documents.title": "에이전트 문서", "builtins.lobe-agent-management.apiName.callAgent": "통화 에이전트", "builtins.lobe-agent-management.apiName.createAgent": "에이전트 생성", @@ -90,6 +90,14 @@ "builtins.lobe-agent.title": "로브 에이전트", "builtins.lobe-claude-code.agent.instruction": "지시사항", "builtins.lobe-claude-code.agent.result": "결과", + "builtins.lobe-claude-code.task.createLabel": "작업 생성: ", + "builtins.lobe-claude-code.task.getLabel": "작업 #{{taskId}} 검사 중", + "builtins.lobe-claude-code.task.listLabel": "작업 목록", + "builtins.lobe-claude-code.task.updateCompleted": "완료됨", + "builtins.lobe-claude-code.task.updateDeleted": "삭제됨", + "builtins.lobe-claude-code.task.updateInProgress": "시작됨", + "builtins.lobe-claude-code.task.updateLabel": "작업 #{{taskId}} 업데이트 중", + "builtins.lobe-claude-code.task.updatePending": "초기화", "builtins.lobe-claude-code.todoWrite.allDone": "모든 작업이 완료되었습니다", "builtins.lobe-claude-code.todoWrite.currentStep": "현재 단계", "builtins.lobe-claude-code.todoWrite.todos": "할 일", diff --git a/locales/ko-KR/providers.json b/locales/ko-KR/providers.json index 1e05daa79e..08f8678375 100644 --- a/locales/ko-KR/providers.json +++ b/locales/ko-KR/providers.json @@ -33,7 +33,6 @@ "jina.description": "2020년에 설립된 Jina AI는 선도적인 검색 AI 기업으로, 벡터 모델, 재정렬기, 소형 언어 모델을 포함한 검색 스택을 통해 신뢰성 높고 고품질의 생성형 및 멀티모달 검색 앱을 구축합니다.", "kimicodingplan.description": "문샷 AI의 Kimi Code는 K2.5를 포함한 Kimi 모델에 접근하여 코딩 작업을 수행할 수 있습니다.", "lmstudio.description": "LM Studio는 데스크탑에서 LLM을 개발하고 실험할 수 있는 애플리케이션입니다.", - "lobehub.description": "LobeHub Cloud는 공식 API를 사용하여 AI 모델에 접근하며, 모델 토큰에 연동된 크레딧으로 사용량을 측정합니다.", "longcat.description": "LongCat은 Meituan에서 독자적으로 개발한 생성형 AI 대형 모델 시리즈입니다. 이는 효율적인 계산 아키텍처와 강력한 멀티모달 기능을 통해 내부 기업 생산성을 향상시키고 혁신적인 애플리케이션을 가능하게 하기 위해 설계되었습니다.", "minimax.description": "2021년에 설립된 MiniMax는 텍스트, 음성, 비전 등 멀티모달 기반의 범용 AI를 개발하며, 조 단위 파라미터의 MoE 텍스트 모델과 Hailuo AI와 같은 앱을 제공합니다.", "minimaxcodingplan.description": "MiniMax 토큰 플랜은 고정 요금 구독을 통해 M2.7을 포함한 MiniMax 모델에 접근하여 코딩 작업을 수행할 수 있습니다.", diff --git a/locales/ko-KR/subscription.json b/locales/ko-KR/subscription.json index ecb9e075e8..50d5e8247c 100644 --- a/locales/ko-KR/subscription.json +++ b/locales/ko-KR/subscription.json @@ -55,6 +55,12 @@ "credits.autoTopUp.toggle": "자동 충전 활성화", "credits.autoTopUp.upgradeHint": "유료 플랜을 구독하여 자동 충전을 활성화하세요", "credits.autoTopUp.validation.targetMustExceedThreshold": "목표 잔액은 임계값보다 커야 합니다", + "credits.costEstimateHint.desc": "예상 모델 비용이 설정한 임계값에 도달하면 전송 전에 경고를 표시합니다", + "credits.costEstimateHint.saveError": "비용 추정 경고 설정 저장에 실패했습니다", + "credits.costEstimateHint.saveSuccess": "비용 추정 경고 설정이 저장되었습니다", + "credits.costEstimateHint.threshold": "경고 임계값", + "credits.costEstimateHint.title": "비용 추정 경고", + "credits.costEstimateHint.validation.threshold": "임계값은 0 이상이어야 합니다", "credits.packages.auto": "자동", "credits.packages.charged": "${{amount}} 청구됨", "credits.packages.expired": "만료됨", diff --git a/locales/ko-KR/tool.json b/locales/ko-KR/tool.json index d0729b3b78..694401feca 100644 --- a/locales/ko-KR/tool.json +++ b/locales/ko-KR/tool.json @@ -40,6 +40,14 @@ "agentMarketplace.render.alreadyInLibraryTag": "이미 라이브러리에 있음", "agentMarketplace.render.alreadyInLibrary_one": "{{count}}개가 이미 라이브러리에 있음", "agentMarketplace.render.alreadyInLibrary_other": "{{count}}개가 이미 라이브러리에 있음", + "claudeCode.askUserQuestion.escape.back": "옵션으로 돌아가기", + "claudeCode.askUserQuestion.escape.enter": "직접 입력하세요", + "claudeCode.askUserQuestion.escape.placeholder": "여기에 답변을 입력하세요…", + "claudeCode.askUserQuestion.multiSelectTag": "(다중 선택)", + "claudeCode.askUserQuestion.skip": "건너뛰기", + "claudeCode.askUserQuestion.submit": "제출", + "claudeCode.askUserQuestion.timeExpired": "시간 초과 — 각 질문의 옵션 1을 사용합니다.", + "claudeCode.askUserQuestion.timeRemaining": "남은 시간: {{time}} · 시간 초과 시 답변되지 않은 질문은 옵션 1로 기본 설정됩니다.", "codeInterpreter-legacy.error": "실행 오류", "codeInterpreter-legacy.executing": "실행 중...", "codeInterpreter-legacy.files": "파일:", diff --git a/locales/ko-KR/topic.json b/locales/ko-KR/topic.json index fd19dfa124..b673300784 100644 --- a/locales/ko-KR/topic.json +++ b/locales/ko-KR/topic.json @@ -56,6 +56,7 @@ "renameModal.title": "토픽 이름 변경", "searchPlaceholder": "주제 검색...", "searchResultEmpty": "검색 결과가 없습니다.", + "sidebar.title": "주제", "taskManager.agent": "작업 에이전트", "taskManager.welcome": "작업에 대해 물어보세요", "temp": "임시", diff --git a/locales/nl-NL/chat.json b/locales/nl-NL/chat.json index d333feaff7..cd80c28214 100644 --- a/locales/nl-NL/chat.json +++ b/locales/nl-NL/chat.json @@ -220,6 +220,13 @@ "inbox.title": "Lobe AI", "input.addAi": "AI-bericht toevoegen", "input.addUser": "Gebruikersbericht toevoegen", + "input.costEstimate.creditsPerMillionTokens": "{{credits}} credits/M tokens", + "input.costEstimate.hint": "Geschatte kosten: ~{{credits}} credits", + "input.costEstimate.inputLabel": "Invoer", + "input.costEstimate.outputLabel": "Uitvoer", + "input.costEstimate.settingsLink": "Waarschuwingsdrempel aanpassen", + "input.costEstimate.tokenCount": "~{{tokens}} tokens", + "input.costEstimate.tooltip": "Geschat op basis van de huidige context, tools en modelprijzen. Werkelijke kosten kunnen variëren.", "input.disclaimer": "Agents kunnen fouten maken. Gebruik je eigen oordeel bij belangrijke informatie.", "input.errorMsg": "Verzenden mislukt: {{errorMsg}}. Probeer opnieuw of later nog eens.", "input.more": "Meer", @@ -779,6 +786,7 @@ "upload.preview.prepareTasks": "Delen voorbereiden...", "upload.preview.status.pending": "Voorbereiden op upload...", "upload.preview.status.processing": "Bestand verwerken...", + "upload.validation.unsupportedFileType": "Niet-ondersteund bestandstype: {{files}}. Ondersteunde afbeeldingen: JPG, PNG, GIF, WebP. Ondersteunde documenten zijn onder andere PDF, Word, Excel, PowerPoint, Markdown, tekst, CSV, JSON en codebestanden.", "upload.validation.videoSizeExceeded": "De bestandsgrootte van de video mag niet groter zijn dan 20MB. Huidige grootte is {{actualSize}}.", "viewMode.fullWidth": "Volledige breedte", "viewMode.normal": "Standaard", @@ -894,6 +902,8 @@ "workingPanel.localFile.closeOther": "Overige Sluiten", "workingPanel.localFile.closeRight": "Sluiten aan de Rechterkant", "workingPanel.localFile.error": "Kon dit bestand niet laden", + "workingPanel.localFile.preview.raw": "Ruw", + "workingPanel.localFile.preview.render": "Voorbeeld", "workingPanel.localFile.truncated": "Bestandsvoorbeeld ingekort tot {{limit}} tekens", "workingPanel.progress": "Progress", "workingPanel.progress.allCompleted": "All tasks completed", @@ -959,6 +969,8 @@ "workingPanel.review.viewMode.unified": "Schakel over naar uniforme weergave", "workingPanel.review.wordWrap.disable": "Tekstterugloop uitschakelen", "workingPanel.review.wordWrap.enable": "Tekstterugloop inschakelen", + "workingPanel.skills.empty": "Geen vaardigheden gevonden in dit project", + "workingPanel.skills.title": "Vaardigheden", "workingPanel.space": "Ruimte", "workingPanel.title": "Working Panel", "you": "Jij", diff --git a/locales/nl-NL/components.json b/locales/nl-NL/components.json index 9f49534643..f7a8477913 100644 --- a/locales/nl-NL/components.json +++ b/locales/nl-NL/components.json @@ -121,6 +121,16 @@ "ModelSwitchPanel.detail.context": "Contextlengte", "ModelSwitchPanel.detail.pricing": "Prijzen", "ModelSwitchPanel.detail.pricing.cachedInput": "Ingevoerde cache ${{amount}}/M", + "ModelSwitchPanel.detail.pricing.credits.cachedInput": "Gecachte invoer {{amount}} credits/M tokens", + "ModelSwitchPanel.detail.pricing.credits.image": "credits/afbeelding", + "ModelSwitchPanel.detail.pricing.credits.input": "Invoer {{amount}} credits/M tokens", + "ModelSwitchPanel.detail.pricing.credits.megapixel": "credits/MP", + "ModelSwitchPanel.detail.pricing.credits.millionCharacters": "credits/M tekens", + "ModelSwitchPanel.detail.pricing.credits.millionTokens": "credits/M tokens", + "ModelSwitchPanel.detail.pricing.credits.output": "Uitvoer {{amount}} credits/M tokens", + "ModelSwitchPanel.detail.pricing.credits.perImage": "~ {{amount}} credits / afbeelding", + "ModelSwitchPanel.detail.pricing.credits.perVideo": "~ {{amount}} credits / video", + "ModelSwitchPanel.detail.pricing.credits.second": "credits/s", "ModelSwitchPanel.detail.pricing.group.audio": "Audio", "ModelSwitchPanel.detail.pricing.group.image": "Afbeelding", "ModelSwitchPanel.detail.pricing.group.text": "Tekst", diff --git a/locales/nl-NL/editor.json b/locales/nl-NL/editor.json index 021ac74b28..5b736f5b23 100644 --- a/locales/nl-NL/editor.json +++ b/locales/nl-NL/editor.json @@ -1,8 +1,10 @@ { "actionTag.category.command": "Commando", + "actionTag.category.projectSkill": "Projectvaardigheid", "actionTag.category.skill": "Vaardigheid", "actionTag.category.tool": "Hulpmiddel", "actionTag.tooltip.command": "Voert vóór het verzenden een slash-commando aan de clientzijde uit.", + "actionTag.tooltip.projectSkill": "Verzonden als een schuine streep-aanroep zodat de CLI van de agent de bijpassende projectvaardigheid uitvoert.", "actionTag.tooltip.skill": "Laadt voor dit verzoek een herbruikbaar vaardigheidspakket.", "actionTag.tooltip.tool": "Markeert een hulpmiddel dat de gebruiker expliciet voor dit verzoek heeft geselecteerd.", "actions.expand.off": "Samenvouwen", diff --git a/locales/nl-NL/home.json b/locales/nl-NL/home.json index 47139769e2..0ff7aff0f5 100644 --- a/locales/nl-NL/home.json +++ b/locales/nl-NL/home.json @@ -11,6 +11,19 @@ "brief.action.ignore": "Negeren", "brief.action.retry": "Opnieuw proberen", "brief.addFeedback": "Feedback delen", + "brief.agentSignal.selfReview.applied.heading": "Bijgewerkt", + "brief.agentSignal.selfReview.applied.summary": "{{count}} droomupdate is toegepast.", + "brief.agentSignal.selfReview.applied.summary_plural": "{{count}} droomupdates zijn toegepast.", + "brief.agentSignal.selfReview.applied.title": "Droombronnen bijgewerkt", + "brief.agentSignal.selfReview.error.heading": "Probleem", + "brief.agentSignal.selfReview.error.summary": "Sommig werk kon niet worden voltooid tijdens deze droom.", + "brief.agentSignal.selfReview.error.title": "Droom stuitte op een probleem", + "brief.agentSignal.selfReview.ideas.summary": "Opgeslagen droomnotities voor toekomstige beoordeling.", + "brief.agentSignal.selfReview.ideas.title": "Droomnotities", + "brief.agentSignal.selfReview.proposal.heading": "Suggestie", + "brief.agentSignal.selfReview.proposal.summary": "{{count}} droomsuggestie heeft je beoordeling nodig.", + "brief.agentSignal.selfReview.proposal.summary_plural": "{{count}} droomsuggesties hebben je beoordeling nodig.", + "brief.agentSignal.selfReview.proposal.title": "Droomsuggestie vereist beoordeling", "brief.collapse": "Minder weergeven", "brief.commentPlaceholder": "Deel je feedback...", "brief.commentSubmit": "Feedback verzenden", diff --git a/locales/nl-NL/hotkey.json b/locales/nl-NL/hotkey.json index 0193e5f4aa..018e8d5ae5 100644 --- a/locales/nl-NL/hotkey.json +++ b/locales/nl-NL/hotkey.json @@ -1,8 +1,6 @@ { "addUserMessage.desc": "Voeg de huidige invoer toe als een gebruikersbericht zonder generatie te starten", "addUserMessage.title": "Gebruikersbericht Toevoegen", - "clearCurrentMessages.desc": "Wis de berichten en geüploade bestanden van het huidige gesprek", - "clearCurrentMessages.title": "Gespreksberichten Wissen", "commandPalette.desc": "Open de globale opdrachtpalet voor snelle toegang tot functies", "commandPalette.title": "Opdrachtpalet", "deleteAndRegenerateMessage.desc": "Verwijder het laatste bericht en genereer opnieuw", diff --git a/locales/nl-NL/models.json b/locales/nl-NL/models.json index d669a1018d..b7dff8c918 100644 --- a/locales/nl-NL/models.json +++ b/locales/nl-NL/models.json @@ -106,7 +106,6 @@ "MiniMax-Hailuo-2.3.description": "Gloednieuw videogenereermodel met uitgebreide verbeteringen in lichaamsbeweging, fysieke realisme en instructienaleving.", "MiniMax-M1.description": "Een nieuw intern redeneermodel met 80K chain-of-thought en 1M input, met prestaties vergelijkbaar met toonaangevende wereldwijde modellen.", "MiniMax-M2-Stable.description": "Ontworpen voor efficiënte codeer- en agentworkflows, met hogere gelijktijdigheid voor commercieel gebruik.", - "MiniMax-M2.1-Lightning.description": "Krachtige meertalige programmeermogelijkheden met snellere en efficiëntere inferentie.", "MiniMax-M2.1-highspeed.description": "Krachtige meertalige programmeermogelijkheden, een volledig verbeterde programmeerervaring. Sneller en efficiënter.", "MiniMax-M2.1.description": "MiniMax-M2.1 is het vlaggenschip open-source grote model van MiniMax, gericht op het oplossen van complexe, realistische taken. De kernkwaliteiten zijn meertalige programmeermogelijkheden en het vermogen om complexe taken als een Agent op te lossen.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: Zelfde prestaties als M2.5 met snellere inferentie.", @@ -315,13 +314,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku is het snelste en meest compacte model van Anthropic, ontworpen voor vrijwel directe reacties met snelle en nauwkeurige prestaties.", "claude-3-opus-20240229.description": "Claude 3 Opus is het krachtigste model van Anthropic voor zeer complexe taken, met uitmuntende prestaties, intelligentie, vloeiendheid en begrip.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet biedt een balans tussen intelligentie en snelheid voor zakelijke toepassingen, met hoge bruikbaarheid tegen lagere kosten en betrouwbare grootschalige inzet.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 is het snelste en meest intelligente Haiku-model van Anthropic, met bliksemsnelle prestaties en uitgebreide denkcapaciteit.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 is Anthropic's snelste en slimste Haiku-model, met bliksemsnelle snelheid en uitgebreide redeneervermogen.", "claude-haiku-4-5.description": "Claude Haiku 4.5 door Anthropic — next-gen Haiku met verbeterde redenering en visie.", "claude-haiku-4.5.description": "Claude Haiku 4.5 is Anthropic's snelste en slimste Haiku-model, met bliksemsnelle snelheid en uitgebreide redeneervermogen.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking is een geavanceerde variant die zijn redeneerproces kan onthullen.", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1 is het nieuwste en meest capabele model van Anthropic voor zeer complexe taken, uitblinkend in prestaties, intelligentie, vloeiendheid en begrip.", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1 is Anthropic's nieuwste en meest capabele model voor zeer complexe taken, uitblinkend in prestaties, intelligentie, vloeiendheid en begrip.", "claude-opus-4-1.description": "Claude Opus 4.1 door Anthropic — premium redeneermodel met diepgaande analysemogelijkheden.", - "claude-opus-4-20250514.description": "Claude Opus 4 is het krachtigste model van Anthropic voor zeer complexe taken, uitblinkend in prestaties, intelligentie, vloeiendheid en begrip.", + "claude-opus-4-20250514.description": "Claude Opus 4 is Anthropic's krachtigste model voor zeer complexe taken, uitblinkend in prestaties, intelligentie, vloeiendheid en begrip.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 is het vlaggenschipmodel van Anthropic, dat uitzonderlijke intelligentie combineert met schaalbare prestaties. Ideaal voor complexe taken die hoogwaardige antwoorden en redenering vereisen.", "claude-opus-4-5.description": "Claude Opus 4.5 door Anthropic — vlaggenschipmodel met topklasse redenering en codering.", "claude-opus-4-6.description": "Claude Opus 4.6 door Anthropic — vlaggenschip met 1M contextvenster en geavanceerde redenering.", @@ -330,8 +329,8 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 is Anthropic's meest intelligente model voor het bouwen van agents en coderen.", "claude-opus-4.6.description": "Claude Opus 4.6 is Anthropic's meest intelligente model voor het bouwen van agents en coderen.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking kan vrijwel directe antwoorden geven of uitgebreide stapsgewijze redenering tonen met zichtbaar proces.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 is het meest intelligente model van Anthropic tot nu toe, met bijna directe reacties of uitgebreide stapsgewijze denkprocessen en fijne controle voor API-gebruikers.", - "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 is het meest intelligente model van Anthropic tot nu toe.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 kan bijna onmiddellijke reacties genereren of uitgebreide stapsgewijze denkprocessen met zichtbaar verloop.", + "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 is Anthropic's meest intelligente model tot nu toe.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 door Anthropic — verbeterde Sonnet met verbeterde codeerprestaties.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 door Anthropic — nieuwste Sonnet met superieure codering en hulpmiddelengebruik.", "claude-sonnet-4.5.description": "Claude Sonnet 4.5 is tot nu toe het meest intelligente model van Anthropic.", @@ -404,7 +403,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) is een innovatief model dat diep taalbegrip en interactie biedt.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 is een next-gen redeneermodel met sterkere complexe redenering en chain-of-thought voor diepgaande analysetaken.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 is een next-gen redeneermodel met sterkere complexe redeneer- en keten-van-denken-capaciteiten.", - "deepseek-chat.description": "Compatibiliteitsalias voor DeepSeek V4 Flash niet-denkmodus. Gepland voor afschaffing — gebruik DeepSeek V4 Flash in plaats daarvan.", + "deepseek-chat.description": "Een nieuw open-source model dat algemene en codevaardigheden combineert. Het behoudt de algemene dialoog van het chatmodel en de sterke codeerkwaliteiten van het coderingsmodel, met betere voorkeurafstemming. DeepSeek-V2.5 verbetert ook schrijven en het opvolgen van instructies.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B is een codeertaalmodel getraind op 2 biljoen tokens (87% code, 13% Chinees/Engels tekst). Het introduceert een contextvenster van 16K en 'fill-in-the-middle'-taken, wat projectniveau codeaanvulling en fragmentinvoeging mogelijk maakt.", "deepseek-coder-v2.description": "DeepSeek Coder V2 is een open-source MoE-codeermodel dat sterk presteert bij programmeertaken, vergelijkbaar met GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 is een open-source MoE-codeermodel dat sterk presteert bij programmeertaken, vergelijkbaar met GPT-4 Turbo.", @@ -426,7 +425,7 @@ "deepseek-r1-fast-online.description": "DeepSeek R1 snelle volledige versie met realtime webzoekfunctie, combineert 671B-capaciteit met snellere reacties.", "deepseek-r1-online.description": "DeepSeek R1 volledige versie met 671B parameters en realtime webzoekfunctie, biedt sterkere begrip- en generatiecapaciteiten.", "deepseek-r1.description": "DeepSeek-R1 gebruikt cold-start data vóór versterkingsleren en presteert vergelijkbaar met OpenAI-o1 op wiskunde, programmeren en redenering.", - "deepseek-reasoner.description": "Compatibiliteitsalias voor DeepSeek V4 Flash denkmodus. Gepland voor afschaffing — gebruik DeepSeek V4 Flash in plaats daarvan.", + "deepseek-reasoner.description": "Een DeepSeek redeneermodel gericht op complexe logische redeneertaken.", "deepseek-v2.description": "DeepSeek V2 is een efficiënt MoE-model voor kosteneffectieve verwerking.", "deepseek-v2:236b.description": "DeepSeek V2 236B is DeepSeek’s codegerichte model met sterke codegeneratie.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 is een MoE-model met 671B parameters en uitmuntende prestaties in programmeren, technische vaardigheden, contextbegrip en verwerking van lange teksten.", @@ -491,8 +490,6 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 is een beeldgeneratiemodel van ByteDance Seed dat tekst- en afbeeldingsinvoer ondersteunt voor zeer controleerbare, hoogwaardige beeldgeneratie. Het genereert beelden op basis van tekstprompts.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 is het nieuwste multimodale beeldmodel van ByteDance, dat tekst-naar-beeld, beeld-naar-beeld en batchbeeldgeneratiecapaciteiten integreert, terwijl het gezond verstand en redeneervermogen bevat. Vergeleken met de vorige 4.0-versie levert het aanzienlijk verbeterde generatiekwaliteit, met betere bewerkingsconsistentie en multi-beeldfusie. Het biedt meer precieze controle over visuele details, produceert kleine teksten en gezichten natuurlijker, en bereikt een harmonieuzere lay-out en kleur, wat de algehele esthetiek verbetert.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite is het nieuwste beeldgeneratiemodel van ByteDance. Voor het eerst integreert het online zoekmogelijkheden, waardoor het real-time webinformatie kan opnemen en de actualiteit van gegenereerde beelden kan verbeteren. De intelligentie van het model is ook geüpgraded, waardoor het complexe instructies en visuele inhoud nauwkeurig kan interpreteren. Bovendien biedt het verbeterde wereldwijde kennisdekking, referentieconsistentie en generatiekwaliteit in professionele scenario's, waardoor het beter voldoet aan visuele creatiebehoeften op ondernemingsniveau.", - "dreamina-seedance-2-0-260128.description": "Seedance 2.0 van ByteDance is het krachtigste videogeneratiemodel, met ondersteuning voor multimodale referentievideogeneratie, videobewerking, video-uitbreiding, tekst-naar-video en afbeelding-naar-video met gesynchroniseerde audio.", - "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast van ByteDance biedt dezelfde mogelijkheden als Seedance 2.0 met snellere generatiesnelheden tegen een concurrerende prijs.", "emohaa.description": "Emohaa is een mentaal gezondheidsmodel met professionele begeleidingsvaardigheden om gebruikers te helpen emotionele problemen te begrijpen.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B is een lichtgewicht open-source model voor lokale en aangepaste implementatie.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview is een previewmodel met 8K context voor het evalueren van ERNIE 4.5.", @@ -506,6 +503,7 @@ "ernie-5.0-thinking-latest.description": "Wenxin 5.0 Thinking is een native full-modale vlaggenschipmodel met geïntegreerde tekst-, beeld-, audio- en videomodellering. Het biedt brede capaciteitsverbeteringen voor complexe vraag-en-antwoord, creatie en agenttoepassingen.", "ernie-5.0-thinking-preview.description": "Wenxin 5.0 Thinking Preview is een native full-modale vlaggenschipmodel met geïntegreerde tekst-, beeld-, audio- en videomodellering. Het biedt brede capaciteitsverbeteringen voor complexe vraag-en-antwoord, creatie en agenttoepassingen.", "ernie-5.0.description": "ERNIE 5.0, het nieuwe generatie model in de ERNIE-serie, is een native multimodaal groot model. Het hanteert een uniforme multimodale modelleerbenadering, waarbij tekst, afbeeldingen, audio en video gezamenlijk worden gemodelleerd om uitgebreide multimodale mogelijkheden te bieden. De fundamentele vaardigheden zijn aanzienlijk verbeterd, met sterke prestaties op benchmarkevaluaties. Het blinkt met name uit in multimodale begrip, instructievolging, creatief schrijven, feitelijke nauwkeurigheid, agentplanning en hulpmiddelengebruik.", + "ernie-5.1.description": "ERNIE 5.1 is het nieuwste model in de ERNIE-serie, met uitgebreide upgrades van zijn fundamentele capaciteiten. Het toont aanzienlijke verbeteringen op gebieden zoals agenten, kennisverwerking, redeneren en diep zoeken. Deze release hanteert een ontkoppelde volledig asynchrone versterkingsleerarchitectuur, specifiek ontworpen om belangrijke uitdagingen aan te pakken in de evolutie van grote modellen naar autonome besluitvorming door agenten, waaronder numerieke discrepanties tussen training en inferentie, lage benutting van heterogene computermiddelen en wereldwijde problemen veroorzaakt door langstaart-effecten. Bovendien worden grootschalige agent-natrainingstechnieken toegepast om de capaciteiten en generalisatieprestaties van het model verder te verbeteren. Door een drie-stappen samenwerkingskader met omgeving, expert en fusieprocessen, zorgt de aanpak niet alleen voor trainingsefficiëntie, maar verbetert het ook aanzienlijk de stabiliteit en prestaties van het model bij complexe taken.", "ernie-char-fiction-8k-preview.description": "ERNIE Character Fiction 8K Preview is een previewmodel voor personage- en plotcreatie, bedoeld voor functietests en evaluatie.", "ernie-char-fiction-8k.description": "ERNIE Character Fiction 8K is een personagegericht model voor romans en plotontwikkeling, geschikt voor het genereren van lange verhalen.", "ernie-image-turbo.description": "ERNIE-Image is een 8B-parameter tekst-naar-afbeelding model ontwikkeld door Baidu. Het behoort tot de top op meerdere benchmarks, met een gedeelde eerste plaats in SuperCLUE in China en leidend in de open-source track.", @@ -517,8 +515,7 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K is een snel denkend model met 32K context voor complexe redenatie en meerstapsgesprekken.", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview is een preview van een denkmodel voor evaluatie en testen.", "ernie-x1.1.description": "ERNIE X1.1 is een preview van een denkmodel voor evaluatie en testen.", - "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5, ontwikkeld door het ByteDance Seed-team, ondersteunt multi-afbeeldingbewerking en compositie. Kenmerken zijn verbeterde onderwerpconsistentie, nauwkeurige instructievolging, ruimtelijk logisch begrip, esthetische expressie, posterlay-out en logo-ontwerp met hoogprecisie tekst-afbeelding rendering.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, ontwikkeld door ByteDance Seed, ondersteunt tekst- en afbeeldingsinvoer voor zeer controleerbare, hoogwaardige afbeeldingsgeneratie vanuit prompts.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 is een beeldgeneratiemodel van ByteDance Seed, dat tekst- en beeldinvoer ondersteunt met zeer controleerbare, hoogwaardige beeldgeneratie. Het genereert beelden op basis van tekstprompts.", "fal-ai/flux-kontext/dev.description": "FLUX.1-model gericht op beeldbewerking, met ondersteuning voor tekst- en afbeeldingsinvoer.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] accepteert tekst en referentieafbeeldingen als invoer, waardoor gerichte lokale bewerkingen en complexe wereldwijde scèneaanpassingen mogelijk zijn.", "fal-ai/flux/krea.description": "Flux Krea [dev] is een afbeeldingsgeneratiemodel met een esthetische voorkeur voor realistische, natuurlijke beelden.", @@ -526,8 +523,8 @@ "fal-ai/hunyuan-image/v3.description": "Een krachtig, native multimodaal afbeeldingsgeneratiemodel.", "fal-ai/imagen4/preview.description": "Hoogwaardig afbeeldingsgeneratiemodel van Google.", "fal-ai/nano-banana.description": "Nano Banana is het nieuwste, snelste en meest efficiënte native multimodale model van Google, waarmee beeldgeneratie en -bewerking via conversatie mogelijk is.", - "fal-ai/qwen-image-edit.description": "Een professioneel afbeeldingsbewerkingsmodel van het Qwen-team, met ondersteuning voor semantische en uiterlijke bewerkingen, nauwkeurige Chinese/Engelse tekstbewerking, stijltransfer, rotatie en meer.", - "fal-ai/qwen-image.description": "Een krachtig afbeeldingsgeneratiemodel van het Qwen-team met sterke Chinese tekstweergave en diverse visuele stijlen.", + "fal-ai/qwen-image-edit.description": "Een professioneel beeldbewerkingsmodel van het Qwen-team dat semantische en uiterlijke bewerkingen ondersteunt, nauwkeurig Chinese en Engelse tekst bewerkt, en hoogwaardige bewerkingen mogelijk maakt zoals stijltransformatie en objectrotatie.", + "fal-ai/qwen-image.description": "Een krachtig beeldgeneratiemodel van het Qwen-team met indrukwekkende Chinese tekstweergave en diverse visuele stijlen.", "flux-1-schnell.description": "Een tekst-naar-beeldmodel met 12 miljard parameters van Black Forest Labs, dat gebruikmaakt van latente adversariële diffusiedistillatie om hoogwaardige beelden te genereren in 1–4 stappen. Het evenaart gesloten alternatieven en is uitgebracht onder de Apache-2.0-licentie voor persoonlijk, onderzoeks- en commercieel gebruik.", "flux-dev.description": "Open-source R&D-beeldgeneratiemodel, efficiënt geoptimaliseerd voor niet-commercieel innovatief onderzoek.", "flux-kontext-max.description": "State-of-the-art contextuele beeldgeneratie en -bewerking, waarbij tekst en afbeeldingen worden gecombineerd voor nauwkeurige, samenhangende resultaten.", @@ -566,10 +563,10 @@ "gemini-3-flash-preview.description": "Gemini 3 Flash is het slimste model dat is gebouwd voor snelheid, met geavanceerde intelligentie en uitstekende zoekverankering.", "gemini-3-flash.description": "Gemini 3 Flash door Google — ultrafast model met ondersteuning voor multimodale invoer.", "gemini-3-pro-image-preview.description": "Gemini 3 Pro Image (Nano Banana Pro) is het beeldgeneratiemodel van Google dat ook multimodale dialogen ondersteunt.", - "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) is het afbeeldingsgeneratiemodel van Google en ondersteunt ook multimodale chat.", + "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) is Google's beeldgeneratiemodel en ondersteunt ook multimodale chat.", "gemini-3-pro-preview.description": "Gemini 3 Pro is het krachtigste agent- en vibe-codingmodel van Google, met rijkere visuele output en diepere interactie bovenop geavanceerde redeneercapaciteiten.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) is het snelste native beeldgeneratiemodel van Google met denksupport, conversatiebeeldgeneratie en bewerking.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) levert Pro-niveau afbeeldingskwaliteit met Flash-snelheid en ondersteuning voor multimodale chat.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) is Google's snelste native beeldgeneratiemodel met denkondersteuning, conversatiegerichte beeldgeneratie en bewerking.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview is het meest kostenefficiënte multimodale model van Google, geoptimaliseerd voor grootschalige agenttaken, vertaling en gegevensverwerking.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite is Google's meest kostenefficiënte multimodale model, geoptimaliseerd voor grootschalige agenttaken, vertaling en gegevensverwerking.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview verbetert Gemini 3 Pro met verbeterde redeneercapaciteiten en voegt ondersteuning toe voor een gemiddeld denkniveau.", @@ -734,8 +731,6 @@ "grok-4-fast-reasoning.description": "We zijn verheugd Grok 4 Fast uit te brengen, onze nieuwste vooruitgang in kosteneffectieve redeneermodellen.", "grok-4.20-0309-non-reasoning.description": "Een niet-redenerende variant voor eenvoudige gebruiksscenario's.", "grok-4.20-0309-reasoning.description": "Intelligent, razendsnel model dat redeneert voordat het reageert.", - "grok-4.20-beta-0309-non-reasoning.description": "Een niet-denkende variant voor eenvoudige gebruiksscenario's.", - "grok-4.20-beta-0309-reasoning.description": "Intelligent, razendsnel model dat redeneert voordat het reageert.", "grok-4.20-multi-agent-0309.description": "Een team van 4 of 16 agents, uitblinkend in onderzoeksgebruiksscenario's. Ondersteunt momenteel geen client-side tools. Ondersteunt alleen xAI server-side tools (bijv. X Search, Web Search tools) en remote MCP tools.", "grok-4.3.description": "Het meest waarheidsgetrouwe grote taalmodel ter wereld", "grok-4.description": "Het nieuwste vlaggenschip van Grok met ongeëvenaarde prestaties in taal, wiskunde en redeneervermogen — een echte alleskunner. Momenteel verwijst het naar grok-4-0709; vanwege beperkte middelen is de prijs tijdelijk 10% hoger dan de officiële prijs en wordt verwacht later terug te keren naar de officiële prijs.", @@ -1220,8 +1215,6 @@ "qwq.description": "QwQ is een redeneermodel binnen de Qwen-familie. In vergelijking met standaard instructie-getrainde modellen biedt het denk- en redeneervermogen dat de prestaties op complexe problemen aanzienlijk verbetert. QwQ-32B is een middelgroot redeneermodel dat zich kan meten met topmodellen zoals DeepSeek-R1 en o1-mini.", "qwq_32b.description": "Middelgroot redeneermodel binnen de Qwen-familie. In vergelijking met standaard instructie-getrainde modellen verbeteren QwQ’s denk- en redeneervermogen de prestaties op complexe problemen aanzienlijk.", "r1-1776.description": "R1-1776 is een na-getrainde variant van DeepSeek R1, ontworpen om ongecensureerde, onbevooroordeelde feitelijke informatie te bieden.", - "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro van ByteDance ondersteunt tekst-naar-video, afbeelding-naar-video (eerste frame, eerste+laatste frame) en audiogeneratie gesynchroniseerd met visuals.", - "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite van BytePlus biedt web-ophaal-geaugmenteerde generatie voor realtime informatie, verbeterde interpretatie van complexe prompts en verbeterde referentieconsistentie voor professionele visuele creatie.", "solar-mini-ja.description": "Solar Mini (Ja) breidt Solar Mini uit met focus op Japans, terwijl het efficiënte, sterke prestaties in Engels en Koreaans behoudt.", "solar-mini.description": "Solar Mini is een compact LLM dat beter presteert dan GPT-3.5, met sterke meertalige ondersteuning voor Engels en Koreaans, en biedt een efficiënte oplossing met een kleine voetafdruk.", "solar-pro.description": "Solar Pro is een intelligent LLM van Upstage, gericht op instructieopvolging op een enkele GPU, met IFEval-scores boven de 80. Momenteel ondersteunt het Engels; de volledige release stond gepland voor november 2024 met uitgebreidere taalondersteuning en langere context.", diff --git a/locales/nl-NL/onboarding.json b/locales/nl-NL/onboarding.json index aabe666237..57fb446ff3 100644 --- a/locales/nl-NL/onboarding.json +++ b/locales/nl-NL/onboarding.json @@ -27,6 +27,13 @@ "agent.layout.skipConfirm.ok": "Voor nu overslaan", "agent.layout.skipConfirm.title": "Onboarding nu overslaan?", "agent.layout.switchMessage": "Even geen zin vandaag? Je kunt schakelen naar {{mode}} of {{skip}}.", + "agent.layout.switchMessageClassic": "Geen zin vandaag? Je kunt overschakelen naar {{mode}}.", + "agent.messenger.cta.discord": "Toevoegen aan Discord", + "agent.messenger.cta.slack": "Toevoegen aan Slack", + "agent.messenger.cta.telegram": "Openen in Telegram", + "agent.messenger.subtitle": "Chat met je agent op Telegram, Slack of Discord", + "agent.messenger.telegramQrCaption": "Scan met de camera van je telefoon", + "agent.messenger.title": "Houd me bij je, waar je ook bericht", "agent.modeSwitch.agent": "Conversatie", "agent.modeSwitch.classic": "Klassiek", "agent.modeSwitch.collapse": "Samenvouwen", @@ -68,6 +75,13 @@ "agent.wrapUp.confirm.content": "Ik sla op wat we tot nu toe hebben besproken. Je kunt altijd later terugkomen om verder te chatten.", "agent.wrapUp.confirm.ok": "Nu afronden", "agent.wrapUp.confirm.title": "Onboarding nu afronden?", + "agentPicker.allCategories": "Alle", + "agentPicker.continue": "Doorgaan", + "agentPicker.skip": "Nu overslaan", + "agentPicker.subtitle": "Voeg er nu een paar toe aan je bibliotheek — ontdek later meer op elk moment.", + "agentPicker.title": "Laten we een paar agents aan je bibliotheek toevoegen", + "agentPicker.title2": "Kies degene die passen bij hoe jij werkt", + "agentPicker.title3": "Je kunt altijd later meer toevoegen — begin met een paar", "back": "Terug", "finish": "Aan de slag", "interests.area.business": "Zakelijk & Strategie", diff --git a/locales/nl-NL/plugin.json b/locales/nl-NL/plugin.json index feeee684e1..4f65e5e314 100644 --- a/locales/nl-NL/plugin.json +++ b/locales/nl-NL/plugin.json @@ -39,8 +39,8 @@ "builtins.lobe-agent-documents.inspector.docCount": "{{count}} documenten", "builtins.lobe-agent-documents.inspector.opsCount": "{{count}} bewerkingen", "builtins.lobe-agent-documents.inspector.opsResult": "{{success}}/{{total}} bewerkingen", - "builtins.lobe-agent-documents.inspector.target.agent": "in agent", - "builtins.lobe-agent-documents.inspector.target.currentTopic": "in onderwerp", + "builtins.lobe-agent-documents.inspector.scope.agent": "in agent", + "builtins.lobe-agent-documents.inspector.scope.currentTopic": "in onderwerp", "builtins.lobe-agent-documents.title": "Agentdocumenten", "builtins.lobe-agent-management.apiName.callAgent": "Bel agent", "builtins.lobe-agent-management.apiName.createAgent": "Maak agent aan", @@ -90,6 +90,14 @@ "builtins.lobe-agent.title": "Lobe Agent", "builtins.lobe-claude-code.agent.instruction": "Instructie", "builtins.lobe-claude-code.agent.result": "Resultaat", + "builtins.lobe-claude-code.task.createLabel": "Taak aanmaken: ", + "builtins.lobe-claude-code.task.getLabel": "Taak inspecteren #{{taskId}}", + "builtins.lobe-claude-code.task.listLabel": "Taken weergeven", + "builtins.lobe-claude-code.task.updateCompleted": "Voltooid", + "builtins.lobe-claude-code.task.updateDeleted": "Verwijderd", + "builtins.lobe-claude-code.task.updateInProgress": "Gestart", + "builtins.lobe-claude-code.task.updateLabel": "Taak bijwerken #{{taskId}}", + "builtins.lobe-claude-code.task.updatePending": "Opnieuw instellen", "builtins.lobe-claude-code.todoWrite.allDone": "Alle taken voltooid", "builtins.lobe-claude-code.todoWrite.currentStep": "Huidige stap", "builtins.lobe-claude-code.todoWrite.todos": "Taken", diff --git a/locales/nl-NL/providers.json b/locales/nl-NL/providers.json index d24aaec7f3..05ef593c8b 100644 --- a/locales/nl-NL/providers.json +++ b/locales/nl-NL/providers.json @@ -33,7 +33,6 @@ "jina.description": "Opgericht in 2020, is Jina AI een toonaangevend zoek-AI-bedrijf. De zoekstack omvat vectormodellen, herordenaars en kleine taalmodellen om betrouwbare, hoogwaardige generatieve en multimodale zoekapps te bouwen.", "kimicodingplan.description": "Kimi Code van Moonshot AI biedt toegang tot Kimi-modellen, waaronder K2.5, voor coderingstaken.", "lmstudio.description": "LM Studio is een desktopapplicatie voor het ontwikkelen en experimenteren met LLM’s op je eigen computer.", - "lobehub.description": "LobeHub Cloud gebruikt officiële API's om toegang te krijgen tot AI-modellen en meet het gebruik met Credits die gekoppeld zijn aan modeltokens.", "longcat.description": "LongCat is een reeks generatieve AI-grote modellen die onafhankelijk zijn ontwikkeld door Meituan. Het is ontworpen om de productiviteit binnen ondernemingen te verbeteren en innovatieve toepassingen mogelijk te maken door middel van een efficiënte computationele architectuur en sterke multimodale mogelijkheden.", "minimax.description": "Opgericht in 2021, bouwt MiniMax algemene AI met multimodale fundamentele modellen, waaronder tekstmodellen met biljoenen parameters, spraakmodellen en visiemodellen, evenals apps zoals Hailuo AI.", "minimaxcodingplan.description": "MiniMax Token Plan biedt toegang tot MiniMax-modellen, waaronder M2.7, voor coderingstaken via een abonnement met vaste kosten.", diff --git a/locales/nl-NL/subscription.json b/locales/nl-NL/subscription.json index 0944240df4..102f6f6883 100644 --- a/locales/nl-NL/subscription.json +++ b/locales/nl-NL/subscription.json @@ -55,6 +55,12 @@ "credits.autoTopUp.toggle": "Automatisch Opladen Inschakelen", "credits.autoTopUp.upgradeHint": "Abonneer je op een betaald plan om automatisch opladen in te schakelen", "credits.autoTopUp.validation.targetMustExceedThreshold": "Doelsaldo moet hoger zijn dan de drempel", + "credits.costEstimateHint.desc": "Toon een lichte waarschuwing voordat u verzendt wanneer de geschatte modelkosten uw drempel bereiken", + "credits.costEstimateHint.saveError": "Het opslaan van de instellingen voor kostenramingswaarschuwingen is mislukt", + "credits.costEstimateHint.saveSuccess": "Instellingen voor kostenramingswaarschuwingen opgeslagen", + "credits.costEstimateHint.threshold": "Waarschuwingsdrempel", + "credits.costEstimateHint.title": "Kostenramingswaarschuwing", + "credits.costEstimateHint.validation.threshold": "Drempel moet groter dan of gelijk aan 0 zijn", "credits.packages.auto": "Automatisch", "credits.packages.charged": "In rekening gebracht ${{amount}}", "credits.packages.expired": "Verlopen", diff --git a/locales/nl-NL/tool.json b/locales/nl-NL/tool.json index 05b645a449..eb22499cdc 100644 --- a/locales/nl-NL/tool.json +++ b/locales/nl-NL/tool.json @@ -40,6 +40,14 @@ "agentMarketplace.render.alreadyInLibraryTag": "Al in bibliotheek", "agentMarketplace.render.alreadyInLibrary_one": "{{count}} al in bibliotheek", "agentMarketplace.render.alreadyInLibrary_other": "{{count}} al in bibliotheek", + "claudeCode.askUserQuestion.escape.back": "Terug naar opties", + "claudeCode.askUserQuestion.escape.enter": "Of typ direct", + "claudeCode.askUserQuestion.escape.placeholder": "Typ hier uw antwoord…", + "claudeCode.askUserQuestion.multiSelectTag": "(meerdere selecties)", + "claudeCode.askUserQuestion.skip": "Overslaan", + "claudeCode.askUserQuestion.submit": "Indienen", + "claudeCode.askUserQuestion.timeExpired": "Tijd verstreken — optie 1 van elke vraag wordt gebruikt.", + "claudeCode.askUserQuestion.timeRemaining": "Resterende tijd: {{time}} · onbeantwoorde vragen worden standaard optie 1 bij timeout.", "codeInterpreter-legacy.error": "Uitvoeringsfout", "codeInterpreter-legacy.executing": "Bezig met uitvoeren...", "codeInterpreter-legacy.files": "Bestanden:", diff --git a/locales/nl-NL/topic.json b/locales/nl-NL/topic.json index dca5d6dbf0..03044cef8b 100644 --- a/locales/nl-NL/topic.json +++ b/locales/nl-NL/topic.json @@ -56,6 +56,7 @@ "renameModal.title": "Onderwerp hernoemen", "searchPlaceholder": "Onderwerpen zoeken...", "searchResultEmpty": "Geen zoekresultaten gevonden.", + "sidebar.title": "Onderwerpen", "taskManager.agent": "Taakagent", "taskManager.welcome": "Vraag me naar je taken", "temp": "Tijdelijk", diff --git a/locales/pl-PL/chat.json b/locales/pl-PL/chat.json index 71cbd20e9d..a6f844d922 100644 --- a/locales/pl-PL/chat.json +++ b/locales/pl-PL/chat.json @@ -220,6 +220,13 @@ "inbox.title": "Lobe AI", "input.addAi": "Dodaj wiadomość AI", "input.addUser": "Dodaj wiadomość użytkownika", + "input.costEstimate.creditsPerMillionTokens": "{{credits}} kredytów/M tokenów", + "input.costEstimate.hint": "Szacowany koszt: ~{{credits}} kredytów", + "input.costEstimate.inputLabel": "Wejście", + "input.costEstimate.outputLabel": "Wyjście", + "input.costEstimate.settingsLink": "Dostosuj próg ostrzeżenia", + "input.costEstimate.tokenCount": "~{{tokens}} tokenów", + "input.costEstimate.tooltip": "Szacowane na podstawie bieżącego kontekstu, narzędzi i cen modelu. Rzeczywisty koszt może się różnić.", "input.disclaimer": "Agenci mogą popełniać błędy. W przypadku ważnych informacji zachowaj ostrożność.", "input.errorMsg": "Wysyłanie nie powiodło się: {{errorMsg}}. Spróbuj ponownie lub wyślij później.", "input.more": "Więcej", @@ -779,6 +786,7 @@ "upload.preview.prepareTasks": "Przygotowywanie fragmentów...", "upload.preview.status.pending": "Przygotowywanie do przesłania...", "upload.preview.status.processing": "Przetwarzanie pliku...", + "upload.validation.unsupportedFileType": "Nieobsługiwany typ pliku: {{files}}. Obsługiwane obrazy: JPG, PNG, GIF, WebP. Obsługiwane dokumenty obejmują PDF, Word, Excel, PowerPoint, Markdown, tekst, CSV, JSON i pliki kodu.", "upload.validation.videoSizeExceeded": "Rozmiar pliku wideo nie może przekraczać 20 MB. Obecny rozmiar pliku to {{actualSize}}.", "viewMode.fullWidth": "Pełna szerokość", "viewMode.normal": "Standardowy", @@ -894,6 +902,8 @@ "workingPanel.localFile.closeOther": "Zamknij pozostałe", "workingPanel.localFile.closeRight": "Zamknij po prawej", "workingPanel.localFile.error": "Nie udało się załadować tego pliku", + "workingPanel.localFile.preview.raw": "Surowe dane", + "workingPanel.localFile.preview.render": "Podgląd", "workingPanel.localFile.truncated": "Podgląd pliku skrócony do {{limit}} znaków", "workingPanel.progress": "Progress", "workingPanel.progress.allCompleted": "All tasks completed", @@ -959,6 +969,8 @@ "workingPanel.review.viewMode.unified": "Przełącz na widok zjednoczony", "workingPanel.review.wordWrap.disable": "Wyłącz zawijanie wierszy", "workingPanel.review.wordWrap.enable": "Włącz zawijanie wierszy", + "workingPanel.skills.empty": "Nie znaleziono umiejętności w tym projekcie", + "workingPanel.skills.title": "Umiejętności", "workingPanel.space": "Przestrzeń", "workingPanel.title": "Working Panel", "you": "Ty", diff --git a/locales/pl-PL/components.json b/locales/pl-PL/components.json index 7ad2e20548..b0e65fe5bd 100644 --- a/locales/pl-PL/components.json +++ b/locales/pl-PL/components.json @@ -121,6 +121,16 @@ "ModelSwitchPanel.detail.context": "Długość kontekstu", "ModelSwitchPanel.detail.pricing": "Cennik", "ModelSwitchPanel.detail.pricing.cachedInput": "Buforowane dane wejściowe ${{amount}}/M", + "ModelSwitchPanel.detail.pricing.credits.cachedInput": "Buforowane dane wejściowe {{amount}} kredytów/M tokenów", + "ModelSwitchPanel.detail.pricing.credits.image": "kredyty/img", + "ModelSwitchPanel.detail.pricing.credits.input": "Dane wejściowe {{amount}} kredytów/M tokenów", + "ModelSwitchPanel.detail.pricing.credits.megapixel": "kredyty/MP", + "ModelSwitchPanel.detail.pricing.credits.millionCharacters": "kredyty/M znaków", + "ModelSwitchPanel.detail.pricing.credits.millionTokens": "kredyty/M tokenów", + "ModelSwitchPanel.detail.pricing.credits.output": "Dane wyjściowe {{amount}} kredytów/M tokenów", + "ModelSwitchPanel.detail.pricing.credits.perImage": "~ {{amount}} kredytów / obraz", + "ModelSwitchPanel.detail.pricing.credits.perVideo": "~ {{amount}} kredytów / wideo", + "ModelSwitchPanel.detail.pricing.credits.second": "kredyty/s", "ModelSwitchPanel.detail.pricing.group.audio": "Audio", "ModelSwitchPanel.detail.pricing.group.image": "Obraz", "ModelSwitchPanel.detail.pricing.group.text": "Tekst", diff --git a/locales/pl-PL/editor.json b/locales/pl-PL/editor.json index 7367204ca6..0dd6233c29 100644 --- a/locales/pl-PL/editor.json +++ b/locales/pl-PL/editor.json @@ -1,8 +1,10 @@ { "actionTag.category.command": "Polecenie", + "actionTag.category.projectSkill": "Umiejętność projektowa", "actionTag.category.skill": "Umiejętność", "actionTag.category.tool": "Narzędzie", "actionTag.tooltip.command": "Uruchamia polecenie slash po stronie klienta przed wysłaniem.", + "actionTag.tooltip.projectSkill": "Wysłane jako wywołanie ukośnika, aby CLI agenta uruchomiło pasującą umiejętność projektową.", "actionTag.tooltip.skill": "Wczytuje pakiet umiejętności wielokrotnego użytku dla tego żądania.", "actionTag.tooltip.tool": "Oznacza narzędzie, które użytkownik jawnie wybrał dla tego żądania.", "actions.expand.off": "Zwiń", diff --git a/locales/pl-PL/home.json b/locales/pl-PL/home.json index 059db601dd..a8a073f749 100644 --- a/locales/pl-PL/home.json +++ b/locales/pl-PL/home.json @@ -11,6 +11,19 @@ "brief.action.ignore": "Ignoruj", "brief.action.retry": "Ponów próbę", "brief.addFeedback": "Prześlij opinię", + "brief.agentSignal.selfReview.applied.heading": "Zaktualizowano", + "brief.agentSignal.selfReview.applied.summary": "Zastosowano {{count}} aktualizację snu.", + "brief.agentSignal.selfReview.applied.summary_plural": "Zastosowano {{count}} aktualizacje snu.", + "brief.agentSignal.selfReview.applied.title": "Zaktualizowane zasoby snu", + "brief.agentSignal.selfReview.error.heading": "Problem", + "brief.agentSignal.selfReview.error.summary": "Niektóre zadania nie mogły zostać ukończone podczas tego snu.", + "brief.agentSignal.selfReview.error.title": "Sen napotkał problem", + "brief.agentSignal.selfReview.ideas.summary": "Zapisano notatki snu do przyszłego przeglądu.", + "brief.agentSignal.selfReview.ideas.title": "Notatki snu", + "brief.agentSignal.selfReview.proposal.heading": "Sugestia", + "brief.agentSignal.selfReview.proposal.summary": "{{count}} sugestia snu wymaga Twojej uwagi.", + "brief.agentSignal.selfReview.proposal.summary_plural": "{{count}} sugestie snu wymagają Twojej uwagi.", + "brief.agentSignal.selfReview.proposal.title": "Sugestia snu wymaga przeglądu", "brief.collapse": "Pokaż mniej", "brief.commentPlaceholder": "Podziel się swoją opinią...", "brief.commentSubmit": "Prześlij opinię", diff --git a/locales/pl-PL/hotkey.json b/locales/pl-PL/hotkey.json index 86ea3b82a6..52d2bfaa98 100644 --- a/locales/pl-PL/hotkey.json +++ b/locales/pl-PL/hotkey.json @@ -1,8 +1,6 @@ { "addUserMessage.desc": "Dodaj bieżący wpis jako wiadomość użytkownika bez uruchamiania generowania", "addUserMessage.title": "Dodaj wiadomość użytkownika", - "clearCurrentMessages.desc": "Wyczyść wiadomości i przesłane pliki z bieżącej rozmowy", - "clearCurrentMessages.title": "Wyczyść wiadomości rozmowy", "commandPalette.desc": "Otwórz globalną paletę poleceń, aby szybko uzyskać dostęp do funkcji", "commandPalette.title": "Paleta poleceń", "deleteAndRegenerateMessage.desc": "Usuń ostatnią wiadomość i wygeneruj ponownie", diff --git a/locales/pl-PL/models.json b/locales/pl-PL/models.json index 53b563b533..876fcb904a 100644 --- a/locales/pl-PL/models.json +++ b/locales/pl-PL/models.json @@ -106,7 +106,6 @@ "MiniMax-Hailuo-2.3.description": "Nowy model generowania wideo z kompleksowymi ulepszeniami w zakresie ruchu ciała, realizmu fizycznego i przestrzegania instrukcji.", "MiniMax-M1.description": "Nowy wewnętrzny model rozumowania z 80 tys. łańcuchów myślowych i 1 mln tokenów wejściowych, oferujący wydajność porównywalną z czołowymi modelami światowymi.", "MiniMax-M2-Stable.description": "Zaprojektowany z myślą o wydajnym kodowaniu i przepływach pracy agentów, z większą równoległością dla zastosowań komercyjnych.", - "MiniMax-M2.1-Lightning.description": "Potężne wielojęzyczne możliwości programowania z szybszym i bardziej wydajnym wnioskowaniem.", "MiniMax-M2.1-highspeed.description": "Potężne wielojęzyczne możliwości programistyczne, kompleksowo ulepszone doświadczenie programowania. Szybszy i bardziej wydajny.", "MiniMax-M2.1.description": "MiniMax-M2.1 to flagowy, otwartoźródłowy model dużej skali od MiniMax, zaprojektowany do rozwiązywania złożonych zadań rzeczywistych. Jego główne atuty to wielojęzyczne możliwości programistyczne oraz zdolność działania jako Agent do rozwiązywania skomplikowanych problemów.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: Ta sama wydajność co M2.5, ale z szybszym wnioskowaniem.", @@ -315,13 +314,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku to najszybszy i najbardziej kompaktowy model firmy Anthropic, zaprojektowany do natychmiastowych odpowiedzi z szybką i dokładną wydajnością.", "claude-3-opus-20240229.description": "Claude 3 Opus to najpotężniejszy model firmy Anthropic do bardzo złożonych zadań, wyróżniający się wydajnością, inteligencją, płynnością i zrozumieniem.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet łączy inteligencję i szybkość dla obciążeń korporacyjnych, oferując wysoką użyteczność przy niższych kosztach i niezawodnym wdrażaniu na dużą skalę.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 to najszybszy i najbardziej inteligentny model Haiku od Anthropic, oferujący błyskawiczną prędkość i rozszerzone myślenie.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 to najszybszy i najinteligentniejszy model Haiku firmy Anthropic, oferujący błyskawiczną szybkość i rozszerzone możliwości rozumowania.", "claude-haiku-4-5.description": "Claude Haiku 4.5 by Anthropic — model nowej generacji Haiku z ulepszonym rozumowaniem i wizją.", "claude-haiku-4.5.description": "Claude Haiku 4.5 to najszybszy i najinteligentniejszy model Haiku firmy Anthropic, charakteryzujący się błyskawiczną szybkością i rozszerzonym rozumowaniem.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking to zaawansowany wariant, który może ujawniać swój proces rozumowania.", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1 to najnowszy i najbardziej zaawansowany model Anthropic do wysoce złożonych zadań, wyróżniający się wydajnością, inteligencją, płynnością i zrozumieniem.", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1 to najnowszy i najbardziej zaawansowany model firmy Anthropic do wykonywania wysoce złożonych zadań, wyróżniający się wydajnością, inteligencją, płynnością i zrozumieniem.", "claude-opus-4-1.description": "Claude Opus 4.1 by Anthropic — model premium do rozumowania z głębokimi możliwościami analizy.", - "claude-opus-4-20250514.description": "Claude Opus 4 to najpotężniejszy model Anthropic do wysoce złożonych zadań, wyróżniający się wydajnością, inteligencją, płynnością i zrozumieniem.", + "claude-opus-4-20250514.description": "Claude Opus 4 to najpotężniejszy model firmy Anthropic do wykonywania wysoce złożonych zadań, wyróżniający się wydajnością, inteligencją, płynnością i zrozumieniem.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 to flagowy model firmy Anthropic, łączący wyjątkową inteligencję z wydajnością na dużą skalę, idealny do złożonych zadań wymagających najwyższej jakości odpowiedzi i rozumowania.", "claude-opus-4-5.description": "Claude Opus 4.5 by Anthropic — flagowy model z najwyższej klasy rozumowaniem i kodowaniem.", "claude-opus-4-6.description": "Claude Opus 4.6 by Anthropic — flagowy model z oknem kontekstowym 1M i zaawansowanym rozumowaniem.", @@ -330,8 +329,8 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 to najbardziej inteligentny model firmy Anthropic do tworzenia agentów i kodowania.", "claude-opus-4.6.description": "Claude Opus 4.6 to najbardziej inteligentny model firmy Anthropic do tworzenia agentów i kodowania.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking może generować natychmiastowe odpowiedzi lub rozszerzone rozumowanie krok po kroku z widocznym procesem.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 to najbardziej inteligentny model Anthropic do tej pory, oferujący niemal natychmiastowe odpowiedzi lub rozszerzone, krok po kroku myślenie z precyzyjną kontrolą dla użytkowników API.", - "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 to najbardziej inteligentny model Anthropic do tej pory.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 potrafi generować niemal natychmiastowe odpowiedzi lub rozbudowane, krok po kroku przemyślenia z widocznym procesem.", + "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 to najbardziej inteligentny model firmy Anthropic do tej pory.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 by Anthropic — ulepszony Sonnet z lepszą wydajnością kodowania.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 by Anthropic — najnowszy Sonnet z lepszym kodowaniem i obsługą narzędzi.", "claude-sonnet-4.5.description": "Claude Sonnet 4.5 to najbardziej inteligentny model firmy Anthropic do tej pory.", @@ -404,7 +403,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) to innowacyjny model oferujący głębokie zrozumienie języka i interakcję.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 to model nowej generacji do rozumowania z silniejszym rozumowaniem złożonym i łańcuchem myśli do zadań wymagających głębokiej analizy.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 to model rozumowania nowej generacji z ulepszonymi zdolnościami do rozwiązywania złożonych problemów i myślenia łańcuchowego.", - "deepseek-chat.description": "Alias kompatybilności dla trybu bezmyślowego DeepSeek V4 Flash. Przeznaczony do wycofania — użyj zamiast tego DeepSeek V4 Flash.", + "deepseek-chat.description": "Nowy model open-source łączący zdolności ogólne i kodowania. Zachowuje ogólną zdolność dialogową modelu czatu oraz silne możliwości kodowania modelu programistycznego, z lepszym dopasowaniem preferencji. DeepSeek-V2.5 dodatkowo poprawia pisanie i wykonywanie instrukcji.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B to model języka kodu wytrenowany na 2T tokenach (87% kod, 13% tekst chiński/angielski). Wprowadza okno kontekstu 16K i zadania uzupełniania w środku, oferując uzupełnianie kodu na poziomie projektu i wypełnianie fragmentów.", "deepseek-coder-v2.description": "DeepSeek Coder V2 to open-source’owy model kodu MoE, który osiąga wysokie wyniki w zadaniach programistycznych, porównywalne z GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 to open-source’owy model kodu MoE, który osiąga wysokie wyniki w zadaniach programistycznych, porównywalne z GPT-4 Turbo.", @@ -426,7 +425,7 @@ "deepseek-r1-fast-online.description": "Szybka pełna wersja DeepSeek R1 z wyszukiwaniem w czasie rzeczywistym, łącząca możliwości modelu 671B z szybszymi odpowiedziami.", "deepseek-r1-online.description": "Pełna wersja DeepSeek R1 z 671 miliardami parametrów i wyszukiwaniem w czasie rzeczywistym, oferująca lepsze rozumienie i generowanie.", "deepseek-r1.description": "DeepSeek-R1 wykorzystuje dane startowe przed RL i osiąga wyniki porównywalne z OpenAI-o1 w zadaniach matematycznych, programistycznych i logicznych.", - "deepseek-reasoner.description": "Alias kompatybilności dla trybu myślowego DeepSeek V4 Flash. Przeznaczony do wycofania — użyj zamiast tego DeepSeek V4 Flash.", + "deepseek-reasoner.description": "Model rozumowania DeepSeek skoncentrowany na złożonych zadaniach logicznego rozumowania.", "deepseek-v2.description": "DeepSeek V2 to wydajny model MoE zoptymalizowany pod kątem efektywności kosztowej.", "deepseek-v2:236b.description": "DeepSeek V2 236B to model skoncentrowany na kodzie, oferujący zaawansowane generowanie kodu.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 to model MoE z 671 miliardami parametrów, wyróżniający się w programowaniu, rozumieniu kontekstu i obsłudze długich tekstów.", @@ -491,8 +490,6 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 to model generowania obrazów od ByteDance Seed, obsługujący wejścia tekstowe i obrazowe z wysoką kontrolą i jakością. Generuje obrazy na podstawie tekstowych promptów.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 to najnowszy multimodalny model obrazu ByteDance, integrujący funkcje tekst-do-obrazu, obraz-do-obrazu i generowanie obrazów w partiach, jednocześnie uwzględniając zdrowy rozsądek i zdolności rozumowania. W porównaniu do poprzedniej wersji 4.0 oferuje znacznie lepszą jakość generowania, lepszą spójność edycji i fuzję wielu obrazów. Zapewnia bardziej precyzyjną kontrolę nad szczegółami wizualnymi, naturalnie generując małe teksty i twarze, a także osiąga bardziej harmonijny układ i kolorystykę, poprawiając estetykę ogólną.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite to najnowszy model generowania obrazów ByteDance. Po raz pierwszy integruje funkcje wyszukiwania online, co pozwala na uwzględnienie informacji w czasie rzeczywistym i poprawę aktualności generowanych obrazów. Inteligencja modelu została również ulepszona, umożliwiając precyzyjną interpretację złożonych instrukcji i treści wizualnych. Dodatkowo oferuje lepsze pokrycie globalnej wiedzy, spójność odniesień i jakość generowania w profesjonalnych scenariuszach, lepiej spełniając potrzeby wizualnej kreacji na poziomie przedsiębiorstw.", - "dreamina-seedance-2-0-260128.description": "Seedance 2.0 od ByteDance to najpotężniejszy model generowania wideo, obsługujący multimodalne generowanie wideo referencyjnego, edycję wideo, rozszerzanie wideo, tekst na wideo oraz obraz na wideo z zsynchronizowanym dźwiękiem.", - "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast od ByteDance oferuje te same możliwości co Seedance 2.0, ale z szybszymi prędkościami generowania i bardziej konkurencyjną ceną.", "emohaa.description": "Emohaa to model zdrowia psychicznego z profesjonalnymi umiejętnościami doradczymi, pomagający użytkownikom zrozumieć problemy emocjonalne.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B to lekki model open-source przeznaczony do lokalnego i dostosowanego wdrażania.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview to model podglądowy z kontekstem 8K, służący do oceny możliwości ERNIE 4.5.", @@ -506,6 +503,7 @@ "ernie-5.0-thinking-latest.description": "Wenxin 5.0 Thinking to flagowy model natywny pełnomodalny z ujednoliconym modelowaniem tekstu, obrazu, dźwięku i wideo. Zapewnia szerokie ulepszenia możliwości w złożonych scenariuszach QA, twórczości i agentów.", "ernie-5.0-thinking-preview.description": "Wenxin 5.0 Thinking Preview to flagowy model natywny pełnomodalny z ujednoliconym modelowaniem tekstu, obrazu, dźwięku i wideo. Zapewnia szerokie ulepszenia możliwości w złożonych scenariuszach QA, twórczości i agentów.", "ernie-5.0.description": "ERNIE 5.0, nowej generacji model z serii ERNIE, to natywnie multimodalny duży model. Przyjmuje zintegrowane podejście do modelowania multimodalnego, wspólnie modelując tekst, obrazy, dźwięk i wideo, aby dostarczyć kompleksowe możliwości multimodalne. Jego podstawowe zdolności zostały znacznie ulepszone, osiągając wysoką wydajność w ocenach benchmarkowych. Szczególnie wyróżnia się w zrozumieniu multimodalnym, przestrzeganiu instrukcji, twórczym pisaniu, dokładności faktograficznej, planowaniu agentów i wykorzystaniu narzędzi.", + "ernie-5.1.description": "ERNIE 5.1 to najnowszy model z serii ERNIE, oferujący kompleksowe ulepszenia swoich podstawowych możliwości. Wykazuje znaczące poprawy w takich obszarach jak agenci, przetwarzanie wiedzy, rozumowanie i głębokie wyszukiwanie. W tej wersji zastosowano odłączoną, w pełni asynchroniczną architekturę uczenia ze wzmocnieniem, zaprojektowaną specjalnie w celu rozwiązania kluczowych wyzwań związanych z ewolucją dużych modeli w kierunku autonomicznego podejmowania decyzji przez agentów, w tym rozbieżności numerycznych między treningiem a inferencją, niskiego wykorzystania heterogenicznych zasobów obliczeniowych oraz globalnych problemów spowodowanych efektami długiego ogona. Ponadto zastosowano techniki post-treningu agentów na dużą skalę, aby dodatkowo zwiększyć możliwości modelu i jego wydajność w zakresie uogólniania. Dzięki trójfazowemu współpracującemu podejściu obejmującemu środowisko, ekspertów i procesy fuzji, podejście to nie tylko zapewnia efektywność treningu, ale także znacząco poprawia stabilność i wydajność modelu w złożonych zadaniach.", "ernie-char-fiction-8k-preview.description": "ERNIE Character Fiction 8K Preview to model podglądowy do tworzenia postaci i fabuły, przeznaczony do oceny funkcji i testów.", "ernie-char-fiction-8k.description": "ERNIE Character Fiction 8K to model osobowościowy do powieści i tworzenia fabuły, odpowiedni do generowania długich historii.", "ernie-image-turbo.description": "ERNIE-Image to model tekst-do-obrazu o 8 miliardach parametrów opracowany przez Baidu. Zajmuje czołowe miejsca w wielu benchmarkach, osiągając ex aequo pierwsze miejsce w SuperCLUE w Chinach i prowadząc w otwartym torze open-source.", @@ -517,8 +515,7 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K to szybki model rozumowania z kontekstem 32K do złożonego rozumowania i dialogów wieloetapowych.", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview to podgląd modelu rozumowania do oceny i testów.", "ernie-x1.1.description": "ERNIE X1.1 to model rozumowania w wersji podglądowej do oceny i testowania.", - "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5, stworzony przez zespół ByteDance Seed, obsługuje edycję i kompozycję wielu obrazów. Funkcje obejmują ulepszoną spójność obiektów, precyzyjne wykonywanie instrukcji, zrozumienie logiki przestrzennej, ekspresję estetyczną, układ plakatów i projektowanie logo z wysoką precyzją renderowania tekstu i obrazu.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, stworzony przez ByteDance Seed, obsługuje wejścia tekstowe i obrazowe do wysoce kontrolowanego, wysokiej jakości generowania obrazów na podstawie podpowiedzi.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 to model generowania obrazów firmy ByteDance Seed, obsługujący wejścia tekstowe i obrazowe oraz oferujący wysoce kontrolowalne, wysokiej jakości generowanie obrazów. Generuje obrazy na podstawie tekstowych wskazówek.", "fal-ai/flux-kontext/dev.description": "Model FLUX.1 skoncentrowany na edycji obrazów, obsługujący wejścia tekstowe i obrazowe.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] przyjmuje tekst i obrazy referencyjne jako dane wejściowe, umożliwiając lokalne edycje i złożone transformacje sceny.", "fal-ai/flux/krea.description": "Flux Krea [dev] to model generowania obrazów z estetycznym ukierunkowaniem na bardziej realistyczne, naturalne obrazy.", @@ -526,8 +523,8 @@ "fal-ai/hunyuan-image/v3.description": "Potężny natywny model multimodalny do generowania obrazów.", "fal-ai/imagen4/preview.description": "Model generowania obrazów wysokiej jakości od Google.", "fal-ai/nano-banana.description": "Nano Banana to najnowszy, najszybszy i najbardziej wydajny natywny model multimodalny Google, umożliwiający generowanie i edycję obrazów w rozmowie.", - "fal-ai/qwen-image-edit.description": "Profesjonalny model edycji obrazów od zespołu Qwen, obsługujący edycje semantyczne i wyglądu, precyzyjną edycję tekstu w języku chińskim/angielskim, transfer stylu, obrót i inne.", - "fal-ai/qwen-image.description": "Potężny model generowania obrazów od zespołu Qwen z silnym renderowaniem tekstu w języku chińskim i różnorodnymi stylami wizualnymi.", + "fal-ai/qwen-image-edit.description": "Profesjonalny model edycji obrazów zespołu Qwen, który obsługuje edycje semantyczne i wyglądu, precyzyjnie edytuje tekst w języku chińskim i angielskim oraz umożliwia wysokiej jakości edycje, takie jak transfer stylu i obrót obiektów.", + "fal-ai/qwen-image.description": "Potężny model generowania obrazów zespołu Qwen, oferujący imponujące renderowanie tekstu w języku chińskim i różnorodne style wizualne.", "flux-1-schnell.description": "Model tekst-na-obraz z 12 miliardami parametrów od Black Forest Labs, wykorzystujący latent adversarial diffusion distillation do generowania wysokiej jakości obrazów w 1–4 krokach. Dorównuje zamkniętym alternatywom i jest dostępny na licencji Apache-2.0 do użytku osobistego, badawczego i komercyjnego.", "flux-dev.description": "Model generowania obrazów do badań i rozwoju o otwartym kodzie źródłowym, zoptymalizowany pod kątem niekomercyjnych badań innowacyjnych.", "flux-kontext-max.description": "Najnowocześniejsze generowanie i edycja obrazów kontekstowych, łączące tekst i obrazy dla precyzyjnych, spójnych wyników.", @@ -566,10 +563,10 @@ "gemini-3-flash-preview.description": "Gemini 3 Flash to najszybszy i najinteligentniejszy model, łączący najnowsze osiągnięcia AI z doskonałym osadzeniem w wynikach wyszukiwania.", "gemini-3-flash.description": "Gemini 3 Flash by Google — ultraszybki model z obsługą wejść multimodalnych.", "gemini-3-pro-image-preview.description": "Gemini 3 Pro Image (Nano Banana Pro) to model generowania obrazów od Google, który obsługuje również dialogi multimodalne.", - "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) to model generowania obrazów od Google, który obsługuje również czat multimodalny.", + "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) to model generowania obrazów firmy Google, który obsługuje również multimodalny czat.", "gemini-3-pro-preview.description": "Gemini 3 Pro to najpotężniejszy model agenta i kodowania nastrojów od Google, oferujący bogatsze wizualizacje i głębszą interakcję przy zaawansowanym rozumowaniu.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) to najszybszy natywny model generowania obrazów od Google z obsługą myślenia, generowaniem obrazów w rozmowach i edycją.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) oferuje jakość obrazu na poziomie Pro z prędkością Flash i obsługą czatu multimodalnego.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) to najszybszy natywny model generowania obrazów firmy Google, oferujący wsparcie dla myślenia, konwersacyjnego generowania obrazów i ich edycji.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview to najbardziej ekonomiczny model multimodalny Google, zoptymalizowany do zadań agentowych o dużej skali, tłumaczeń i przetwarzania danych.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite to najbardziej opłacalny model multimodalny Google, zoptymalizowany do zadań agentowych o dużej skali, tłumaczeń i przetwarzania danych.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview ulepsza Gemini 3 Pro, oferując lepsze zdolności rozumowania i wsparcie dla średniego poziomu myślenia.", @@ -734,8 +731,6 @@ "grok-4-fast-reasoning.description": "Z radością prezentujemy Grok 4 Fast — nasz najnowszy postęp w dziedzinie modeli rozumowania o wysokiej opłacalności.", "grok-4.20-0309-non-reasoning.description": "Wariant bez rozumowania do prostych przypadków użycia.", "grok-4.20-0309-reasoning.description": "Inteligentny, błyskawiczny model, który rozumuje przed odpowiedzią.", - "grok-4.20-beta-0309-non-reasoning.description": "Wariant bezmyślowy do prostych przypadków użycia.", - "grok-4.20-beta-0309-reasoning.description": "Inteligentny, błyskawiczny model, który rozważa przed udzieleniem odpowiedzi.", "grok-4.20-multi-agent-0309.description": "Zespół 4 lub 16 agentów, doskonały w przypadkach badawczych. Obecnie nie obsługuje narzędzi po stronie klienta. Obsługuje tylko narzędzia po stronie serwera xAI (np. X Search, Web Search tools) i zdalne narzędzia MCP.", "grok-4.3.description": "Najbardziej poszukujący prawdy duży model językowy na świecie", "grok-4.description": "Najnowszy flagowy model Grok z niezrównaną wydajnością w języku, matematyce i rozumowaniu — prawdziwy wszechstronny model. Obecnie wskazuje na grok-4-0709; z powodu ograniczonych zasobów jego cena jest tymczasowo o 10% wyższa od oficjalnej i oczekuje się, że powróci do oficjalnej ceny później.", @@ -1220,8 +1215,6 @@ "qwq.description": "QwQ to model rozumowania z rodziny Qwen. W porównaniu do standardowych modeli dostrojonych instrukcyjnie, oferuje zaawansowane myślenie i rozumowanie, co znacząco poprawia wydajność w zadaniach trudnych. QwQ-32B to model średniej wielkości, konkurujący z czołowymi modelami rozumowania, takimi jak DeepSeek-R1 i o1-mini.", "qwq_32b.description": "Model rozumowania średniej wielkości z rodziny Qwen. W porównaniu do standardowych modeli dostrojonych instrukcyjnie, zdolności myślenia i rozumowania QwQ znacząco poprawiają wydajność w trudnych zadaniach.", "r1-1776.description": "R1-1776 to wariant modelu DeepSeek R1 po dodatkowym treningu, zaprojektowany do dostarczania nieocenzurowanych, bezstronnych informacji faktograficznych.", - "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro od ByteDance obsługuje tekst na wideo, obraz na wideo (pierwsza klatka, pierwsza + ostatnia klatka) oraz generowanie dźwięku zsynchronizowanego z wizualizacjami.", - "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite od BytePlus oferuje generowanie wspomagane wyszukiwaniem w sieci w czasie rzeczywistym, ulepszoną interpretację złożonych podpowiedzi oraz poprawioną spójność odniesień do profesjonalnego tworzenia wizualnego.", "solar-mini-ja.description": "Solar Mini (Ja) rozszerza Solar Mini o nacisk na język japoński, zachowując jednocześnie wydajność w języku angielskim i koreańskim.", "solar-mini.description": "Solar Mini to kompaktowy model LLM, który przewyższa GPT-3.5, oferując silne możliwości wielojęzyczne w języku angielskim i koreańskim oraz efektywne działanie przy małych zasobach.", "solar-pro.description": "Solar Pro to inteligentny model LLM od Upstage, skoncentrowany na wykonywaniu instrukcji na pojedynczym GPU, z wynikami IFEval powyżej 80. Obecnie obsługuje język angielski; pełna wersja z rozszerzonym wsparciem językowym i dłuższym kontekstem planowana jest na listopad 2024.", diff --git a/locales/pl-PL/onboarding.json b/locales/pl-PL/onboarding.json index fd01f978f0..d4eeceb18f 100644 --- a/locales/pl-PL/onboarding.json +++ b/locales/pl-PL/onboarding.json @@ -27,6 +27,13 @@ "agent.layout.skipConfirm.ok": "Pomiń na razie", "agent.layout.skipConfirm.title": "Pominąć konfigurację wstępną?", "agent.layout.switchMessage": "Nie masz dziś ochoty? Możesz przełączyć na {{mode}} lub {{skip}}.", + "agent.layout.switchMessageClassic": "Nie podoba Ci się dzisiaj? Możesz przełączyć się na {{mode}}.", + "agent.messenger.cta.discord": "Dodaj do Discorda", + "agent.messenger.cta.slack": "Dodaj do Slacka", + "agent.messenger.cta.telegram": "Otwórz w Telegramie", + "agent.messenger.subtitle": "Czat z Twoim agentem na Telegramie, Slacku lub Discordzie", + "agent.messenger.telegramQrCaption": "Zeskanuj aparatem telefonu", + "agent.messenger.title": "Zabierz mnie ze sobą, gdziekolwiek piszesz", "agent.modeSwitch.agent": "Konwersacyjny", "agent.modeSwitch.classic": "Klasyczny", "agent.modeSwitch.collapse": "Zwiń", @@ -68,6 +75,13 @@ "agent.wrapUp.confirm.content": "Zapiszę to, co omówiliśmy do tej pory. Zawsze możesz wrócić i porozmawiać dalej.", "agent.wrapUp.confirm.ok": "Zakończ teraz", "agent.wrapUp.confirm.title": "Zakończyć onboarding?", + "agentPicker.allCategories": "Wszystkie", + "agentPicker.continue": "Kontynuuj", + "agentPicker.skip": "Pomiń na razie", + "agentPicker.subtitle": "Dodaj kilka do swojej biblioteki teraz — odkrywaj więcej w dowolnym momencie później.", + "agentPicker.title": "Dodajmy kilka agentów do Twojej biblioteki", + "agentPicker.title2": "Wybierz tych, którzy pasują do Twojego stylu pracy", + "agentPicker.title3": "Zawsze możesz dodać więcej później — zacznij od kilku", "back": "Wstecz", "finish": "Zaczynamy", "interests.area.business": "Biznes i strategia", diff --git a/locales/pl-PL/plugin.json b/locales/pl-PL/plugin.json index 9539ebd2b2..3e7a5974b3 100644 --- a/locales/pl-PL/plugin.json +++ b/locales/pl-PL/plugin.json @@ -39,8 +39,8 @@ "builtins.lobe-agent-documents.inspector.docCount": "{{count}} dokumentów", "builtins.lobe-agent-documents.inspector.opsCount": "{{count}} operacji", "builtins.lobe-agent-documents.inspector.opsResult": "{{success}}/{{total}} operacji", - "builtins.lobe-agent-documents.inspector.target.agent": "w agencie", - "builtins.lobe-agent-documents.inspector.target.currentTopic": "w temacie", + "builtins.lobe-agent-documents.inspector.scope.agent": "w agencie", + "builtins.lobe-agent-documents.inspector.scope.currentTopic": "w temacie", "builtins.lobe-agent-documents.title": "Dokumenty agenta", "builtins.lobe-agent-management.apiName.callAgent": "Zadzwoń do agenta", "builtins.lobe-agent-management.apiName.createAgent": "Utwórz agenta", @@ -90,6 +90,14 @@ "builtins.lobe-agent.title": "Agent Lobe", "builtins.lobe-claude-code.agent.instruction": "Instrukcja", "builtins.lobe-claude-code.agent.result": "Wynik", + "builtins.lobe-claude-code.task.createLabel": "Tworzenie zadania: ", + "builtins.lobe-claude-code.task.getLabel": "Inspekcja zadania #{{taskId}}", + "builtins.lobe-claude-code.task.listLabel": "Lista zadań", + "builtins.lobe-claude-code.task.updateCompleted": "Zakończono", + "builtins.lobe-claude-code.task.updateDeleted": "Usunięto", + "builtins.lobe-claude-code.task.updateInProgress": "Rozpoczęto", + "builtins.lobe-claude-code.task.updateLabel": "Aktualizacja zadania #{{taskId}}", + "builtins.lobe-claude-code.task.updatePending": "Zresetowano", "builtins.lobe-claude-code.todoWrite.allDone": "Wszystkie zadania ukończone", "builtins.lobe-claude-code.todoWrite.currentStep": "Aktualny krok", "builtins.lobe-claude-code.todoWrite.todos": "Zadania", diff --git a/locales/pl-PL/providers.json b/locales/pl-PL/providers.json index 87cb4c8a9c..c61dde8836 100644 --- a/locales/pl-PL/providers.json +++ b/locales/pl-PL/providers.json @@ -33,7 +33,6 @@ "jina.description": "Założona w 2020 roku, Jina AI to wiodąca firma zajmująca się wyszukiwaniem AI. Jej stos wyszukiwania obejmuje modele wektorowe, rerankery i małe modele językowe do tworzenia niezawodnych, wysokiej jakości aplikacji generatywnych i multimodalnych.", "kimicodingplan.description": "Kimi Code od Moonshot AI zapewnia dostęp do modeli Kimi, w tym K2.5, do zadań związanych z kodowaniem.", "lmstudio.description": "LM Studio to aplikacja desktopowa do tworzenia i testowania LLM-ów na własnym komputerze.", - "lobehub.description": "LobeHub Cloud korzysta z oficjalnych interfejsów API do uzyskiwania dostępu do modeli AI i mierzy zużycie za pomocą Kredytów powiązanych z tokenami modeli.", "longcat.description": "LongCat to seria dużych modeli generatywnej sztucznej inteligencji, niezależnie opracowanych przez Meituan. Został zaprojektowany, aby zwiększyć produktywność wewnętrzną przedsiębiorstwa i umożliwić innowacyjne zastosowania dzięki wydajnej architekturze obliczeniowej i silnym możliwościom multimodalnym.", "minimax.description": "Założona w 2021 roku, MiniMax tworzy AI ogólnego przeznaczenia z multimodalnymi modelami bazowymi, w tym tekstowymi modelami MoE z bilionami parametrów, modelami mowy i wizji oraz aplikacjami takimi jak Hailuo AI.", "minimaxcodingplan.description": "MiniMax Token Plan zapewnia dostęp do modeli MiniMax, w tym M2.7, do zadań związanych z kodowaniem w ramach subskrypcji o stałej opłacie.", diff --git a/locales/pl-PL/subscription.json b/locales/pl-PL/subscription.json index 8b1fbbf245..81014dddc6 100644 --- a/locales/pl-PL/subscription.json +++ b/locales/pl-PL/subscription.json @@ -55,6 +55,12 @@ "credits.autoTopUp.toggle": "Włącz automatyczne doładowanie", "credits.autoTopUp.upgradeHint": "Subskrybuj płatny plan, aby włączyć automatyczne doładowanie", "credits.autoTopUp.validation.targetMustExceedThreshold": "Docelowe saldo musi być większe niż próg", + "credits.costEstimateHint.desc": "Wyświetl lekkie ostrzeżenie przed wysłaniem, gdy szacowany koszt modelu osiągnie Twój próg", + "credits.costEstimateHint.saveError": "Nie udało się zapisać ustawień alertu szacowanego kosztu", + "credits.costEstimateHint.saveSuccess": "Ustawienia alertu szacowanego kosztu zostały zapisane", + "credits.costEstimateHint.threshold": "Próg ostrzeżenia", + "credits.costEstimateHint.title": "Alert szacowanego kosztu", + "credits.costEstimateHint.validation.threshold": "Próg musi być większy lub równy 0", "credits.packages.auto": "Automatyczne", "credits.packages.charged": "Obciążono {{amount}} $", "credits.packages.expired": "Wygasło", diff --git a/locales/pl-PL/tool.json b/locales/pl-PL/tool.json index 8b77571d12..c99b072c46 100644 --- a/locales/pl-PL/tool.json +++ b/locales/pl-PL/tool.json @@ -40,6 +40,14 @@ "agentMarketplace.render.alreadyInLibraryTag": "Już w bibliotece", "agentMarketplace.render.alreadyInLibrary_one": "{{count}} już w bibliotece", "agentMarketplace.render.alreadyInLibrary_other": "{{count}} już w bibliotece", + "claudeCode.askUserQuestion.escape.back": "Powrót do opcji", + "claudeCode.askUserQuestion.escape.enter": "Lub wpisz bezpośrednio", + "claudeCode.askUserQuestion.escape.placeholder": "Wpisz swoją odpowiedź tutaj…", + "claudeCode.askUserQuestion.multiSelectTag": "(wielokrotny wybór)", + "claudeCode.askUserQuestion.skip": "Pomiń", + "claudeCode.askUserQuestion.submit": "Zatwierdź", + "claudeCode.askUserQuestion.timeExpired": "Czas minął — użyto opcji 1 dla każdego pytania.", + "claudeCode.askUserQuestion.timeRemaining": "Pozostały czas: {{time}} · nieodpowiedziane pytania domyślnie wybierają opcję 1 po upływie czasu.", "codeInterpreter-legacy.error": "Błąd Wykonania", "codeInterpreter-legacy.executing": "Wykonywanie...", "codeInterpreter-legacy.files": "Pliki:", diff --git a/locales/pl-PL/topic.json b/locales/pl-PL/topic.json index b14eb3b697..f9db3f28aa 100644 --- a/locales/pl-PL/topic.json +++ b/locales/pl-PL/topic.json @@ -56,6 +56,7 @@ "renameModal.title": "Zmień nazwę tematu", "searchPlaceholder": "Szukaj tematów...", "searchResultEmpty": "Brak wyników wyszukiwania.", + "sidebar.title": "Tematy", "taskManager.agent": "Agent Zadań", "taskManager.welcome": "Zapytaj mnie o swoje zadania", "temp": "Tymczasowy", diff --git a/locales/pt-BR/chat.json b/locales/pt-BR/chat.json index bc218fa9e4..47ec747098 100644 --- a/locales/pt-BR/chat.json +++ b/locales/pt-BR/chat.json @@ -220,6 +220,13 @@ "inbox.title": "Lobe AI", "input.addAi": "Adicionar mensagem de IA", "input.addUser": "Adicionar mensagem de usuário", + "input.costEstimate.creditsPerMillionTokens": "{{credits}} créditos/M tokens", + "input.costEstimate.hint": "Custo estimado: ~{{credits}} créditos", + "input.costEstimate.inputLabel": "Entrada", + "input.costEstimate.outputLabel": "Saída", + "input.costEstimate.settingsLink": "Ajustar limite de aviso", + "input.costEstimate.tokenCount": "~{{tokens}} tokens", + "input.costEstimate.tooltip": "Estimado com base no contexto atual, ferramentas e preços do modelo. O custo real pode variar.", "input.disclaimer": "Agentes podem cometer erros. Use seu julgamento para informações críticas.", "input.errorMsg": "Falha ao enviar: {{errorMsg}}. Tente novamente ou envie mais tarde.", "input.more": "Mais", @@ -779,6 +786,7 @@ "upload.preview.prepareTasks": "Preparando blocos...", "upload.preview.status.pending": "Preparando para envio...", "upload.preview.status.processing": "Processando arquivo...", + "upload.validation.unsupportedFileType": "Tipo de arquivo não suportado: {{files}}. Imagens suportadas: JPG, PNG, GIF, WebP. Documentos suportados incluem PDF, Word, Excel, PowerPoint, Markdown, texto, CSV, JSON e arquivos de código.", "upload.validation.videoSizeExceeded": "O tamanho do vídeo não deve exceder 20MB. Tamanho atual: {{actualSize}}.", "viewMode.fullWidth": "Largura Total", "viewMode.normal": "Padrão", @@ -894,6 +902,8 @@ "workingPanel.localFile.closeOther": "Fechar Outros", "workingPanel.localFile.closeRight": "Fechar à Direita", "workingPanel.localFile.error": "Não foi possível carregar este arquivo", + "workingPanel.localFile.preview.raw": "Bruto", + "workingPanel.localFile.preview.render": "Visualizar", "workingPanel.localFile.truncated": "Pré-visualização do arquivo truncada para {{limit}} caracteres", "workingPanel.progress": "Progress", "workingPanel.progress.allCompleted": "All tasks completed", @@ -959,6 +969,8 @@ "workingPanel.review.viewMode.unified": "Alternar para visualização unificada", "workingPanel.review.wordWrap.disable": "Desativar quebra de linha", "workingPanel.review.wordWrap.enable": "Ativar quebra de linha", + "workingPanel.skills.empty": "Nenhuma habilidade encontrada neste projeto", + "workingPanel.skills.title": "Habilidades", "workingPanel.space": "Espaço", "workingPanel.title": "Working Panel", "you": "Você", diff --git a/locales/pt-BR/components.json b/locales/pt-BR/components.json index 76e4852782..70265c9fd5 100644 --- a/locales/pt-BR/components.json +++ b/locales/pt-BR/components.json @@ -121,6 +121,16 @@ "ModelSwitchPanel.detail.context": "Comprimento do Contexto", "ModelSwitchPanel.detail.pricing": "Preços", "ModelSwitchPanel.detail.pricing.cachedInput": "Entrada em cache ${{amount}}/M", + "ModelSwitchPanel.detail.pricing.credits.cachedInput": "Entrada em cache {{amount}} créditos/M tokens", + "ModelSwitchPanel.detail.pricing.credits.image": "créditos/img", + "ModelSwitchPanel.detail.pricing.credits.input": "Entrada {{amount}} créditos/M tokens", + "ModelSwitchPanel.detail.pricing.credits.megapixel": "créditos/MP", + "ModelSwitchPanel.detail.pricing.credits.millionCharacters": "créditos/M caracteres", + "ModelSwitchPanel.detail.pricing.credits.millionTokens": "créditos/M tokens", + "ModelSwitchPanel.detail.pricing.credits.output": "Saída {{amount}} créditos/M tokens", + "ModelSwitchPanel.detail.pricing.credits.perImage": "~ {{amount}} créditos / imagem", + "ModelSwitchPanel.detail.pricing.credits.perVideo": "~ {{amount}} créditos / vídeo", + "ModelSwitchPanel.detail.pricing.credits.second": "créditos/s", "ModelSwitchPanel.detail.pricing.group.audio": "Áudio", "ModelSwitchPanel.detail.pricing.group.image": "Imagem", "ModelSwitchPanel.detail.pricing.group.text": "Texto", diff --git a/locales/pt-BR/editor.json b/locales/pt-BR/editor.json index 7fb25b7afc..c743070015 100644 --- a/locales/pt-BR/editor.json +++ b/locales/pt-BR/editor.json @@ -1,8 +1,10 @@ { "actionTag.category.command": "Comando", + "actionTag.category.projectSkill": "Habilidade do projeto", "actionTag.category.skill": "Habilidade", "actionTag.category.tool": "Ferramenta", "actionTag.tooltip.command": "Executa um comando slash no cliente antes do envio.", + "actionTag.tooltip.projectSkill": "Enviado como uma invocação de barra para que o CLI do agente execute a habilidade correspondente do projeto.", "actionTag.tooltip.skill": "Carrega um pacote de habilidades reutilizável para esta solicitação.", "actionTag.tooltip.tool": "Marca uma ferramenta que o usuário selecionou explicitamente para esta solicitação.", "actions.expand.off": "Recolher", diff --git a/locales/pt-BR/home.json b/locales/pt-BR/home.json index 132e602b71..68e73deab6 100644 --- a/locales/pt-BR/home.json +++ b/locales/pt-BR/home.json @@ -11,6 +11,19 @@ "brief.action.ignore": "Ignorar", "brief.action.retry": "Tentar novamente", "brief.addFeedback": "Compartilhar feedback", + "brief.agentSignal.selfReview.applied.heading": "Atualizado", + "brief.agentSignal.selfReview.applied.summary": "{{count}} atualização de sonho foi aplicada.", + "brief.agentSignal.selfReview.applied.summary_plural": "{{count}} atualizações de sonho foram aplicadas.", + "brief.agentSignal.selfReview.applied.title": "Recursos de sonho atualizados", + "brief.agentSignal.selfReview.error.heading": "Problema", + "brief.agentSignal.selfReview.error.summary": "Alguns trabalhos não puderam ser concluídos durante este sonho.", + "brief.agentSignal.selfReview.error.title": "O sonho encontrou um problema", + "brief.agentSignal.selfReview.ideas.summary": "Notas de sonho salvas para revisão futura.", + "brief.agentSignal.selfReview.ideas.title": "Notas do sonho", + "brief.agentSignal.selfReview.proposal.heading": "Sugestão", + "brief.agentSignal.selfReview.proposal.summary": "{{count}} sugestão de sonho precisa da sua revisão.", + "brief.agentSignal.selfReview.proposal.summary_plural": "{{count}} sugestões de sonho precisam da sua revisão.", + "brief.agentSignal.selfReview.proposal.title": "Sugestão de sonho precisa de revisão", "brief.collapse": "Mostrar menos", "brief.commentPlaceholder": "Compartilhe seu feedback...", "brief.commentSubmit": "Enviar feedback", diff --git a/locales/pt-BR/hotkey.json b/locales/pt-BR/hotkey.json index 5dc229b2fa..0a70caa794 100644 --- a/locales/pt-BR/hotkey.json +++ b/locales/pt-BR/hotkey.json @@ -1,8 +1,6 @@ { "addUserMessage.desc": "Adicione a entrada atual como uma mensagem do usuário sem iniciar a geração", "addUserMessage.title": "Adicionar Mensagem do Usuário", - "clearCurrentMessages.desc": "Limpe as mensagens e arquivos enviados da conversa atual", - "clearCurrentMessages.title": "Limpar Mensagens da Conversa", "commandPalette.desc": "Abra o painel de comandos global para acesso rápido às funcionalidades", "commandPalette.title": "Painel de Comandos", "deleteAndRegenerateMessage.desc": "Excluir a última mensagem e gerar novamente", diff --git a/locales/pt-BR/models.json b/locales/pt-BR/models.json index e6833f6073..e974f0a680 100644 --- a/locales/pt-BR/models.json +++ b/locales/pt-BR/models.json @@ -106,7 +106,6 @@ "MiniMax-Hailuo-2.3.description": "Novo modelo de geração de vídeo com melhorias abrangentes em movimento corporal, realismo físico e seguimento de instruções.", "MiniMax-M1.description": "Um novo modelo de raciocínio interno com 80 mil cadeias de pensamento e 1 milhão de tokens de entrada, oferecendo desempenho comparável aos principais modelos globais.", "MiniMax-M2-Stable.description": "Projetado para fluxos de trabalho de codificação e agentes eficientes, com maior concorrência para uso comercial.", - "MiniMax-M2.1-Lightning.description": "Capacidades poderosas de programação multilíngue com inferência mais rápida e eficiente.", "MiniMax-M2.1-highspeed.description": "Poderosas capacidades de programação multilíngue, experiência de programação amplamente aprimorada. Mais rápido e eficiente.", "MiniMax-M2.1.description": "MiniMax-M2.1 é o principal modelo open-source da MiniMax, focado em resolver tarefas complexas do mundo real. Seus principais pontos fortes são as capacidades de programação multilíngue e a habilidade de atuar como um Agente para resolver tarefas complexas.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: Mesmo desempenho do M2.5 com inferência mais rápida.", @@ -315,7 +314,7 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku é o modelo mais rápido e compacto da Anthropic, projetado para respostas quase instantâneas com desempenho rápido e preciso.", "claude-3-opus-20240229.description": "Claude 3 Opus é o modelo mais poderoso da Anthropic para tarefas altamente complexas, com excelência em desempenho, inteligência, fluência e compreensão.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet equilibra inteligência e velocidade para cargas de trabalho empresariais, oferecendo alta utilidade com menor custo e implantação confiável em larga escala.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 é o modelo Haiku mais rápido e inteligente da Anthropic, com velocidade relâmpago e pensamento ampliado.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 é o modelo Haiku mais rápido e inteligente da Anthropic, com velocidade relâmpago e raciocínio ampliado.", "claude-haiku-4-5.description": "Claude Haiku 4.5 da Anthropic — nova geração do Haiku, com raciocínio e visão aprimorados.", "claude-haiku-4.5.description": "Claude Haiku 4.5 é o modelo Haiku mais rápido e inteligente da Anthropic, com velocidade relâmpago e raciocínio ampliado.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking é uma variante avançada que pode revelar seu processo de raciocínio.", @@ -330,7 +329,7 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 é o modelo mais inteligente da Anthropic para criação de agentes e codificação.", "claude-opus-4.6.description": "Claude Opus 4.6 é o modelo mais inteligente da Anthropic para criação de agentes e codificação.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking pode produzir respostas quase instantâneas ou pensamento passo a passo estendido com processo visível.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 é o modelo mais inteligente da Anthropic até o momento, oferecendo respostas quase instantâneas ou pensamento detalhado passo a passo com controle refinado para usuários de API.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 pode produzir respostas quase instantâneas ou raciocínio passo a passo detalhado com processo visível.", "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 é o modelo mais inteligente da Anthropic até o momento.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 da Anthropic — versão aprimorada do Sonnet com desempenho superior em programação.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 da Anthropic — última geração do Sonnet, com alta qualidade em programação e uso de ferramentas.", @@ -404,7 +403,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "O DeepSeek LLM Chat (67B) é um modelo inovador que oferece compreensão profunda da linguagem e interação.", "deepseek-ai/deepseek-v3.1-terminus.description": "O DeepSeek V3.1 é um modelo de raciocínio de nova geração com raciocínio complexo mais forte e cadeia de pensamento para tarefas de análise profunda.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 é um modelo de raciocínio de próxima geração com capacidades mais fortes de raciocínio complexo e cadeia de pensamento.", - "deepseek-chat.description": "Alias de compatibilidade para o modo sem pensamento do DeepSeek V4 Flash. Programado para descontinuação — use o DeepSeek V4 Flash.", + "deepseek-chat.description": "Um novo modelo de código aberto que combina habilidades gerais e de codificação. Ele preserva o diálogo geral do modelo de chat e a forte capacidade de codificação do modelo de programador, com melhor alinhamento de preferências. O DeepSeek-V2.5 também melhora a escrita e o seguimento de instruções.", "deepseek-coder-33B-instruct.description": "O DeepSeek Coder 33B é um modelo de linguagem para código treinado com 2 trilhões de tokens (87% código, 13% texto em chinês/inglês). Introduz uma janela de contexto de 16K e tarefas de preenchimento intermediário, oferecendo preenchimento de código em nível de projeto e inserção de trechos.", "deepseek-coder-v2.description": "O DeepSeek Coder V2 é um modelo de código MoE open-source com forte desempenho em tarefas de programação, comparável ao GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "O DeepSeek Coder V2 é um modelo de código MoE open-source com forte desempenho em tarefas de programação, comparável ao GPT-4 Turbo.", @@ -426,7 +425,7 @@ "deepseek-r1-fast-online.description": "Versão completa e rápida do DeepSeek R1 com busca em tempo real na web, combinando capacidade de 671B com respostas mais ágeis.", "deepseek-r1-online.description": "Versão completa do DeepSeek R1 com 671B de parâmetros e busca em tempo real na web, oferecendo compreensão e geração mais robustas.", "deepseek-r1.description": "O DeepSeek-R1 usa dados de inicialização a frio antes do RL e apresenta desempenho comparável ao OpenAI-o1 em matemática, programação e raciocínio.", - "deepseek-reasoner.description": "Alias de compatibilidade para o modo de pensamento do DeepSeek V4 Flash. Programado para descontinuação — use o DeepSeek V4 Flash.", + "deepseek-reasoner.description": "Um modelo de raciocínio DeepSeek focado em tarefas complexas de raciocínio lógico.", "deepseek-v2.description": "O DeepSeek V2 é um modelo MoE eficiente para processamento econômico.", "deepseek-v2:236b.description": "O DeepSeek V2 236B é o modelo da DeepSeek focado em código com forte geração de código.", "deepseek-v3-0324.description": "O DeepSeek-V3-0324 é um modelo MoE com 671B de parâmetros, com destaque em programação, capacidade técnica, compreensão de contexto e manipulação de textos longos.", @@ -491,8 +490,6 @@ "doubao-seedream-4-0-250828.description": "O Seedream 4.0 é um modelo de geração de imagem da ByteDance Seed, que suporta entradas de texto e imagem com geração de imagem altamente controlável e de alta qualidade. Gera imagens a partir de comandos de texto.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 é o mais recente modelo multimodal de imagem da ByteDance, integrando capacidades de texto-para-imagem, imagem-para-imagem e geração de imagens em lote, enquanto incorpora senso comum e habilidades de raciocínio. Comparado à versão anterior 4.0, oferece qualidade de geração significativamente melhorada, com maior consistência de edição e fusão de múltiplas imagens. Oferece controle mais preciso sobre detalhes visuais, produzindo texto pequeno e rostos pequenos de forma mais natural, além de alcançar layouts e cores mais harmoniosos, melhorando a estética geral.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite é o mais recente modelo de geração de imagens da ByteDance. Pela primeira vez, integra capacidades de recuperação online, permitindo incorporar informações da web em tempo real e melhorar a atualidade das imagens geradas. A inteligência do modelo também foi aprimorada, permitindo interpretação precisa de instruções complexas e conteúdo visual. Além disso, oferece melhor cobertura de conhecimento global, consistência de referência e qualidade de geração em cenários profissionais, atendendo melhor às necessidades de criação visual em nível empresarial.", - "dreamina-seedance-2-0-260128.description": "Seedance 2.0 da ByteDance é o modelo de geração de vídeo mais poderoso, suportando geração de vídeo multimodal com referência, edição de vídeo, extensão de vídeo, texto para vídeo e imagem para vídeo com áudio sincronizado.", - "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast da ByteDance oferece as mesmas capacidades do Seedance 2.0 com velocidades de geração mais rápidas a um preço mais competitivo.", "emohaa.description": "O Emohaa é um modelo voltado para saúde mental com habilidades profissionais de aconselhamento para ajudar os usuários a compreender questões emocionais.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B é um modelo leve de código aberto para implantação local e personalizada.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview é um modelo de pré-visualização com contexto de 8K para avaliação do ERNIE 4.5.", @@ -506,6 +503,7 @@ "ernie-5.0-thinking-latest.description": "Wenxin 5.0 Thinking é um modelo nativo multimodal de ponta com modelagem unificada de texto, imagem, áudio e vídeo. Oferece amplas melhorias de capacidade para perguntas e respostas complexas, criação e cenários com agentes.", "ernie-5.0-thinking-preview.description": "Pré-visualização do Wenxin 5.0 Thinking, modelo nativo multimodal de ponta com modelagem unificada de texto, imagem, áudio e vídeo. Oferece amplas melhorias de capacidade para perguntas e respostas complexas, criação e cenários com agentes.", "ernie-5.0.description": "ERNIE 5.0, o modelo de nova geração da série ERNIE, é um modelo multimodal nativo. Ele adota uma abordagem unificada de modelagem multimodal, combinando texto, imagens, áudio e vídeo para oferecer capacidades abrangentes. Suas habilidades fundamentais foram significativamente aprimoradas, com forte desempenho em avaliações de benchmark. Destaca-se em compreensão multimodal, seguimento de instruções, escrita criativa, precisão factual, planejamento de agentes e uso de ferramentas.", + "ernie-5.1.description": "ERNIE 5.1 é o modelo mais recente da série ERNIE, com atualizações abrangentes em suas capacidades fundamentais. Ele demonstra melhorias significativas em áreas como agentes, processamento de conhecimento, raciocínio e busca profunda. Esta versão adota uma arquitetura de aprendizado por reforço totalmente assíncrona e desacoplada, projetada especificamente para abordar desafios-chave na evolução de grandes modelos em direção à tomada de decisão autônoma por agentes, incluindo discrepâncias numéricas entre treinamento e inferência, baixa utilização de recursos computacionais heterogêneos e problemas globais causados por efeitos de cauda longa. Além disso, técnicas de pós-treinamento em larga escala para agentes são empregadas para aprimorar ainda mais as capacidades e o desempenho de generalização do modelo. Por meio de uma estrutura colaborativa de três estágios envolvendo processos de ambiente, especialista e fusão, a abordagem não apenas garante eficiência no treinamento, mas também melhora significativamente a estabilidade e o desempenho do modelo em tarefas complexas.", "ernie-char-fiction-8k-preview.description": "Pré-visualização do ERNIE Character Fiction 8K, modelo para criação de personagens e enredos, voltado para avaliação e testes de recursos.", "ernie-char-fiction-8k.description": "ERNIE Character Fiction 8K é um modelo de personagem para romances e criação de enredos, adequado para geração de histórias longas.", "ernie-image-turbo.description": "ERNIE-Image é um modelo de texto-para-imagem de 8B parâmetros desenvolvido pela Baidu. Está entre os melhores em vários benchmarks, alcançando primeiro lugar no SuperCLUE na China e liderança na categoria open-source.", @@ -517,8 +515,7 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K é um modelo de raciocínio rápido com contexto de 32K para raciocínio complexo e bate-papo de múltiplas interações.", "ernie-x1.1-preview.description": "Pré-visualização do modelo de raciocínio ERNIE X1.1 para avaliação e testes.", "ernie-x1.1.description": "ERNIE X1.1 é um modelo de pensamento em pré-visualização para avaliação e testes.", - "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5, desenvolvido pela equipe Seed da ByteDance, suporta edição e composição de múltiplas imagens. Apresenta consistência aprimorada de objetos, seguimento preciso de instruções, compreensão de lógica espacial, expressão estética, layout de pôster e design de logotipo com renderização de texto-imagem de alta precisão.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, desenvolvido pela ByteDance Seed, suporta entradas de texto e imagem para geração altamente controlável e de alta qualidade a partir de prompts.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 é um modelo de geração de imagens da ByteDance Seed, que suporta entradas de texto e imagem com geração de imagens altamente controlável e de alta qualidade. Ele gera imagens a partir de comandos de texto.", "fal-ai/flux-kontext/dev.description": "Modelo FLUX.1 focado em edição de imagens, com suporte a entradas de texto e imagem.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] aceita texto e imagens de referência como entrada, permitindo edições locais direcionadas e transformações complexas de cena.", "fal-ai/flux/krea.description": "Flux Krea [dev] é um modelo de geração de imagens com viés estético para imagens mais realistas e naturais.", @@ -526,8 +523,8 @@ "fal-ai/hunyuan-image/v3.description": "Um poderoso modelo multimodal nativo de geração de imagens.", "fal-ai/imagen4/preview.description": "Modelo de geração de imagens de alta qualidade do Google.", "fal-ai/nano-banana.description": "Nano Banana é o modelo multimodal nativo mais novo, rápido e eficiente do Google, permitindo geração e edição de imagens por meio de conversas.", - "fal-ai/qwen-image-edit.description": "Um modelo profissional de edição de imagens da equipe Qwen, suportando edições semânticas e de aparência, edição precisa de texto em chinês/inglês, transferência de estilo, rotação e mais.", - "fal-ai/qwen-image.description": "Um modelo poderoso de geração de imagens da equipe Qwen com forte renderização de texto em chinês e estilos visuais diversificados.", + "fal-ai/qwen-image-edit.description": "Um modelo profissional de edição de imagens da equipe Qwen que suporta edições semânticas e de aparência, edita com precisão textos em chinês e inglês, e permite edições de alta qualidade, como transferência de estilo e rotação de objetos.", + "fal-ai/qwen-image.description": "Um poderoso modelo de geração de imagens da equipe Qwen com impressionante renderização de texto em chinês e estilos visuais diversos.", "flux-1-schnell.description": "Modelo de texto para imagem com 12 bilhões de parâmetros da Black Forest Labs, usando difusão adversarial latente para gerar imagens de alta qualidade em 1 a 4 etapas. Rivaliza com alternativas fechadas e é lançado sob licença Apache-2.0 para uso pessoal, acadêmico e comercial.", "flux-dev.description": "Modelo open-source de geração de imagens para P&D, otimizado de forma eficiente para pesquisa inovadora não comercial.", "flux-kontext-max.description": "Geração e edição de imagens contextuais de última geração, combinando texto e imagens para resultados precisos e coerentes.", @@ -569,7 +566,7 @@ "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) é o modelo de geração de imagens do Google e também suporta chat multimodal.", "gemini-3-pro-preview.description": "Gemini 3 Pro é o agente mais poderoso do Google, com capacidades de codificação emocional e visuais aprimoradas, além de raciocínio de última geração.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) é o modelo de geração de imagens nativo mais rápido do Google, com suporte a raciocínio, geração e edição de imagens conversacionais.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) oferece qualidade de imagem em nível Pro com velocidade Flash e suporte a chat multimodal.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) é o modelo nativo de geração de imagens mais rápido do Google, com suporte a raciocínio, geração e edição de imagens em conversas.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview é o modelo multimodal mais econômico do Google, otimizado para tarefas agentivas de alto volume, tradução e processamento de dados.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite é o modelo multimodal mais econômico do Google, otimizado para tarefas agentivas de alto volume, tradução e processamento de dados.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview melhora o Gemini 3 Pro com capacidades de raciocínio aprimoradas e adiciona suporte a nível médio de pensamento.", @@ -734,8 +731,6 @@ "grok-4-fast-reasoning.description": "Estamos entusiasmados em lançar o Grok 4 Fast, nosso mais recente avanço em modelos de raciocínio com ótimo custo-benefício.", "grok-4.20-0309-non-reasoning.description": "Variante sem raciocínio para casos de uso simples.", "grok-4.20-0309-reasoning.description": "Modelo inteligente e extremamente rápido que raciocina antes de responder.", - "grok-4.20-beta-0309-non-reasoning.description": "Uma variante sem raciocínio para casos de uso simples.", - "grok-4.20-beta-0309-reasoning.description": "Modelo inteligente e extremamente rápido que raciocina antes de responder.", "grok-4.20-multi-agent-0309.description": "Equipe de 4 ou 16 agentes. Excelente para pesquisas, sem suporte atual a ferramentas do lado do cliente. Suporta apenas ferramentas do servidor xAI (como X Search e Web Search) e ferramentas MCP remotas.", "grok-4.3.description": "O modelo de linguagem de grande porte mais comprometido com a verdade no mundo.", "grok-4.description": "O mais recente modelo Grok de ponta com desempenho incomparável em linguagem, matemática e raciocínio — um verdadeiro polivalente. Atualmente aponta para o grok-4-0709; devido a recursos limitados, está temporariamente 10% acima do preço oficial e espera-se que retorne ao preço oficial posteriormente.", @@ -1220,8 +1215,6 @@ "qwq.description": "QwQ é um modelo de raciocínio da família Qwen. Em comparação com modelos ajustados por instruções padrão, oferece habilidades de pensamento e raciocínio que melhoram significativamente o desempenho em tarefas difíceis. O QwQ-32B é um modelo de porte médio que compete com os principais modelos como DeepSeek-R1 e o1-mini.", "qwq_32b.description": "Modelo de raciocínio de porte médio da família Qwen. Em comparação com modelos ajustados por instruções padrão, as habilidades de pensamento e raciocínio do QwQ aumentam significativamente o desempenho em tarefas difíceis.", "r1-1776.description": "R1-1776 é uma variante pós-treinada do DeepSeek R1 projetada para fornecer informações factuais sem censura e imparciais.", - "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro da ByteDance suporta texto para vídeo, imagem para vídeo (primeiro quadro, primeiro+último quadro) e geração de áudio sincronizado com visuais.", - "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite da BytePlus apresenta geração aumentada por recuperação na web para informações em tempo real, interpretação aprimorada de prompts complexos e consistência melhorada de referências para criação visual profissional.", "solar-mini-ja.description": "Solar Mini (Ja) estende o Solar Mini com foco no japonês, mantendo desempenho eficiente e forte em inglês e coreano.", "solar-mini.description": "Solar Mini é um LLM compacto que supera o GPT-3.5, com forte capacidade multilíngue suportando inglês e coreano, oferecendo uma solução eficiente e de baixo custo.", "solar-pro.description": "Solar Pro é um LLM de alta inteligência da Upstage, focado em seguir instruções em uma única GPU, com pontuações IFEval acima de 80. Atualmente suporta inglês; o lançamento completo está previsto para novembro de 2024 com suporte expandido a idiomas e contexto mais longo.", diff --git a/locales/pt-BR/onboarding.json b/locales/pt-BR/onboarding.json index de3723e2d6..26f263b700 100644 --- a/locales/pt-BR/onboarding.json +++ b/locales/pt-BR/onboarding.json @@ -27,6 +27,13 @@ "agent.layout.skipConfirm.ok": "Pular por enquanto", "agent.layout.skipConfirm.title": "Pular a configuração inicial agora?", "agent.layout.switchMessage": "Não está no clima hoje? Você pode mudar para {{mode}} ou {{skip}}.", + "agent.layout.switchMessageClassic": "Não está gostando hoje? Você pode alternar para {{mode}}.", + "agent.messenger.cta.discord": "Adicionar ao Discord", + "agent.messenger.cta.slack": "Adicionar ao Slack", + "agent.messenger.cta.telegram": "Abrir no Telegram", + "agent.messenger.subtitle": "Converse com seu agente no Telegram, Slack ou Discord", + "agent.messenger.telegramQrCaption": "Escaneie com a câmera do seu celular", + "agent.messenger.title": "Leve-me com você, onde quer que você envie mensagens", "agent.modeSwitch.agent": "Conversacional", "agent.modeSwitch.classic": "Clássico", "agent.modeSwitch.collapse": "Recolher", @@ -68,6 +75,13 @@ "agent.wrapUp.confirm.content": "Vou salvar o que vimos até agora. Você sempre pode voltar e conversar mais depois.", "agent.wrapUp.confirm.ok": "Finalizar agora", "agent.wrapUp.confirm.title": "Finalizar o onboarding agora?", + "agentPicker.allCategories": "Todos", + "agentPicker.continue": "Continuar", + "agentPicker.skip": "Pular por enquanto", + "agentPicker.subtitle": "Adicione alguns à sua biblioteca agora — descubra mais a qualquer momento depois.", + "agentPicker.title": "Vamos adicionar alguns agentes à sua biblioteca", + "agentPicker.title2": "Escolha os que combinam com a sua forma de trabalhar", + "agentPicker.title3": "Você sempre pode adicionar mais depois — comece com alguns", "back": "Voltar", "finish": "Começar", "interests.area.business": "Negócios e Estratégia", diff --git a/locales/pt-BR/plugin.json b/locales/pt-BR/plugin.json index a47ef38b6a..73d55ab8ad 100644 --- a/locales/pt-BR/plugin.json +++ b/locales/pt-BR/plugin.json @@ -39,8 +39,8 @@ "builtins.lobe-agent-documents.inspector.docCount": "{{count}} documentos", "builtins.lobe-agent-documents.inspector.opsCount": "{{count}} operações", "builtins.lobe-agent-documents.inspector.opsResult": "{{success}}/{{total}} operações", - "builtins.lobe-agent-documents.inspector.target.agent": "no agente", - "builtins.lobe-agent-documents.inspector.target.currentTopic": "no tópico", + "builtins.lobe-agent-documents.inspector.scope.agent": "no agente", + "builtins.lobe-agent-documents.inspector.scope.currentTopic": "no tópico", "builtins.lobe-agent-documents.title": "Documentos do Agente", "builtins.lobe-agent-management.apiName.callAgent": "Agente de chamada", "builtins.lobe-agent-management.apiName.createAgent": "Criar agente", @@ -90,6 +90,14 @@ "builtins.lobe-agent.title": "Agente Lobe", "builtins.lobe-claude-code.agent.instruction": "Instrução", "builtins.lobe-claude-code.agent.result": "Resultado", + "builtins.lobe-claude-code.task.createLabel": "Criando tarefa: ", + "builtins.lobe-claude-code.task.getLabel": "Inspecionando tarefa #{{taskId}}", + "builtins.lobe-claude-code.task.listLabel": "Listando tarefas", + "builtins.lobe-claude-code.task.updateCompleted": "Concluído", + "builtins.lobe-claude-code.task.updateDeleted": "Excluído", + "builtins.lobe-claude-code.task.updateInProgress": "Iniciado", + "builtins.lobe-claude-code.task.updateLabel": "Atualizando tarefa #{{taskId}}", + "builtins.lobe-claude-code.task.updatePending": "Redefinir", "builtins.lobe-claude-code.todoWrite.allDone": "Todas as tarefas concluídas", "builtins.lobe-claude-code.todoWrite.currentStep": "Etapa atual", "builtins.lobe-claude-code.todoWrite.todos": "Tarefas", diff --git a/locales/pt-BR/providers.json b/locales/pt-BR/providers.json index 3df739cec1..e45fb62c70 100644 --- a/locales/pt-BR/providers.json +++ b/locales/pt-BR/providers.json @@ -33,7 +33,6 @@ "jina.description": "Fundada em 2020, a Jina AI é uma empresa líder em busca com IA. Sua pilha de busca inclui modelos vetoriais, reranqueadores e pequenos modelos de linguagem para construir aplicativos generativos e multimodais confiáveis e de alta qualidade.", "kimicodingplan.description": "O Kimi Code da Moonshot AI oferece acesso aos modelos Kimi, incluindo o K2.5, para tarefas de codificação.", "lmstudio.description": "O LM Studio é um aplicativo de desktop para desenvolver e experimentar com LLMs no seu computador.", - "lobehub.description": "O LobeHub Cloud utiliza APIs oficiais para acessar modelos de IA e mede o uso com Créditos vinculados aos tokens dos modelos.", "longcat.description": "LongCat é uma série de grandes modelos de IA generativa desenvolvidos de forma independente pela Meituan. Ele foi projetado para aumentar a produtividade interna da empresa e possibilitar aplicações inovadoras por meio de uma arquitetura computacional eficiente e fortes capacidades multimodais.", "minimax.description": "Fundada em 2021, a MiniMax desenvolve IA de uso geral com modelos fundamentais multimodais, incluindo modelos de texto com trilhões de parâmetros, modelos de fala e visão, além de aplicativos como o Hailuo AI.", "minimaxcodingplan.description": "O Plano de Tokens MiniMax oferece acesso aos modelos MiniMax, incluindo o M2.7, para tarefas de codificação por meio de uma assinatura de taxa fixa.", diff --git a/locales/pt-BR/subscription.json b/locales/pt-BR/subscription.json index 6ede84ffce..cabb0e1a1f 100644 --- a/locales/pt-BR/subscription.json +++ b/locales/pt-BR/subscription.json @@ -55,6 +55,12 @@ "credits.autoTopUp.toggle": "Ativar Recarga Automática", "credits.autoTopUp.upgradeHint": "Assine um plano pago para ativar a recarga automática", "credits.autoTopUp.validation.targetMustExceedThreshold": "O saldo alvo deve ser maior que o limite", + "credits.costEstimateHint.desc": "Exibir um aviso leve antes de enviar quando o custo estimado do modelo atingir seu limite", + "credits.costEstimateHint.saveError": "Falha ao salvar as configurações de alerta de estimativa de custo", + "credits.costEstimateHint.saveSuccess": "Configurações de alerta de estimativa de custo salvas", + "credits.costEstimateHint.threshold": "Limite de Aviso", + "credits.costEstimateHint.title": "Alerta de Estimativa de Custo", + "credits.costEstimateHint.validation.threshold": "O limite deve ser maior ou igual a 0", "credits.packages.auto": "Automático", "credits.packages.charged": "Cobrado R${{amount}}", "credits.packages.expired": "Expirado", diff --git a/locales/pt-BR/tool.json b/locales/pt-BR/tool.json index fc1a858758..52969edb55 100644 --- a/locales/pt-BR/tool.json +++ b/locales/pt-BR/tool.json @@ -40,6 +40,14 @@ "agentMarketplace.render.alreadyInLibraryTag": "Já na biblioteca", "agentMarketplace.render.alreadyInLibrary_one": "{{count}} já na biblioteca", "agentMarketplace.render.alreadyInLibrary_other": "{{count}} já na biblioteca", + "claudeCode.askUserQuestion.escape.back": "Voltar para as opções", + "claudeCode.askUserQuestion.escape.enter": "Ou digite diretamente", + "claudeCode.askUserQuestion.escape.placeholder": "Digite sua resposta aqui…", + "claudeCode.askUserQuestion.multiSelectTag": "(seleção múltipla)", + "claudeCode.askUserQuestion.skip": "Pular", + "claudeCode.askUserQuestion.submit": "Enviar", + "claudeCode.askUserQuestion.timeExpired": "Tempo esgotado — usando a opção 1 de cada pergunta.", + "claudeCode.askUserQuestion.timeRemaining": "Tempo restante: {{time}} · perguntas não respondidas serão definidas como a opção 1 ao expirar o tempo.", "codeInterpreter-legacy.error": "Erro de Execução", "codeInterpreter-legacy.executing": "Executando...", "codeInterpreter-legacy.files": "Arquivos:", diff --git a/locales/pt-BR/topic.json b/locales/pt-BR/topic.json index 6b4d4ca155..8eb6986f88 100644 --- a/locales/pt-BR/topic.json +++ b/locales/pt-BR/topic.json @@ -56,6 +56,7 @@ "renameModal.title": "Renomear tópico", "searchPlaceholder": "Buscar Tópicos...", "searchResultEmpty": "Nenhum resultado encontrado.", + "sidebar.title": "Tópicos", "taskManager.agent": "Agente de Tarefas", "taskManager.welcome": "Pergunte-me sobre suas tarefas", "temp": "Temporário", diff --git a/locales/ru-RU/chat.json b/locales/ru-RU/chat.json index b1dfb31f2a..ef6e087dc8 100644 --- a/locales/ru-RU/chat.json +++ b/locales/ru-RU/chat.json @@ -220,6 +220,13 @@ "inbox.title": "Lobe AI", "input.addAi": "Добавить сообщение от ИИ", "input.addUser": "Добавить сообщение пользователя", + "input.costEstimate.creditsPerMillionTokens": "{{credits}} кредитов/М токенов", + "input.costEstimate.hint": "Ориентировочная стоимость: ~{{credits}} кредитов", + "input.costEstimate.inputLabel": "Ввод", + "input.costEstimate.outputLabel": "Вывод", + "input.costEstimate.settingsLink": "Настроить порог предупреждения", + "input.costEstimate.tokenCount": "~{{tokens}} токенов", + "input.costEstimate.tooltip": "Оценка на основе текущего контекста, инструментов и цен модели. Фактическая стоимость может отличаться.", "input.disclaimer": "Агенты могут ошибаться. Используйте собственное суждение для критически важной информации.", "input.errorMsg": "Не удалось отправить: {{errorMsg}}. Повторите попытку позже.", "input.more": "Ещё", @@ -779,6 +786,7 @@ "upload.preview.prepareTasks": "Подготовка фрагментов...", "upload.preview.status.pending": "Подготовка к загрузке...", "upload.preview.status.processing": "Обработка файла...", + "upload.validation.unsupportedFileType": "Неподдерживаемый тип файла: {{files}}. Поддерживаемые изображения: JPG, PNG, GIF, WebP. Поддерживаемые документы: PDF, Word, Excel, PowerPoint, Markdown, текст, CSV, JSON и файлы кода.", "upload.validation.videoSizeExceeded": "Размер видеофайла не должен превышать 20 МБ. Текущий размер: {{actualSize}}.", "viewMode.fullWidth": "Полная ширина", "viewMode.normal": "Стандартный", @@ -894,6 +902,8 @@ "workingPanel.localFile.closeOther": "Закрыть остальные", "workingPanel.localFile.closeRight": "Закрыть справа", "workingPanel.localFile.error": "Не удалось загрузить этот файл", + "workingPanel.localFile.preview.raw": "Сырой", + "workingPanel.localFile.preview.render": "Предпросмотр", "workingPanel.localFile.truncated": "Предварительный просмотр файла сокращен до {{limit}} символов", "workingPanel.progress": "Progress", "workingPanel.progress.allCompleted": "All tasks completed", @@ -959,6 +969,8 @@ "workingPanel.review.viewMode.unified": "Переключиться на объединенный вид", "workingPanel.review.wordWrap.disable": "Отключить перенос слов", "workingPanel.review.wordWrap.enable": "Включить перенос слов", + "workingPanel.skills.empty": "В этом проекте не найдено навыков", + "workingPanel.skills.title": "Навыки", "workingPanel.space": "Пространство", "workingPanel.title": "Working Panel", "you": "Вы", diff --git a/locales/ru-RU/components.json b/locales/ru-RU/components.json index c9eb481b8f..dcd716a927 100644 --- a/locales/ru-RU/components.json +++ b/locales/ru-RU/components.json @@ -121,6 +121,16 @@ "ModelSwitchPanel.detail.context": "Длина контекста", "ModelSwitchPanel.detail.pricing": "Цены", "ModelSwitchPanel.detail.pricing.cachedInput": "Кэшированный ввод ${{amount}}/М", + "ModelSwitchPanel.detail.pricing.credits.cachedInput": "Кэшированный ввод {{amount}} кредитов/М токенов", + "ModelSwitchPanel.detail.pricing.credits.image": "кредиты/изображение", + "ModelSwitchPanel.detail.pricing.credits.input": "Ввод {{amount}} кредитов/М токенов", + "ModelSwitchPanel.detail.pricing.credits.megapixel": "кредиты/МП", + "ModelSwitchPanel.detail.pricing.credits.millionCharacters": "кредиты/М символов", + "ModelSwitchPanel.detail.pricing.credits.millionTokens": "кредиты/М токенов", + "ModelSwitchPanel.detail.pricing.credits.output": "Вывод {{amount}} кредитов/М токенов", + "ModelSwitchPanel.detail.pricing.credits.perImage": "~ {{amount}} кредитов / изображение", + "ModelSwitchPanel.detail.pricing.credits.perVideo": "~ {{amount}} кредитов / видео", + "ModelSwitchPanel.detail.pricing.credits.second": "кредиты/с", "ModelSwitchPanel.detail.pricing.group.audio": "Аудио", "ModelSwitchPanel.detail.pricing.group.image": "Изображение", "ModelSwitchPanel.detail.pricing.group.text": "Текст", diff --git a/locales/ru-RU/editor.json b/locales/ru-RU/editor.json index d60cca18dd..98d223b1b0 100644 --- a/locales/ru-RU/editor.json +++ b/locales/ru-RU/editor.json @@ -1,8 +1,10 @@ { "actionTag.category.command": "Команда", + "actionTag.category.projectSkill": "Навык проекта", "actionTag.category.skill": "Навык", "actionTag.category.tool": "Инструмент", "actionTag.tooltip.command": "Выполняет клиентскую slash-команду перед отправкой.", + "actionTag.tooltip.projectSkill": "Отправляется как вызов через слэш, чтобы CLI агента запускал соответствующий навык проекта.", "actionTag.tooltip.skill": "Загружает для этого запроса переиспользуемый пакет навыков.", "actionTag.tooltip.tool": "Помечает инструмент, который пользователь явно выбрал для этого запроса.", "actions.expand.off": "Свернуть", diff --git a/locales/ru-RU/home.json b/locales/ru-RU/home.json index 543407b921..ec084e8ce9 100644 --- a/locales/ru-RU/home.json +++ b/locales/ru-RU/home.json @@ -11,6 +11,19 @@ "brief.action.ignore": "Игнорировать", "brief.action.retry": "Повторить", "brief.addFeedback": "Оставить отзыв", + "brief.agentSignal.selfReview.applied.heading": "Обновлено", + "brief.agentSignal.selfReview.applied.summary": "Применено {{count}} обновление мечты.", + "brief.agentSignal.selfReview.applied.summary_plural": "Применено {{count}} обновлений мечты.", + "brief.agentSignal.selfReview.applied.title": "Обновленные ресурсы мечты", + "brief.agentSignal.selfReview.error.heading": "Проблема", + "brief.agentSignal.selfReview.error.summary": "Некоторая работа не была завершена во время этой мечты.", + "brief.agentSignal.selfReview.error.title": "Мечта столкнулась с проблемой", + "brief.agentSignal.selfReview.ideas.summary": "Сохраненные заметки о мечте для будущего просмотра.", + "brief.agentSignal.selfReview.ideas.title": "Заметки о мечте", + "brief.agentSignal.selfReview.proposal.heading": "Предложение", + "brief.agentSignal.selfReview.proposal.summary": "{{count}} предложение по мечте требует вашего рассмотрения.", + "brief.agentSignal.selfReview.proposal.summary_plural": "{{count}} предложений по мечте требуют вашего рассмотрения.", + "brief.agentSignal.selfReview.proposal.title": "Предложение по мечте требует рассмотрения", "brief.collapse": "Показать меньше", "brief.commentPlaceholder": "Поделитесь своим отзывом...", "brief.commentSubmit": "Отправить отзыв", diff --git a/locales/ru-RU/hotkey.json b/locales/ru-RU/hotkey.json index c79050ed84..60783e8841 100644 --- a/locales/ru-RU/hotkey.json +++ b/locales/ru-RU/hotkey.json @@ -1,8 +1,6 @@ { "addUserMessage.desc": "Добавить текущий ввод как сообщение пользователя без запуска генерации", "addUserMessage.title": "Добавить сообщение пользователя", - "clearCurrentMessages.desc": "Очистить сообщения и загруженные файлы из текущего разговора", - "clearCurrentMessages.title": "Очистить сообщения разговора", "commandPalette.desc": "Открыть глобальную палитру команд для быстрого доступа к функциям", "commandPalette.title": "Палитра команд", "deleteAndRegenerateMessage.desc": "Удалить последнее сообщение и сгенерировать заново", diff --git a/locales/ru-RU/models.json b/locales/ru-RU/models.json index 101e45b4b2..1268d3d76a 100644 --- a/locales/ru-RU/models.json +++ b/locales/ru-RU/models.json @@ -106,7 +106,6 @@ "MiniMax-Hailuo-2.3.description": "Совершенно новая модель генерации видео с комплексными улучшениями в движении тела, физическом реализме и следовании инструкциям.", "MiniMax-M1.description": "Новая внутренняя модель рассуждений с поддержкой 80K цепочек размышлений и 1M входных токенов, обеспечивающая производительность на уровне ведущих мировых моделей.", "MiniMax-M2-Stable.description": "Создана для эффективного программирования и работы агентов, с повышенной параллельностью для коммерческого использования.", - "MiniMax-M2.1-Lightning.description": "Мощные многоязычные возможности программирования с более быстрым и эффективным выводом.", "MiniMax-M2.1-highspeed.description": "Мощные многоязычные программные возможности, всесторонне улучшенный опыт программирования. Быстрее и эффективнее.", "MiniMax-M2.1.description": "MiniMax-M2.1 — это флагманская модель с открытым исходным кодом от MiniMax, ориентированная на решение сложных задач из реального мира. Её ключевые преимущества — поддержка многозадачного программирования и способность выступать в роли интеллектуального агента.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: Та же производительность, что и у M2.5, но с ускоренным выводом.", @@ -315,11 +314,11 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku — самая быстрая и компактная модель от Anthropic, предназначенная для мгновенных ответов с высокой точностью и скоростью.", "claude-3-opus-20240229.description": "Claude 3 Opus — самая мощная модель от Anthropic для высокосложных задач, превосходящая по производительности, интеллекту, беглости и пониманию.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet сочетает интеллект и скорость для корпоративных задач, обеспечивая высокую полезность при низкой стоимости и надежное масштабируемое развертывание.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 — самая быстрая и интеллектуальная модель Haiku от Anthropic, с молниеносной скоростью и расширенным мышлением.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 — самая быстрая и умная модель Haiku от Anthropic, с молниеносной скоростью и расширенными возможностями рассуждения.", "claude-haiku-4-5.description": "Claude Haiku 4.5 от Anthropic — модель нового поколения с улучшенными возможностями рассуждения и работы с изображениями.", "claude-haiku-4.5.description": "Claude Haiku 4.5 — это самая быстрая и умная модель Haiku от Anthropic, с молниеносной скоростью и расширенными возможностями рассуждения.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking — продвинутая версия, способная демонстрировать процесс рассуждения.", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1 — последняя и самая мощная модель Anthropic для выполнения сложных задач, превосходящая в производительности, интеллекте, беглости и понимании.", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1 — новейшая и самая мощная модель Anthropic для выполнения сложных задач, превосходящая в производительности, интеллекте, беглости и понимании.", "claude-opus-4-1.description": "Claude Opus 4.1 от Anthropic — премиальная модель рассуждения с глубокими аналитическими возможностями.", "claude-opus-4-20250514.description": "Claude Opus 4 — самая мощная модель Anthropic для выполнения сложных задач, превосходящая в производительности, интеллекте, беглости и понимании.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 — флагманская модель от Anthropic, сочетающая выдающийся интеллект с масштабируемой производительностью, идеально подходящая для сложных задач, требующих высококачественных ответов и рассуждений.", @@ -330,7 +329,7 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 — это самая интеллектуальная модель Anthropic для создания агентов и программирования.", "claude-opus-4.6.description": "Claude Opus 4.6 — это самая интеллектуальная модель Anthropic для создания агентов и программирования.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking может выдавать как мгновенные ответы, так и пошаговое рассуждение с видимым процессом.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 — самая интеллектуальная модель Anthropic на сегодняшний день, предлагающая мгновенные ответы или пошаговое мышление с тонкой настройкой для пользователей API.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 может выдавать почти мгновенные ответы или пошаговые рассуждения с видимым процессом.", "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 — самая интеллектуальная модель Anthropic на сегодняшний день.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 от Anthropic — улучшенная модель Sonnet с повышенной производительностью в программировании.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 от Anthropic — последняя версия Sonnet с превосходными возможностями в программировании и использовании инструментов.", @@ -404,7 +403,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) — инновационная модель с глубоким пониманием языка и возможностью взаимодействия.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 — модель нового поколения для рассуждений, обладающая улучшенными возможностями для сложных рассуждений и цепочек размышлений, подходящая для задач глубокого анализа.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 — это модель рассуждений следующего поколения с улучшенными возможностями сложных рассуждений и цепочки размышлений.", - "deepseek-chat.description": "Алиас совместимости для режима без мышления DeepSeek V4 Flash. Планируется к устареванию — используйте DeepSeek V4 Flash.", + "deepseek-chat.description": "Новая модель с открытым исходным кодом, объединяющая общие и кодовые способности. Она сохраняет общий диалоговый стиль чат-модели и сильные навыки кодирования кодовой модели, с улучшенной согласованностью предпочтений. DeepSeek-V2.5 также улучшает навыки письма и следование инструкциям.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B — языковая модель для программирования, обученная на 2 триллионах токенов (87% кода, 13% китайского/английского текста). Поддерживает контекстное окно 16K и задачи заполнения в середине, обеспечивая автодополнение на уровне проекта и вставку фрагментов кода.", "deepseek-coder-v2.description": "DeepSeek Coder V2 — модель кода с открытым исходным кодом, демонстрирующая высокую производительность в задачах программирования, сопоставимую с GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 — модель кода с открытым исходным кодом, демонстрирующая высокую производительность в задачах программирования, сопоставимую с GPT-4 Turbo.", @@ -426,7 +425,7 @@ "deepseek-r1-fast-online.description": "Быстрая полная версия DeepSeek R1 с поиском в интернете в реальном времени, объединяющая возможности масштаба 671B и ускоренный отклик.", "deepseek-r1-online.description": "Полная версия DeepSeek R1 с 671B параметрами и поиском в интернете в реальном времени, обеспечивающая улучшенное понимание и генерацию.", "deepseek-r1.description": "DeepSeek-R1 использует данные холодного старта до этапа RL и демонстрирует сопоставимую с OpenAI-o1 производительность в математике, программировании и логическом мышлении.", - "deepseek-reasoner.description": "Алиас совместимости для режима мышления DeepSeek V4 Flash. Планируется к устареванию — используйте DeepSeek V4 Flash.", + "deepseek-reasoner.description": "Модель DeepSeek, ориентированная на выполнение сложных логических задач.", "deepseek-v2.description": "DeepSeek V2 — эффективная модель MoE для экономичной обработки.", "deepseek-v2:236b.description": "DeepSeek V2 236B — модель DeepSeek, ориентированная на программирование, с высокой способностью к генерации кода.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 — модель MoE с 671B параметрами, выделяющаяся в программировании, технических задачах, понимании контекста и работе с длинными текстами.", @@ -491,8 +490,6 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 — модель генерации изображений от ByteDance Seed, поддерживающая ввод текста и изображений с высококачественной и управляемой генерацией. Генерирует изображения по текстовым подсказкам.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 — это последняя мультимодальная модель изображений от ByteDance, объединяющая возможности преобразования текста в изображение, изображения в изображение и пакетной генерации изображений, а также включающая здравый смысл и способности к рассуждению. По сравнению с предыдущей версией 4.0, она обеспечивает значительно улучшенное качество генерации, лучшую согласованность редактирования и слияние нескольких изображений. Модель предлагает более точный контроль над визуальными деталями, естественно воспроизводя мелкий текст и лица, а также достигает более гармоничного макета и цветовой палитры, улучшая общую эстетику.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite — это последняя модель генерации изображений от ByteDance. Впервые она интегрирует возможности онлайн-поиска, что позволяет использовать информацию в реальном времени и улучшать актуальность создаваемых изображений. Интеллект модели также был обновлен, что позволяет точно интерпретировать сложные инструкции и визуальный контент. Кроме того, она предлагает улучшенное покрытие глобальных знаний, согласованность ссылок и качество генерации в профессиональных сценариях, лучше удовлетворяя потребности корпоративного визуального творчества.", - "dreamina-seedance-2-0-260128.description": "Seedance 2.0 от ByteDance — самая мощная модель генерации видео, поддерживающая мультимодальную генерацию видео по ссылке, редактирование видео, расширение видео, текст-видео и изображение-видео с синхронизированным аудио.", - "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast от ByteDance предлагает те же возможности, что и Seedance 2.0, с более высокой скоростью генерации и конкурентной ценой.", "emohaa.description": "Emohaa — модель для поддержки психического здоровья с профессиональными навыками консультирования, помогающая пользователям разобраться в эмоциональных проблемах.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B — легковесная модель с открытым исходным кодом для локального и кастомизированного развертывания.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview — модель с контекстом 8K для предварительной оценки возможностей ERNIE 4.5.", @@ -506,6 +503,7 @@ "ernie-5.0-thinking-latest.description": "Wenxin 5.0 Thinking — флагманская модель с нативной полной мультимодальностью, объединяющая текст, изображение, аудио и видео. Обеспечивает широкие улучшения для сложных задач Вопрос-Ответ, творчества и агентов.", "ernie-5.0-thinking-preview.description": "Wenxin 5.0 Thinking Preview — предварительная версия флагманской мультимодальной модели с объединением текста, изображения, аудио и видео. Обеспечивает широкие улучшения для сложных задач Вопрос-Ответ, творчества и агентов.", "ernie-5.0.description": "ERNIE 5.0 — модель нового поколения в серии ERNIE, являющаяся изначально мультимодальной. Она использует унифицированный подход к моделированию текста, изображений, аудио и видео, обеспечивая комплексные мультимодальные возможности. Базовые способности значительно улучшены, модель демонстрирует высокие результаты в бенчмарках. Особенно сильна в мультимодальном понимании, следовании инструкциям, творческом письме, фактической точности, планировании и использовании инструментов.", + "ernie-5.1.description": "ERNIE 5.1 — последняя модель серии ERNIE, с комплексными улучшениями базовых возможностей. Она демонстрирует значительные улучшения в таких областях, как агенты, обработка знаний, рассуждение и глубокий поиск. В этом выпуске используется раздельная полностью асинхронная архитектура обучения с подкреплением, специально разработанная для решения ключевых задач в эволюции больших моделей к автономному принятию решений агентами, включая числовые несоответствия между обучением и выводом, низкую эффективность использования гетерогенных вычислительных ресурсов и глобальные проблемы, вызванные эффектами длинного хвоста. Кроме того, применяются методы крупномасштабного пост-обучения агентов для дальнейшего повышения возможностей модели и её способности к обобщению. Благодаря трехэтапной совместной структуре, включающей процессы окружающей среды, экспертов и слияния, подход не только обеспечивает эффективность обучения, но и значительно улучшает стабильность и производительность модели при выполнении сложных задач.", "ernie-char-fiction-8k-preview.description": "ERNIE Character Fiction 8K Preview — предварительная модель для создания персонажей и сюжетов, предназначенная для оценки и тестирования.", "ernie-char-fiction-8k.description": "ERNIE Character Fiction 8K — модель персонажа для написания романов и создания сюжетов, подходит для генерации длинных историй.", "ernie-image-turbo.description": "ERNIE-Image — 8-миллиардная текст‑в‑изображение модель, разработанная Baidu. Она входит в число лучших по многим бенчмаркам, занимая первое место в SuperCLUE в Китае и лидируя в категории открытых моделей.", @@ -517,8 +515,7 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K — быстрая модель мышления с контекстом 32K для сложного рассуждения и многотурового общения.", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview — предварительная версия модели мышления для оценки и тестирования.", "ernie-x1.1.description": "ERNIE X1.1 — это предварительная версия модели мышления для оценки и тестирования.", - "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5, созданная командой ByteDance Seed, поддерживает редактирование и композицию нескольких изображений. Обладает улучшенной согласованностью объектов, точным следованием инструкциям, пониманием пространственной логики, эстетическим выражением, дизайном постеров и логотипов с высокоточной генерацией текст-изображений.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, созданная ByteDance Seed, поддерживает текстовые и визуальные входные данные для высококонтролируемой генерации изображений высокого качества на основе подсказок.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 — модель генерации изображений от ByteDance Seed, поддерживающая ввод текста и изображений с высококонтролируемой, качественной генерацией изображений. Она создает изображения на основе текстовых запросов.", "fal-ai/flux-kontext/dev.description": "Модель FLUX.1, ориентированная на редактирование изображений, поддерживает ввод текста и изображений.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] принимает текст и эталонные изображения, позволяя выполнять локальные правки и сложные глобальные трансформации сцены.", "fal-ai/flux/krea.description": "Flux Krea [dev] — модель генерации изображений с эстетическим уклоном в сторону более реалистичных и естественных изображений.", @@ -526,8 +523,8 @@ "fal-ai/hunyuan-image/v3.description": "Мощная нативная мультимодальная модель генерации изображений.", "fal-ai/imagen4/preview.description": "Модель генерации изображений высокого качества от Google.", "fal-ai/nano-banana.description": "Nano Banana — новейшая, самая быстрая и эффективная нативная мультимодальная модель от Google, поддерживающая генерацию и редактирование изображений в диалоговом режиме.", - "fal-ai/qwen-image-edit.description": "Профессиональная модель редактирования изображений от команды Qwen, поддерживающая семантические и визуальные изменения, точное редактирование текста на китайском/английском языках, перенос стиля, вращение и многое другое.", - "fal-ai/qwen-image.description": "Мощная модель генерации изображений от команды Qwen с сильной поддержкой китайского текста и разнообразными визуальными стилями.", + "fal-ai/qwen-image-edit.description": "Профессиональная модель редактирования изображений от команды Qwen, поддерживающая семантические и визуальные правки, точное редактирование текста на китайском и английском языках, а также высококачественные правки, такие как перенос стиля и вращение объектов.", + "fal-ai/qwen-image.description": "Мощная модель генерации изображений от команды Qwen с впечатляющим рендерингом текста на китайском языке и разнообразными визуальными стилями.", "flux-1-schnell.description": "Модель преобразования текста в изображение с 12 миллиардами параметров от Black Forest Labs, использующая латентную диффузию с дистилляцией для генерации качественных изображений за 1–4 шага. Конкурирует с закрытыми аналогами и распространяется по лицензии Apache-2.0 для личного, исследовательского и коммерческого использования.", "flux-dev.description": "Открытая исследовательская модель генерации изображений, оптимизированная для некоммерческих инновационных исследований.", "flux-kontext-max.description": "Передовая генерация и редактирование изображений с учётом контекста, объединяющая текст и изображения для точных и согласованных результатов.", @@ -569,7 +566,7 @@ "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) — модель генерации изображений от Google, также поддерживающая мультимодальный чат.", "gemini-3-pro-preview.description": "Gemini 3 Pro — самая мощная агентная модель от Google с поддержкой визуализации и глубокой интерактивности, основанная на передовых возможностях рассуждения.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) — это самая быстрая нативная модель генерации изображений от Google с поддержкой мышления, генерации и редактирования изображений в диалоговом режиме.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) обеспечивает качество изображений уровня Pro с высокой скоростью генерации и поддержкой мультимодального чата.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) — самая быстрая модель генерации изображений от Google с поддержкой размышлений, генерации и редактирования изображений в рамках диалога.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview — самая экономичная мультимодальная модель от Google, оптимизированная для задач с высоким объемом, перевода и обработки данных.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite — самая экономичная мультимодальная модель от Google, оптимизированная для задач с высоким объемом, перевода и обработки данных.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview улучшает Gemini 3 Pro с расширенными возможностями рассуждений и добавляет поддержку среднего уровня мышления.", @@ -734,8 +731,6 @@ "grok-4-fast-reasoning.description": "Мы рады представить Grok 4 Fast — наш последний прогресс в области экономичных моделей рассуждения.", "grok-4.20-0309-non-reasoning.description": "Вариант без рассуждений для простых задач.", "grok-4.20-0309-reasoning.description": "Интеллектуальная, сверхбыстрая модель, которая размышляет перед тем, как ответить.", - "grok-4.20-beta-0309-non-reasoning.description": "Вариант без мышления для простых случаев использования.", - "grok-4.20-beta-0309-reasoning.description": "Интеллектуальная, сверхбыстрая модель, которая размышляет перед ответом.", "grok-4.20-multi-agent-0309.description": "Команда из 4 или 16 агентов. Превосходна для исследовательских задач. Пока не поддерживает клиентские инструменты. Поддерживает только серверные инструменты xAI (например, X Search, Web Search) и удалённые MCP-инструменты.", "grok-4.3.description": "Самая правдолюбивая крупная языковая модель в мире.", "grok-4.description": "Последняя флагманская модель Grok с непревзойденной производительностью в языке, математике и рассуждениях — настоящий универсал. В настоящее время указывает на grok-4-0709; из-за ограниченных ресурсов временно цена на 10% выше официальной и ожидается возвращение к официальной цене позже.", @@ -1220,8 +1215,6 @@ "qwq.description": "QwQ — модель логического вывода из семейства Qwen. По сравнению со стандартными моделями, обученными на инструкциях, она обладает способностями к мышлению и логике, которые значительно улучшают производительность на сложных задачах. QwQ-32B — среднеразмерная модель, успешно конкурирующая с ведущими моделями, такими как DeepSeek-R1 и o1-mini.", "qwq_32b.description": "Среднеразмерная модель логического вывода из семейства Qwen. По сравнению со стандартными моделями, обученными на инструкциях, способности QwQ к мышлению и логике значительно повышают производительность на сложных задачах.", "r1-1776.description": "R1-1776 — дообученный вариант DeepSeek R1, предназначенный для предоставления нецензурированной, объективной и достоверной информации.", - "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro от ByteDance поддерживает текст-видео, изображение-видео (первый кадр, первый+последний кадр) и генерацию аудио, синхронизированного с визуальными эффектами.", - "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite от BytePlus предлагает генерацию с дополнением веб-поиска для получения актуальной информации, улучшенную интерпретацию сложных подсказок и повышенную согласованность ссылок для профессионального визуального творчества.", "solar-mini-ja.description": "Solar Mini (Ja) расширяет возможности Solar Mini с акцентом на японский язык, сохраняя при этом высокую эффективность и производительность на английском и корейском.", "solar-mini.description": "Solar Mini — компактная LLM-модель, превосходящая GPT-3.5, с мощной многоязычной поддержкой английского и корейского языков, предлагающая эффективное решение с малым объемом.", "solar-pro.description": "Solar Pro — интеллектуальная LLM-модель от Upstage, ориентированная на следование инструкциям на одном GPU, с результатами IFEval выше 80. В настоящее время поддерживает английский язык; полный релиз с расширенной языковой поддержкой и увеличенным контекстом запланирован на ноябрь 2024 года.", diff --git a/locales/ru-RU/onboarding.json b/locales/ru-RU/onboarding.json index 65d7784847..c94e55fd42 100644 --- a/locales/ru-RU/onboarding.json +++ b/locales/ru-RU/onboarding.json @@ -27,6 +27,13 @@ "agent.layout.skipConfirm.ok": "Пропустить пока", "agent.layout.skipConfirm.title": "Пропустить вводный этап сейчас?", "agent.layout.switchMessage": "Не в настроении сегодня? Можно переключиться на {{mode}} или {{skip}}.", + "agent.layout.switchMessageClassic": "Не нравится сегодня? Вы можете переключиться на {{mode}}.", + "agent.messenger.cta.discord": "Добавить в Discord", + "agent.messenger.cta.slack": "Добавить в Slack", + "agent.messenger.cta.telegram": "Открыть в Telegram", + "agent.messenger.subtitle": "Общайтесь с вашим агентом в Telegram, Slack или Discord", + "agent.messenger.telegramQrCaption": "Сканируйте камерой телефона", + "agent.messenger.title": "Держите меня рядом, где бы вы ни общались", "agent.modeSwitch.agent": "Разговорный", "agent.modeSwitch.classic": "Классический", "agent.modeSwitch.collapse": "Свернуть", @@ -68,6 +75,13 @@ "agent.wrapUp.confirm.content": "Я сохраню всё, что мы обсудили. Вы всегда можете вернуться и продолжить разговор позже.", "agent.wrapUp.confirm.ok": "Завершить сейчас", "agent.wrapUp.confirm.title": "Завершить ознакомление сейчас?", + "agentPicker.allCategories": "Все", + "agentPicker.continue": "Продолжить", + "agentPicker.skip": "Пропустить на сейчас", + "agentPicker.subtitle": "Добавьте несколько в свою библиотеку сейчас — откройте больше в любое время позже.", + "agentPicker.title": "Давайте добавим несколько агентов в вашу библиотеку", + "agentPicker.title2": "Выберите тех, кто соответствует вашему стилю работы", + "agentPicker.title3": "Вы всегда можете добавить больше позже — начните с нескольких", "back": "Назад", "finish": "Начать", "interests.area.business": "Бизнес и стратегия", diff --git a/locales/ru-RU/plugin.json b/locales/ru-RU/plugin.json index 233db06c6b..f2c4f974b4 100644 --- a/locales/ru-RU/plugin.json +++ b/locales/ru-RU/plugin.json @@ -39,8 +39,8 @@ "builtins.lobe-agent-documents.inspector.docCount": "{{count}} документов", "builtins.lobe-agent-documents.inspector.opsCount": "{{count}} операций", "builtins.lobe-agent-documents.inspector.opsResult": "{{success}}/{{total}} операций", - "builtins.lobe-agent-documents.inspector.target.agent": "в агенте", - "builtins.lobe-agent-documents.inspector.target.currentTopic": "в теме", + "builtins.lobe-agent-documents.inspector.scope.agent": "в агенте", + "builtins.lobe-agent-documents.inspector.scope.currentTopic": "в теме", "builtins.lobe-agent-documents.title": "Документы агента", "builtins.lobe-agent-management.apiName.callAgent": "Вызвать агента", "builtins.lobe-agent-management.apiName.createAgent": "Создать агента", @@ -90,6 +90,14 @@ "builtins.lobe-agent.title": "Агент Lobe", "builtins.lobe-claude-code.agent.instruction": "Инструкция", "builtins.lobe-claude-code.agent.result": "Результат", + "builtins.lobe-claude-code.task.createLabel": "Создание задачи: ", + "builtins.lobe-claude-code.task.getLabel": "Просмотр задачи #{{taskId}}", + "builtins.lobe-claude-code.task.listLabel": "Список задач", + "builtins.lobe-claude-code.task.updateCompleted": "Завершено", + "builtins.lobe-claude-code.task.updateDeleted": "Удалено", + "builtins.lobe-claude-code.task.updateInProgress": "Начато", + "builtins.lobe-claude-code.task.updateLabel": "Обновление задачи #{{taskId}}", + "builtins.lobe-claude-code.task.updatePending": "Сброс", "builtins.lobe-claude-code.todoWrite.allDone": "Все задачи выполнены", "builtins.lobe-claude-code.todoWrite.currentStep": "Текущий шаг", "builtins.lobe-claude-code.todoWrite.todos": "Задачи", diff --git a/locales/ru-RU/providers.json b/locales/ru-RU/providers.json index d61fd0b5d4..14c784b315 100644 --- a/locales/ru-RU/providers.json +++ b/locales/ru-RU/providers.json @@ -33,7 +33,6 @@ "jina.description": "Основанная в 2020 году, Jina AI — ведущая компания в области поискового ИИ. Её стек включает векторные модели, переоценщики и малые языковые модели для создания надежных генеративных и мультимодальных поисковых приложений.", "kimicodingplan.description": "Kimi Code от Moonshot AI предоставляет доступ к моделям Kimi, включая K2.5, для выполнения задач кодирования.", "lmstudio.description": "LM Studio — это настольное приложение для разработки и экспериментов с LLM на вашем компьютере.", - "lobehub.description": "LobeHub Cloud использует официальные API для доступа к моделям ИИ и измеряет использование с помощью Кредитов, связанных с токенами моделей.", "longcat.description": "LongCat — это серия больших моделей генеративного ИИ, разработанных Meituan. Она предназначена для повышения внутренней производительности предприятия и создания инновационных приложений благодаря эффективной вычислительной архитектуре и мощным мультимодальным возможностям.", "minimax.description": "Основанная в 2021 году, MiniMax разрабатывает универсальные ИИ-модели на базе мультимодальных основ, включая текстовые модели с триллионами параметров, речевые и визуальные модели, а также приложения, такие как Hailuo AI.", "minimaxcodingplan.description": "План токенов MiniMax предоставляет доступ к моделям MiniMax, включая M2.7, для выполнения задач кодирования по подписке с фиксированной оплатой.", diff --git a/locales/ru-RU/subscription.json b/locales/ru-RU/subscription.json index 86a8054040..94e8f6573c 100644 --- a/locales/ru-RU/subscription.json +++ b/locales/ru-RU/subscription.json @@ -55,6 +55,12 @@ "credits.autoTopUp.toggle": "Включить автоматическое пополнение", "credits.autoTopUp.upgradeHint": "Подпишитесь на платный план, чтобы включить автоматическое пополнение", "credits.autoTopUp.validation.targetMustExceedThreshold": "Целевой баланс должен быть больше порога", + "credits.costEstimateHint.desc": "Показывать легкое предупреждение перед отправкой, когда предполагаемая стоимость модели достигает вашего порога", + "credits.costEstimateHint.saveError": "Не удалось сохранить настройки предупреждения о предполагаемой стоимости", + "credits.costEstimateHint.saveSuccess": "Настройки предупреждения о предполагаемой стоимости сохранены", + "credits.costEstimateHint.threshold": "Порог предупреждения", + "credits.costEstimateHint.title": "Предупреждение о предполагаемой стоимости", + "credits.costEstimateHint.validation.threshold": "Порог должен быть больше или равен 0", "credits.packages.auto": "Авто", "credits.packages.charged": "Списано ${{amount}}", "credits.packages.expired": "Истёк", diff --git a/locales/ru-RU/tool.json b/locales/ru-RU/tool.json index b215b6e31e..4bd5566bcf 100644 --- a/locales/ru-RU/tool.json +++ b/locales/ru-RU/tool.json @@ -40,6 +40,14 @@ "agentMarketplace.render.alreadyInLibraryTag": "Уже в библиотеке", "agentMarketplace.render.alreadyInLibrary_one": "{{count}} уже в библиотеке", "agentMarketplace.render.alreadyInLibrary_other": "{{count}} уже в библиотеке", + "claudeCode.askUserQuestion.escape.back": "Вернуться к вариантам", + "claudeCode.askUserQuestion.escape.enter": "Или введите напрямую", + "claudeCode.askUserQuestion.escape.placeholder": "Введите ваш ответ здесь…", + "claudeCode.askUserQuestion.multiSelectTag": "(множественный выбор)", + "claudeCode.askUserQuestion.skip": "Пропустить", + "claudeCode.askUserQuestion.submit": "Отправить", + "claudeCode.askUserQuestion.timeExpired": "Время истекло — используется вариант 1 для каждого вопроса.", + "claudeCode.askUserQuestion.timeRemaining": "Оставшееся время: {{time}} · на вопросы без ответа по истечении времени будет выбран вариант 1.", "codeInterpreter-legacy.error": "Ошибка выполнения", "codeInterpreter-legacy.executing": "Выполнение...", "codeInterpreter-legacy.files": "Файлы:", diff --git a/locales/ru-RU/topic.json b/locales/ru-RU/topic.json index 473d49c94c..c8aedb8759 100644 --- a/locales/ru-RU/topic.json +++ b/locales/ru-RU/topic.json @@ -56,6 +56,7 @@ "renameModal.title": "Переименовать тему", "searchPlaceholder": "Поиск тем...", "searchResultEmpty": "Результаты не найдены.", + "sidebar.title": "Темы", "taskManager.agent": "Агент задач", "taskManager.welcome": "Спросите меня о ваших задачах", "temp": "Временный", diff --git a/locales/tr-TR/chat.json b/locales/tr-TR/chat.json index 7595afb7bb..5a99e75d9c 100644 --- a/locales/tr-TR/chat.json +++ b/locales/tr-TR/chat.json @@ -220,6 +220,13 @@ "inbox.title": "Lobe AI", "input.addAi": "Yapay Zeka mesajı ekle", "input.addUser": "Kullanıcı mesajı ekle", + "input.costEstimate.creditsPerMillionTokens": "{{credits}} kredi/M token", + "input.costEstimate.hint": "Tahmini maliyet: ~{{credits}} kredi", + "input.costEstimate.inputLabel": "Girdi", + "input.costEstimate.outputLabel": "Çıktı", + "input.costEstimate.settingsLink": "Uyarı eşiğini ayarla", + "input.costEstimate.tokenCount": "~{{tokens}} token", + "input.costEstimate.tooltip": "Mevcut bağlam, araçlar ve model fiyatlandırmasına göre tahmin edilmiştir. Gerçek maliyet değişebilir.", "input.disclaimer": "Ajanlar hata yapabilir. Kritik bilgiler için kendi yargınızı kullanın.", "input.errorMsg": "Gönderme başarısız: {{errorMsg}}. Tekrar deneyin veya daha sonra yeniden gönderin.", "input.more": "Daha Fazla", @@ -779,6 +786,7 @@ "upload.preview.prepareTasks": "Parçalara ayrılıyor...", "upload.preview.status.pending": "Yüklemeye hazırlanıyor...", "upload.preview.status.processing": "Dosya işleniyor...", + "upload.validation.unsupportedFileType": "Desteklenmeyen dosya türü: {{files}}. Desteklenen görseller: JPG, PNG, GIF, WebP. Desteklenen belgeler: PDF, Word, Excel, PowerPoint, Markdown, metin, CSV, JSON ve kod dosyaları.", "upload.validation.videoSizeExceeded": "Video dosya boyutu 20MB'ı geçmemelidir. Mevcut dosya boyutu {{actualSize}}.", "viewMode.fullWidth": "Tam Genişlik", "viewMode.normal": "Standart", @@ -894,6 +902,8 @@ "workingPanel.localFile.closeOther": "Diğerlerini Kapat", "workingPanel.localFile.closeRight": "Sağdakileri Kapat", "workingPanel.localFile.error": "Bu dosya yüklenemedi", + "workingPanel.localFile.preview.raw": "Ham", + "workingPanel.localFile.preview.render": "Önizleme", "workingPanel.localFile.truncated": "Dosya önizlemesi {{limit}} karakterle sınırlandırıldı", "workingPanel.progress": "Progress", "workingPanel.progress.allCompleted": "All tasks completed", @@ -959,6 +969,8 @@ "workingPanel.review.viewMode.unified": "Birleşik görünüme geç", "workingPanel.review.wordWrap.disable": "Kelime kaydırmayı devre dışı bırak", "workingPanel.review.wordWrap.enable": "Kelime kaydırmayı etkinleştir", + "workingPanel.skills.empty": "Bu projede beceri bulunamadı", + "workingPanel.skills.title": "Beceriler", "workingPanel.space": "Boşluk", "workingPanel.title": "Working Panel", "you": "Sen", diff --git a/locales/tr-TR/components.json b/locales/tr-TR/components.json index e7ff131cb0..f97bf7e6e6 100644 --- a/locales/tr-TR/components.json +++ b/locales/tr-TR/components.json @@ -121,6 +121,16 @@ "ModelSwitchPanel.detail.context": "Bağlam Uzunluğu", "ModelSwitchPanel.detail.pricing": "Fiyatlandırma", "ModelSwitchPanel.detail.pricing.cachedInput": "Önbelleğe Alınmış Girdi ${{amount}}/M", + "ModelSwitchPanel.detail.pricing.credits.cachedInput": "Önbelleğe alınmış giriş {{amount}} kredi/M jeton", + "ModelSwitchPanel.detail.pricing.credits.image": "kredi/img", + "ModelSwitchPanel.detail.pricing.credits.input": "Giriş {{amount}} kredi/M jeton", + "ModelSwitchPanel.detail.pricing.credits.megapixel": "kredi/MP", + "ModelSwitchPanel.detail.pricing.credits.millionCharacters": "kredi/M karakter", + "ModelSwitchPanel.detail.pricing.credits.millionTokens": "kredi/M jeton", + "ModelSwitchPanel.detail.pricing.credits.output": "Çıkış {{amount}} kredi/M jeton", + "ModelSwitchPanel.detail.pricing.credits.perImage": "~ {{amount}} kredi / resim", + "ModelSwitchPanel.detail.pricing.credits.perVideo": "~ {{amount}} kredi / video", + "ModelSwitchPanel.detail.pricing.credits.second": "kredi/sn", "ModelSwitchPanel.detail.pricing.group.audio": "Ses", "ModelSwitchPanel.detail.pricing.group.image": "Görsel", "ModelSwitchPanel.detail.pricing.group.text": "Metin", diff --git a/locales/tr-TR/editor.json b/locales/tr-TR/editor.json index 9626b865c2..8314f01de4 100644 --- a/locales/tr-TR/editor.json +++ b/locales/tr-TR/editor.json @@ -1,8 +1,10 @@ { "actionTag.category.command": "Komut", + "actionTag.category.projectSkill": "Proje becerisi", "actionTag.category.skill": "Beceri", "actionTag.category.tool": "Araç", "actionTag.tooltip.command": "Göndermeden önce istemci tarafında bir slash komutu çalıştırır.", + "actionTag.tooltip.projectSkill": "Ajanın CLI'sinin eşleşen proje becerisini çalıştırması için bir eğik çizgi çağrısı olarak gönderilir.", "actionTag.tooltip.skill": "Bu istek için yeniden kullanılabilir bir beceri paketini yükler.", "actionTag.tooltip.tool": "Kullanıcının bu istek için açıkça seçtiği bir aracı işaretler.", "actions.expand.off": "Daralt", diff --git a/locales/tr-TR/home.json b/locales/tr-TR/home.json index 8f97503187..ebbb5fcdf7 100644 --- a/locales/tr-TR/home.json +++ b/locales/tr-TR/home.json @@ -11,6 +11,19 @@ "brief.action.ignore": "Yoksay", "brief.action.retry": "Yeniden dene", "brief.addFeedback": "Geri bildirim paylaş", + "brief.agentSignal.selfReview.applied.heading": "Güncellendi", + "brief.agentSignal.selfReview.applied.summary": "{{count}} rüya güncellemesi uygulandı.", + "brief.agentSignal.selfReview.applied.summary_plural": "{{count}} rüya güncellemesi uygulandı.", + "brief.agentSignal.selfReview.applied.title": "Rüya güncellenmiş kaynaklar", + "brief.agentSignal.selfReview.error.heading": "Sorun", + "brief.agentSignal.selfReview.error.summary": "Bu rüya sırasında bazı işler tamamlanamadı.", + "brief.agentSignal.selfReview.error.title": "Rüya bir sorunla karşılaştı", + "brief.agentSignal.selfReview.ideas.summary": "Gelecekteki inceleme için rüya notları kaydedildi.", + "brief.agentSignal.selfReview.ideas.title": "Rüya notları", + "brief.agentSignal.selfReview.proposal.heading": "Öneri", + "brief.agentSignal.selfReview.proposal.summary": "{{count}} rüya önerisi incelemenizi bekliyor.", + "brief.agentSignal.selfReview.proposal.summary_plural": "{{count}} rüya önerisi incelemenizi bekliyor.", + "brief.agentSignal.selfReview.proposal.title": "Rüya önerisi inceleme bekliyor", "brief.collapse": "Daha az göster", "brief.commentPlaceholder": "Geri bildiriminizi paylaşın...", "brief.commentSubmit": "Geri bildirimi gönder", diff --git a/locales/tr-TR/hotkey.json b/locales/tr-TR/hotkey.json index b1479a67e6..e787f720ee 100644 --- a/locales/tr-TR/hotkey.json +++ b/locales/tr-TR/hotkey.json @@ -1,8 +1,6 @@ { "addUserMessage.desc": "Mevcut girdiyi kullanıcı mesajı olarak ekle, ancak oluşturmayı tetikleme", "addUserMessage.title": "Kullanıcı Mesajı Ekle", - "clearCurrentMessages.desc": "Geçerli konuşmadaki mesajları ve yüklenen dosyaları temizle", - "clearCurrentMessages.title": "Konuşma Mesajlarını Temizle", "commandPalette.desc": "Özelliklere hızlı erişim için genel komut paletini aç", "commandPalette.title": "Komut Paleti", "deleteAndRegenerateMessage.desc": "Son mesajı sil ve yeniden oluştur", diff --git a/locales/tr-TR/models.json b/locales/tr-TR/models.json index e763c6a197..564ec6e173 100644 --- a/locales/tr-TR/models.json +++ b/locales/tr-TR/models.json @@ -106,7 +106,6 @@ "MiniMax-Hailuo-2.3.description": "Vücut hareketi, fiziksel gerçekçilik ve talimat takibinde kapsamlı yükseltmelerle yepyeni bir video üretim modeli.", "MiniMax-M1.description": "80K düşünce zinciri ve 1M giriş desteğiyle üst düzey modellerle karşılaştırılabilir performans sunan yeni bir yerli akıl yürütme modeli.", "MiniMax-M2-Stable.description": "Ticari kullanım için daha yüksek eşzamanlılık sunan, verimli kodlama ve ajan iş akışları için tasarlanmıştır.", - "MiniMax-M2.1-Lightning.description": "Güçlü çok dilli programlama yetenekleriyle daha hızlı ve daha verimli çıkarım.", "MiniMax-M2.1-highspeed.description": "Güçlü çok dilli programlama yetenekleri, kapsamlı olarak geliştirilmiş bir programlama deneyimi. Daha hızlı ve daha verimli.", "MiniMax-M2.1.description": "MiniMax-M2.1, MiniMax tarafından geliştirilen amiral gemisi açık kaynak büyük modeldir ve karmaşık gerçek dünya görevlerini çözmeye odaklanır. Temel güçlü yönleri çok dilli programlama yetenekleri ve bir Ajan olarak karmaşık görevleri çözme becerisidir.", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: M2.5 ile aynı performans, ancak daha hızlı çıkarım.", @@ -315,13 +314,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku, Anthropic’in en hızlı ve en kompakt modelidir; anında yanıtlar için hızlı ve doğru performans sunar.", "claude-3-opus-20240229.description": "Claude 3 Opus, karmaşık görevler için Anthropic’in en güçlü modelidir; performans, zeka, akıcılık ve anlama konularında üstündür.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet, kurumsal iş yükleri için zeka ve hızı dengeler; düşük maliyetle yüksek fayda ve güvenilir büyük ölçekli dağıtım sunar.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5, Anthropic'in en hızlı ve en akıllı Haiku modeli olup, yıldırım hızında ve genişletilmiş düşünme yeteneğine sahiptir.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5, Anthropic'in en hızlı ve en akıllı Haiku modeli olup, yıldırım hızında ve gelişmiş akıl yürütme yeteneklerine sahiptir.", "claude-haiku-4-5.description": "Anthropic’in Claude Haiku 4.5 modeli — gelişmiş akıl yürütme ve görsel yeteneklere sahip yeni nesil Haiku.", "claude-haiku-4.5.description": "Claude Haiku 4.5, Anthropic'in en hızlı ve en akıllı Haiku modeli olup, yıldırım hızında ve gelişmiş akıl yürütme yeteneklerine sahiptir.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking, akıl yürütme sürecini görünür şekilde ortaya koyabilen gelişmiş bir varyanttır.", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1, Anthropic'in en yeni ve en yetenekli modeli olup, performans, zeka, akıcılık ve anlama konularında üstün başarı sağlar.", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1, Anthropic'in en yeni ve en yetenekli modeli olup, son derece karmaşık görevlerde performans, zeka, akıcılık ve anlayış açısından üstünlük sağlar.", "claude-opus-4-1.description": "Anthropic’in Claude Opus 4.1 modeli — derin analiz yeteneklerine sahip üst seviye akıl yürütme modeli.", - "claude-opus-4-20250514.description": "Claude Opus 4, Anthropic'in en güçlü modeli olup, performans, zeka, akıcılık ve anlama konularında üstün başarı sağlar.", + "claude-opus-4-20250514.description": "Claude Opus 4, Anthropic'in son derece karmaşık görevler için en güçlü modeli olup, performans, zeka, akıcılık ve kavrama açısından üstünlük sağlar.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5, Anthropic’in amiral gemisi modelidir; olağanüstü zeka ile ölçeklenebilir performansı birleştirir. En yüksek kaliteli yanıtlar ve akıl yürütme gerektiren karmaşık görevler için idealdir.", "claude-opus-4-5.description": "Anthropic’in Claude Opus 4.5 modeli — üst düzey akıl yürütme ve kodlama yeteneklerine sahip amiral gemisi model.", "claude-opus-4-6.description": "Anthropic’in Claude Opus 4.6 modeli — 1M bağlam penceresi ve gelişmiş akıl yürütme yetenekleriyle amiral gemisi sürümü.", @@ -330,7 +329,7 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6, Anthropic'in ajanlar oluşturma ve kodlama için en akıllı modelidir.", "claude-opus-4.6.description": "Claude Opus 4.6, Anthropic'in ajanlar oluşturma ve kodlama için en akıllı modelidir.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking, anında yanıtlar veya adım adım düşünme süreçleri üretebilir.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4, Anthropic'in bugüne kadarki en akıllı modeli olup, API kullanıcıları için ince ayarlı kontrol ile anında yanıtlar veya adım adım genişletilmiş düşünme sunar.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4, anında yanıtlar veya görünür bir süreçle adım adım düşünme yeteneği sunabilir.", "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5, Anthropic'in bugüne kadarki en akıllı modelidir.", "claude-sonnet-4-5.description": "Anthropic’in Claude Sonnet 4.5 modeli — geliştirilmiş kodlama performansıyla güçlendirilmiş Sonnet sürümü.", "claude-sonnet-4-6.description": "Anthropic’in Claude Sonnet 4.6 modeli — üstün kodlama ve araç kullanımıyla en yeni Sonnet.", @@ -404,7 +403,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B), derin dil anlama ve etkileşim sunan yenilikçi bir modeldir.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1, karmaşık akıl yürütme ve düşünce zinciriyle derin analiz görevleri için geliştirilmiş yeni nesil bir modeldir.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2, daha güçlü karmaşık akıl yürütme ve düşünce zinciri yeteneklerine sahip yeni nesil bir akıl yürütme modelidir.", - "deepseek-chat.description": "DeepSeek V4 Flash düşünme modu için uyumluluk takma adı. Kullanımdan kaldırılması planlanıyor — bunun yerine DeepSeek V4 Flash kullanın.", + "deepseek-chat.description": "Genel ve kod yeteneklerini birleştiren yeni bir açık kaynak modeli. Sohbet modelinin genel diyaloğunu ve kodlayıcı modelinin güçlü kodlama yeteneklerini korur, daha iyi tercih uyumu sağlar. DeepSeek-V2.5 ayrıca yazma ve talimatları takip etme yeteneklerini geliştirir.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B, 2T token (%%87 kod, %%13 Çince/İngilizce metin) ile eğitilmiş bir kodlama dil modelidir. 16K bağlam penceresi ve ortadan doldurma görevleri sunar; proje düzeyinde kod tamamlama ve kod parçacığı doldurma sağlar.", "deepseek-coder-v2.description": "DeepSeek Coder V2, GPT-4 Turbo ile karşılaştırılabilir güçlü performansa sahip açık kaynaklı bir MoE kod modeli.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2, GPT-4 Turbo ile karşılaştırılabilir güçlü performansa sahip açık kaynaklı bir MoE kod modeli.", @@ -426,7 +425,7 @@ "deepseek-r1-fast-online.description": "Gerçek zamanlı web aramasıyla 671B ölçekli yetenek ve hızlı yanıtları birleştiren DeepSeek R1 hızlı tam sürüm.", "deepseek-r1-online.description": "671B parametreli ve gerçek zamanlı web aramasına sahip DeepSeek R1 tam sürüm; güçlü anlama ve üretim sunar.", "deepseek-r1.description": "DeepSeek-R1, RL öncesi soğuk başlangıç verileri kullanır ve matematik, kodlama ve akıl yürütmede OpenAI-o1 ile karşılaştırılabilir performans sunar.", - "deepseek-reasoner.description": "DeepSeek V4 Flash düşünme modu için uyumluluk takma adı. Kullanımdan kaldırılması planlanıyor — bunun yerine DeepSeek V4 Flash kullanın.", + "deepseek-reasoner.description": "Karmaşık mantıksal akıl yürütme görevlerine odaklanan bir DeepSeek akıl yürütme modeli.", "deepseek-v2.description": "DeepSeek V2, maliyet etkin işlem için verimli bir MoE modelidir.", "deepseek-v2:236b.description": "DeepSeek V2 236B, güçlü kod üretimi sunan DeepSeek’in kod odaklı modelidir.", "deepseek-v3-0324.description": "DeepSeek-V3-0324, programlama ve teknik yetenek, bağlam anlama ve uzun metin işleme konularında öne çıkan 671B parametreli bir MoE modelidir.", @@ -491,8 +490,6 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0, ByteDance Seed tarafından geliştirilen bir görsel üretim modelidir; metin ve görsel girişlerini destekler, yüksek kaliteli ve kontrol edilebilir görseller üretir. Metin istemlerinden görseller oluşturur.", "doubao-seedream-4-5-251128.description": "Seedream 4.5, ByteDance'in en son çok modlu görüntü modelidir. Metinden görüntüye, görüntüden görüntüye ve toplu görüntü oluşturma yeteneklerini entegre ederken, sağduyu ve akıl yürütme yeteneklerini içerir. Önceki 4.0 sürümüne kıyasla, daha iyi düzenleme tutarlılığı ve çoklu görüntü birleştirme ile önemli ölçüde geliştirilmiş üretim kalitesi sunar. Görsel detaylar üzerinde daha hassas kontrol sağlar, küçük metin ve küçük yüzleri daha doğal bir şekilde üretir ve daha uyumlu düzen ve renk elde ederek genel estetiği artırır.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite, ByteDance'in en son görüntü oluşturma modelidir. İlk kez çevrimiçi bilgi alma yeteneklerini entegre ederek gerçek zamanlı web bilgilerini dahil etmesine ve oluşturulan görüntülerin zamanlamasını artırmasına olanak tanır. Modelin zekası da yükseltilmiş, karmaşık talimatları ve görsel içeriği hassas bir şekilde yorumlama yeteneği kazandırılmıştır. Ayrıca, profesyonel senaryolarda daha iyi küresel bilgi kapsamı, referans tutarlılığı ve üretim kalitesi sunarak kurumsal düzeyde görsel yaratım ihtiyaçlarını daha iyi karşılar.", - "dreamina-seedance-2-0-260128.description": "ByteDance tarafından geliştirilen Seedance 2.0, en güçlü video üretim modeli olup, çok modlu referans video üretimi, video düzenleme, video genişletme, metinden videoya ve görüntüden videoya senkronize ses ile destek sağlar.", - "dreamina-seedance-2-0-fast-260128.description": "ByteDance tarafından geliştirilen Seedance 2.0 Fast, Seedance 2.0 ile aynı yetenekleri daha hızlı üretim hızları ve daha rekabetçi fiyatlarla sunar.", "emohaa.description": "Emohaa, kullanıcıların duygusal sorunları anlamalarına yardımcı olmak için profesyonel danışmanlık yeteneklerine sahip bir ruh sağlığı modelidir.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B, yerel ve özelleştirilmiş dağıtım için açık kaynaklı hafif bir modeldir.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Önizleme, ERNIE 4.5'in değerlendirilmesi için 8K bağlam önizleme modelidir.", @@ -506,6 +503,7 @@ "ernie-5.0-thinking-latest.description": "Wenxin 5.0 Thinking, metin, görsel, ses ve video modellemesini birleştiren yerel tam modlu amiral gemisi modelidir. Karmaşık QA, içerik üretimi ve ajan senaryoları için kapsamlı yetenek geliştirmeleri sunar.", "ernie-5.0-thinking-preview.description": "Wenxin 5.0 Thinking Önizleme, metin, görsel, ses ve video modellemesini birleştiren yerel tam modlu amiral gemisi modelidir. Karmaşık QA, içerik üretimi ve ajan senaryoları için kapsamlı yetenek geliştirmeleri sunar.", "ernie-5.0.description": "ERNIE 5.0, ERNIE serisinin yeni nesil modelidir ve doğal olarak çok modludur. Metin, görüntü, ses ve videoyu tek bir birleşik çok modlu modelleme yaklaşımıyla işler. Temel yetenekleri önemli ölçüde geliştirilmiş olup çok yönlü kıyaslamalarda güçlü performans sergiler. Özellikle çok modlu anlama, talimat takip etme, yaratıcı yazma, gerçeklik doğruluğu, aracı planlama ve araç kullanımı konularında üstündür.", + "ernie-5.1.description": "ERNIE 5.1, ERNIE serisinin en son modeli olup, temel yeteneklerinde kapsamlı iyileştirmeler sunar. Ajanlar, bilgi işleme, akıl yürütme ve derin arama gibi alanlarda önemli gelişmeler gösterir. Bu sürüm, büyük modellerin otonom ajan karar verme evrimiyle ilgili temel zorlukları ele almak için özel olarak tasarlanmış, ayrık tam asenkron pekiştirmeli öğrenme mimarisini benimser. Eğitim–çıkarım sayısal tutarsızlıkları, heterojen bilgi işlem kaynaklarının düşük kullanımı ve uzun kuyruk etkilerinden kaynaklanan küresel sorunlar gibi zorlukları ele alır. Ayrıca, büyük ölçekli ajan sonrası eğitim teknikleri kullanılarak modelin yetenekleri ve genelleme performansı daha da geliştirilir. Çevre, uzman ve füzyon süreçlerini içeren üç aşamalı işbirlikçi bir çerçeve aracılığıyla yaklaşım, yalnızca eğitim verimliliğini sağlamakla kalmaz, aynı zamanda karmaşık görevlerde modelin kararlılığını ve performansını önemli ölçüde artırır.", "ernie-char-fiction-8k-preview.description": "ERNIE Character Fiction 8K Önizleme, karakter ve hikâye oluşturma özelliklerinin değerlendirilmesi ve test edilmesi için bir önizleme modelidir.", "ernie-char-fiction-8k.description": "ERNIE Character Fiction 8K, romanlar ve hikâye kurgusu için kişilik odaklı bir modeldir; uzun biçimli hikâye üretimi için uygundur.", "ernie-image-turbo.description": "ERNIE-Image, Baidu tarafından geliştirilen 8B parametreli bir metinden görüntüye modelidir. Çin’de SuperCLUE dahil birçok kıyaslamada üst sıralarda yer almakta ve açık kaynak kategorisinde lider konumdadır.", @@ -517,8 +515,7 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K, karmaşık akıl yürütme ve çoklu dönüşlü sohbetler için 32K bağlamlı hızlı düşünme modelidir.", "ernie-x1.1-preview.description": "ERNIE X1.1 Önizleme, değerlendirme ve test için bir düşünme modeli önizlemesidir.", "ernie-x1.1.description": "ERNIE X1.1, değerlendirme ve test için bir düşünme-modeli önizlemesidir.", - "fal-ai/bytedance/seedream/v4.5.description": "ByteDance Seed ekibi tarafından geliştirilen Seedream 4.5, çoklu görüntü düzenleme ve kompozisyonu destekler. Geliştirilmiş konu tutarlılığı, hassas talimat takibi, mekansal mantık anlayışı, estetik ifade, poster düzeni ve logo tasarımı ile yüksek hassasiyetli metin-görüntü oluşturma özelliklerine sahiptir.", - "fal-ai/bytedance/seedream/v4.description": "ByteDance Seed tarafından geliştirilen Seedream 4.0, metin ve görüntü girdilerini destekleyerek, istemlerden yüksek kaliteli ve kontrol edilebilir görüntü üretimi sağlar.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, ByteDance Seed'in bir görüntü oluşturma modeli olup, metin ve görüntü girdilerini destekler ve son derece kontrol edilebilir, yüksek kaliteli görüntü oluşturma sağlar. Metin istemlerinden görüntüler oluşturur.", "fal-ai/flux-kontext/dev.description": "FLUX.1 modeli, metin ve görsel girdileri destekleyen görsel düzenleme odaklı bir modeldir.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro], metin ve referans görselleri girdi olarak alarak hedefe yönelik yerel düzenlemeler ve karmaşık sahne dönüşümleri sağlar.", "fal-ai/flux/krea.description": "Flux Krea [dev], daha gerçekçi ve doğal görseller üretmeye eğilimli estetik önyargıya sahip bir görsel üretim modelidir.", @@ -526,8 +523,8 @@ "fal-ai/hunyuan-image/v3.description": "Güçlü bir yerel çok modlu görsel üretim modelidir.", "fal-ai/imagen4/preview.description": "Google tarafından geliştirilen yüksek kaliteli görsel üretim modeli.", "fal-ai/nano-banana.description": "Nano Banana, Google’ın en yeni, en hızlı ve en verimli yerel çok modlu modelidir. Konuşma yoluyla görsel üretim ve düzenleme sağlar.", - "fal-ai/qwen-image-edit.description": "Qwen ekibinden profesyonel bir görüntü düzenleme modeli olup, semantik ve görünüm düzenlemeleri, hassas Çince/İngilizce metin düzenleme, stil transferi, döndürme ve daha fazlasını destekler.", - "fal-ai/qwen-image.description": "Qwen ekibinden güçlü bir görüntü üretim modeli olup, güçlü Çince metin işleme ve çeşitli görsel stiller sunar.", + "fal-ai/qwen-image-edit.description": "Qwen ekibinden profesyonel bir görüntü düzenleme modeli olup, semantik ve görünüm düzenlemelerini destekler, Çince ve İngilizce metni hassas bir şekilde düzenler ve stil transferi ve nesne döndürme gibi yüksek kaliteli düzenlemeler sağlar.", + "fal-ai/qwen-image.description": "Qwen ekibinden güçlü bir görüntü oluşturma modeli olup, etkileyici Çince metin işleme ve çeşitli görsel stiller sunar.", "flux-1-schnell.description": "Black Forest Labs tarafından geliştirilen 12 milyar parametreli metinden görsele model. Latent adversarial diffusion distillation yöntemiyle 1-4 adımda yüksek kaliteli görseller üretir. Kapalı kaynaklı alternatiflerle rekabet eder ve kişisel, araştırma ve ticari kullanım için Apache-2.0 lisansı ile sunulur.", "flux-dev.description": "Açık kaynaklı Ar-Ge görsel üretim modelidir; ticari olmayan yenilikçi araştırmalar için verimli şekilde optimize edilmiştir.", "flux-kontext-max.description": "Metin ve görselleri birleştirerek hassas ve tutarlı sonuçlar sunan son teknoloji bağlamsal görsel üretim ve düzenleme.", @@ -566,10 +563,10 @@ "gemini-3-flash-preview.description": "Gemini 3 Flash, hız için tasarlanmış en akıllı modeldir. En son yapay zeka zekasını mükemmel arama temellendirmesiyle birleştirir.", "gemini-3-flash.description": "Google’ın Gemini 3 Flash modeli — çok hızlı ve çok modlu giriş desteğine sahiptir.", "gemini-3-pro-image-preview.description": "Gemini 3 Pro Image (Nano Banana Pro), Google'ın çok modlu diyaloğu da destekleyen görüntü oluşturma modelidir.", - "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro), Google'ın görüntü üretim modeli olup, çok modlu sohbeti de destekler.", + "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro), Google'ın görüntü oluşturma modeli olup, çok modlu sohbeti de destekler.", "gemini-3-pro-preview.description": "Gemini 3 Pro, Google’ın en güçlü ajan ve vibe-coding modelidir. En yeni akıl yürütme yeteneklerinin üzerine zengin görseller ve derin etkileşim sunar.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2), Google'ın düşünme desteği, konuşmalı görüntü oluşturma ve düzenleme özelliklerine sahip en hızlı yerel görüntü oluşturma modelidir.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2), Flash hızında Pro seviyesinde görüntü kalitesi sunar ve çok modlu sohbeti destekler.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2), Google'ın düşünme desteği, sohbet tabanlı görüntü oluşturma ve düzenleme özellikleriyle en hızlı yerel görüntü oluşturma modelidir.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview, Google'ın en maliyet etkin çok modlu modeli olup, yüksek hacimli ajan görevleri, çeviri ve veri işleme için optimize edilmiştir.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite, Google'ın en maliyet etkin çok modlu modelidir ve yüksek hacimli ajan görevleri, çeviri ve veri işleme için optimize edilmiştir.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview, Gemini 3 Pro'ya kıyasla geliştirilmiş akıl yürütme yetenekleri sunar ve orta düzey düşünme desteği ekler.", @@ -734,8 +731,6 @@ "grok-4-fast-reasoning.description": "Grok 4 Fast modelimizi sunmaktan heyecan duyuyoruz; uygun maliyetli akıl yürütme modellerinde en son gelişmemizdir.", "grok-4.20-0309-non-reasoning.description": "Basit kullanım senaryoları için akıl yürütme içermeyen varyant.", "grok-4.20-0309-reasoning.description": "Yanıt vermeden önce akıl yürüten, son derece hızlı ve akıllı model.", - "grok-4.20-beta-0309-non-reasoning.description": "Basit kullanım durumları için düşünme gerektirmeyen bir varyant.", - "grok-4.20-beta-0309-reasoning.description": "Yanıt vermeden önce düşünen, son derece hızlı ve akıllı model.", "grok-4.20-multi-agent-0309.description": "4 veya 16 ajanlık bir ekip; araştırma senaryolarında üstündür. Şu anda istemci tarafı araçları desteklemez. Yalnızca xAI sunucu tarafı araçlarını (örn. X Search, Web Search araçları) ve uzak MCP araçlarını destekler.", "grok-4.3.description": "Dünyanın en doğruyu arayan büyük dil modeli", "grok-4.description": "Dil, matematik ve akıl yürütme konularında rakipsiz performansa sahip en son Grok amiral gemisi — gerçek bir çok yönlü model. Şu anda grok-4-0709'a işaret ediyor; sınırlı kaynaklar nedeniyle geçici olarak resmi fiyatlandırmadan %10 daha yüksek ve daha sonra resmi fiyatına dönmesi bekleniyor.", @@ -1220,8 +1215,6 @@ "qwq.description": "QwQ, Qwen ailesine ait bir akıl yürütme modelidir. Standart talimatla eğitilmiş modellere kıyasla düşünme ve akıl yürütme yetenekleriyle özellikle zor problemler üzerinde performansı önemli ölçüde artırır. QwQ-32B, DeepSeek-R1 ve o1-mini gibi üst düzey modellerle rekabet eden orta boyutlu bir modeldir.", "qwq_32b.description": "Qwen ailesine ait orta boyutlu bir akıl yürütme modelidir. Standart talimatla eğitilmiş modellere kıyasla QwQ’nun düşünme ve akıl yürütme yetenekleri, özellikle zor problemler üzerinde performansı önemli ölçüde artırır.", "r1-1776.description": "R1-1776, sansürsüz ve tarafsız gerçek bilgi sunmak üzere DeepSeek R1 üzerine eğitilmiş bir varyanttır.", - "seedance-1-5-pro-251215.description": "ByteDance tarafından geliştirilen Seedance 1.5 Pro, metinden videoya, görüntüden videoya (ilk kare, ilk+son kare) ve görsellerle senkronize ses üretimini destekler.", - "seedream-5-0-260128.description": "BytePlus tarafından geliştirilen ByteDance-Seedream-5.0-lite, gerçek zamanlı bilgi için web arama ile zenginleştirilmiş üretim, karmaşık istem yorumlama ve profesyonel görsel oluşturma için geliştirilmiş referans tutarlılığı sağlar.", "solar-mini-ja.description": "Solar Mini (Ja), Japonca odaklı geliştirilmiş Solar Mini modelidir; İngilizce ve Korece'de de verimli ve güçlü performans sunar.", "solar-mini.description": "Solar Mini, GPT-3.5'i geride bırakan kompakt bir LLM'dir. İngilizce ve Korece destekli çok dilli yetenekleriyle verimli ve küçük boyutlu bir çözüm sunar.", "solar-pro.description": "Solar Pro, Upstage tarafından geliştirilen yüksek zekaya sahip bir LLM'dir. Tek bir GPU üzerinde talimat izleme odaklıdır ve IFEval skorları 80'in üzerindedir. Şu anda İngilizce desteklidir; tam sürüm Kasım 2024'te daha fazla dil desteği ve uzun bağlamla planlanmıştır.", diff --git a/locales/tr-TR/onboarding.json b/locales/tr-TR/onboarding.json index 52d493d1db..4e52e2d5aa 100644 --- a/locales/tr-TR/onboarding.json +++ b/locales/tr-TR/onboarding.json @@ -27,6 +27,13 @@ "agent.layout.skipConfirm.ok": "Şimdilik atla", "agent.layout.skipConfirm.title": "Şimdilik başlangıcı atlamak istiyor musun?", "agent.layout.switchMessage": "Bugün modunda değil misin? {{mode}} ya da {{skip}} seçebilirsin.", + "agent.layout.switchMessageClassic": "Bugün böyle hissetmiyor musunuz? {{mode}} moduna geçebilirsiniz.", + "agent.messenger.cta.discord": "Discord'a Ekle", + "agent.messenger.cta.slack": "Slack'e Ekle", + "agent.messenger.cta.telegram": "Telegram'da Aç", + "agent.messenger.subtitle": "Ajanınızla Telegram, Slack veya Discord üzerinden sohbet edin", + "agent.messenger.telegramQrCaption": "Telefon kameranızla tarayın", + "agent.messenger.title": "Mesajlaştığınız her yerde beni yanınızda tutun", "agent.modeSwitch.agent": "Sohbet Odaklı", "agent.modeSwitch.classic": "Klasik", "agent.modeSwitch.collapse": "Daralt", @@ -68,6 +75,13 @@ "agent.wrapUp.confirm.content": "Şimdiye kadar konuştuklarımızı kaydedeceğim. Daha sonra her zaman geri dönüp sohbet edebilirsiniz.", "agent.wrapUp.confirm.ok": "Şimdi bitir", "agent.wrapUp.confirm.title": "Başlatmayı şimdi bitirmek istiyor musunuz?", + "agentPicker.allCategories": "Tümü", + "agentPicker.continue": "Devam Et", + "agentPicker.skip": "Şimdilik Atla", + "agentPicker.subtitle": "Şimdi kütüphanenize birkaç tane ekleyin — daha sonra istediğiniz zaman keşfedebilirsiniz.", + "agentPicker.title": "Kütüphanenize birkaç ajan ekleyelim", + "agentPicker.title2": "Çalışma şeklinize uygun olanları seçin", + "agentPicker.title3": "Her zaman daha fazlasını ekleyebilirsiniz — birkaçıyla başlayın", "back": "Geri", "finish": "Başlayalım", "interests.area.business": "İş ve Strateji", diff --git a/locales/tr-TR/plugin.json b/locales/tr-TR/plugin.json index 498dc98c7d..9701013994 100644 --- a/locales/tr-TR/plugin.json +++ b/locales/tr-TR/plugin.json @@ -39,8 +39,8 @@ "builtins.lobe-agent-documents.inspector.docCount": "{{count}} belge", "builtins.lobe-agent-documents.inspector.opsCount": "{{count}} işlem", "builtins.lobe-agent-documents.inspector.opsResult": "{{success}}/{{total}} işlem", - "builtins.lobe-agent-documents.inspector.target.agent": "ajan içinde", - "builtins.lobe-agent-documents.inspector.target.currentTopic": "konu içinde", + "builtins.lobe-agent-documents.inspector.scope.agent": "ajan içinde", + "builtins.lobe-agent-documents.inspector.scope.currentTopic": "konu içinde", "builtins.lobe-agent-documents.title": "Aracı Belgeleri", "builtins.lobe-agent-management.apiName.callAgent": "Çağrı temsilcisi", "builtins.lobe-agent-management.apiName.createAgent": "Temsilci oluştur", @@ -90,6 +90,14 @@ "builtins.lobe-agent.title": "Lobe Ajanı", "builtins.lobe-claude-code.agent.instruction": "Talimat", "builtins.lobe-claude-code.agent.result": "Sonuç", + "builtins.lobe-claude-code.task.createLabel": "Görev oluşturuluyor: ", + "builtins.lobe-claude-code.task.getLabel": "Görev inceleniyor #{{taskId}}", + "builtins.lobe-claude-code.task.listLabel": "Görevler listeleniyor", + "builtins.lobe-claude-code.task.updateCompleted": "Tamamlandı", + "builtins.lobe-claude-code.task.updateDeleted": "Silindi", + "builtins.lobe-claude-code.task.updateInProgress": "Başlatıldı", + "builtins.lobe-claude-code.task.updateLabel": "Görev güncelleniyor #{{taskId}}", + "builtins.lobe-claude-code.task.updatePending": "Sıfırla", "builtins.lobe-claude-code.todoWrite.allDone": "Tüm görevler tamamlandı", "builtins.lobe-claude-code.todoWrite.currentStep": "Geçerli adım", "builtins.lobe-claude-code.todoWrite.todos": "Yapılacaklar", diff --git a/locales/tr-TR/providers.json b/locales/tr-TR/providers.json index 7548148d43..02005f3045 100644 --- a/locales/tr-TR/providers.json +++ b/locales/tr-TR/providers.json @@ -33,7 +33,6 @@ "jina.description": "2020 yılında kurulan Jina AI, önde gelen bir arama yapay zekası şirketidir. Vektör modelleri, yeniden sıralayıcılar ve küçük dil modelleri içeren arama yığını ile güvenilir ve yüksek kaliteli üretken ve çok modlu arama uygulamaları geliştirir.", "kimicodingplan.description": "Moonshot AI'den Kimi Code, kodlama görevleri için K2.5 dahil olmak üzere Kimi modellerine erişim sağlar.", "lmstudio.description": "LM Studio, bilgisayarınızda büyük dil modelleriyle geliştirme ve denemeler yapmanızı sağlayan bir masaüstü uygulamasıdır.", - "lobehub.description": "LobeHub Cloud, resmi API'leri kullanarak yapay zeka modellerine erişir ve kullanımını model jetonlarına bağlı Kredilerle ölçer.", "longcat.description": "LongCat, Meituan tarafından bağımsız olarak geliştirilen bir dizi üretken yapay zeka büyük modelidir. Verimli bir hesaplama mimarisi ve güçlü çok modlu yetenekler aracılığıyla kurumsal iç verimliliği artırmak ve yenilikçi uygulamaları mümkün kılmak için tasarlanmıştır.", "minimax.description": "2021 yılında kurulan MiniMax, çok modlu temel modellerle genel amaçlı yapay zeka geliştirir. Trilyon parametreli MoE metin modelleri, ses ve görsel modellerin yanı sıra Hailuo AI gibi uygulamalar sunar.", "minimaxcodingplan.description": "MiniMax Token Planı, sabit ücretli bir abonelik aracılığıyla kodlama görevleri için M2.7 dahil olmak üzere MiniMax modellerine erişim sağlar.", diff --git a/locales/tr-TR/subscription.json b/locales/tr-TR/subscription.json index 82ed2c5573..846ed937ed 100644 --- a/locales/tr-TR/subscription.json +++ b/locales/tr-TR/subscription.json @@ -55,6 +55,12 @@ "credits.autoTopUp.toggle": "Otomatik Yüklemeyi Etkinleştir", "credits.autoTopUp.upgradeHint": "Otomatik yüklemeyi etkinleştirmek için ücretli bir plana abone olun", "credits.autoTopUp.validation.targetMustExceedThreshold": "Hedef bakiye eşik değerinden büyük olmalıdır", + "credits.costEstimateHint.desc": "Tahmini model maliyeti belirlediğiniz eşiğe ulaştığında gönderimden önce hafif bir uyarı göster", + "credits.costEstimateHint.saveError": "Maliyet tahmini uyarı ayarları kaydedilemedi", + "credits.costEstimateHint.saveSuccess": "Maliyet tahmini uyarı ayarları kaydedildi", + "credits.costEstimateHint.threshold": "Uyarı Eşiği", + "credits.costEstimateHint.title": "Maliyet Tahmini Uyarısı", + "credits.costEstimateHint.validation.threshold": "Eşik 0'a eşit veya daha büyük olmalıdır", "credits.packages.auto": "Otomatik", "credits.packages.charged": "${{amount}} tutarında ücret alındı", "credits.packages.expired": "Süresi Doldu", diff --git a/locales/tr-TR/tool.json b/locales/tr-TR/tool.json index 5d012c0d09..da12c7c6e3 100644 --- a/locales/tr-TR/tool.json +++ b/locales/tr-TR/tool.json @@ -40,6 +40,14 @@ "agentMarketplace.render.alreadyInLibraryTag": "Zaten kütüphanede", "agentMarketplace.render.alreadyInLibrary_one": "{{count}} zaten kütüphanede", "agentMarketplace.render.alreadyInLibrary_other": "{{count}} zaten kütüphanede", + "claudeCode.askUserQuestion.escape.back": "Seçeneklere geri dön", + "claudeCode.askUserQuestion.escape.enter": "Ya da doğrudan yazın", + "claudeCode.askUserQuestion.escape.placeholder": "Cevabınızı buraya yazın…", + "claudeCode.askUserQuestion.multiSelectTag": "(çoklu seçim)", + "claudeCode.askUserQuestion.skip": "Atla", + "claudeCode.askUserQuestion.submit": "Gönder", + "claudeCode.askUserQuestion.timeExpired": "Süre doldu — her sorunun 1. seçeneği kullanılıyor.", + "claudeCode.askUserQuestion.timeRemaining": "Kalan süre: {{time}} · cevaplanmayan sorular zaman aşımında 1. seçeneğe varsayılan olarak ayarlanır.", "codeInterpreter-legacy.error": "Yürütme Hatası", "codeInterpreter-legacy.executing": "Yürütülüyor...", "codeInterpreter-legacy.files": "Dosyalar:", diff --git a/locales/tr-TR/topic.json b/locales/tr-TR/topic.json index 2a9f7afbbf..c0e3f082b9 100644 --- a/locales/tr-TR/topic.json +++ b/locales/tr-TR/topic.json @@ -56,6 +56,7 @@ "renameModal.title": "Konuyu Yeniden Adlandır", "searchPlaceholder": "Konularda Ara...", "searchResultEmpty": "Arama sonucu bulunamadı.", + "sidebar.title": "Konular", "taskManager.agent": "Görev Aracısı", "taskManager.welcome": "Görevlerin hakkında bana soru sor", "temp": "Geçici", diff --git a/locales/vi-VN/chat.json b/locales/vi-VN/chat.json index 86798dc8fa..a321d940a7 100644 --- a/locales/vi-VN/chat.json +++ b/locales/vi-VN/chat.json @@ -220,6 +220,13 @@ "inbox.title": "Lobe AI", "input.addAi": "Thêm tin nhắn AI", "input.addUser": "Thêm tin nhắn người dùng", + "input.costEstimate.creditsPerMillionTokens": "{{credits}} tín dụng/M token", + "input.costEstimate.hint": "Chi phí ước tính: ~{{credits}} tín dụng", + "input.costEstimate.inputLabel": "Đầu vào", + "input.costEstimate.outputLabel": "Đầu ra", + "input.costEstimate.settingsLink": "Điều chỉnh ngưỡng cảnh báo", + "input.costEstimate.tokenCount": "~{{tokens}} token", + "input.costEstimate.tooltip": "Ước tính từ ngữ cảnh hiện tại, công cụ và giá mô hình. Chi phí thực tế có thể thay đổi.", "input.disclaimer": "Tác nhân có thể mắc lỗi. Hãy sử dụng phán đoán của bạn với thông tin quan trọng.", "input.errorMsg": "Gửi thất bại: {{errorMsg}}. Vui lòng thử lại hoặc gửi sau.", "input.more": "Thêm", @@ -779,6 +786,7 @@ "upload.preview.prepareTasks": "Đang chuẩn bị phân đoạn...", "upload.preview.status.pending": "Đang chuẩn bị tải lên...", "upload.preview.status.processing": "Đang xử lý tệp...", + "upload.validation.unsupportedFileType": "Loại tệp không được hỗ trợ: {{files}}. Hình ảnh được hỗ trợ: JPG, PNG, GIF, WebP. Các tài liệu được hỗ trợ bao gồm PDF, Word, Excel, PowerPoint, Markdown, văn bản, CSV, JSON và tệp mã.", "upload.validation.videoSizeExceeded": "Kích thước tệp video không được vượt quá 20MB. Kích thước hiện tại là {{actualSize}}.", "viewMode.fullWidth": "Toàn chiều rộng", "viewMode.normal": "Chuẩn", @@ -894,6 +902,8 @@ "workingPanel.localFile.closeOther": "Đóng Khác", "workingPanel.localFile.closeRight": "Đóng bên Phải", "workingPanel.localFile.error": "Không thể tải tệp này", + "workingPanel.localFile.preview.raw": "Thô", + "workingPanel.localFile.preview.render": "Xem trước", "workingPanel.localFile.truncated": "Xem trước tệp bị cắt ngắn đến {{limit}} ký tự", "workingPanel.progress": "Progress", "workingPanel.progress.allCompleted": "All tasks completed", @@ -959,6 +969,8 @@ "workingPanel.review.viewMode.unified": "Chuyển sang chế độ xem hợp nhất", "workingPanel.review.wordWrap.disable": "Tắt ngắt dòng", "workingPanel.review.wordWrap.enable": "Bật ngắt dòng", + "workingPanel.skills.empty": "Không tìm thấy kỹ năng nào trong dự án này", + "workingPanel.skills.title": "Kỹ năng", "workingPanel.space": "Khoảng trống", "workingPanel.title": "Working Panel", "you": "Bạn", diff --git a/locales/vi-VN/components.json b/locales/vi-VN/components.json index eba180eec4..b5058b5389 100644 --- a/locales/vi-VN/components.json +++ b/locales/vi-VN/components.json @@ -121,6 +121,16 @@ "ModelSwitchPanel.detail.context": "Độ dài ngữ cảnh", "ModelSwitchPanel.detail.pricing": "Giá cả", "ModelSwitchPanel.detail.pricing.cachedInput": "Đầu vào đã lưu ${{amount}}/M", + "ModelSwitchPanel.detail.pricing.credits.cachedInput": "Đầu vào được lưu trữ {{amount}} tín dụng/M tokens", + "ModelSwitchPanel.detail.pricing.credits.image": "tín dụng/hình ảnh", + "ModelSwitchPanel.detail.pricing.credits.input": "Đầu vào {{amount}} tín dụng/M tokens", + "ModelSwitchPanel.detail.pricing.credits.megapixel": "tín dụng/MP", + "ModelSwitchPanel.detail.pricing.credits.millionCharacters": "tín dụng/M ký tự", + "ModelSwitchPanel.detail.pricing.credits.millionTokens": "tín dụng/M tokens", + "ModelSwitchPanel.detail.pricing.credits.output": "Đầu ra {{amount}} tín dụng/M tokens", + "ModelSwitchPanel.detail.pricing.credits.perImage": "~ {{amount}} tín dụng / hình ảnh", + "ModelSwitchPanel.detail.pricing.credits.perVideo": "~ {{amount}} tín dụng / video", + "ModelSwitchPanel.detail.pricing.credits.second": "tín dụng/giây", "ModelSwitchPanel.detail.pricing.group.audio": "Âm thanh", "ModelSwitchPanel.detail.pricing.group.image": "Hình ảnh", "ModelSwitchPanel.detail.pricing.group.text": "Văn bản", diff --git a/locales/vi-VN/editor.json b/locales/vi-VN/editor.json index 58ddfde407..acabfe9099 100644 --- a/locales/vi-VN/editor.json +++ b/locales/vi-VN/editor.json @@ -1,8 +1,10 @@ { "actionTag.category.command": "Lệnh", + "actionTag.category.projectSkill": "Kỹ năng dự án", "actionTag.category.skill": "Kỹ năng", "actionTag.category.tool": "Công cụ", "actionTag.tooltip.command": "Chạy lệnh slash phía client trước khi gửi.", + "actionTag.tooltip.projectSkill": "Được gửi dưới dạng lệnh gạch chéo để CLI của tác nhân chạy kỹ năng dự án tương ứng.", "actionTag.tooltip.skill": "Tải gói kỹ năng có thể tái sử dụng cho yêu cầu này.", "actionTag.tooltip.tool": "Đánh dấu công cụ mà người dùng đã chọn rõ ràng cho yêu cầu này.", "actions.expand.off": "Thu gọn", diff --git a/locales/vi-VN/home.json b/locales/vi-VN/home.json index acda768800..53e61bd315 100644 --- a/locales/vi-VN/home.json +++ b/locales/vi-VN/home.json @@ -11,6 +11,19 @@ "brief.action.ignore": "Bỏ qua", "brief.action.retry": "Thử lại", "brief.addFeedback": "Gửi phản hồi", + "brief.agentSignal.selfReview.applied.heading": "Đã cập nhật", + "brief.agentSignal.selfReview.applied.summary": "Đã áp dụng {{count}} cập nhật giấc mơ.", + "brief.agentSignal.selfReview.applied.summary_plural": "Đã áp dụng {{count}} cập nhật giấc mơ.", + "brief.agentSignal.selfReview.applied.title": "Tài nguyên giấc mơ đã được cập nhật", + "brief.agentSignal.selfReview.error.heading": "Vấn đề", + "brief.agentSignal.selfReview.error.summary": "Một số công việc không thể hoàn thành trong giấc mơ này.", + "brief.agentSignal.selfReview.error.title": "Giấc mơ gặp vấn đề", + "brief.agentSignal.selfReview.ideas.summary": "Đã lưu ghi chú giấc mơ để xem lại sau.", + "brief.agentSignal.selfReview.ideas.title": "Ghi chú giấc mơ", + "brief.agentSignal.selfReview.proposal.heading": "Đề xuất", + "brief.agentSignal.selfReview.proposal.summary": "{{count}} đề xuất giấc mơ cần bạn xem xét.", + "brief.agentSignal.selfReview.proposal.summary_plural": "{{count}} đề xuất giấc mơ cần bạn xem xét.", + "brief.agentSignal.selfReview.proposal.title": "Đề xuất giấc mơ cần được xem xét", "brief.collapse": "Thu gọn", "brief.commentPlaceholder": "Chia sẻ phản hồi của bạn...", "brief.commentSubmit": "Gửi phản hồi", diff --git a/locales/vi-VN/hotkey.json b/locales/vi-VN/hotkey.json index e3209c940f..02fb2c468a 100644 --- a/locales/vi-VN/hotkey.json +++ b/locales/vi-VN/hotkey.json @@ -1,8 +1,6 @@ { "addUserMessage.desc": "Thêm nội dung hiện tại dưới dạng tin nhắn người dùng mà không kích hoạt tạo phản hồi", "addUserMessage.title": "Thêm Tin Nhắn Người Dùng", - "clearCurrentMessages.desc": "Xóa các tin nhắn và tệp đã tải lên trong cuộc trò chuyện hiện tại", - "clearCurrentMessages.title": "Xóa Tin Nhắn Cuộc Trò Chuyện", "commandPalette.desc": "Mở bảng lệnh toàn cục để truy cập nhanh các tính năng", "commandPalette.title": "Bảng Lệnh", "deleteAndRegenerateMessage.desc": "Xóa tin nhắn cuối cùng và tạo lại", diff --git a/locales/vi-VN/models.json b/locales/vi-VN/models.json index 7669a8cae8..6d0d37f5d5 100644 --- a/locales/vi-VN/models.json +++ b/locales/vi-VN/models.json @@ -106,7 +106,6 @@ "MiniMax-Hailuo-2.3.description": "Mô hình tạo video hoàn toàn mới với các nâng cấp toàn diện về chuyển động cơ thể, tính hiện thực vật lý và tuân thủ hướng dẫn.", "MiniMax-M1.description": "Mô hình suy luận nội bộ mới với 80K chuỗi suy nghĩ và đầu vào 1M, đạt hiệu suất tương đương các mô hình hàng đầu toàn cầu.", "MiniMax-M2-Stable.description": "Được xây dựng cho lập trình hiệu quả và quy trình tác tử, với khả năng đồng thời cao hơn cho mục đích thương mại.", - "MiniMax-M2.1-Lightning.description": "Khả năng lập trình đa ngôn ngữ mạnh mẽ với suy luận nhanh hơn và hiệu quả hơn.", "MiniMax-M2.1-highspeed.description": "Khả năng lập trình đa ngôn ngữ mạnh mẽ, trải nghiệm lập trình được nâng cấp toàn diện. Nhanh hơn và hiệu quả hơn.", "MiniMax-M2.1.description": "Khả năng lập trình đa ngôn ngữ mạnh mẽ, trải nghiệm lập trình được nâng cấp toàn diện", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed: Hiệu suất tương tự như M2.5 với suy luận nhanh hơn.", @@ -315,13 +314,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku là mô hình nhanh nhất và nhỏ gọn nhất của Anthropic, được thiết kế cho phản hồi gần như tức thì với hiệu suất nhanh và chính xác.", "claude-3-opus-20240229.description": "Claude 3 Opus là mô hình mạnh mẽ nhất của Anthropic cho các tác vụ phức tạp, xuất sắc về hiệu suất, trí tuệ, lưu loát và hiểu biết.", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet cân bằng giữa trí tuệ và tốc độ cho khối lượng công việc doanh nghiệp, mang lại giá trị cao với chi phí thấp hơn và triển khai quy mô lớn đáng tin cậy.", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 là mô hình Haiku nhanh nhất và thông minh nhất của Anthropic, với tốc độ vượt trội và khả năng tư duy mở rộng.", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 là mô hình Haiku nhanh nhất và thông minh nhất của Anthropic, với tốc độ cực nhanh và khả năng suy luận mở rộng.", "claude-haiku-4-5.description": "Claude Haiku 4.5 của Anthropic — Haiku thế hệ mới với khả năng lý luận và thị giác nâng cao.", "claude-haiku-4.5.description": "Claude Haiku 4.5 là mô hình Haiku nhanh nhất và thông minh nhất của Anthropic, với tốc độ vượt trội và khả năng suy luận mở rộng.", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking là biến thể nâng cao có thể hiển thị quá trình suy luận của nó.", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1 là mô hình mới nhất và mạnh mẽ nhất của Anthropic dành cho các nhiệm vụ phức tạp, vượt trội về hiệu suất, trí tuệ, sự lưu loát và khả năng hiểu biết.", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1 là mô hình mới nhất và mạnh mẽ nhất của Anthropic dành cho các nhiệm vụ phức tạp cao, vượt trội về hiệu suất, trí thông minh, sự lưu loát và khả năng hiểu biết.", "claude-opus-4-1.description": "Claude Opus 4.1 của Anthropic — mô hình lý luận cao cấp với khả năng phân tích sâu.", - "claude-opus-4-20250514.description": "Claude Opus 4 là mô hình mạnh mẽ nhất của Anthropic dành cho các nhiệm vụ phức tạp, vượt trội về hiệu suất, trí tuệ, sự lưu loát và khả năng hiểu biết.", + "claude-opus-4-20250514.description": "Claude Opus 4 là mô hình mạnh mẽ nhất của Anthropic dành cho các nhiệm vụ phức tạp cao, vượt trội về hiệu suất, trí thông minh, sự lưu loát và khả năng hiểu.", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 là mô hình hàng đầu của Anthropic, kết hợp trí tuệ vượt trội với hiệu suất có thể mở rộng, lý tưởng cho các tác vụ phức tạp đòi hỏi phản hồi và suy luận chất lượng cao nhất.", "claude-opus-4-5.description": "Claude Opus 4.5 của Anthropic — mô hình hàng đầu với khả năng lý luận và mã hóa đỉnh cao.", "claude-opus-4-6.description": "Claude Opus 4.6 của Anthropic — mô hình hàng đầu với cửa sổ ngữ cảnh 1 triệu và khả năng lý luận nâng cao.", @@ -330,7 +329,7 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 là mô hình thông minh nhất của Anthropic dành cho việc xây dựng các tác nhân và lập trình.", "claude-opus-4.6.description": "Claude Opus 4.6 là mô hình thông minh nhất của Anthropic dành cho việc xây dựng các tác nhân và lập trình.", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking có thể tạo phản hồi gần như tức thì hoặc suy luận từng bước mở rộng với quy trình hiển thị.", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 là mô hình thông minh nhất của Anthropic cho đến nay, cung cấp phản hồi gần như tức thì hoặc tư duy từng bước mở rộng với khả năng kiểm soát chi tiết cho người dùng API.", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 có thể tạo ra các phản hồi gần như tức thì hoặc suy nghĩ từng bước mở rộng với quy trình hiển thị rõ ràng.", "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 là mô hình thông minh nhất của Anthropic cho đến nay.", "claude-sonnet-4-5.description": "Claude Sonnet 4.5 của Anthropic — Sonnet được cải tiến với hiệu suất mã hóa nâng cao.", "claude-sonnet-4-6.description": "Claude Sonnet 4.6 của Anthropic — Sonnet mới nhất với khả năng mã hóa và sử dụng công cụ vượt trội.", @@ -404,7 +403,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat (67B) là một mô hình sáng tạo cung cấp khả năng hiểu và tương tác ngôn ngữ sâu sắc.", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 là mô hình suy luận thế hệ mới với khả năng suy luận phức tạp mạnh mẽ và chuỗi suy nghĩ cho các tác vụ phân tích chuyên sâu.", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 là mô hình suy luận thế hệ mới với khả năng suy luận phức tạp mạnh mẽ và tư duy chuỗi.", - "deepseek-chat.description": "Tên thay thế tương thích cho chế độ không tư duy của DeepSeek V4 Flash. Dự kiến sẽ bị loại bỏ — hãy sử dụng DeepSeek V4 Flash thay thế.", + "deepseek-chat.description": "Một mô hình mã nguồn mở mới kết hợp khả năng tổng quát và mã hóa. Nó duy trì đối thoại tổng quát của mô hình trò chuyện và khả năng mã hóa mạnh mẽ của mô hình lập trình, với sự căn chỉnh sở thích tốt hơn. DeepSeek-V2.5 cũng cải thiện khả năng viết và tuân thủ hướng dẫn.", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B là mô hình ngôn ngữ lập trình được huấn luyện trên 2 nghìn tỷ token (87% mã nguồn, 13% văn bản tiếng Trung/Anh). Mô hình này hỗ trợ cửa sổ ngữ cảnh 16K và nhiệm vụ điền vào giữa đoạn mã, cung cấp khả năng hoàn thành mã ở cấp độ dự án và chèn đoạn mã chính xác.", "deepseek-coder-v2.description": "DeepSeek Coder V2 là mô hình mã nguồn MoE mã nguồn mở với hiệu suất mạnh mẽ trong các tác vụ lập trình, có thể so sánh với GPT-4 Turbo.", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 là mô hình mã nguồn MoE mã nguồn mở với hiệu suất mạnh mẽ trong các tác vụ lập trình, có thể so sánh với GPT-4 Turbo.", @@ -426,7 +425,7 @@ "deepseek-r1-fast-online.description": "Phiên bản đầy đủ DeepSeek R1 nhanh với tìm kiếm web thời gian thực, kết hợp khả năng 671B và phản hồi nhanh hơn.", "deepseek-r1-online.description": "Phiên bản đầy đủ DeepSeek R1 với 671B tham số và tìm kiếm web thời gian thực, mang lại khả năng hiểu và tạo nội dung mạnh mẽ hơn.", "deepseek-r1.description": "DeepSeek-R1 sử dụng dữ liệu khởi động lạnh trước khi áp dụng học tăng cường và đạt hiệu suất tương đương OpenAI-o1 trong các tác vụ toán học, lập trình và suy luận.", - "deepseek-reasoner.description": "Tên thay thế tương thích cho chế độ tư duy của DeepSeek V4 Flash. Dự kiến sẽ bị loại bỏ — hãy sử dụng DeepSeek V4 Flash thay thế.", + "deepseek-reasoner.description": "Một mô hình suy luận DeepSeek tập trung vào các nhiệm vụ suy luận logic phức tạp.", "deepseek-v2.description": "DeepSeek V2 là mô hình MoE hiệu quả cho xử lý tiết kiệm chi phí.", "deepseek-v2:236b.description": "DeepSeek V2 236B là mô hình tập trung vào mã nguồn của DeepSeek với khả năng tạo mã mạnh mẽ.", "deepseek-v3-0324.description": "DeepSeek-V3-0324 là mô hình MoE với 671B tham số, nổi bật về lập trình, khả năng kỹ thuật, hiểu ngữ cảnh và xử lý văn bản dài.", @@ -491,8 +490,6 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 là mô hình tạo hình ảnh từ ByteDance Seed, hỗ trợ đầu vào văn bản và hình ảnh với khả năng tạo hình ảnh chất lượng cao, dễ kiểm soát. Mô hình tạo hình ảnh từ văn bản gợi ý.", "doubao-seedream-4-5-251128.description": "Seedream 4.5 là mô hình hình ảnh đa phương thức mới nhất của ByteDance, tích hợp khả năng tạo hình ảnh từ văn bản, chỉnh sửa hình ảnh và tạo hình ảnh hàng loạt, đồng thời kết hợp khả năng suy luận và kiến thức thông thường. So với phiên bản 4.0 trước đó, nó mang lại chất lượng tạo hình ảnh được cải thiện đáng kể, với sự nhất quán chỉnh sửa tốt hơn và khả năng hợp nhất nhiều hình ảnh. Nó cung cấp khả năng kiểm soát chi tiết hình ảnh chính xác hơn, tạo ra văn bản nhỏ và khuôn mặt nhỏ một cách tự nhiên hơn, và đạt được bố cục và màu sắc hài hòa hơn, nâng cao tính thẩm mỹ tổng thể.", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite là mô hình tạo hình ảnh mới nhất của ByteDance. Lần đầu tiên, nó tích hợp khả năng truy xuất trực tuyến, cho phép kết hợp thông tin web theo thời gian thực và nâng cao tính kịp thời của hình ảnh được tạo. Trí tuệ của mô hình cũng được nâng cấp, cho phép diễn giải chính xác các hướng dẫn phức tạp và nội dung hình ảnh. Ngoài ra, nó cung cấp phạm vi kiến thức toàn cầu được cải thiện, tính nhất quán tham chiếu và chất lượng tạo hình ảnh trong các tình huống chuyên nghiệp, đáp ứng tốt hơn nhu cầu sáng tạo hình ảnh ở cấp độ doanh nghiệp.", - "dreamina-seedance-2-0-260128.description": "Seedance 2.0 của ByteDance là mô hình tạo video mạnh mẽ nhất, hỗ trợ tạo video tham chiếu đa phương thức, chỉnh sửa video, mở rộng video, chuyển văn bản thành video và chuyển hình ảnh thành video với âm thanh đồng bộ.", - "dreamina-seedance-2-0-fast-260128.description": "Seedance 2.0 Fast của ByteDance cung cấp các khả năng tương tự như Seedance 2.0 với tốc độ tạo nhanh hơn và giá cả cạnh tranh hơn.", "emohaa.description": "Emohaa là mô hình hỗ trợ sức khỏe tinh thần với khả năng tư vấn chuyên nghiệp, giúp người dùng hiểu rõ các vấn đề cảm xúc.", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B là mô hình nhẹ mã nguồn mở, phù hợp để triển khai cục bộ và tùy chỉnh.", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview là mô hình xem trước với ngữ cảnh 8K để đánh giá ERNIE 4.5.", @@ -506,6 +503,7 @@ "ernie-5.0-thinking-latest.description": "Wenxin 5.0 Thinking là mô hình chủ lực toàn phương thức bản địa với khả năng mô hình hóa văn bản, hình ảnh, âm thanh và video thống nhất. Nó mang lại nâng cấp toàn diện cho các tình huống hỏi đáp phức tạp, sáng tạo và đại lý.", "ernie-5.0-thinking-preview.description": "Wenxin 5.0 Thinking Preview là mô hình chủ lực toàn phương thức bản địa với khả năng mô hình hóa văn bản, hình ảnh, âm thanh và video thống nhất. Nó mang lại nâng cấp toàn diện cho các tình huống hỏi đáp phức tạp, sáng tạo và đại lý.", "ernie-5.0.description": "ERNIE 5.0, mô hình thế hệ mới trong dòng ERNIE, là một mô hình lớn đa phương thức bản địa. Nó áp dụng cách tiếp cận mô hình hóa đa phương thức thống nhất, kết hợp văn bản, hình ảnh, âm thanh và video để cung cấp khả năng đa phương thức toàn diện. Các khả năng nền tảng của nó đã được nâng cấp đáng kể, đạt hiệu suất mạnh mẽ trong các đánh giá chuẩn. Đặc biệt xuất sắc trong việc hiểu đa phương thức, tuân theo hướng dẫn, viết sáng tạo, độ chính xác thực tế, lập kế hoạch tác nhân và sử dụng công cụ.", + "ernie-5.1.description": "ERNIE 5.1 là mô hình mới nhất trong dòng ERNIE, với các nâng cấp toàn diện về khả năng nền tảng. Nó thể hiện sự cải thiện đáng kể trong các lĩnh vực như tác nhân, xử lý kiến thức, suy luận và tìm kiếm sâu. Phiên bản này áp dụng kiến trúc học tăng cường không đồng bộ hoàn toàn, được thiết kế đặc biệt để giải quyết các thách thức chính trong việc phát triển các mô hình lớn hướng tới ra quyết định tự động của tác nhân, bao gồm sự khác biệt số học giữa huấn luyện và suy luận, việc sử dụng thấp tài nguyên tính toán không đồng nhất, và các vấn đề toàn cầu do hiệu ứng đuôi dài gây ra. Ngoài ra, các kỹ thuật huấn luyện sau quy mô lớn dành cho tác nhân được áp dụng để tăng cường hơn nữa khả năng và hiệu suất tổng quát của mô hình. Thông qua khung hợp tác ba giai đoạn bao gồm môi trường, chuyên gia và quy trình hợp nhất, cách tiếp cận này không chỉ đảm bảo hiệu quả huấn luyện mà còn cải thiện đáng kể sự ổn định và hiệu suất của mô hình trong các nhiệm vụ phức tạp.", "ernie-char-fiction-8k-preview.description": "ERNIE Character Fiction 8K Preview là bản xem trước mô hình tạo nhân vật và cốt truyện để đánh giá và thử nghiệm tính năng.", "ernie-char-fiction-8k.description": "ERNIE Character Fiction 8K là mô hình nhân vật dành cho tiểu thuyết và sáng tạo cốt truyện, phù hợp để tạo truyện dài.", "ernie-image-turbo.description": "ERNIE-Image là mô hình chuyển văn bản thành hình ảnh với 8 tỷ tham số được phát triển bởi Baidu. Nó nằm trong số những mô hình hàng đầu trên nhiều tiêu chuẩn, đạt vị trí đầu tiên trong SuperCLUE tại Trung Quốc và dẫn đầu trong lĩnh vực mã nguồn mở.", @@ -517,8 +515,7 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K là mô hình tư duy nhanh với ngữ cảnh 32K dành cho lý luận phức tạp và trò chuyện nhiều lượt.", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview là bản xem trước mô hình tư duy để đánh giá và thử nghiệm.", "ernie-x1.1.description": "ERNIE X1.1 là mô hình suy nghĩ thử nghiệm dành cho đánh giá và kiểm tra.", - "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5, được phát triển bởi đội ngũ Seed của ByteDance, hỗ trợ chỉnh sửa và ghép nhiều hình ảnh. Tính năng bao gồm cải thiện tính nhất quán của chủ thể, tuân thủ hướng dẫn chính xác, hiểu biết logic không gian, biểu đạt thẩm mỹ, bố cục poster và thiết kế logo với khả năng kết xuất văn bản-hình ảnh chính xác cao.", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0, được phát triển bởi ByteDance Seed, hỗ trợ đầu vào văn bản và hình ảnh để tạo hình ảnh chất lượng cao, có khả năng kiểm soát cao từ các gợi ý.", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 là mô hình tạo hình ảnh từ ByteDance Seed, hỗ trợ đầu vào văn bản và hình ảnh với khả năng tạo hình ảnh chất lượng cao, có tính kiểm soát cao. Nó tạo ra hình ảnh từ các gợi ý văn bản.", "fal-ai/flux-kontext/dev.description": "Mô hình FLUX.1 tập trung vào chỉnh sửa hình ảnh, hỗ trợ đầu vào văn bản và hình ảnh.", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] chấp nhận đầu vào là văn bản và hình ảnh tham chiếu, cho phép chỉnh sửa cục bộ chính xác và biến đổi toàn cảnh phức tạp.", "fal-ai/flux/krea.description": "Flux Krea [dev] là mô hình tạo hình ảnh với thiên hướng thẩm mỹ hướng đến hình ảnh chân thực và tự nhiên hơn.", @@ -526,8 +523,8 @@ "fal-ai/hunyuan-image/v3.description": "Mô hình tạo hình ảnh đa phương thức mạnh mẽ bản địa.", "fal-ai/imagen4/preview.description": "Mô hình tạo hình ảnh chất lượng cao từ Google.", "fal-ai/nano-banana.description": "Nano Banana là mô hình đa phương thức bản địa mới nhất, nhanh nhất và hiệu quả nhất của Google, cho phép tạo và chỉnh sửa hình ảnh thông qua hội thoại.", - "fal-ai/qwen-image-edit.description": "Mô hình chỉnh sửa hình ảnh chuyên nghiệp từ đội ngũ Qwen, hỗ trợ chỉnh sửa ngữ nghĩa và ngoại hình, chỉnh sửa văn bản tiếng Trung/Anh chính xác, chuyển đổi phong cách, xoay hình và nhiều hơn nữa.", - "fal-ai/qwen-image.description": "Mô hình tạo hình ảnh mạnh mẽ từ đội ngũ Qwen với khả năng kết xuất văn bản tiếng Trung mạnh mẽ và phong cách hình ảnh đa dạng.", + "fal-ai/qwen-image-edit.description": "Một mô hình chỉnh sửa hình ảnh chuyên nghiệp từ đội ngũ Qwen, hỗ trợ chỉnh sửa ngữ nghĩa và hình thức, chỉnh sửa chính xác văn bản tiếng Trung và tiếng Anh, và cho phép các chỉnh sửa chất lượng cao như chuyển đổi phong cách và xoay đối tượng.", + "fal-ai/qwen-image.description": "Một mô hình tạo hình ảnh mạnh mẽ từ đội ngũ Qwen với khả năng hiển thị văn bản tiếng Trung ấn tượng và các phong cách hình ảnh đa dạng.", "flux-1-schnell.description": "Mô hình chuyển văn bản thành hình ảnh với 12 tỷ tham số từ Black Forest Labs, sử dụng phương pháp khuếch tán đối kháng tiềm ẩn để tạo hình ảnh chất lượng cao chỉ trong 1–4 bước. Mô hình cạnh tranh với các lựa chọn đóng và được phát hành theo giấy phép Apache-2.0 cho mục đích cá nhân, nghiên cứu và thương mại.", "flux-dev.description": "Mô hình tạo ảnh R&D mã nguồn mở, tối ưu hiệu quả cho nghiên cứu sáng tạo phi thương mại.", "flux-kontext-max.description": "Tạo và chỉnh sửa hình ảnh theo ngữ cảnh tiên tiến, kết hợp văn bản và hình ảnh để tạo ra kết quả chính xác và mạch lạc.", @@ -569,7 +566,7 @@ "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image (Nano Banana Pro) là mô hình tạo hình ảnh của Google và cũng hỗ trợ trò chuyện đa phương thức.", "gemini-3-pro-preview.description": "Gemini 3 Pro là mô hình mạnh mẽ nhất của Google, kết hợp khả năng mã hóa cảm xúc và suy luận tiên tiến, mang đến hình ảnh phong phú và tương tác sâu sắc.", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) là mô hình tạo hình ảnh bản địa nhanh nhất của Google với hỗ trợ suy nghĩ, tạo hình ảnh đối thoại và chỉnh sửa.", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) cung cấp chất lượng hình ảnh cấp độ Pro với tốc độ Flash và hỗ trợ trò chuyện đa phương thức.", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image (Nano Banana 2) là mô hình tạo hình ảnh nhanh nhất của Google với hỗ trợ tư duy, tạo và chỉnh sửa hình ảnh trong hội thoại.", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview là mô hình đa phương thức tiết kiệm chi phí nhất của Google, được tối ưu hóa cho các nhiệm vụ tác nhân khối lượng lớn, dịch thuật và xử lý dữ liệu.", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite là mô hình đa phương thức tiết kiệm chi phí nhất của Google, được tối ưu hóa cho các nhiệm vụ đại lý khối lượng lớn, dịch thuật và xử lý dữ liệu.", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview cải tiến Gemini 3 Pro với khả năng suy luận nâng cao và bổ sung hỗ trợ mức suy nghĩ trung bình.", @@ -734,8 +731,6 @@ "grok-4-fast-reasoning.description": "Chúng tôi rất vui mừng ra mắt Grok 4 Fast, bước tiến mới nhất trong các mô hình suy luận tiết kiệm chi phí.", "grok-4.20-0309-non-reasoning.description": "Biến thể không lý luận dành cho các trường hợp sử dụng đơn giản.", "grok-4.20-0309-reasoning.description": "Mô hình thông minh, siêu nhanh, lý luận trước khi phản hồi.", - "grok-4.20-beta-0309-non-reasoning.description": "Biến thể không tư duy dành cho các trường hợp sử dụng đơn giản.", - "grok-4.20-beta-0309-reasoning.description": "Mô hình thông minh, cực nhanh, có khả năng tư duy trước khi phản hồi.", "grok-4.20-multi-agent-0309.description": "Một nhóm gồm 4 hoặc 16 tác nhân, xuất sắc trong các trường hợp nghiên cứu. Hiện không hỗ trợ công cụ phía khách hàng. Chỉ hỗ trợ công cụ phía máy chủ xAI (ví dụ: X Search, công cụ tìm kiếm web) và công cụ MCP từ xa.", "grok-4.3.description": "Mô hình ngôn ngữ lớn tìm kiếm sự thật nhất trên thế giới", "grok-4.description": "Mô hình Grok hàng đầu mới nhất với hiệu suất vượt trội trong ngôn ngữ, toán học và suy luận — một mô hình toàn diện thực sự. Hiện tại đang trỏ đến grok-4-0709; do nguồn lực hạn chế, giá tạm thời cao hơn 10% so với giá chính thức và dự kiến sẽ trở lại giá chính thức sau.", @@ -1220,8 +1215,6 @@ "qwq.description": "QwQ là mô hình lập luận trong họ Qwen. So với các mô hình điều chỉnh theo hướng dẫn tiêu chuẩn, nó mang lại khả năng tư duy và lập luận giúp cải thiện đáng kể hiệu suất các tác vụ phía sau, đặc biệt là các vấn đề khó. QwQ-32B là mô hình lập luận tầm trung có thể cạnh tranh với các mô hình hàng đầu như DeepSeek-R1 và o1-mini.", "qwq_32b.description": "Mô hình lập luận tầm trung trong họ Qwen. So với các mô hình điều chỉnh theo hướng dẫn tiêu chuẩn, khả năng tư duy và lập luận của QwQ giúp cải thiện đáng kể hiệu suất các tác vụ phía sau, đặc biệt là các vấn đề khó.", "r1-1776.description": "R1-1776 là biến thể hậu huấn luyện của DeepSeek R1 được thiết kế để cung cấp thông tin thực tế không kiểm duyệt, không thiên lệch.", - "seedance-1-5-pro-251215.description": "Seedance 1.5 Pro của ByteDance hỗ trợ chuyển văn bản thành video, chuyển hình ảnh thành video (khung đầu tiên, khung đầu tiên + khung cuối cùng) và tạo âm thanh đồng bộ với hình ảnh.", - "seedream-5-0-260128.description": "ByteDance-Seedream-5.0-lite của BytePlus có tính năng tạo nội dung tăng cường truy xuất web để cung cấp thông tin theo thời gian thực, cải thiện diễn giải gợi ý phức tạp và tăng cường tính nhất quán tham chiếu cho sáng tạo hình ảnh chuyên nghiệp.", "solar-mini-ja.description": "Solar Mini (Ja) mở rộng Solar Mini với trọng tâm vào tiếng Nhật trong khi vẫn duy trì hiệu suất mạnh mẽ và hiệu quả với tiếng Anh và tiếng Hàn.", "solar-mini.description": "Solar Mini là mô hình ngôn ngữ nhỏ gọn vượt trội hơn GPT-3.5, với khả năng đa ngôn ngữ mạnh mẽ hỗ trợ tiếng Anh và tiếng Hàn, mang lại giải pháp hiệu quả với dung lượng nhỏ.", "solar-pro.description": "Solar Pro là mô hình ngôn ngữ thông minh cao từ Upstage, tập trung vào tuân thủ hướng dẫn trên một GPU duy nhất, với điểm IFEval trên 80. Hiện hỗ trợ tiếng Anh; bản phát hành đầy đủ dự kiến vào tháng 11 năm 2024 với hỗ trợ ngôn ngữ mở rộng và ngữ cảnh dài hơn.", diff --git a/locales/vi-VN/onboarding.json b/locales/vi-VN/onboarding.json index 5fff6b171c..12d6843534 100644 --- a/locales/vi-VN/onboarding.json +++ b/locales/vi-VN/onboarding.json @@ -27,6 +27,13 @@ "agent.layout.skipConfirm.ok": "Tạm thời bỏ qua", "agent.layout.skipConfirm.title": "Bỏ qua phần giới thiệu chứ?", "agent.layout.switchMessage": "Hôm nay không thoải mái à? Bạn có thể chuyển sang {{mode}} hoặc {{skip}}.", + "agent.layout.switchMessageClassic": "Không cảm thấy phù hợp hôm nay? Bạn có thể chuyển sang {{mode}}.", + "agent.messenger.cta.discord": "Thêm vào Discord", + "agent.messenger.cta.slack": "Thêm vào Slack", + "agent.messenger.cta.telegram": "Mở trong Telegram", + "agent.messenger.subtitle": "Trò chuyện với trợ lý của bạn trên Telegram, Slack hoặc Discord", + "agent.messenger.telegramQrCaption": "Quét bằng camera điện thoại của bạn", + "agent.messenger.title": "Giữ tôi bên bạn, bất cứ nơi nào bạn nhắn tin", "agent.modeSwitch.agent": "Hội thoại", "agent.modeSwitch.classic": "Cổ điển", "agent.modeSwitch.collapse": "Thu gọn", @@ -68,6 +75,13 @@ "agent.wrapUp.confirm.content": "Tôi sẽ lưu lại những gì chúng ta đã trao đổi. Bạn luôn có thể quay lại trò chuyện thêm sau.", "agent.wrapUp.confirm.ok": "Kết thúc ngay", "agent.wrapUp.confirm.title": "Kết thúc onboarding bây giờ?", + "agentPicker.allCategories": "Tất cả", + "agentPicker.continue": "Tiếp tục", + "agentPicker.skip": "Bỏ qua bây giờ", + "agentPicker.subtitle": "Thêm một vài vào thư viện của bạn ngay bây giờ — khám phá thêm bất cứ lúc nào sau này.", + "agentPicker.title": "Hãy thêm một vài trợ lý vào thư viện của bạn", + "agentPicker.title2": "Chọn những cái phù hợp với cách bạn làm việc", + "agentPicker.title3": "Bạn luôn có thể thêm nhiều hơn sau này — bắt đầu với một vài cái", "back": "Quay lại", "finish": "Bắt đầu ngay", "interests.area.business": "Kinh doanh & Chiến lược", diff --git a/locales/vi-VN/plugin.json b/locales/vi-VN/plugin.json index c63cd6c7ea..d932bec025 100644 --- a/locales/vi-VN/plugin.json +++ b/locales/vi-VN/plugin.json @@ -39,8 +39,8 @@ "builtins.lobe-agent-documents.inspector.docCount": "{{count}} tài liệu", "builtins.lobe-agent-documents.inspector.opsCount": "{{count}} thao tác", "builtins.lobe-agent-documents.inspector.opsResult": "{{success}}/{{total}} thao tác", - "builtins.lobe-agent-documents.inspector.target.agent": "trong tác nhân", - "builtins.lobe-agent-documents.inspector.target.currentTopic": "trong chủ đề", + "builtins.lobe-agent-documents.inspector.scope.agent": "trong tác nhân", + "builtins.lobe-agent-documents.inspector.scope.currentTopic": "trong chủ đề", "builtins.lobe-agent-documents.title": "Tài liệu của Agent", "builtins.lobe-agent-management.apiName.callAgent": "Gọi đại lý", "builtins.lobe-agent-management.apiName.createAgent": "Tạo đại lý", @@ -90,6 +90,14 @@ "builtins.lobe-agent.title": "Tác nhân Lobe", "builtins.lobe-claude-code.agent.instruction": "Hướng dẫn", "builtins.lobe-claude-code.agent.result": "Kết quả", + "builtins.lobe-claude-code.task.createLabel": "Đang tạo nhiệm vụ: ", + "builtins.lobe-claude-code.task.getLabel": "Đang kiểm tra nhiệm vụ #{{taskId}}", + "builtins.lobe-claude-code.task.listLabel": "Đang liệt kê các nhiệm vụ", + "builtins.lobe-claude-code.task.updateCompleted": "Hoàn thành", + "builtins.lobe-claude-code.task.updateDeleted": "Đã xóa", + "builtins.lobe-claude-code.task.updateInProgress": "Đã bắt đầu", + "builtins.lobe-claude-code.task.updateLabel": "Đang cập nhật nhiệm vụ #{{taskId}}", + "builtins.lobe-claude-code.task.updatePending": "Đặt lại", "builtins.lobe-claude-code.todoWrite.allDone": "Tất cả nhiệm vụ đã hoàn thành", "builtins.lobe-claude-code.todoWrite.currentStep": "Bước hiện tại", "builtins.lobe-claude-code.todoWrite.todos": "Công việc cần làm", diff --git a/locales/vi-VN/providers.json b/locales/vi-VN/providers.json index 26790e7dda..f02a27c541 100644 --- a/locales/vi-VN/providers.json +++ b/locales/vi-VN/providers.json @@ -33,7 +33,6 @@ "jina.description": "Thành lập năm 2020, Jina AI là công ty hàng đầu về AI tìm kiếm. Bộ công cụ tìm kiếm của họ bao gồm mô hình vector, bộ xếp hạng lại và mô hình ngôn ngữ nhỏ để xây dựng ứng dụng tìm kiếm sinh và đa phương thức chất lượng cao.", "kimicodingplan.description": "Kimi Code từ Moonshot AI cung cấp quyền truy cập vào các mô hình Kimi bao gồm K2.5 cho các nhiệm vụ lập trình.", "lmstudio.description": "LM Studio là ứng dụng máy tính để phát triển và thử nghiệm LLM ngay trên máy của bạn.", - "lobehub.description": "LobeHub Cloud sử dụng các API chính thức để truy cập các mô hình AI và đo lường việc sử dụng bằng Tín dụng gắn liền với các token của mô hình.", "longcat.description": "LongCat là một loạt các mô hình AI tạo sinh lớn được phát triển độc lập bởi Meituan. Nó được thiết kế để nâng cao năng suất nội bộ của doanh nghiệp và thúc đẩy các ứng dụng sáng tạo thông qua kiến trúc tính toán hiệu quả và khả năng đa phương thức mạnh mẽ.", "minimax.description": "Thành lập năm 2021, MiniMax xây dựng AI đa năng với các mô hình nền tảng đa phương thức, bao gồm mô hình văn bản MoE hàng nghìn tỷ tham số, mô hình giọng nói và thị giác, cùng các ứng dụng như Hailuo AI.", "minimaxcodingplan.description": "MiniMax Token Plan cung cấp quyền truy cập vào các mô hình MiniMax bao gồm M2.7 cho các nhiệm vụ lập trình thông qua gói đăng ký cố định.", diff --git a/locales/vi-VN/subscription.json b/locales/vi-VN/subscription.json index 8285dc7f70..06a95bf15b 100644 --- a/locales/vi-VN/subscription.json +++ b/locales/vi-VN/subscription.json @@ -55,6 +55,12 @@ "credits.autoTopUp.toggle": "Bật tự động nạp tiền", "credits.autoTopUp.upgradeHint": "Đăng ký gói trả phí để bật tự động nạp tiền", "credits.autoTopUp.validation.targetMustExceedThreshold": "Số dư mục tiêu phải lớn hơn ngưỡng", + "credits.costEstimateHint.desc": "Hiển thị cảnh báo nhẹ trước khi gửi khi chi phí ước tính của mô hình đạt ngưỡng của bạn", + "credits.costEstimateHint.saveError": "Không thể lưu cài đặt cảnh báo ước tính chi phí", + "credits.costEstimateHint.saveSuccess": "Đã lưu cài đặt cảnh báo ước tính chi phí", + "credits.costEstimateHint.threshold": "Ngưỡng Cảnh Báo", + "credits.costEstimateHint.title": "Cảnh Báo Ước Tính Chi Phí", + "credits.costEstimateHint.validation.threshold": "Ngưỡng phải lớn hơn hoặc bằng 0", "credits.packages.auto": "Tự động", "credits.packages.charged": "Đã tính phí ${{amount}}", "credits.packages.expired": "Đã hết hạn", diff --git a/locales/vi-VN/tool.json b/locales/vi-VN/tool.json index 4619abd024..fb4555329b 100644 --- a/locales/vi-VN/tool.json +++ b/locales/vi-VN/tool.json @@ -40,6 +40,14 @@ "agentMarketplace.render.alreadyInLibraryTag": "Đã có trong thư viện", "agentMarketplace.render.alreadyInLibrary_one": "{{count}} đã có trong thư viện", "agentMarketplace.render.alreadyInLibrary_other": "{{count}} đã có trong thư viện", + "claudeCode.askUserQuestion.escape.back": "Quay lại các tùy chọn", + "claudeCode.askUserQuestion.escape.enter": "Hoặc nhập trực tiếp", + "claudeCode.askUserQuestion.escape.placeholder": "Nhập câu trả lời của bạn tại đây…", + "claudeCode.askUserQuestion.multiSelectTag": "(chọn nhiều)", + "claudeCode.askUserQuestion.skip": "Bỏ qua", + "claudeCode.askUserQuestion.submit": "Gửi", + "claudeCode.askUserQuestion.timeExpired": "Hết thời gian — sử dụng tùy chọn 1 cho mỗi câu hỏi.", + "claudeCode.askUserQuestion.timeRemaining": "Thời gian còn lại: {{time}} · các câu hỏi chưa trả lời sẽ mặc định chọn tùy chọn 1 khi hết giờ.", "codeInterpreter-legacy.error": "Lỗi thực thi", "codeInterpreter-legacy.executing": "Đang thực thi...", "codeInterpreter-legacy.files": "Tệp:", diff --git a/locales/vi-VN/topic.json b/locales/vi-VN/topic.json index 94a1763e16..4ad0ce4b6e 100644 --- a/locales/vi-VN/topic.json +++ b/locales/vi-VN/topic.json @@ -56,6 +56,7 @@ "renameModal.title": "Đổi tên chủ đề", "searchPlaceholder": "Tìm kiếm chủ đề...", "searchResultEmpty": "Không tìm thấy kết quả tìm kiếm.", + "sidebar.title": "Chủ đề", "taskManager.agent": "Tác vụ Agent", "taskManager.welcome": "Hãy hỏi tôi về các công việc của bạn", "temp": "Tạm thời", diff --git a/locales/zh-CN/chat.json b/locales/zh-CN/chat.json index 7b8dd3fd98..69e71dcc36 100644 --- a/locales/zh-CN/chat.json +++ b/locales/zh-CN/chat.json @@ -220,13 +220,13 @@ "inbox.title": "Lobe AI", "input.addAi": "添加一条助理消息", "input.addUser": "添加一条用户消息", - "input.costEstimate.creditsPerMillionTokens": "{{credits}} 积分/M tokens", - "input.costEstimate.hint": "预估费用:约 {{credits}} 积分", + "input.costEstimate.creditsPerMillionTokens": "{{credits}} 积分/百万标记", + "input.costEstimate.hint": "预计费用:约{{credits}}积分", "input.costEstimate.inputLabel": "输入", "input.costEstimate.outputLabel": "输出", - "input.costEstimate.settingsLink": "调整提醒阈值", - "input.costEstimate.tokenCount": "约 {{tokens}} tokens", - "input.costEstimate.tooltip": "基于当前上下文、工具和模型定价估算。实际费用可能不同。", + "input.costEstimate.settingsLink": "调整警告阈值", + "input.costEstimate.tokenCount": "约{{tokens}}标记", + "input.costEstimate.tooltip": "根据当前上下文、工具和模型定价估算。实际费用可能有所不同。", "input.disclaimer": "助理也可能出错。关键信息请以你的判断为准", "input.errorMsg": "发送遇到了问题:{{errorMsg}}。你可以重试,或稍后再发", "input.more": "更多", @@ -786,6 +786,7 @@ "upload.preview.prepareTasks": "正在准备分块…", "upload.preview.status.pending": "准备上传…", "upload.preview.status.processing": "文件处理中…", + "upload.validation.unsupportedFileType": "不支持的文件类型:{{files}}。支持的图片格式:JPG、PNG、GIF、WebP。支持的文档包括PDF、Word、Excel、PowerPoint、Markdown、文本、CSV、JSON和代码文件。", "upload.validation.videoSizeExceeded": "视频文件不能超过 20MB。当前为 {{actualSize}}", "viewMode.fullWidth": "全宽显示", "viewMode.normal": "普通", diff --git a/locales/zh-CN/components.json b/locales/zh-CN/components.json index d00fbea2ef..9470e31cae 100644 --- a/locales/zh-CN/components.json +++ b/locales/zh-CN/components.json @@ -121,16 +121,16 @@ "ModelSwitchPanel.detail.context": "上下文长度", "ModelSwitchPanel.detail.pricing": "价格", "ModelSwitchPanel.detail.pricing.cachedInput": "缓存输入 ${{amount}}/百万", - "ModelSwitchPanel.detail.pricing.credits.cachedInput": "缓存输入 {{amount}} 积分/M tokens", - "ModelSwitchPanel.detail.pricing.credits.image": "积分/张", - "ModelSwitchPanel.detail.pricing.credits.input": "输入 {{amount}} 积分/M tokens", - "ModelSwitchPanel.detail.pricing.credits.megapixel": "积分/MP", - "ModelSwitchPanel.detail.pricing.credits.millionCharacters": "积分/M chars", - "ModelSwitchPanel.detail.pricing.credits.millionTokens": "积分/M tokens", - "ModelSwitchPanel.detail.pricing.credits.output": "输出 {{amount}} 积分/M tokens", - "ModelSwitchPanel.detail.pricing.credits.perImage": "约 {{amount}} 积分/张", - "ModelSwitchPanel.detail.pricing.credits.perVideo": "约 {{amount}} 积分/条", - "ModelSwitchPanel.detail.pricing.credits.second": "积分/s", + "ModelSwitchPanel.detail.pricing.credits.cachedInput": "缓存输入 {{amount}} 积分/M 令牌", + "ModelSwitchPanel.detail.pricing.credits.image": "积分/图片", + "ModelSwitchPanel.detail.pricing.credits.input": "输入 {{amount}} 积分/M 令牌", + "ModelSwitchPanel.detail.pricing.credits.megapixel": "积分/百万像素", + "ModelSwitchPanel.detail.pricing.credits.millionCharacters": "积分/百万字符", + "ModelSwitchPanel.detail.pricing.credits.millionTokens": "积分/百万令牌", + "ModelSwitchPanel.detail.pricing.credits.output": "输出 {{amount}} 积分/M 令牌", + "ModelSwitchPanel.detail.pricing.credits.perImage": "约 {{amount}} 积分 / 图片", + "ModelSwitchPanel.detail.pricing.credits.perVideo": "约 {{amount}} 积分 / 视频", + "ModelSwitchPanel.detail.pricing.credits.second": "积分/秒", "ModelSwitchPanel.detail.pricing.group.audio": "音频", "ModelSwitchPanel.detail.pricing.group.image": "图像", "ModelSwitchPanel.detail.pricing.group.text": "文本", diff --git a/locales/zh-CN/hotkey.json b/locales/zh-CN/hotkey.json index 1a6bd64708..6ac063772e 100644 --- a/locales/zh-CN/hotkey.json +++ b/locales/zh-CN/hotkey.json @@ -1,8 +1,6 @@ { "addUserMessage.desc": "将当前输入内容添加为用户消息,但不触发生成", "addUserMessage.title": "添加一条用户消息", - "clearCurrentMessages.desc": "清空当前会话的消息和上传的文件", - "clearCurrentMessages.title": "清空会话消息", "commandPalette.desc": "打开全局命令面板快速访问功能", "commandPalette.title": "命令面板", "deleteAndRegenerateMessage.desc": "删除最后一条消息并重新生成", diff --git a/locales/zh-CN/models.json b/locales/zh-CN/models.json index 8319380d37..f3343819cc 100644 --- a/locales/zh-CN/models.json +++ b/locales/zh-CN/models.json @@ -106,7 +106,6 @@ "MiniMax-Hailuo-2.3.description": "全新视频生成模型,在身体动作、物理真实感和指令遵循性方面全面升级。", "MiniMax-M1.description": "一款全新自研推理模型,支持 80K 思维链和 100 万输入,性能媲美全球顶尖模型。", "MiniMax-M2-Stable.description": "专为高效编程与智能体工作流打造,具备更高并发能力,适用于商业场景。", - "MiniMax-M2.1-Lightning.description": "强大的多语言编程能力,推理速度更快、更高效。", "MiniMax-M2.1-highspeed.description": "强大的多语言编程能力,全面升级的编程体验。更快、更高效。", "MiniMax-M2.1.description": "MiniMax-M2.1 是 MiniMax 推出的旗舰开源大模型,专注于解决复杂的现实世界任务。其核心优势在于多语言编程能力以及作为智能体解决复杂任务的能力。", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 Highspeed:与M2.5性能相同,但推理速度更快。", @@ -315,13 +314,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku 是 Anthropic 推出的最快、最紧凑的模型,专为近乎即时响应而设计,具备快速且准确的性能。", "claude-3-opus-20240229.description": "Claude 3 Opus 是 Anthropic 最强大的模型,适用于高度复杂的任务,在性能、智能、流畅性和理解力方面表现卓越。", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet 在智能与速度之间取得平衡,适用于企业级工作负载,提供高效能与低成本的可靠部署。", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 是 Anthropic 最快、最智能的 Haiku 模型,具有闪电般的速度和扩展的思维能力。", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 是 Anthropic 最快速、最智能的 Haiku 模型,具有闪电般的速度和扩展的推理能力。", "claude-haiku-4-5.description": "Anthropic 的 Claude Haiku 4.5 —— 新一代 Haiku,具备更强推理与视觉能力。", "claude-haiku-4.5.description": "Claude Haiku 4.5 是 Anthropic 最快速、最智能的 Haiku 模型,具有闪电般的速度和扩展的推理能力。", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking 是一款高级变体,能够展示其推理过程。", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1 是 Anthropic 最新且最强大的模型,专为处理高度复杂的任务而设计,表现卓越,智能、流畅性和理解力均出类拔萃。", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1 是 Anthropic 最新且最强大的模型,专为高度复杂的任务设计,表现出卓越的性能、智能、流畅性和理解力。", "claude-opus-4-1.description": "Anthropic 的 Claude Opus 4.1 —— 高端推理模型,具备深度分析能力。", - "claude-opus-4-20250514.description": "Claude Opus 4 是 Anthropic 最强大的模型,专为处理高度复杂的任务而设计,表现卓越,智能、流畅性和理解力均出类拔萃。", + "claude-opus-4-20250514.description": "Claude Opus 4 是 Anthropic 最强大的模型,专为高度复杂的任务设计,表现出卓越的性能、智能、流畅性和理解力。", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 是 Anthropic 的旗舰模型,结合卓越智能与可扩展性能,适用于需要最高质量响应与推理的复杂任务。", "claude-opus-4-5.description": "Anthropic 的 Claude Opus 4.5 —— 旗舰模型,具备顶级推理与编码能力。", "claude-opus-4-6.description": "Anthropic 的 Claude Opus 4.6 —— 具备 100 万上下文窗口的旗舰模型,拥有先进推理能力。", @@ -330,7 +329,7 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 是 Anthropic 最智能的模型,用于构建代理和编程。", "claude-opus-4.6.description": "Claude Opus 4.6 是 Anthropic 最智能的模型,用于构建代理和编程。", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking 可生成近乎即时的响应或可视化的逐步推理过程。", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 是迄今为止 Anthropic 最智能的模型,提供近乎即时的响应或扩展的逐步思考,并为 API 用户提供精细化控制。", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 能够生成几乎即时的响应,或通过可见的过程进行逐步推理。", "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 是迄今为止 Anthropic 最智能的模型。", "claude-sonnet-4-5.description": "Anthropic 的 Claude Sonnet 4.5 —— 性能增强的新一代 Sonnet,具备更强编码能力。", "claude-sonnet-4-6.description": "Anthropic 的 Claude Sonnet 4.6 —— 最新 Sonnet 模型,在编码与工具使用方面表现更强。", @@ -404,7 +403,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat(67B)是一款创新模型,具备深度语言理解与交互能力。", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 是下一代推理模型,具备更强的复杂推理与链式思维能力,适用于深度分析任务。", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2是下一代推理模型,具备更强的复杂推理和链式思维能力。", - "deepseek-chat.description": "DeepSeek V4 Flash 非思考模式的兼容别名。计划弃用——请改用 DeepSeek V4 Flash。", + "deepseek-chat.description": "一个结合了通用能力和代码能力的新开源模型。它保留了聊天模型的通用对话能力和编码模型的强大编程能力,并具有更好的偏好对齐。DeepSeek-V2.5 还改进了写作和指令遵循能力。", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B 是一款代码语言模型,训练于 2T 数据(87% 代码,13% 中英文文本)。支持 16K 上下文窗口与中间填充任务,提供项目级代码补全与片段填充。", "deepseek-coder-v2.description": "DeepSeek Coder V2 是一款开源 MoE 编程模型,在编程任务中表现强劲,可媲美 GPT-4 Turbo。", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 是一款开源 MoE 编程模型,在编程任务中表现强劲,可媲美 GPT-4 Turbo。", @@ -426,7 +425,7 @@ "deepseek-r1-fast-online.description": "DeepSeek R1 快速全量版本,支持实时网页搜索,结合 671B 规模能力与更快响应。", "deepseek-r1-online.description": "DeepSeek R1 全量版本,具备 671B 参数与实时网页搜索,提供更强理解与生成能力。", "deepseek-r1.description": "DeepSeek-R1 在强化学习前使用冷启动数据,在数学、编程和推理任务中表现可与 OpenAI-o1 相媲美。", - "deepseek-reasoner.description": "DeepSeek V4 Flash 思考模式的兼容别名。计划弃用——请改用 DeepSeek V4 Flash。", + "deepseek-reasoner.description": "一个专注于复杂逻辑推理任务的 DeepSeek 推理模型。", "deepseek-v2.description": "DeepSeek V2 是一款高效的 MoE 模型,适用于成本敏感型处理任务。", "deepseek-v2:236b.description": "DeepSeek V2 236B 是 DeepSeek 推出的代码专用模型,具备强大代码生成能力。", "deepseek-v3-0324.description": "DeepSeek-V3-0324 是一款拥有 671B 参数的 MoE 模型,在编程与技术能力、上下文理解和长文本处理方面表现突出。", @@ -491,8 +490,6 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 是字节跳动 Seed 推出的图像生成模型,支持文本与图像输入,具备高度可控的高质量图像生成能力。可根据文本提示生成图像。", "doubao-seedream-4-5-251128.description": "Seedream 4.5是字节跳动最新的多模态图像模型,集成了文本生成图像、图像生成图像和批量图像生成功能,同时具备常识和推理能力。与之前的4.0版本相比,生成质量显著提升,编辑一致性和多图融合效果更好。它对视觉细节的控制更加精确,能够更自然地生成小文字和小面部,并实现更和谐的布局和色彩,提升整体美感。", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite是字节跳动最新的图像生成模型。首次集成在线检索功能,能够结合实时网络信息,提升生成图像的时效性。模型智能性也得到升级,能够精确解析复杂指令和视觉内容。此外,它在专业场景中的全球知识覆盖、参考一致性和生成质量方面均有提升,更好地满足企业级视觉创作需求。", - "dreamina-seedance-2-0-260128.description": "字节跳动推出的 Seedance 2.0 是最强大的视频生成模型,支持多模态参考视频生成、视频编辑、视频扩展、文本生成视频以及图像生成视频,并同步音频。", - "dreamina-seedance-2-0-fast-260128.description": "字节跳动推出的 Seedance 2.0 Fast 提供与 Seedance 2.0 相同的功能,但生成速度更快,价格更具竞争力。", "emohaa.description": "Emohaa 是一款心理健康模型,具备专业咨询能力,帮助用户理解情绪问题。", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B 是一款开源轻量级模型,适用于本地和定制化部署。", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview 是一款用于评估 ERNIE 4.5 的 8K 上下文预览模型。", @@ -506,6 +503,7 @@ "ernie-5.0-thinking-latest.description": "文心 5.0 Thinking 是一款原生全模态旗舰模型,统一建模文本、图像、音频与视频,在复杂问答、创作与智能体场景中实现全面能力升级。", "ernie-5.0-thinking-preview.description": "文心 5.0 Thinking Preview 是一款原生全模态旗舰模型,统一建模文本、图像、音频与视频,在复杂问答、创作与智能体场景中实现全面能力升级。", "ernie-5.0.description": "ERNIE 5.0 是 ERNIE 系列的新一代原生多模态大模型。通过统一多模态建模方法,对文本、图像、音频和视频进行联合建模,提供全面的多模态能力。其基础能力全面升级,在多项基准测试中取得强劲表现,尤其在多模态理解、指令跟随、创意写作、事实准确性、Agent 规划和工具调用方面表现突出。", + "ernie-5.1.description": "ERNIE 5.1 是 ERNIE 系列的最新模型,其基础能力全面升级。在代理、知识处理、推理和深度搜索等领域表现出显著改进。本次发布采用了解耦的全异步强化学习架构,专为解决大模型向自主代理决策演进中的关键挑战而设计,包括训练与推理数值差异、异构计算资源利用率低以及长尾效应引发的全局问题。此外,还采用了大规模代理后训练技术,进一步增强了模型的能力和泛化性能。通过环境、专家和融合过程的三阶段协作框架,这种方法不仅确保了训练效率,还显著提升了模型在复杂任务中的稳定性和表现。", "ernie-char-fiction-8k-preview.description": "ERNIE Character Fiction 8K 预览版是一款用于角色与情节创作的模型预览,适用于功能评估与测试。", "ernie-char-fiction-8k.description": "ERNIE Character Fiction 8K 是一款面向小说与情节创作的角色模型,适合长篇故事生成。", "ernie-image-turbo.description": "ERNIE-Image 是百度推出的 80 亿参数文生图模型,在多项评测中名列前茅,在中国 SuperCLUE 上并列第一,并在开源赛道保持领先。", @@ -517,8 +515,7 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K 是一款快速思考模型,具备 32K 上下文能力,适合复杂推理与多轮对话。", "ernie-x1.1-preview.description": "ERNIE X1.1 Preview 是一款用于评估与测试的思考模型预览版。", "ernie-x1.1.description": "ERNIE X1.1是一个用于评估和测试的思维模型预览版。", - "fal-ai/bytedance/seedream/v4.5.description": "字节跳动 Seed 团队打造的 Seedream 4.5 支持多图编辑与合成,具备增强的主体一致性、精准的指令执行、空间逻辑理解、美学表达、海报布局和标志设计,并提供高精度的图文渲染。", - "fal-ai/bytedance/seedream/v4.description": "字节跳动 Seed 团队打造的 Seedream 4.0 支持文本和图像输入,可根据提示生成高质量、可控性强的图像。", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 是字节跳动 Seed 的图像生成模型,支持文本和图像输入,能够生成高度可控、高质量的图像。它可以根据文本提示生成图像。", "fal-ai/flux-kontext/dev.description": "FLUX.1 模型专注于图像编辑,支持文本与图像输入。", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] 接受文本与参考图像输入,支持局部精准编辑与复杂全局场景变换。", "fal-ai/flux/krea.description": "Flux Krea [dev] 是一款图像生成模型,偏好更真实自然的美学风格。", @@ -526,8 +523,8 @@ "fal-ai/hunyuan-image/v3.description": "一款强大的原生多模态图像生成模型。", "fal-ai/imagen4/preview.description": "来自 Google 的高质量图像生成模型。", "fal-ai/nano-banana.description": "Nano Banana 是 Google 最新、最快、最高效的原生多模态模型,支持通过对话生成与编辑图像。", - "fal-ai/qwen-image-edit.description": "Qwen 团队推出的专业图像编辑模型,支持语义和外观编辑、精准的中英文文本编辑、风格转换、旋转等功能。", - "fal-ai/qwen-image.description": "Qwen 团队推出的强大图像生成模型,具备优秀的中文文本渲染能力和多样化的视觉风格。", + "fal-ai/qwen-image-edit.description": "Qwen 团队推出的专业图像编辑模型,支持语义和外观编辑,能够精准编辑中英文文本,并实现高质量的编辑效果,如风格迁移和物体旋转。", + "fal-ai/qwen-image.description": "Qwen 团队推出的强大图像生成模型,具有令人印象深刻的中文文本渲染能力和多样化的视觉风格。", "flux-1-schnell.description": "来自 Black Forest Labs 的 120 亿参数文本转图像模型,采用潜在对抗扩散蒸馏技术,可在 1-4 步内生成高质量图像。性能媲美闭源模型,采用 Apache-2.0 许可,适用于个人、研究与商业用途。", "flux-dev.description": "开源研发图像生成模型,专为非商业创新研究进行高效优化。", "flux-kontext-max.description": "最先进的上下文图像生成与编辑模型,结合文本与图像输入,实现精准一致的结果。", @@ -566,10 +563,10 @@ "gemini-3-flash-preview.description": "Gemini 3 Flash 是一款以速度为核心的智能模型,融合前沿智能与卓越的搜索能力。", "gemini-3-flash.description": "Google 的 Gemini 3 Flash —— 超高速模型,支持多模态输入。", "gemini-3-pro-image-preview.description": "Gemini 3 Pro Image(Nano Banana Pro)是 Google 的图像生成模型,同时支持多模态对话。", - "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image(Nano Banana Pro)是谷歌的图像生成模型,同时支持多模态聊天。", + "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image(Nano Banana Pro)是 Google 的图像生成模型,同时支持多模态聊天。", "gemini-3-pro-preview.description": "Gemini 3 Pro 是 Google 最强大的智能体与编程模型,在最先进推理基础上提供更丰富的视觉效果与更深入的交互体验。", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image(Nano Banana 2)是 Google 最快的原生图像生成模型,支持思考、对话式图像生成和编辑。", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image(Nano Banana 2)以闪电速度提供专业级图像质量,并支持多模态聊天。", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image(Nano Banana 2)是 Google 最快速的原生图像生成模型,支持思维能力、对话式图像生成和编辑功能。", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview是谷歌最具成本效益的多模态模型,专为高容量代理任务、翻译和数据处理优化。", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite 是谷歌最具成本效益的多模态模型,优化用于高容量代理任务、翻译和数据处理。", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview在Gemini 3 Pro的基础上增强了推理能力,并增加了中等思维水平支持。", @@ -734,8 +731,6 @@ "grok-4-fast-reasoning.description": "我们很高兴发布 Grok 4 Fast,这是我们在高性价比推理模型方面的最新进展。", "grok-4.20-0309-non-reasoning.description": "无推理模式版本,适用于简单场景。", "grok-4.20-0309-reasoning.description": "智能、高速的推理模型,先思考后回答。", - "grok-4.20-beta-0309-non-reasoning.description": "适用于简单用例的非推理版本。", - "grok-4.20-beta-0309-reasoning.description": "智能且极快的模型,在响应前进行推理。", "grok-4.20-multi-agent-0309.description": "由 4 至 16 个 Agent 组成的团队,擅长科研类任务。目前不支持客户端工具,只支持 xAI 服务器端工具(如 X Search、Web Search)及远程 MCP 工具。", "grok-4.3.description": "世界上最追求真理的大型语言模型", "grok-4.description": "最新的 Grok 旗舰模型,在语言、数学和推理方面表现无与伦比——真正的全能选手。目前指向 grok-4-0709;由于资源有限,价格暂时比官方定价高 10%,预计稍后会恢复到官方价格。", @@ -1220,8 +1215,6 @@ "qwq.description": "QwQ 是 Qwen 系列中的推理模型。相比标准指令微调模型,具备更强的思维与推理能力,显著提升下游复杂任务表现。QwQ-32B 是一款中型推理模型,性能可媲美 DeepSeek-R1 和 o1-mini 等顶级模型。", "qwq_32b.description": "Qwen 系列中的中型推理模型。相比标准指令微调模型,QwQ 的思维与推理能力显著提升下游复杂任务表现。", "r1-1776.description": "R1-1776 是 DeepSeek R1 的后训练版本,旨在提供无审查、无偏见的真实信息。", - "seedance-1-5-pro-251215.description": "字节跳动推出的 Seedance 1.5 Pro 支持文本生成视频、图像生成视频(首帧、首帧+尾帧)以及与视觉同步的音频生成。", - "seedream-5-0-260128.description": "字节跳动 BytePlus 推出的 Seedream 5.0 Lite,支持基于网页检索增强的实时信息生成,复杂提示解释能力提升,并改进参考一致性,适用于专业视觉创作。", "solar-mini-ja.description": "Solar Mini (Ja) 是 Solar Mini 的日语增强版本,同时保持在英语和韩语中的高效强性能。", "solar-mini.description": "Solar Mini 是一款紧凑型大语言模型,性能超越 GPT-3.5,具备强大的多语言能力,支持英语和韩语,提供高效的小体积解决方案。", "solar-pro.description": "Solar Pro 是 Upstage 推出的高智能大语言模型,专注于单 GPU 上的指令跟随任务,IFEval 得分超过 80。目前支持英语,完整版本计划于 2024 年 11 月发布,届时将扩展语言支持并提升上下文长度。", diff --git a/locales/zh-CN/onboarding.json b/locales/zh-CN/onboarding.json index 959e4f09ef..417caee33d 100644 --- a/locales/zh-CN/onboarding.json +++ b/locales/zh-CN/onboarding.json @@ -28,6 +28,12 @@ "agent.layout.skipConfirm.title": "暂时跳过入门引导?", "agent.layout.switchMessage": "暂时不想继续?可以切换到 {{mode}}{{skip}}。", "agent.layout.switchMessageClassic": "暂时不想继续?可以切换到 {{mode}}。", + "agent.messenger.cta.discord": "添加到 Discord", + "agent.messenger.cta.slack": "添加到 Slack", + "agent.messenger.cta.telegram": "在 Telegram 中打开", + "agent.messenger.subtitle": "在 Telegram、Slack 或 Discord 上继续和我聊天", + "agent.messenger.telegramQrCaption": "用手机相机扫码打开", + "agent.messenger.title": "在你常用的聊天平台继续找到我", "agent.modeSwitch.agent": "对话模式", "agent.modeSwitch.classic": "经典模式", "agent.modeSwitch.collapse": "收起", diff --git a/locales/zh-CN/plugin.json b/locales/zh-CN/plugin.json index 6dedd09130..06ce66082e 100644 --- a/locales/zh-CN/plugin.json +++ b/locales/zh-CN/plugin.json @@ -39,8 +39,8 @@ "builtins.lobe-agent-documents.inspector.docCount": "{{count}} 篇", "builtins.lobe-agent-documents.inspector.opsCount": "{{count}} 项操作", "builtins.lobe-agent-documents.inspector.opsResult": "{{success}}/{{total}} 项", - "builtins.lobe-agent-documents.inspector.target.agent": "Agent 范围", - "builtins.lobe-agent-documents.inspector.target.currentTopic": "当前话题", + "builtins.lobe-agent-documents.inspector.scope.agent": "在代理中", + "builtins.lobe-agent-documents.inspector.scope.currentTopic": "在主题中", "builtins.lobe-agent-documents.title": "Agent 文档", "builtins.lobe-agent-management.apiName.callAgent": "呼叫代理", "builtins.lobe-agent-management.apiName.createAgent": "创建代理", diff --git a/locales/zh-CN/providers.json b/locales/zh-CN/providers.json index 3d00f55a6e..52bcd46109 100644 --- a/locales/zh-CN/providers.json +++ b/locales/zh-CN/providers.json @@ -33,7 +33,6 @@ "jina.description": "Jina AI 成立于 2020 年,是领先的搜索 AI 公司,其搜索技术栈包括向量模型、重排序器与小型语言模型,支持构建高质量的生成式与多模态搜索应用。", "kimicodingplan.description": "Kimi Code Plan 由 Moonshot AI 提供,通过固定月费订阅方式访问 Kimi 模型(包括 K2.5)用于编程任务。", "lmstudio.description": "LM Studio 是一款桌面应用,支持在本地开发与实验大语言模型。", - "lobehub.description": "LobeHub Cloud 使用官方 API 访问 AI 模型,并通过与模型令牌相关的积分来衡量使用情况。", "longcat.description": "LongCat 是由美团自主研发的生成式 AI 大模型系列,旨在通过高效的计算架构和强大的多模态能力,提升企业内部工作效率并推动创新应用的发展。", "minimax.description": "MiniMax 成立于 2021 年,致力于构建通用 AI,拥有多模态基础模型,包括万亿参数的 MoE 文本模型、语音模型与视觉模型,并推出海螺 AI 等应用。", "minimaxcodingplan.description": "MiniMax Token Plan 提供对 MiniMax 模型(包括 M2.7)的访问,适用于编程任务,采用固定月费订阅模式。", diff --git a/locales/zh-CN/subscription.json b/locales/zh-CN/subscription.json index 928a8b1757..9711fa26ef 100644 --- a/locales/zh-CN/subscription.json +++ b/locales/zh-CN/subscription.json @@ -55,12 +55,12 @@ "credits.autoTopUp.toggle": "启用自动充值", "credits.autoTopUp.upgradeHint": "订阅付费计划以启用自动充值", "credits.autoTopUp.validation.targetMustExceedThreshold": "目标余额必须大于触发阈值", - "credits.costEstimateHint.desc": "当下一次模型调用的预估费用达到阈值时,在发送前显示轻量提醒", - "credits.costEstimateHint.saveError": "保存费用预估提醒设置失败", - "credits.costEstimateHint.saveSuccess": "费用预估提醒设置已保存", - "credits.costEstimateHint.threshold": "提醒阈值", - "credits.costEstimateHint.title": "费用预估提醒", - "credits.costEstimateHint.validation.threshold": "阈值必须大于或等于 0", + "credits.costEstimateHint.desc": "当估算的模型成本达到您的阈值时,发送前显示一个轻量级警告", + "credits.costEstimateHint.saveError": "无法保存成本估算警报设置", + "credits.costEstimateHint.saveSuccess": "成本估算警报设置已保存", + "credits.costEstimateHint.threshold": "警告阈值", + "credits.costEstimateHint.title": "成本估算警报", + "credits.costEstimateHint.validation.threshold": "阈值必须大于或等于0", "credits.packages.auto": "自动", "credits.packages.charged": "已扣费 ${{amount}}", "credits.packages.expired": "已过期", diff --git a/locales/zh-TW/chat.json b/locales/zh-TW/chat.json index 90a0f4d4e9..d9bccb13e9 100644 --- a/locales/zh-TW/chat.json +++ b/locales/zh-TW/chat.json @@ -220,6 +220,13 @@ "inbox.title": "Lobe AI", "input.addAi": "新增一條 AI 訊息", "input.addUser": "新增一條使用者訊息", + "input.costEstimate.creditsPerMillionTokens": "{{credits}} 點數/M 個標記", + "input.costEstimate.hint": "預估成本:~{{credits}} 點數", + "input.costEstimate.inputLabel": "輸入", + "input.costEstimate.outputLabel": "輸出", + "input.costEstimate.settingsLink": "調整警告閾值", + "input.costEstimate.tokenCount": "~{{tokens}} 個標記", + "input.costEstimate.tooltip": "根據當前上下文、工具和模型定價估算。實際成本可能有所不同。", "input.disclaimer": "AI 也可能會犯錯,請檢查重要資訊", "input.errorMsg": "訊息發送失敗,請檢查網路後重試:{{errorMsg}}", "input.more": "更多", @@ -779,6 +786,7 @@ "upload.preview.prepareTasks": "準備分塊...", "upload.preview.status.pending": "準備上傳...", "upload.preview.status.processing": "檔案處理中...", + "upload.validation.unsupportedFileType": "不支援的檔案類型:{{files}}。支援的圖片格式:JPG、PNG、GIF、WebP。支援的文件包括 PDF、Word、Excel、PowerPoint、Markdown、文字檔、CSV、JSON 和程式碼檔案。", "upload.validation.videoSizeExceeded": "影片檔案大小不能超過 20MB,當前檔案大小為 {{actualSize}}", "viewMode.fullWidth": "全寬顯示", "viewMode.normal": "一般", @@ -894,6 +902,8 @@ "workingPanel.localFile.closeOther": "關閉其他", "workingPanel.localFile.closeRight": "關閉右側", "workingPanel.localFile.error": "無法載入此檔案", + "workingPanel.localFile.preview.raw": "原始檔案", + "workingPanel.localFile.preview.render": "預覽", "workingPanel.localFile.truncated": "檔案預覽已截斷至 {{limit}} 個字元", "workingPanel.progress": "進度", "workingPanel.progress.allCompleted": "所有任務已完成", @@ -959,6 +969,8 @@ "workingPanel.review.viewMode.unified": "切換至統一檢視", "workingPanel.review.wordWrap.disable": "停用自動換行", "workingPanel.review.wordWrap.enable": "啟用自動換行", + "workingPanel.skills.empty": "此專案中未找到技能", + "workingPanel.skills.title": "技能", "workingPanel.space": "空間", "workingPanel.title": "Working Panel", "you": "你", diff --git a/locales/zh-TW/components.json b/locales/zh-TW/components.json index 252c3ec8c0..b4adb6d659 100644 --- a/locales/zh-TW/components.json +++ b/locales/zh-TW/components.json @@ -121,6 +121,16 @@ "ModelSwitchPanel.detail.context": "上下文長度", "ModelSwitchPanel.detail.pricing": "價格", "ModelSwitchPanel.detail.pricing.cachedInput": "快取輸入 ${{amount}}/百萬", + "ModelSwitchPanel.detail.pricing.credits.cachedInput": "快取輸入 {{amount}} 點數/M tokens", + "ModelSwitchPanel.detail.pricing.credits.image": "點數/圖片", + "ModelSwitchPanel.detail.pricing.credits.input": "輸入 {{amount}} 點數/M tokens", + "ModelSwitchPanel.detail.pricing.credits.megapixel": "點數/MP", + "ModelSwitchPanel.detail.pricing.credits.millionCharacters": "點數/M 字元", + "ModelSwitchPanel.detail.pricing.credits.millionTokens": "點數/M tokens", + "ModelSwitchPanel.detail.pricing.credits.output": "輸出 {{amount}} 點數/M tokens", + "ModelSwitchPanel.detail.pricing.credits.perImage": "~ {{amount}} 點數 / 圖片", + "ModelSwitchPanel.detail.pricing.credits.perVideo": "~ {{amount}} 點數 / 影片", + "ModelSwitchPanel.detail.pricing.credits.second": "點數/秒", "ModelSwitchPanel.detail.pricing.group.audio": "音訊", "ModelSwitchPanel.detail.pricing.group.image": "圖像", "ModelSwitchPanel.detail.pricing.group.text": "文字", diff --git a/locales/zh-TW/editor.json b/locales/zh-TW/editor.json index 3b102e6266..7519090c34 100644 --- a/locales/zh-TW/editor.json +++ b/locales/zh-TW/editor.json @@ -1,8 +1,10 @@ { "actionTag.category.command": "命令", + "actionTag.category.projectSkill": "專案技能", "actionTag.category.skill": "技能", "actionTag.category.tool": "工具", "actionTag.tooltip.command": "在發送前執行客戶端斜線命令。", + "actionTag.tooltip.projectSkill": "以斜線調用的方式發送,讓代理的 CLI 執行匹配的專案技能。", "actionTag.tooltip.skill": "為此次請求載入可重用的技能包。", "actionTag.tooltip.tool": "標記使用者在此次請求中明確選擇的工具。", "actions.expand.off": "收合", diff --git a/locales/zh-TW/home.json b/locales/zh-TW/home.json index 2bacf0ca89..87eb4e9d4e 100644 --- a/locales/zh-TW/home.json +++ b/locales/zh-TW/home.json @@ -11,6 +11,19 @@ "brief.action.ignore": "忽略", "brief.action.retry": "重試", "brief.addFeedback": "分享意見回饋", + "brief.agentSignal.selfReview.applied.heading": "已更新", + "brief.agentSignal.selfReview.applied.summary": "已套用 {{count}} 個夢境更新。", + "brief.agentSignal.selfReview.applied.summary_plural": "已套用 {{count}} 個夢境更新。", + "brief.agentSignal.selfReview.applied.title": "夢境更新的資源", + "brief.agentSignal.selfReview.error.heading": "問題", + "brief.agentSignal.selfReview.error.summary": "在此夢境中有些工作未能完成。", + "brief.agentSignal.selfReview.error.title": "夢境遇到問題", + "brief.agentSignal.selfReview.ideas.summary": "已保存夢境筆記以供日後檢視。", + "brief.agentSignal.selfReview.ideas.title": "夢境筆記", + "brief.agentSignal.selfReview.proposal.heading": "建議", + "brief.agentSignal.selfReview.proposal.summary": "{{count}} 個夢境建議需要您的檢視。", + "brief.agentSignal.selfReview.proposal.summary_plural": "{{count}} 個夢境建議需要您的檢視。", + "brief.agentSignal.selfReview.proposal.title": "夢境建議需要檢視", "brief.collapse": "顯示較少", "brief.commentPlaceholder": "分享您的意見回饋...", "brief.commentSubmit": "提交意見回饋", diff --git a/locales/zh-TW/hotkey.json b/locales/zh-TW/hotkey.json index 752b6babbf..85b77a47f4 100644 --- a/locales/zh-TW/hotkey.json +++ b/locales/zh-TW/hotkey.json @@ -1,8 +1,6 @@ { "addUserMessage.desc": "將當前輸入內容添加為使用者消息,但不觸發生成", "addUserMessage.title": "添加一條使用者消息", - "clearCurrentMessages.desc": "清空當前會話的消息和上傳的檔案", - "clearCurrentMessages.title": "清空會話消息", "commandPalette.desc": "開啟全域指令面板以快速存取功能", "commandPalette.title": "指令面板", "deleteAndRegenerateMessage.desc": "刪除最後一則訊息並重新產生", diff --git a/locales/zh-TW/models.json b/locales/zh-TW/models.json index 3da71550e0..9ed75d857d 100644 --- a/locales/zh-TW/models.json +++ b/locales/zh-TW/models.json @@ -106,7 +106,6 @@ "MiniMax-Hailuo-2.3.description": "全新影像生成模型,全面升級身體動作、物理真實性及指令遵循性。", "MiniMax-M1.description": "一款內部開發的推理模型,具備 80K 思路鏈與 100 萬輸入,效能媲美全球頂尖模型。", "MiniMax-M2-Stable.description": "專為高效編碼與代理流程設計,具備更高併發能力,適用於商業應用。", - "MiniMax-M2.1-Lightning.description": "強大的多語言編程能力,具有更快、更高效的推理性能。", "MiniMax-M2.1-highspeed.description": "強大的多語言編程能力,全面升級的編程體驗。速度更快,效率更高。", "MiniMax-M2.1.description": "MiniMax-M2.1 是 MiniMax 推出的旗艦開源大型模型,專注於解決複雜的真實世界任務。其核心優勢在於多語言程式設計能力與作為智能代理執行複雜任務的能力。", "MiniMax-M2.5-highspeed.description": "MiniMax M2.5 高速版:與 M2.5 性能相同,但推理速度更快。", @@ -315,13 +314,13 @@ "claude-3-haiku-20240307.description": "Claude 3 Haiku 是 Anthropic 推出的最快速且最精簡的模型,設計用於即時回應,具備快速且準確的表現。", "claude-3-opus-20240229.description": "Claude 3 Opus 是 Anthropic 最強大的模型,適用於高度複雜任務,具備卓越的效能、智慧、流暢度與理解力。", "claude-3-sonnet-20240229.description": "Claude 3 Sonnet 在智慧與速度之間取得平衡,適合企業工作負載,提供高效能與低成本的大規模部署。", - "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 是 Anthropic 最快且最智能的 Haiku 模型,擁有閃電般的速度和延展性思維。", + "claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 是 Anthropic 最快速且最智能的 Haiku 模型,擁有閃電般的速度和延展的推理能力。", "claude-haiku-4-5.description": "Anthropic 的 Claude Haiku 4.5 —— 新一代 Haiku,具備更強推理與視覺能力。", "claude-haiku-4.5.description": "Claude Haiku 4.5 是 Anthropic 最快速且最聰明的 Haiku 模型,具備閃電般的速度和延展的推理能力。", "claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking 是一個進階版本,能夠揭示其推理過程。", - "claude-opus-4-1-20250805.description": "Claude Opus 4.1 是 Anthropic 最新且最強大的模型,專為高度複雜的任務設計,表現卓越,智能、流暢性和理解力均出色。", + "claude-opus-4-1-20250805.description": "Claude Opus 4.1 是 Anthropic 最新且最強大的模型,專為高度複雜的任務設計,表現卓越,展現出色的智能、流暢性和理解力。", "claude-opus-4-1.description": "Anthropic 的 Claude Opus 4.1 —— 旗艦級推理模型,擅長深度分析。", - "claude-opus-4-20250514.description": "Claude Opus 4 是 Anthropic 最強大的模型,專為高度複雜的任務設計,表現卓越,智能、流暢性和理解力均出色。", + "claude-opus-4-20250514.description": "Claude Opus 4 是 Anthropic 最強大的模型,專為高度複雜的任務設計,表現卓越,展現出色的智能、流暢性和理解力。", "claude-opus-4-5-20251101.description": "Claude Opus 4.5 是 Anthropic 的旗艦模型,結合卓越智慧與可擴展效能,適合需要最高品質回應與推理的複雜任務。", "claude-opus-4-5.description": "Anthropic 的 Claude Opus 4.5 —— 旗艦模型,具備頂級推理與程式能力。", "claude-opus-4-6.description": "Anthropic 的 Claude Opus 4.6 —— 支援 100 萬上下文的旗艦模型,擁有先進推理能力。", @@ -330,8 +329,8 @@ "claude-opus-4.6-fast.description": "Claude Opus 4.6 是 Anthropic 最智能的模型,用於構建代理和編碼。", "claude-opus-4.6.description": "Claude Opus 4.6 是 Anthropic 最智能的模型,用於構建代理和編碼。", "claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking 可產生即時回應或延伸的逐步思考,並顯示其推理過程。", - "claude-sonnet-4-20250514.description": "Claude Sonnet 4 是 Anthropic 至今最智能的模型,提供近乎即時的回應或延展的逐步思考,為 API 使用者提供精細的控制。", - "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 是 Anthropic 至今最智能的模型。", + "claude-sonnet-4-20250514.description": "Claude Sonnet 4 能夠生成近乎即時的回應或提供可見過程的逐步推理。", + "claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 是迄今為止 Anthropic 最智能的模型。", "claude-sonnet-4-5.description": "Anthropic 的 Claude Sonnet 4.5 —— 強化版 Sonnet,提升程式能力。", "claude-sonnet-4-6.description": "Anthropic 的 Claude Sonnet 4.6 —— 最新 Sonnet,具備更卓越的程式與工具使用能力。", "claude-sonnet-4.5.description": "Claude Sonnet 4.5 是 Anthropic 迄今為止最智能的模型。", @@ -404,7 +403,7 @@ "deepseek-ai/deepseek-llm-67b-chat.description": "DeepSeek LLM Chat(67B)是一款創新模型,具備深層語言理解與互動能力。", "deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 是新一代推理模型,具備更強的複雜推理與思維鏈能力,適用於深度分析任務。", "deepseek-ai/deepseek-v3.2.description": "DeepSeek V3.2 是下一代推理模型,具備更強的複雜推理和連鎖思維能力。", - "deepseek-chat.description": "DeepSeek V4 Flash 非思考模式的兼容別名。計劃淘汰 — 請改用 DeepSeek V4 Flash。", + "deepseek-chat.description": "一款結合通用能力與程式能力的開源模型。它保留了聊天模型的通用對話能力和程式模型的強大編程能力,並且在偏好對齊方面表現更佳。DeepSeek-V2.5 還改進了寫作和指令遵循能力。", "deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B 是一款程式語言模型,訓練於 2T token(87% 程式碼,13% 中英文文本),支援 16K 上下文視窗與中間填充任務,提供專案級程式補全與片段填充功能。", "deepseek-coder-v2.description": "DeepSeek Coder V2 是一款開源 MoE 程式模型,在程式任務中表現強勁,媲美 GPT-4 Turbo。", "deepseek-coder-v2:236b.description": "DeepSeek Coder V2 是一款開源 MoE 程式模型,在程式任務中表現強勁,媲美 GPT-4 Turbo。", @@ -426,7 +425,7 @@ "deepseek-r1-fast-online.description": "DeepSeek R1 快速全量版,支援即時網頁搜尋,結合 671B 規模能力與快速回應。", "deepseek-r1-online.description": "DeepSeek R1 全量版擁有 671B 參數與即時網頁搜尋功能,提供更強的理解與生成能力。", "deepseek-r1.description": "DeepSeek-R1 在強化學習前使用冷啟動資料,於數學、程式碼與推理任務中表現可媲美 OpenAI-o1。", - "deepseek-reasoner.description": "DeepSeek V4 Flash 思考模式的兼容別名。計劃淘汰 — 請改用 DeepSeek V4 Flash。", + "deepseek-reasoner.description": "一款專注於複雜邏輯推理任務的 DeepSeek 推理模型。", "deepseek-v2.description": "DeepSeek V2 是一款高效的 MoE 模型,適用於具成本效益的處理任務。", "deepseek-v2:236b.description": "DeepSeek V2 236B 是 DeepSeek 專注於程式碼生成的模型,具備強大能力。", "deepseek-v3-0324.description": "DeepSeek-V3-0324 是一款擁有 671B 參數的 MoE 模型,在程式設計、技術能力、語境理解與長文本處理方面表現出色。", @@ -491,8 +490,6 @@ "doubao-seedream-4-0-250828.description": "Seedream 4.0 是字節跳動 Seed 團隊推出的圖像生成模型,支援文字與圖像輸入,實現高度可控、高品質的圖像生成。可根據文字提示生成圖像。", "doubao-seedream-4-5-251128.description": "Seedream 4.5 是字節跳動最新的多模態圖像模型,整合了文本生成圖像、圖像生成圖像和批量圖像生成功能,同時融入了常識和推理能力。與之前的 4.0 版本相比,生成質量顯著提升,編輯一致性和多圖融合效果更好。它對視覺細節的控制更加精確,能更自然地生成小字和小臉,並實現更和諧的佈局和色彩,提升整體美感。", "doubao-seedream-5-0-260128.description": "Doubao-Seedream-5.0-lite 是字節跳動最新的圖像生成模型。首次整合了在線檢索功能,能夠結合實時網絡信息,提升生成圖像的時效性。模型的智能性也得到了升級,能夠精確解讀複雜指令和視覺內容。此外,它在專業場景中的全球知識覆蓋、參考一致性和生成質量方面均有改進,更好地滿足企業級視覺創作需求。", - "dreamina-seedance-2-0-260128.description": "字節跳動的 Seedance 2.0 是最強大的視頻生成模型,支持多模態參考視頻生成、視頻編輯、視頻擴展、文本生成視頻以及圖像生成視頻,並同步音頻。", - "dreamina-seedance-2-0-fast-260128.description": "字節跳動的 Seedance 2.0 Fast 提供與 Seedance 2.0 相同的功能,但生成速度更快,價格更具競爭力。", "emohaa.description": "Emohaa 是一款心理健康模型,具備專業諮詢能力,協助使用者理解情緒問題。", "ernie-4.5-0.3b.description": "ERNIE 4.5 0.3B 是一款開源輕量級模型,適合本地與客製化部署。", "ernie-4.5-8k-preview.description": "ERNIE 4.5 8K Preview 是一款 8K 上下文預覽模型,用於評估 ERNIE 4.5。", @@ -506,6 +503,7 @@ "ernie-5.0-thinking-latest.description": "文心 5.0 思考版是一款原生全模態旗艦模型,整合文字、圖像、音訊與影片建模,全面升級複雜問答、創作與智能體場景的能力。", "ernie-5.0-thinking-preview.description": "文心 5.0 思考預覽版是一款原生全模態旗艦模型,整合文字、圖像、音訊與影片建模,全面升級複雜問答、創作與智能體場景的能力。", "ernie-5.0.description": "ERNIE 5.0 是 ERNIE 系列新世代模型,屬於原生多模態大型模型。採用統一多模態建模方法,可同時建模文本、圖片、音訊與影片,具備全面的多模態能力。基礎能力大幅升級,在基準測試中表現亮眼,特別擅長多模態理解、指令遵循、創作寫作、事實準確性、代理規劃與工具使用。", + "ernie-5.1.description": "ERNIE 5.1 是 ERNIE 系列的最新模型,基礎能力全面升級。在代理、知識處理、推理和深度搜索等領域展現了顯著的改進。本次版本採用了解耦的全異步強化學習架構,專門針對大型模型向自主代理決策演進過程中的關鍵挑戰進行設計,包括訓練與推理數值差異、異構計算資源利用率低以及由長尾效應引發的全局問題。此外,還採用了大規模代理後訓練技術,進一步提升模型的能力和泛化性能。通過環境、專家和融合三階段協作框架,此方法不僅確保了訓練效率,還顯著提升了模型在複雜任務中的穩定性和表現。", "ernie-char-fiction-8k-preview.description": "ERNIE 角色小說 8K 預覽版是一款面向角色與情節創作的模型預覽,用於功能評估與測試。", "ernie-char-fiction-8k.description": "ERNIE 角色小說 8K 是一款面向小說與情節創作的角色模型,適合長篇故事生成。", "ernie-image-turbo.description": "ERNIE-Image 是百度推出的 80 億參數文生圖模型,在多項基準測試中名列前茅,在中國 SuperCLUE 中獲得並列第一,並在開源賽道領先。", @@ -517,8 +515,7 @@ "ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K 是一款快速思考模型,具備 32K 上下文,適合複雜推理與多輪對話。", "ernie-x1.1-preview.description": "ERNIE X1.1 預覽版是一款思考模型預覽,用於評估與測試。", "ernie-x1.1.description": "ERNIE X1.1 是一個用於評估和測試的思考模型預覽版。", - "fal-ai/bytedance/seedream/v4.5.description": "Seedream 4.5,由字節跳動 Seed 團隊打造,支持多圖像編輯和合成。特點包括增強的主題一致性、精確的指令執行、空間邏輯理解、美學表達、海報佈局和標誌設計,以及高精度的文字圖像渲染。", - "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0,由字節跳動 Seed 團隊打造,支持文本和圖像輸入,能從提示中生成高度可控、高質量的圖像。", + "fal-ai/bytedance/seedream/v4.description": "Seedream 4.0 是來自字節跳動 Seed 的圖像生成模型,支持文本和圖像輸入,能夠生成高度可控且高品質的圖像。它可以根據文本提示生成圖像。", "fal-ai/flux-kontext/dev.description": "FLUX.1 模型專注於圖像編輯,支援文字與圖像輸入。", "fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] 接受文字與參考圖像輸入,實現目標區域編輯與複雜場景轉換。", "fal-ai/flux/krea.description": "Flux Krea [dev] 是一款圖像生成模型,偏好更真實自然的美學風格。", @@ -526,8 +523,8 @@ "fal-ai/hunyuan-image/v3.description": "一款強大的原生多模態圖像生成模型。", "fal-ai/imagen4/preview.description": "來自 Google 的高品質圖像生成模型。", "fal-ai/nano-banana.description": "Nano Banana 是 Google 最新、最快且最高效的原生多模態模型,支援透過對話進行圖像生成與編輯。", - "fal-ai/qwen-image-edit.description": "Qwen 團隊的專業圖像編輯模型,支持語義和外觀編輯、精確的中英文文字編輯、風格轉換、旋轉等功能。", - "fal-ai/qwen-image.description": "Qwen 團隊的強大圖像生成模型,具有強大的中文文字渲染能力和多樣化的視覺風格。", + "fal-ai/qwen-image-edit.description": "Qwen 團隊推出的專業圖像編輯模型,支持語義和外觀編輯,精確編輯中英文文本,並能進行高品質的編輯,例如風格轉換和物體旋轉。", + "fal-ai/qwen-image.description": "Qwen 團隊推出的強大圖像生成模型,擁有令人印象深刻的中文文本渲染能力和多樣化的視覺風格。", "flux-1-schnell.description": "來自黑森林實驗室的 12B 參數文字轉圖像模型,透過潛在對抗擴散蒸餾技術,在 1 至 4 步內生成高品質圖像。其表現媲美封閉式替代方案,並以 Apache-2.0 授權釋出,供個人、研究與商業用途。", "flux-dev.description": "開源研發導向的影像生成模型,針對非商業創新研究進行高效優化。", "flux-kontext-max.description": "最先進的語境圖像生成與編輯技術,結合文字與圖像輸入,實現精準且一致的結果。", @@ -569,7 +566,7 @@ "gemini-3-pro-image-preview:image.description": "Gemini 3 Pro Image(Nano Banana Pro)是 Google 的圖像生成模型,並支持多模態聊天。", "gemini-3-pro-preview.description": "Gemini 3 Pro 是 Google 最強大的智能代理與情境編碼模型,具備頂尖推理能力、豐富視覺表現與深度互動。", "gemini-3.1-flash-image-preview.description": "Gemini 3.1 Flash Image (Nano Banana 2) 是 Google 最快的原生影像生成模型,支持思考、對話式影像生成與編輯。", - "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image(Nano Banana 2)以閃電速度提供專業級圖像質量,並支持多模態聊天。", + "gemini-3.1-flash-image-preview:image.description": "Gemini 3.1 Flash Image(Nano Banana 2)是 Google 最快速的原生圖像生成模型,支持思維能力、對話式圖像生成和編輯。", "gemini-3.1-flash-lite-preview.description": "Gemini 3.1 Flash-Lite Preview 是 Google 最具成本效益的多模態模型,專為高容量代理任務、翻譯和數據處理而優化。", "gemini-3.1-flash-lite.description": "Gemini 3.1 Flash-Lite 是 Google 最具成本效益的多模態模型,專為高容量代理任務、翻譯和數據處理而優化。", "gemini-3.1-pro-preview.description": "Gemini 3.1 Pro Preview 在 Gemini 3 Pro 的基礎上增強了推理能力,並新增了中等思考層級支持。", @@ -734,8 +731,6 @@ "grok-4-fast-reasoning.description": "我們很高興推出 Grok 4 Fast,這是我們在高性價比推理模型上的最新進展。", "grok-4.20-0309-non-reasoning.description": "不具推理功能的版本,適用於簡單使用情境。", "grok-4.20-0309-reasoning.description": "智慧、極速的模型,會先思考再作答。", - "grok-4.20-beta-0309-non-reasoning.description": "適用於簡單使用場景的非推理變體。", - "grok-4.20-beta-0309-reasoning.description": "智能且極速的模型,在回應前進行推理。", "grok-4.20-multi-agent-0309.description": "由 4 或 16 個代理組成的團隊,擅長研究類任務。目前不支援客戶端工具,只支援 xAI 伺服器端工具(如 X Search、Web Search)及遠端 MCP 工具。", "grok-4.3.description": "世界上最追求真理的大型語言模型", "grok-4.description": "最新的 Grok 旗艦模型,擁有無與倫比的語言、數學和推理能力 — 真正的全能型模型。目前指向 grok-4-0709;由於資源有限,暫時比官方定價高 10%,預計稍後會恢復至官方價格。", @@ -1220,8 +1215,6 @@ "qwq.description": "QwQ 是 Qwen 系列中的推理模型。相較於標準指令微調模型,它具備更強的思考與推理能力,顯著提升下游任務表現,特別是在處理困難問題時。QwQ-32B 是中型推理模型,表現可媲美 DeepSeek-R1 與 o1-mini 等頂尖模型。", "qwq_32b.description": "Qwen 系列中的中型推理模型。相較於標準指令微調模型,QwQ 的思考與推理能力顯著提升下游任務表現,特別是在處理困難問題時。", "r1-1776.description": "R1-1776 是 DeepSeek R1 的後訓練版本,旨在提供未經審查、無偏見的事實資訊。", - "seedance-1-5-pro-251215.description": "字節跳動的 Seedance 1.5 Pro 支持文本生成視頻、圖像生成視頻(首幀、首幀+尾幀)以及與視覺同步的音頻生成。", - "seedream-5-0-260128.description": "字節跳動 BytePlus 的 Seedream 5.0 Lite,支持基於網絡檢索的增強生成,提供即時信息、複雜提示解釋增強,以及改進的參考一致性,用於專業視覺創作。", "solar-mini-ja.description": "Solar Mini (Ja) 是 Solar Mini 的日文強化版本,同時維持在英文與韓文上的高效能表現。", "solar-mini.description": "Solar Mini 是一款緊湊型大型語言模型,效能超越 GPT-3.5,具備強大的多語言能力,支援英文與韓文,提供高效能且佔用資源小的解決方案。", "solar-pro.description": "Solar Pro 是 Upstage 推出的高智慧大型語言模型,專注於單 GPU 上的指令遵循任務,IFEval 分數超過 80。目前支援英文,完整版本預計於 2024 年 11 月推出,將擴展語言支援與上下文長度。", diff --git a/locales/zh-TW/onboarding.json b/locales/zh-TW/onboarding.json index 7040b96ad4..9c569b181e 100644 --- a/locales/zh-TW/onboarding.json +++ b/locales/zh-TW/onboarding.json @@ -27,6 +27,13 @@ "agent.layout.skipConfirm.ok": "先跳過", "agent.layout.skipConfirm.title": "要先跳過新手引導嗎?", "agent.layout.switchMessage": "暫時不想繼續?可以切換到 {{mode}}{{skip}}。", + "agent.layout.switchMessageClassic": "今天不想用這個嗎?您可以切換到{{mode}}。", + "agent.messenger.cta.discord": "添加到 Discord", + "agent.messenger.cta.slack": "添加到 Slack", + "agent.messenger.cta.telegram": "在 Telegram 中開啟", + "agent.messenger.subtitle": "在 Telegram、Slack 或 Discord 上與您的代理聊天", + "agent.messenger.telegramQrCaption": "用手機相機掃描", + "agent.messenger.title": "無論您在哪裡聊天,都帶上我", "agent.modeSwitch.agent": "對話模式", "agent.modeSwitch.classic": "經典模式", "agent.modeSwitch.collapse": "收合", @@ -68,6 +75,13 @@ "agent.wrapUp.confirm.content": "我會先儲存我們目前的內容。之後您隨時都能再回來聊天。", "agent.wrapUp.confirm.ok": "現在結束", "agent.wrapUp.confirm.title": "要現在結束使用引導嗎?", + "agentPicker.allCategories": "全部", + "agentPicker.continue": "繼續", + "agentPicker.skip": "暫時跳過", + "agentPicker.subtitle": "現在就添加一些到您的庫中 — 隨時探索更多。", + "agentPicker.title": "讓我們為您的庫添加一些代理", + "agentPicker.title2": "選擇符合您工作方式的代理", + "agentPicker.title3": "您隨時可以添加更多 — 先從幾個開始", "back": "上一步", "finish": "開始使用", "interests.area.business": "商業與策略", diff --git a/locales/zh-TW/plugin.json b/locales/zh-TW/plugin.json index c2fedd38b5..96369f7dde 100644 --- a/locales/zh-TW/plugin.json +++ b/locales/zh-TW/plugin.json @@ -39,8 +39,8 @@ "builtins.lobe-agent-documents.inspector.docCount": "{{count}} 文件", "builtins.lobe-agent-documents.inspector.opsCount": "{{count}} 操作", "builtins.lobe-agent-documents.inspector.opsResult": "{{success}}/{{total}} 操作", - "builtins.lobe-agent-documents.inspector.target.agent": "在代理中", - "builtins.lobe-agent-documents.inspector.target.currentTopic": "在主題中", + "builtins.lobe-agent-documents.inspector.scope.agent": "在代理中", + "builtins.lobe-agent-documents.inspector.scope.currentTopic": "在主題中", "builtins.lobe-agent-documents.title": "代理文件", "builtins.lobe-agent-management.apiName.callAgent": "呼叫代理", "builtins.lobe-agent-management.apiName.createAgent": "建立代理", @@ -90,6 +90,14 @@ "builtins.lobe-agent.title": "Lobe 代理", "builtins.lobe-claude-code.agent.instruction": "指令", "builtins.lobe-claude-code.agent.result": "結果", + "builtins.lobe-claude-code.task.createLabel": "建立任務:", + "builtins.lobe-claude-code.task.getLabel": "檢查任務 #{{taskId}}", + "builtins.lobe-claude-code.task.listLabel": "列出任務", + "builtins.lobe-claude-code.task.updateCompleted": "已完成", + "builtins.lobe-claude-code.task.updateDeleted": "已刪除", + "builtins.lobe-claude-code.task.updateInProgress": "已開始", + "builtins.lobe-claude-code.task.updateLabel": "更新任務 #{{taskId}}", + "builtins.lobe-claude-code.task.updatePending": "重置", "builtins.lobe-claude-code.todoWrite.allDone": "所有任務皆已完成", "builtins.lobe-claude-code.todoWrite.currentStep": "目前步驟", "builtins.lobe-claude-code.todoWrite.todos": "待辦項目", diff --git a/locales/zh-TW/providers.json b/locales/zh-TW/providers.json index 68348ea3ce..41054f2aa6 100644 --- a/locales/zh-TW/providers.json +++ b/locales/zh-TW/providers.json @@ -33,7 +33,6 @@ "jina.description": "Jina AI 成立於 2020 年,是領先的搜尋 AI 公司。其搜尋技術堆疊包含向量模型、重排序器與小型語言模型,打造可靠且高品質的生成式與多模態搜尋應用。", "kimicodingplan.description": "來自 Moonshot AI 的 Kimi Code 提供對 Kimi 模型(包括 K2.5)的訪問,用於編碼任務。", "lmstudio.description": "LM Studio 是一款桌面應用程式,可在本機開發與實驗大型語言模型。", - "lobehub.description": "LobeHub Cloud 使用官方 API 存取 AI 模型,並以與模型代幣相關的點數來計算使用量。", "longcat.description": "LongCat 是美團自主研發的一系列生成式 AI 大模型。其設計旨在通過高效的計算架構和強大的多模態能力,提升企業內部生產力並實現創新應用。", "minimax.description": "MiniMax 成立於 2021 年,致力於打造通用 AI,擁有多模態基礎模型,包括兆級參數的 MoE 文本模型、語音模型與視覺模型,並推出如海螺 AI 等應用。", "minimaxcodingplan.description": "MiniMax 代幣計劃通過固定費用訂閱提供對 MiniMax 模型(包括 M2.7)的訪問,用於編碼任務。", diff --git a/locales/zh-TW/subscription.json b/locales/zh-TW/subscription.json index 687ba12858..66a92efae6 100644 --- a/locales/zh-TW/subscription.json +++ b/locales/zh-TW/subscription.json @@ -55,6 +55,12 @@ "credits.autoTopUp.toggle": "啟用自動加值", "credits.autoTopUp.upgradeHint": "訂閱付費方案以啟用自動加值", "credits.autoTopUp.validation.targetMustExceedThreshold": "目標餘額必須大於觸發門檻", + "credits.costEstimateHint.desc": "當預估模型成本達到您的門檻時,顯示輕量級警告提示", + "credits.costEstimateHint.saveError": "無法保存成本預估警報設置", + "credits.costEstimateHint.saveSuccess": "成本預估警報設置已保存", + "credits.costEstimateHint.threshold": "警告門檻", + "credits.costEstimateHint.title": "成本預估警報", + "credits.costEstimateHint.validation.threshold": "門檻必須大於或等於 0", "credits.packages.auto": "自動", "credits.packages.charged": "已收取 ${{amount}}", "credits.packages.expired": "已過期", diff --git a/locales/zh-TW/tool.json b/locales/zh-TW/tool.json index c044d08e7d..d76b71d33a 100644 --- a/locales/zh-TW/tool.json +++ b/locales/zh-TW/tool.json @@ -40,6 +40,14 @@ "agentMarketplace.render.alreadyInLibraryTag": "已在資料庫中", "agentMarketplace.render.alreadyInLibrary_one": "{{count}} 已在資料庫中", "agentMarketplace.render.alreadyInLibrary_other": "{{count}} 已在資料庫中", + "claudeCode.askUserQuestion.escape.back": "返回選項", + "claudeCode.askUserQuestion.escape.enter": "或直接輸入", + "claudeCode.askUserQuestion.escape.placeholder": "在此輸入您的答案…", + "claudeCode.askUserQuestion.multiSelectTag": "(多選)", + "claudeCode.askUserQuestion.skip": "跳過", + "claudeCode.askUserQuestion.submit": "提交", + "claudeCode.askUserQuestion.timeExpired": "時間已到 — 每個問題使用選項 1。", + "claudeCode.askUserQuestion.timeRemaining": "剩餘時間:{{time}} · 未回答的問題在超時時默認為選項 1。", "codeInterpreter-legacy.error": "執行錯誤", "codeInterpreter-legacy.executing": "執行中...", "codeInterpreter-legacy.files": "檔案:", diff --git a/locales/zh-TW/topic.json b/locales/zh-TW/topic.json index eea92ca4d8..818d0b45fd 100644 --- a/locales/zh-TW/topic.json +++ b/locales/zh-TW/topic.json @@ -56,6 +56,7 @@ "renameModal.title": "重新命名主題", "searchPlaceholder": "搜尋話題...", "searchResultEmpty": "目前沒有搜尋結果", + "sidebar.title": "主題", "taskManager.agent": "任務代理", "taskManager.welcome": "可以詢問我有關你的待辦事項", "temp": "臨時", diff --git a/packages/agent-runtime/src/types/hooks.ts b/packages/agent-runtime/src/types/hooks.ts index cf11bc8bd0..b4f239a600 100644 --- a/packages/agent-runtime/src/types/hooks.ts +++ b/packages/agent-runtime/src/types/hooks.ts @@ -30,9 +30,30 @@ export type AgentHookType = /** * Unified event payload passed to hook handlers and webhook payloads */ +/** + * Outbound attachment carried alongside the agent's final reply text. + * Populated only on `onComplete`. JSON-safe so it survives webhook delivery. + */ +export interface HookEventAttachment { + /** Base64-encoded bytes. Used when no fetchable URL exists. */ + data?: string; + /** Remote URL the downstream consumer can GET to retrieve the bytes. */ + fetchUrl?: string; + mimeType?: string; + name?: string; + type: 'image' | 'file' | 'video' | 'audio'; +} + export interface AgentHookEvent { // Identification agentId: string; + /** + * Outbound attachments extracted from the final assistant message's + * multimodal `content` parts (or tool messages that produced image/file + * outputs). Set on `onComplete` events; downstream consumers (bot reply + * callbacks) forward these to platform messengers. + */ + attachments?: HookEventAttachment[]; /** LLM text output (afterStep only) */ content?: string; // Statistics diff --git a/packages/builtin-tool-message/src/ExecutionRuntime/index.ts b/packages/builtin-tool-message/src/ExecutionRuntime/index.ts index 806574850a..660d1bb5d0 100644 --- a/packages/builtin-tool-message/src/ExecutionRuntime/index.ts +++ b/packages/builtin-tool-message/src/ExecutionRuntime/index.ts @@ -105,6 +105,7 @@ export type { SearchMessagesState, SendDirectMessageParams, SendDirectMessageState, + SendMessageAttachment, SendMessageParams, SendMessageState, ToggleBotParams, diff --git a/packages/builtin-tool-message/src/systemRole.ts b/packages/builtin-tool-message/src/systemRole.ts index 832b1b85d6..9bc2a45f6a 100644 --- a/packages/builtin-tool-message/src/systemRole.ts +++ b/packages/builtin-tool-message/src/systemRole.ts @@ -125,6 +125,7 @@ Skipping step 1 will silently wipe other entries. - Uses iLink Bot API with long-polling for message delivery - Sending messages requires a context token from an active conversation - Only sendMessage is available, and only within active conversation context +- Outbound text, images, files, and videos are supported (each media item is sent as a separate message per protocol) - Message operations may fail if no active conversation context exists `; diff --git a/packages/builtin-tool-message/src/types.ts b/packages/builtin-tool-message/src/types.ts index 658828dd1d..44a989167e 100644 --- a/packages/builtin-tool-message/src/types.ts +++ b/packages/builtin-tool-message/src/types.ts @@ -92,7 +92,31 @@ export interface SendDirectMessageState { // --- Core Message Operations --- +/** + * JSON-safe outbound attachment for `sendMessage`. Either `data` (base64) or + * `fetchUrl` (remote URL) must be set. Prefer `fetchUrl` to keep payload size + * small when the binary already lives somewhere reachable. + * + * Mirrors `BotMessageAttachment` on the bot-reply callback path so the agent + * runtime, callback service, and Messager tool/CLI all speak the same shape. + */ +export interface SendMessageAttachment { + /** Base64-encoded bytes. Used when no fetchable URL exists. */ + data?: string; + /** Remote URL the platform server can GET to retrieve the bytes. */ + fetchUrl?: string; + mimeType?: string; + name?: string; + type: 'image' | 'file' | 'video' | 'audio'; +} + export interface SendMessageParams { + /** + * Optional: outbound media attachments (images / files / video / audio). + * Platforms that don't support outbound media silently drop these so the + * text leg still ships. + */ + attachments?: SendMessageAttachment[]; /** Channel / conversation / room ID */ channelId: string; /** Message content (text, markdown depending on platform support) */ diff --git a/packages/chat-adapter-wechat/src/adapter.test.ts b/packages/chat-adapter-wechat/src/adapter.test.ts index 83c0f86489..f989454595 100644 --- a/packages/chat-adapter-wechat/src/adapter.test.ts +++ b/packages/chat-adapter-wechat/src/adapter.test.ts @@ -2,7 +2,7 @@ import type { MockInstance } from 'vitest'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { createWechatAdapter, downloadMediaFromRawMessage, WechatAdapter } from './adapter'; -import { WechatApiClient } from './api'; +import { WechatApiClient, WechatUploadMediaType } from './api'; import type { WechatRawMessage } from './types'; import { MessageItemType, MessageState, MessageType } from './types'; @@ -523,6 +523,146 @@ describe('WechatAdapter', () => { await expect(adapter.startTyping('t')).resolves.toBeUndefined(); }); }); + + // ---------- postMessage (outbound text + attachments) ---------- + + describe('postMessage', () => { + const threadId = 'wechat:single:user_x@im.wechat'; + let sendMessageSpy: MockInstance; + let sendItemSpy: MockInstance; + let uploadSpy: MockInstance; + + beforeEach(() => { + adapter.setContextToken(threadId, 'ctx_tok'); + sendMessageSpy = vi + .spyOn((adapter as any).api, 'sendMessage') + .mockResolvedValue({ ret: 0 }) as MockInstance; + sendItemSpy = vi + .spyOn((adapter as any).api, 'sendItem') + .mockResolvedValue({ ret: 0 }) as MockInstance; + uploadSpy = vi.spyOn((adapter as any).api, 'uploadCdnMedia').mockResolvedValue({ + aesKey: 'AES_B64', + cipherSize: 64, + encryptQueryParam: 'ENC_QP', + rawSize: 50, + }) as MockInstance; + }); + + it('sends pure text via sendMessage and never touches the media path', async () => { + const raw = await adapter.postMessage(threadId, 'hello world'); + + expect(sendMessageSpy).toHaveBeenCalledWith('user_x@im.wechat', 'hello world', 'ctx_tok'); + expect(uploadSpy).not.toHaveBeenCalled(); + expect(sendItemSpy).not.toHaveBeenCalled(); + expect(raw.raw.item_list).toHaveLength(1); + expect(raw.raw.item_list[0].type).toBe(MessageItemType.TEXT); + }); + + it('uploads and sends an image attachment as a separate IMAGE item', async () => { + const bytes = Buffer.from('pretend image bytes'); + + await adapter.postMessage(threadId, { + attachments: [ + { data: bytes, mimeType: 'image/png', name: 'pic.png', type: 'image', url: '' }, + ], + markdown: 'check this out', + }); + + // Text should be sent first via sendMessage, image as a separate sendItem. + expect(sendMessageSpy).toHaveBeenCalledTimes(1); + expect(uploadSpy).toHaveBeenCalledWith( + 'user_x@im.wechat', + WechatUploadMediaType.IMAGE, + bytes, + ); + expect(sendItemSpy).toHaveBeenCalledTimes(1); + + const [, item, contextToken] = sendItemSpy.mock.calls[0]; + expect(contextToken).toBe('ctx_tok'); + expect(item.type).toBe(MessageItemType.IMAGE); + expect(item.image_item?.media).toEqual({ + aes_key: 'AES_B64', + encrypt_query_param: 'ENC_QP', + encrypt_type: 1, + }); + }); + + it('routes a file attachment to a FILE item carrying file_name + len', async () => { + const bytes = Buffer.from('pdf bytes here'); + + await adapter.postMessage(threadId, { + attachments: [ + { data: bytes, mimeType: 'application/pdf', name: 'report.pdf', type: 'file', url: '' }, + ], + raw: '', + }); + + expect(uploadSpy).toHaveBeenCalledWith('user_x@im.wechat', WechatUploadMediaType.FILE, bytes); + expect(sendMessageSpy).not.toHaveBeenCalled(); // empty raw text → skip + const [, item] = sendItemSpy.mock.calls[0]; + expect(item.type).toBe(MessageItemType.FILE); + expect(item.file_item?.file_name).toBe('report.pdf'); + expect(item.file_item?.len).toBe(String(bytes.length)); + }); + + it('falls back to attachment.url when data is absent', async () => { + const remoteBytes = Buffer.from([1, 2, 3, 4, 5]); + vi.stubGlobal( + 'fetch', + vi.fn().mockResolvedValueOnce(new Response(new Uint8Array(remoteBytes), { status: 200 })), + ); + + await adapter.postMessage(threadId, { + attachments: [ + { + mimeType: 'image/jpeg', + name: 'photo.jpg', + type: 'image', + url: 'https://cdn.example/photo.jpg', + }, + ], + raw: '', + }); + + expect(uploadSpy).toHaveBeenCalledTimes(1); + const uploadedBytes = uploadSpy.mock.calls[0][2]; + expect(Buffer.from(uploadedBytes).equals(remoteBytes)).toBe(true); + }); + + it('promotes a FileUpload (no type field) to FILE based on mimeType', async () => { + const bytes = Buffer.from('arbitrary bytes'); + + await adapter.postMessage(threadId, { + files: [{ data: bytes, filename: 'notes.md', mimeType: 'text/markdown' }], + raw: '', + }); + + expect(uploadSpy).toHaveBeenCalledWith('user_x@im.wechat', WechatUploadMediaType.FILE, bytes); + const [, item] = sendItemSpy.mock.calls[0]; + expect(item.type).toBe(MessageItemType.FILE); + expect(item.file_item?.file_name).toBe('notes.md'); + }); + + it('keeps sending other attachments when one upload fails', async () => { + uploadSpy.mockRejectedValueOnce(new Error('CDN exploded')).mockResolvedValueOnce({ + aesKey: 'AES_B64', + cipherSize: 16, + encryptQueryParam: 'ENC_OK', + rawSize: 4, + }); + + await adapter.postMessage(threadId, { + attachments: [ + { data: Buffer.from('first'), name: 'a.png', type: 'image', url: '' }, + { data: Buffer.from('ok'), name: 'b.png', type: 'image', url: '' }, + ], + raw: '', + }); + + expect(uploadSpy).toHaveBeenCalledTimes(2); + expect(sendItemSpy).toHaveBeenCalledTimes(1); // second one succeeded + }); + }); }); // ---------- createWechatAdapter factory ---------- diff --git a/packages/chat-adapter-wechat/src/adapter.ts b/packages/chat-adapter-wechat/src/adapter.ts index acce50bfb1..cb47aa18c0 100644 --- a/packages/chat-adapter-wechat/src/adapter.ts +++ b/packages/chat-adapter-wechat/src/adapter.ts @@ -7,6 +7,7 @@ import type { EmojiValue, FetchOptions, FetchResult, + FileUpload, FormattedContent, Logger, RawMessage, @@ -16,9 +17,9 @@ import type { import { Message, parseMarkdown } from 'chat'; import mime from 'mime'; -import { WechatApiClient } from './api'; +import { WechatApiClient, WechatUploadMediaType } from './api'; import { WechatFormatConverter } from './format-converter'; -import type { WechatAdapterConfig, WechatRawMessage, WechatThreadId } from './types'; +import type { MessageItem, WechatAdapterConfig, WechatRawMessage, WechatThreadId } from './types'; import { MessageItemType, MessageState, MessageType } from './types'; /** @@ -306,6 +307,100 @@ async function downloadImageItemFromRaw( return undefined; } +/** + * Normalized outbound media descriptor used by `WechatAdapter.postMessage`. + * Bridges chat-sdk's two attachment shapes (Attachment vs FileUpload) into a + * single buffer-backed record before uploading to the iLink CDN. + */ +interface OutboundMediaSpec { + buffer: Buffer; + mimeType?: string; + name?: string; + type: 'image' | 'file' | 'video' | 'audio'; +} + +/** + * Resolve an Attachment's binary bytes from any of the SDK's three sources: + * inline `data`, lazy `fetchData()`, or `url`. Returns undefined if none work. + */ +async function loadAttachmentBuffer( + attachment: Attachment, + logger?: Pick, +): Promise { + if (attachment.data) { + return blobOrBufferToBuffer(attachment.data); + } + if (typeof attachment.fetchData === 'function') { + try { + return await attachment.fetchData(); + } catch (error) { + logger?.warn?.('Attachment fetchData failed: %s', error); + } + } + if (attachment.url) { + try { + const response = await fetch(attachment.url, { + signal: AbortSignal.timeout(15_000), + }); + if (response.ok) { + return Buffer.from(await response.arrayBuffer()); + } + logger?.warn?.('Attachment url fetch failed: HTTP %d', response.status); + } catch (error) { + logger?.warn?.('Attachment url fetch failed: %s', error); + } + } + return undefined; +} + +async function fileUploadToBuffer(file: FileUpload): Promise { + return blobOrBufferToBuffer(file.data); +} + +async function blobOrBufferToBuffer( + data: Buffer | Blob | ArrayBuffer, +): Promise { + if (Buffer.isBuffer(data)) return data; + if (data instanceof ArrayBuffer) return Buffer.from(data); + if (typeof Blob !== 'undefined' && data instanceof Blob) { + return Buffer.from(await data.arrayBuffer()); + } + return undefined; +} + +/** + * Infer a chat-sdk Attachment.type from a filename or mime type when we only + * have a FileUpload (which doesn't carry the type field). + */ +function inferAttachmentType( + filename: string, + mimeType?: string, +): 'image' | 'file' | 'video' | 'audio' { + const resolvedMime = mimeType || mime.getType(filename) || ''; + if (resolvedMime.startsWith('image/')) return 'image'; + if (resolvedMime.startsWith('video/')) return 'video'; + if (resolvedMime.startsWith('audio/')) return 'audio'; + return 'file'; +} + +function mapToUploadMediaType(type: 'image' | 'file' | 'video' | 'audio'): WechatUploadMediaType { + switch (type) { + case 'image': { + return WechatUploadMediaType.IMAGE; + } + case 'video': { + return WechatUploadMediaType.VIDEO; + } + case 'audio': { + return WechatUploadMediaType.VOICE; + } + case 'file': + default: { + return WechatUploadMediaType.FILE; + } + } +} + /** * WeChat (iLink) adapter for Chat SDK. * @@ -406,7 +501,37 @@ export class WechatAdapter implements Adapter const text = this.formatConverter.renderPostable(message); const contextToken = this.contextTokens.get(threadId) || ''; - await this.api.sendMessage(id, text, contextToken); + const sentItems: MessageItem[] = []; + + if (text.trim()) { + await this.api.sendMessage(id, text, contextToken); + sentItems.push({ text_item: { text }, type: MessageItemType.TEXT }); + } + + // Per protocol-spec §6.7, media items are sent as separate sendmessage calls + // (one item per request). We collect attachments + files from the postable + // payload, materialize their bytes, and upload each to the iLink CDN. + const mediaSpecs = await this.collectMediaSpecs(message); + for (const spec of mediaSpecs) { + try { + const item = await this.uploadAndBuildMediaItem(id, spec); + await this.api.sendItem(id, item, contextToken); + sentItems.push(item); + } catch (error) { + // Single-attachment failure shouldn't abort the rest — log and continue. + this.logger.warn( + 'Failed to send %s attachment "%s" to WeChat: %s', + spec.type, + spec.name ?? '(unnamed)', + error, + ); + } + } + + // Fall back to an empty TEXT item if nothing was sent (preserves previous + // behavior where postMessage always produced a raw message). + const itemList = + sentItems.length > 0 ? sentItems : [{ text_item: { text }, type: MessageItemType.TEXT }]; return { id: `bot_${Date.now()}`, @@ -415,7 +540,7 @@ export class WechatAdapter implements Adapter context_token: contextToken, create_time_ms: Date.now(), from_user_id: this._botUserId || '', - item_list: [{ text_item: { text }, type: MessageItemType.TEXT }], + item_list: itemList, message_id: 0, message_state: MessageState.FINISH, message_type: MessageType.BOT, @@ -425,6 +550,101 @@ export class WechatAdapter implements Adapter }; } + /** + * Pull `attachments` and `files` off a postable message (the shape varies by + * union member) and normalize them into a flat list with the bytes we'll need. + */ + private async collectMediaSpecs(message: AdapterPostableMessage): Promise { + if (typeof message === 'string') return []; + + const attachments: Attachment[] = []; + const files: FileUpload[] = []; + + // PostableRaw / PostableMarkdown / PostableAst all use the same `attachments` + `files` shape. + // PostableCard only carries `files`. CardElement carries neither. + if ('attachments' in message && Array.isArray(message.attachments)) { + attachments.push(...message.attachments); + } + if ('files' in message && Array.isArray(message.files)) { + files.push(...message.files); + } + + const specs: OutboundMediaSpec[] = []; + + for (const attachment of attachments) { + const buffer = await loadAttachmentBuffer(attachment, this.logger); + if (!buffer) continue; + specs.push({ + buffer, + mimeType: attachment.mimeType, + name: attachment.name, + type: attachment.type, + }); + } + + for (const file of files) { + const buffer = await fileUploadToBuffer(file); + if (!buffer) continue; + specs.push({ + buffer, + mimeType: file.mimeType, + name: file.filename, + type: inferAttachmentType(file.filename, file.mimeType), + }); + } + + return specs; + } + + /** + * Upload one media buffer to the iLink CDN and build the corresponding + * MessageItem to send via {@link WechatApiClient.sendItem}. + */ + private async uploadAndBuildMediaItem( + toUserId: string, + spec: OutboundMediaSpec, + ): Promise { + const mediaType = mapToUploadMediaType(spec.type); + const result = await this.api.uploadCdnMedia(toUserId, mediaType, spec.buffer); + const cdnMedia = { + aes_key: result.aesKey, + encrypt_query_param: result.encryptQueryParam, + encrypt_type: 1 as const, + }; + + switch (mediaType) { + case WechatUploadMediaType.IMAGE: { + return { + image_item: { media: cdnMedia }, + type: MessageItemType.IMAGE, + }; + } + case WechatUploadMediaType.VIDEO: { + return { + type: MessageItemType.VIDEO, + video_item: { media: cdnMedia, video_size: result.cipherSize }, + }; + } + case WechatUploadMediaType.VOICE: { + return { + type: MessageItemType.VOICE, + voice_item: { media: cdnMedia }, + }; + } + case WechatUploadMediaType.FILE: + default: { + return { + file_item: { + file_name: spec.name, + len: String(spec.buffer.length), + media: cdnMedia, + }, + type: MessageItemType.FILE, + }; + } + } + } + async editMessage( threadId: string, _messageId: string, diff --git a/packages/chat-adapter-wechat/src/api.test.ts b/packages/chat-adapter-wechat/src/api.test.ts index 89f95d1912..e3a520a0fd 100644 --- a/packages/chat-adapter-wechat/src/api.test.ts +++ b/packages/chat-adapter-wechat/src/api.test.ts @@ -1,4 +1,4 @@ -import { createCipheriv } from 'node:crypto'; +import { createCipheriv, createDecipheriv } from 'node:crypto'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; @@ -9,8 +9,9 @@ import { pollQrStatus, resolveAesKey, WechatApiClient, + WechatUploadMediaType, } from './api'; -import { WECHAT_RET_CODES } from './types'; +import { MessageItemType, WECHAT_RET_CODES } from './types'; // ---- helpers ---- @@ -248,6 +249,134 @@ describe('WechatApiClient', () => { }); }); + // ---------- sendItem ---------- + + describe('sendItem', () => { + it('should send a single media item with the provided MessageItem shape', async () => { + mockFetch.mockResolvedValueOnce(jsonResponse({ ret: 0 })); + + const item = { + image_item: { + media: { + aes_key: 'AABB', + encrypt_query_param: 'qqp', + encrypt_type: 1 as const, + }, + }, + type: MessageItemType.IMAGE, + }; + await client.sendItem('user_1', item, 'ctx'); + + const body = JSON.parse(mockFetch.mock.calls[0][1]!.body as string); + expect(body.msg.to_user_id).toBe('user_1'); + expect(body.msg.context_token).toBe('ctx'); + expect(body.msg.item_list).toEqual([item]); + expect(body.msg.item_list).toHaveLength(1); + }); + }); + + // ---------- uploadCdnMedia ---------- + + describe('uploadCdnMedia', () => { + it('should follow the 3-step getuploadurl → encrypt → POST CDN flow', async () => { + // Step 1: getuploadurl response + mockFetch.mockResolvedValueOnce(jsonResponse({ upload_param: 'UP_PARAM_xyz' })); + // Step 2: CDN upload — returns x-encrypted-param header + mockFetch.mockResolvedValueOnce( + new Response('', { + headers: { 'x-encrypted-param': 'ENC_QP_abc' }, + status: 200, + }), + ); + + const plaintext = Buffer.from('hello image bytes'); + const result = await client.uploadCdnMedia( + 'user_1@im.wechat', + WechatUploadMediaType.IMAGE, + plaintext, + ); + + expect(result.encryptQueryParam).toBe('ENC_QP_abc'); + expect(result.rawSize).toBe(plaintext.length); + expect(result.cipherSize).toBeGreaterThanOrEqual(plaintext.length); + + // Step 1 body shape + const step1Url = mockFetch.mock.calls[0][0] as string; + expect(step1Url).toContain('/ilink/bot/getuploadurl'); + const step1Body = JSON.parse(mockFetch.mock.calls[0][1]!.body as string); + expect(step1Body.media_type).toBe(WechatUploadMediaType.IMAGE); + expect(step1Body.to_user_id).toBe('user_1@im.wechat'); + expect(step1Body.no_need_thumb).toBe(true); + expect(step1Body.aeskey).toMatch(/^[\da-f]{32}$/); + expect(step1Body.filekey).toMatch(/^[\da-f]{32}$/); + expect(step1Body.rawsize).toBe(plaintext.length); + + // Step 2 should POST to CDN /upload with octet-stream body + const step2Url = mockFetch.mock.calls[1][0] as string; + expect(step2Url).toContain(`${CDN_BASE_URL}/upload`); + expect(step2Url).toContain('encrypted_query_param=UP_PARAM_xyz'); + expect(step2Url).toContain(`filekey=${step1Body.filekey}`); + const step2Headers = mockFetch.mock.calls[1][1]!.headers as Record; + expect(step2Headers['Content-Type']).toBe('application/octet-stream'); + + // Returned aes_key should be base64(hex_string) — round-trips back to the hex + const decoded = Buffer.from(result.aesKey, 'base64').toString('ascii'); + expect(decoded).toBe(step1Body.aeskey); + }); + + it('should round-trip — uploaded ciphertext decrypts to the original plaintext', async () => { + let capturedAeskey: string | undefined; + let capturedCiphertext: Buffer | undefined; + + mockFetch.mockImplementationOnce(async (_url, init) => { + capturedAeskey = JSON.parse(init!.body as string).aeskey; + return jsonResponse({ upload_param: 'UP' }); + }); + mockFetch.mockImplementationOnce(async (_url, init) => { + const body = init!.body as ArrayBuffer | Uint8Array; + capturedCiphertext = Buffer.from(body as ArrayBufferLike); + return new Response('', { + headers: { 'x-encrypted-param': 'ENC' }, + status: 200, + }); + }); + + const plaintext = Buffer.from('round-trip test payload — 中文也要工作 ✓'); + await client.uploadCdnMedia('u', WechatUploadMediaType.FILE, plaintext); + + const key = Buffer.from(capturedAeskey!, 'hex'); + const decipher = createDecipheriv('aes-128-ecb', key, null); + const decrypted = Buffer.concat([decipher.update(capturedCiphertext!), decipher.final()]); + expect(decrypted.equals(plaintext)).toBe(true); + }); + + it('should throw if getuploadurl returns no upload_param', async () => { + mockFetch.mockResolvedValueOnce(jsonResponse({})); + + await expect( + client.uploadCdnMedia('u', WechatUploadMediaType.FILE, Buffer.from('x')), + ).rejects.toThrow('empty upload_param'); + }); + + it('should throw if CDN upload responds non-2xx', async () => { + mockFetch.mockResolvedValueOnce(jsonResponse({ upload_param: 'UP' })); + mockFetch.mockResolvedValueOnce(new Response('forbidden', { status: 403 })); + + await expect( + client.uploadCdnMedia('u', WechatUploadMediaType.FILE, Buffer.from('x')), + ).rejects.toThrow('CDN upload failed: 403'); + }); + + it('should throw if CDN response is missing x-encrypted-param header', async () => { + mockFetch.mockResolvedValueOnce(jsonResponse({ upload_param: 'UP' })); + mockFetch.mockResolvedValueOnce(new Response('', { status: 200 })); + + await expect( + client.uploadCdnMedia('u', WechatUploadMediaType.FILE, Buffer.from('x')), + ).rejects.toThrow('missing x-encrypted-param'); + }); + }); + describe('getConfig', () => { it('should return config with typing_ticket', async () => { mockFetch.mockResolvedValueOnce(jsonResponse({ ret: 0, typing_ticket: 'ticket_abc' })); diff --git a/packages/chat-adapter-wechat/src/api.ts b/packages/chat-adapter-wechat/src/api.ts index cbe01877a1..c4544fad44 100644 --- a/packages/chat-adapter-wechat/src/api.ts +++ b/packages/chat-adapter-wechat/src/api.ts @@ -1,4 +1,4 @@ -import { createDecipheriv } from 'node:crypto'; +import { createCipheriv, createDecipheriv, createHash, randomBytes } from 'node:crypto'; import type { BaseInfo, @@ -10,6 +10,26 @@ import type { } from './types'; import { MessageItemType, MessageState, MessageType, WECHAT_RET_CODES } from './types'; +/** iLink CDN media types — see protocol-spec §8.2. */ +export enum WechatUploadMediaType { + IMAGE = 1, + VIDEO = 2, + FILE = 3, + VOICE = 4, +} + +/** Result of uploading media to the iLink CDN. */ +export interface WechatUploadResult { + /** Base64-encoded hex string of the AES key — the format expected in outbound `media.aes_key`. */ + aesKey: string; + /** AES-128-ECB ciphertext size (matches `mid_size` for image_item / video_item). */ + cipherSize: number; + /** `encrypt_query_param` returned by CDN; place into outbound `media.encrypt_query_param`. */ + encryptQueryParam: string; + /** Plaintext file size. */ + rawSize: number; +} + export const DEFAULT_BASE_URL = 'https://ilinkai.weixin.qq.com'; export const CDN_BASE_URL = 'https://novac2c.cdn.weixin.qq.com/c2c'; @@ -122,37 +142,130 @@ export class WechatApiClient { let lastResponse: WechatSendMessageResponse = { ret: 0 }; for (const chunk of chunks) { - const item: MessageItem = { - text_item: { text: chunk }, - type: MessageItemType.TEXT, - }; - - const body = { - base_info: BASE_INFO, - msg: { - client_id: crypto.randomUUID(), - context_token: contextToken, - from_user_id: '', - item_list: [item], - message_state: MessageState.FINISH, - message_type: MessageType.BOT, - to_user_id: toUserId, - }, - }; - - const response = await fetch(`${this.baseUrl}/ilink/bot/sendmessage`, { - body: JSON.stringify(body), - headers: buildHeaders(this.botToken), - method: 'POST', - signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS), - }); - - lastResponse = await parseResponse(response, 'sendmessage'); + lastResponse = await this.sendItem( + toUserId, + { text_item: { text: chunk }, type: MessageItemType.TEXT }, + contextToken, + ); } return lastResponse; } + /** + * Send a single MessageItem (text or media) via iLink Bot API. + * + * Per protocol-spec §6.7, the stable public pattern is one MessageItem per + * request — text + media messages are sent as separate calls. Callers should + * generate fresh `client_id`s per call; this method allocates one internally. + */ + async sendItem( + toUserId: string, + item: MessageItem, + contextToken: string, + ): Promise { + const body = { + base_info: BASE_INFO, + msg: { + client_id: crypto.randomUUID(), + context_token: contextToken, + from_user_id: '', + item_list: [item], + message_state: MessageState.FINISH, + message_type: MessageType.BOT, + to_user_id: toUserId, + }, + }; + + const response = await fetch(`${this.baseUrl}/ilink/bot/sendmessage`, { + body: JSON.stringify(body), + headers: buildHeaders(this.botToken), + method: 'POST', + signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS), + }); + + return parseResponse(response, 'sendmessage'); + } + + /** + * Upload outbound media to the iLink CDN. + * + * Implements the 3-step flow from protocol-spec §8.2: + * 1. `getuploadurl` — request `upload_param` with media metadata + AES key + * 2. Local AES-128-ECB + PKCS7 encrypt + * 3. POST ciphertext to CDN; read `x-encrypted-param` response header + * + * The returned `aesKey` is base64-of-hex-string (the format openclaw uses for + * outbound `media.aes_key`, see protocol-spec §8.4 format B). Plug the result + * directly into `image_item.media` / `file_item.media` / `video_item.media`. + */ + async uploadCdnMedia( + toUserId: string, + mediaType: WechatUploadMediaType, + plaintext: Buffer, + ): Promise { + const aesKeyBuf = randomBytes(16); + const aesKeyHex = aesKeyBuf.toString('hex'); + const filekey = randomBytes(16).toString('hex'); + const rawSize = plaintext.length; + const ciphertext = encryptAesEcb(plaintext, aesKeyBuf); + const cipherSize = ciphertext.length; + const rawfilemd5 = createHash('md5').update(plaintext).digest('hex'); + + // Step 1: request upload_param + const uploadParamResp = await fetch(`${this.baseUrl}/ilink/bot/getuploadurl`, { + body: JSON.stringify({ + aeskey: aesKeyHex, + base_info: BASE_INFO, + filekey, + filesize: cipherSize, + media_type: mediaType, + no_need_thumb: true, + rawfilemd5, + rawsize: rawSize, + to_user_id: toUserId, + }), + headers: buildHeaders(this.botToken), + method: 'POST', + signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS), + }); + + const { upload_param: uploadParam } = await parseResponse<{ upload_param?: string }>( + uploadParamResp, + 'getuploadurl', + ); + if (!uploadParam) { + throw new Error('getuploadurl returned empty upload_param'); + } + + // Step 2 + 3: upload ciphertext to CDN + const cdnUrl = `${CDN_BASE_URL}/upload?encrypted_query_param=${encodeURIComponent( + uploadParam, + )}&filekey=${filekey}`; + const cdnResp = await fetch(cdnUrl, { + body: new Uint8Array(ciphertext), + headers: { 'Content-Type': 'application/octet-stream' }, + method: 'POST', + signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS), + }); + + if (!cdnResp.ok) { + const text = await cdnResp.text().catch(() => ''); + throw new Error(`CDN upload failed: ${cdnResp.status} ${text}`); + } + + const encryptQueryParam = cdnResp.headers.get('x-encrypted-param'); + if (!encryptQueryParam) { + throw new Error('CDN upload response missing x-encrypted-param header'); + } + + // Outbound media.aes_key encoding follows openclaw: base64 of the 32-char hex string + // (protocol-spec §8.4 format B). Inbound code accepts both formats. + const aesKey = Buffer.from(aesKeyHex, 'ascii').toString('base64'); + + return { aesKey, cipherSize, encryptQueryParam, rawSize }; + } + /** * Send typing indicator via iLink Bot API. */ @@ -325,6 +438,16 @@ function decryptAesEcb(ciphertext: Buffer, key: Buffer): Buffer { return Buffer.concat([decipher.update(ciphertext), decipher.final()]); } +/** + * AES-128-ECB encrypt with PKCS7 padding (Node's default for createCipheriv). + * + * Used for outbound media uploads — see {@link WechatApiClient.uploadCdnMedia}. + */ +function encryptAesEcb(plaintext: Buffer, key: Buffer): Buffer { + const cipher = createCipheriv('aes-128-ecb', key, null); + return Buffer.concat([cipher.update(plaintext), cipher.final()]); +} + /** * Resolve the 16-byte AES key from the two possible sources and encodings. * diff --git a/packages/chat-adapter-wechat/src/index.ts b/packages/chat-adapter-wechat/src/index.ts index 1260a5b6b6..ff509fc595 100644 --- a/packages/chat-adapter-wechat/src/index.ts +++ b/packages/chat-adapter-wechat/src/index.ts @@ -1,8 +1,10 @@ export { createWechatAdapter, downloadMediaFromRawMessage, WechatAdapter } from './adapter'; export type { QrCodeResponse, QrStatusResponse } from './api'; export { DEFAULT_BASE_URL, fetchQrCode, pollQrStatus, WechatApiClient } from './api'; +export { WechatUploadMediaType } from './api'; export { WechatFormatConverter } from './format-converter'; export type { + MessageItem, WechatAdapterConfig, WechatGetConfigResponse, WechatGetUpdatesResponse, diff --git a/src/features/Onboarding/Agent/CompletionPanel.tsx b/src/features/Onboarding/Agent/CompletionPanel.tsx index df774499d3..310a5d2062 100644 --- a/src/features/Onboarding/Agent/CompletionPanel.tsx +++ b/src/features/Onboarding/Agent/CompletionPanel.tsx @@ -8,6 +8,7 @@ import { useAgentMeta } from '@/features/Conversation/hooks/useAgentMeta'; import LobeMessage from '@/routes/onboarding/components/LobeMessage'; import FeedbackPanel from './FeedbackPanel'; +import MessengerIntegrations from './MessengerIntegrations'; import { staticStyle } from './staticStyle'; interface CompletionPanelProps { @@ -23,38 +24,36 @@ const CompletionPanel = memo( const agentMeta = useAgentMeta(); return (
- - - - {t('agent.completionSubtitle')} - - - {showFeedback && topicId && ( - - )} + + + + + {t('agent.completionSubtitle')} + + + {showFeedback && topicId && ( + + )} + +
); diff --git a/src/features/Onboarding/Agent/MessengerIntegrations.tsx b/src/features/Onboarding/Agent/MessengerIntegrations.tsx new file mode 100644 index 0000000000..58186a2586 --- /dev/null +++ b/src/features/Onboarding/Agent/MessengerIntegrations.tsx @@ -0,0 +1,170 @@ +'use client'; + +import { Button, Flexbox, Popover, Skeleton, Text } from '@lobehub/ui'; +import { Discord, Slack, Telegram } from '@lobehub/ui/icons'; +import { Divider, QRCode } from 'antd'; +import { createStaticStyles } from 'antd-style'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; +import useSWR from 'swr'; + +import { buildTelegramDeepLink, PlatformAvatar } from '@/features/Messenger/constants'; +import { messengerService } from '@/services/messenger'; + +const SLACK_INSTALL_HREF = '/api/agent/messenger/slack/install'; +const DISCORD_INSTALL_HREF = '/api/agent/messenger/discord/install'; + +const styles = createStaticStyles(({ css, cssVar }) => ({ + divider: css` + width: 100%; + min-width: 320px; + margin-block: 0 6px; + + &::before, + &::after { + border-block-start: 1px dashed ${cssVar.colorBorder} !important; + } + `, + group: css` + display: flex; + flex-wrap: wrap; + gap: 12px; + justify-content: center; + + @media (width <= 540px) { + flex-direction: column; + width: 100%; + } + `, + qrIconOverlay: css` + pointer-events: none; + + position: absolute; + z-index: 1; + inset-block-start: 50%; + inset-inline-start: 50%; + transform: translate(-50%, -50%); + + border: 3px solid ${cssVar.colorBgContainer}; + border-radius: 50%; + + line-height: 0; + `, + qrWrap: css` + position: relative; + padding: 12px; + border-radius: 12px; + background: ${cssVar.colorBgContainer}; + `, + wrapper: css` + gap: 14px; + align-items: center; + margin-block-start: 48px; + `, +})); + +const MessengerIntegrations = memo(() => { + const { t } = useTranslation('onboarding'); + + const { data, isLoading } = useSWR('messenger:availablePlatforms', () => + messengerService.availablePlatforms(), + ); + + if (isLoading) { + return ( + + + + +
+ {[0, 1, 2].map((i) => ( + + ))} +
+
+ ); + } + + const platforms = data ?? []; + if (platforms.length === 0) return null; + + const byId = new Map(platforms.map((p) => [p.id, p])); + const slack = byId.get('slack'); + const discord = byId.get('discord'); + const telegram = byId.get('telegram'); + + return ( + + + + {t('agent.messenger.subtitle')} + + +
+ {slack && ( + + )} + {discord && ( + + )} + {telegram?.botUsername && ( + +
+ +
+ +
+
+ + {t('agent.messenger.telegramQrCaption')} + + + } + > + +
+ )} +
+
+ ); +}); + +MessengerIntegrations.displayName = 'OnboardingMessengerIntegrations'; + +export default MessengerIntegrations; diff --git a/src/locales/default/onboarding.ts b/src/locales/default/onboarding.ts index da63fe650b..2b02a1780a 100644 --- a/src/locales/default/onboarding.ts +++ b/src/locales/default/onboarding.ts @@ -31,6 +31,12 @@ export default { 'agent.feedback.rateGood': 'Rate this onboarding positively', 'agent.feedback.submit': 'Send feedback', 'agent.feedback.thanks': 'Thanks for the feedback!', + 'agent.messenger.cta.discord': 'Add to Discord', + 'agent.messenger.cta.slack': 'Add to Slack', + 'agent.messenger.cta.telegram': 'Open in Telegram', + 'agent.messenger.subtitle': 'Chat with your agent on Telegram, Slack, or Discord', + 'agent.messenger.telegramQrCaption': 'Scan with your phone camera', + 'agent.messenger.title': 'Keep me with you, wherever you message', 'agent.modeSwitch.reset': 'Reset Flow', 'agent.progress': '{{currentStep}}/{{totalSteps}}', 'agent.stage.agentIdentity': 'Agent Identity', diff --git a/src/server/routers/lambda/botMessage.ts b/src/server/routers/lambda/botMessage.ts index 67b68aafa5..1852119736 100644 --- a/src/server/routers/lambda/botMessage.ts +++ b/src/server/routers/lambda/botMessage.ts @@ -144,6 +144,17 @@ export const botMessageRouter = router({ sendMessage: botMessageProcedure .input( z.object({ + attachments: z + .array( + z.object({ + data: z.string().optional(), + fetchUrl: z.string().url().optional(), + mimeType: z.string().optional(), + name: z.string().optional(), + type: z.enum(['image', 'file', 'video', 'audio']), + }), + ) + .optional(), botId: z.string(), channelId: z.string(), content: z.string(), @@ -154,6 +165,7 @@ export const botMessageRouter = router({ .mutation(async ({ input, ctx }) => { const { service, platform } = await resolveBot(ctx.agentBotProviderModel, input.botId); return service.sendMessage({ + attachments: input.attachments, channelId: input.channelId, content: input.content, embeds: input.embeds, diff --git a/src/server/services/agentRuntime/CompletionLifecycle.ts b/src/server/services/agentRuntime/CompletionLifecycle.ts index 78e69a04d7..0e46cdb162 100644 --- a/src/server/services/agentRuntime/CompletionLifecycle.ts +++ b/src/server/services/agentRuntime/CompletionLifecycle.ts @@ -275,12 +275,26 @@ export class CompletionLifecycle { private buildLifecycleEvent(operationId: string, state: any, reason: string) { const metadata = state?.metadata || {}; - const lastAssistantContent = state?.messages - ?.slice() + const messages: any[] = Array.isArray(state?.messages) ? state.messages : []; + + // Pull text content off the **final** assistant turn. Content may be a + // plain string or an OpenAI-style multimodal part array; for the array + // case we concatenate the text parts so the reply body is preserved. + // + // We deliberately match on `role === 'assistant'` only — not on whether + // the turn has any text — so an image-only or tool-output final turn + // doesn't fall through to an earlier assistant message and ship stale + // text alongside the current attachments. + const lastAssistantMessage = messages + .slice() .reverse() - .find( - (m: { content?: string; role: string }) => m.role === 'assistant' && m.content, - )?.content; + .find((m: { content?: unknown; role: string }) => m.role === 'assistant'); + const lastAssistantContent = lastAssistantMessage + ? extractTextFromMessageContent(lastAssistantMessage.content) + : undefined; + + const attachments = extractOutboundAttachments(messages); + const duration = state?.createdAt ? Date.now() - new Date(state.createdAt).getTime() : undefined; @@ -288,6 +302,7 @@ export class CompletionLifecycle { return { event: { agentId: metadata?.agentId || '', + attachments: attachments.length > 0 ? attachments : undefined, cost: state?.cost?.total, duration, errorDetail: state?.error, @@ -308,3 +323,168 @@ export class CompletionLifecycle { }; } } + +// -------------------------------------------------------------------------- +// Outbound attachment extraction +// -------------------------------------------------------------------------- + +type OutboundAttachment = { + data?: string; + fetchUrl?: string; + mimeType?: string; + name?: string; + type: 'image' | 'file' | 'video' | 'audio'; +}; + +const DATA_URL_RE = /^data:([^;]+);base64,(.+)$/; + +const inferAttachmentTypeFromMime = (mimeType: string | undefined): OutboundAttachment['type'] => { + if (!mimeType) return 'file'; + if (mimeType.startsWith('image/')) return 'image'; + if (mimeType.startsWith('video/')) return 'video'; + if (mimeType.startsWith('audio/')) return 'audio'; + return 'file'; +}; + +/** + * Materialize a `url` field — either a `data:` URL (extract base64 inline) or + * a remote URL (record fetchUrl). Returns undefined for unsupported shapes. + */ +const buildAttachmentFromUrl = ( + url: string | undefined, + fallbackType: OutboundAttachment['type'] = 'image', +): OutboundAttachment | undefined => { + if (!url || typeof url !== 'string') return undefined; + const dataMatch = url.match(DATA_URL_RE); + if (dataMatch) { + const mimeType = dataMatch[1]; + return { + data: dataMatch[2], + mimeType, + type: inferAttachmentTypeFromMime(mimeType), + }; + } + // Bare http(s) URL — let the downstream messenger fetch it lazily. + if (/^https?:\/\//.test(url)) { + return { fetchUrl: url, type: fallbackType }; + } + return undefined; +}; + +/** + * Pull text out of a message's `content` field. Accepts both string and + * OpenAI-style multimodal arrays `[{ type: 'text', text }, { type: 'image_url', image_url: { url } }]`. + */ +const extractTextFromMessageContent = (content: unknown): string | undefined => { + if (typeof content === 'string') return content || undefined; + if (!Array.isArray(content)) return undefined; + const parts: string[] = []; + for (const part of content) { + if (typeof part === 'string') { + parts.push(part); + } else if (part && typeof part === 'object' && (part as any).type === 'text') { + const text = (part as { text?: unknown }).text; + if (typeof text === 'string') parts.push(text); + } + } + const joined = parts.join(''); + return joined || undefined; +}; + +/** + * Extract image/file parts from a message's `content` array. Each entry is + * mapped to the JSON-safe outbound attachment shape (data or fetchUrl). + */ +const extractAttachmentsFromContent = (content: unknown): OutboundAttachment[] => { + if (!Array.isArray(content)) return []; + const out: OutboundAttachment[] = []; + for (const part of content) { + if (!part || typeof part !== 'object') continue; + const type = (part as { type?: unknown }).type; + if (type === 'image_url') { + const url = (part as { image_url?: { url?: string } }).image_url?.url; + const att = buildAttachmentFromUrl(url, 'image'); + if (att) out.push(att); + } else if (type === 'image') { + // Anthropic-style: { type: 'image', source: { type: 'base64', media_type, data } } + const source = (part as { source?: { data?: string; media_type?: string; type?: string } }) + .source; + if (source?.type === 'base64' && source.data) { + const mimeType = source.media_type; + out.push({ + data: source.data, + mimeType, + type: inferAttachmentTypeFromMime(mimeType), + }); + } else if (source?.type === 'url') { + const url = (source as { url?: string }).url; + const att = buildAttachmentFromUrl(url, 'image'); + if (att) out.push(att); + } + } else if (type === 'file' || type === 'file_url') { + const file = (part as { file?: { url?: string; name?: string; mime_type?: string } }).file; + const att = buildAttachmentFromUrl(file?.url, 'file'); + if (att) { + att.name = file?.name ?? att.name; + att.mimeType = file?.mime_type ?? att.mimeType; + out.push(att); + } + } + } + return out; +}; + +/** + * Walk recent messages and collect outbound image/file attachments to send + * alongside the reply. Scans the last assistant message *and* any tool + * messages that came after the previous assistant turn — tool-generated + * images (e.g. a drawing tool that returns an image_url result) need to be + * delivered with the next reply. + * + * Deduplicates by data/fetchUrl identity. + */ +const extractOutboundAttachments = (messages: any[]): OutboundAttachment[] => { + if (!Array.isArray(messages) || messages.length === 0) return []; + + // Walk from the end backwards: collect attachments until we hit the + // previous assistant turn that already has text — that boundary marks + // "the current reply window". + const collected: OutboundAttachment[] = []; + let crossedFinalAssistant = false; + + for (let i = messages.length - 1; i >= 0; i--) { + const msg = messages[i]; + if (!msg || typeof msg !== 'object') continue; + const role = (msg as { role?: string }).role; + const content = (msg as { content?: unknown }).content; + + if (role === 'assistant') { + if (!crossedFinalAssistant) { + // The final assistant turn: harvest its multimodal parts. + collected.push(...extractAttachmentsFromContent(content)); + crossedFinalAssistant = true; + continue; + } + // A previous assistant turn — stop walking, we don't want to dredge up + // attachments from prior conversation rounds. + break; + } + + if (role === 'tool') { + // Tool results between the previous assistant turn and the final one. + collected.push(...extractAttachmentsFromContent(content)); + } + } + + // Reverse so message-order (older first) is preserved, then dedupe. + collected.reverse(); + const seen = new Set(); + const result: OutboundAttachment[] = []; + for (const att of collected) { + const key = att.fetchUrl ?? att.data ?? ''; + if (!key || seen.has(key)) continue; + seen.add(key); + result.push(att); + } + return result; +}; diff --git a/src/server/services/agentRuntime/__tests__/CompletionLifecycle.test.ts b/src/server/services/agentRuntime/__tests__/CompletionLifecycle.test.ts index af72016c72..38f84f6499 100644 --- a/src/server/services/agentRuntime/__tests__/CompletionLifecycle.test.ts +++ b/src/server/services/agentRuntime/__tests__/CompletionLifecycle.test.ts @@ -94,3 +94,104 @@ describe('CompletionLifecycle.extractErrorMessage', () => { expect(result).toBe('Budget exceeded'); }); }); + +describe('CompletionLifecycle.buildLifecycleEvent', () => { + const callBuild = (state: unknown, reason = 'completed') => + (buildLifecycle() as any).buildLifecycleEvent('op-1', state, reason); + + it('extracts text content from a plain-string final assistant turn', () => { + const state = { + messages: [ + { content: 'user prompt', role: 'user' }, + { content: 'final answer', role: 'assistant' }, + ], + metadata: { agentId: 'agent-1', userId: 'user-1' }, + }; + + const { event } = callBuild(state); + + expect(event.lastAssistantContent).toBe('final answer'); + expect(event.attachments).toBeUndefined(); + }); + + it('concatenates text parts from a multimodal final assistant turn', () => { + const state = { + messages: [ + { + content: [ + { text: 'here is the image: ', type: 'text' }, + { image_url: { url: 'https://cdn.example.com/a.png' }, type: 'image_url' }, + { text: '\n\nhope it helps', type: 'text' }, + ], + role: 'assistant', + }, + ], + metadata: {}, + }; + + const { event } = callBuild(state); + + expect(event.lastAssistantContent).toBe('here is the image: \n\nhope it helps'); + expect(event.attachments).toEqual([ + expect.objectContaining({ fetchUrl: 'https://cdn.example.com/a.png', type: 'image' }), + ]); + }); + + it('returns undefined text for image-only final assistant turn (no fallback to earlier text)', () => { + // Regression: the previous implementation `.find(m => role === 'assistant' && hasText)` + // would skip the image-only final turn and walk back to the earlier text + // turn, shipping stale prose alongside the current image. The fix matches + // on role only — text must be undefined when the final turn has no text. + const state = { + messages: [ + { content: 'stale prior text', role: 'assistant' }, + { content: 'follow-up prompt', role: 'user' }, + { + content: [{ image_url: { url: 'https://cdn.example.com/new.png' }, type: 'image_url' }], + role: 'assistant', + }, + ], + metadata: {}, + }; + + const { event } = callBuild(state); + + expect(event.lastAssistantContent).toBeUndefined(); + expect(event.attachments).toEqual([ + expect.objectContaining({ fetchUrl: 'https://cdn.example.com/new.png', type: 'image' }), + ]); + }); + + it('returns undefined text when there are no assistant messages', () => { + const state = { + messages: [{ content: 'just a user prompt', role: 'user' }], + metadata: {}, + }; + + const { event } = callBuild(state); + + expect(event.lastAssistantContent).toBeUndefined(); + expect(event.attachments).toBeUndefined(); + }); + + it('returns undefined text when content is an empty string', () => { + // `extractTextFromMessageContent` returns undefined for empty strings, so + // an empty-string final assistant turn must not pretend it has text. + const state = { + messages: [{ content: '', role: 'assistant' }], + metadata: {}, + }; + + const { event } = callBuild(state); + + expect(event.lastAssistantContent).toBeUndefined(); + }); + + it('handles missing messages array gracefully', () => { + const { event } = callBuild({ metadata: { agentId: 'a' } }); + + expect(event.lastAssistantContent).toBeUndefined(); + expect(event.attachments).toBeUndefined(); + expect(event.agentId).toBe('a'); + }); +}); diff --git a/src/server/services/bot/AgentBridgeService.ts b/src/server/services/bot/AgentBridgeService.ts index cde4510b2d..acbfd9680e 100644 --- a/src/server/services/bot/AgentBridgeService.ts +++ b/src/server/services/bot/AgentBridgeService.ts @@ -38,6 +38,53 @@ import { const log = debug('lobe-server:bot:agent-bridge'); +/** + * Convert hook-event JSON-safe attachments (`{ data?: base64, fetchUrl? }`) + * into chat-sdk `Attachment` shape (`{ data?: Buffer, url? }`) so they can + * ride along `thread.post({ markdown, attachments })` in local mode. Returns + * `undefined` when there are no attachments to send. + */ +function hookEventAttachmentsToChatSdk( + attachments: + | Array<{ + data?: string; + fetchUrl?: string; + mimeType?: string; + name?: string; + type: 'image' | 'file' | 'video' | 'audio'; + }> + | undefined, +): + | Array<{ + data?: Buffer; + mimeType?: string; + name?: string; + type: 'image' | 'file' | 'video' | 'audio'; + url?: string; + }> + | undefined { + if (!attachments?.length) return undefined; + const out = []; + for (const att of attachments) { + if (att.fetchUrl) { + out.push({ + mimeType: att.mimeType, + name: att.name, + type: att.type, + url: att.fetchUrl, + }); + } else if (att.data) { + out.push({ + data: Buffer.from(att.data, 'base64'), + mimeType: att.mimeType, + name: att.name, + type: att.type, + }); + } + } + return out.length > 0 ? out : undefined; +} + const EXECUTION_TIMEOUT = 30 * 60 * 1000; // 30 minutes // If the last activity in a bot topic is older than this threshold, @@ -1212,36 +1259,58 @@ export class AgentBridgeService { try { const lastAssistantContent = event.lastAssistantContent; + // Convert hook-event attachments (JSON-safe) to chat-sdk + // Attachment shape. Only the *last* chunk carries + // attachments so a multi-chunk reply doesn't repeat the + // image/file once per chunk. + const lastChunkAttachments = hookEventAttachmentsToChatSdk( + event.attachments as any, + ); + const hasText = !!lastAssistantContent; + const hasAttachments = !!lastChunkAttachments?.length; - if (lastAssistantContent) { - const replyBody = renderFinalReply(lastAssistantContent); - const replyStats = { - elapsedMs: event.duration ?? getElapsedMs(), - llmCalls: event.llmCalls ?? 0, - toolCalls: event.toolCalls ?? 0, - totalCost: event.cost ?? 0, - totalTokens: event.totalTokens ?? 0, - }; - // See progress-handler note above: keep the body as - // markdown and let the Chat SDK adapter render it with the - // platform's parse_mode. `formatReply` only appends a - // plain-text stats line. - const finalText = client?.formatReply?.(replyBody, replyStats) ?? replyBody; + if (hasText || hasAttachments) { + let chunks: string[]; + if (hasText) { + const replyBody = renderFinalReply(lastAssistantContent!); + const replyStats = { + elapsedMs: event.duration ?? getElapsedMs(), + llmCalls: event.llmCalls ?? 0, + toolCalls: event.toolCalls ?? 0, + totalCost: event.cost ?? 0, + totalTokens: event.totalTokens ?? 0, + }; + // See progress-handler note above: keep the body as + // markdown and let the Chat SDK adapter render it with the + // platform's parse_mode. `formatReply` only appends a + // plain-text stats line. + const finalText = client?.formatReply?.(replyBody, replyStats) ?? replyBody; + chunks = splitMessage(finalText, charLimit); + if (chunks.length === 0) chunks = ['']; + } else { + // Attachment-only reply — drive one empty chunk so the + // attachments still get posted via buildPostable. + chunks = ['']; + } - const chunks = splitMessage(finalText, charLimit); + const lastIdx = chunks.length - 1; + const buildPostable = (chunk: string, idx: number) => + idx === lastIdx && hasAttachments + ? { attachments: lastChunkAttachments!, markdown: chunk } + : { markdown: chunk }; try { if (progressMessage) { if (chunks[0] !== lastProgressText) { - await progressMessage.edit({ markdown: chunks[0] }); + await progressMessage.edit(buildPostable(chunks[0], 0)); lastProgressText = chunks[0]; } for (let i = 1; i < chunks.length; i++) { - await thread.post({ markdown: chunks[i] }); + await thread.post(buildPostable(chunks[i], i)); } } else { - for (const chunk of chunks) { - await thread.post({ markdown: chunk }); + for (let i = 0; i < chunks.length; i++) { + await thread.post(buildPostable(chunks[i], i)); } } } catch (error) { @@ -1249,14 +1318,18 @@ export class AgentBridgeService { } log( - 'executeWithCallback[local]: got response (%d chars, %d chunks)', - lastAssistantContent.length, + 'executeWithCallback[local]: got response (%d chars, %d chunks, %d attachments)', + lastAssistantContent?.length ?? 0, chunks.length, + lastChunkAttachments?.length ?? 0, ); - resolve({ reply: lastAssistantContent, topicId: resolvedTopicId }); + resolve({ reply: lastAssistantContent ?? '', topicId: resolvedTopicId }); - // Fire-and-forget: summarize topic title in DB - if (resolvedTopicId && prompt) { + // Fire-and-forget: summarize topic title in DB. Only when + // we have text to summarize on — image-only replies skip + // title generation (the prompt itself still drives it on + // the next round). + if (resolvedTopicId && prompt && lastAssistantContent) { const topicModel = new TopicModel(this.db, this.userId); topicModel .findById(resolvedTopicId) diff --git a/src/server/services/bot/BotCallbackService.ts b/src/server/services/bot/BotCallbackService.ts index 6ec151608f..0d48ea42f7 100644 --- a/src/server/services/bot/BotCallbackService.ts +++ b/src/server/services/bot/BotCallbackService.ts @@ -15,7 +15,13 @@ import { messengerPlatformRegistry } from '@/server/services/messenger/platforms import { SystemAgentService } from '@/server/services/systemAgent'; import { AgentBridgeService } from './AgentBridgeService'; -import type { BotReplyLocale, PlatformClient, PlatformMessenger, UsageStats } from './platforms'; +import type { + BotMessageAttachment, + BotReplyLocale, + PlatformClient, + PlatformMessenger, + UsageStats, +} from './platforms'; import { getBotReplyLocale, getStepReactionEmoji, @@ -37,6 +43,14 @@ const log = debug('lobe-server:bot:callback'); export interface BotCallbackBody { applicationId: string; + /** + * Outbound attachments (images/files) extracted from the agent's final + * assistant message or recent tool results. Forwarded to the platform + * messenger so platforms with attachment support (WeChat) can deliver them + * alongside the reply text. Platforms without attachment support silently + * drop these. + */ + attachments?: BotMessageAttachment[]; content?: string; cost?: number; duration?: number; @@ -346,7 +360,8 @@ export class BotCallbackService { charLimit?: number, canEdit = true, ): Promise { - const { reason, lastAssistantContent, errorMessage, errorType, operationId } = body; + const { reason, lastAssistantContent, errorMessage, errorType, operationId, attachments } = + body; if (reason === 'error') { log( @@ -371,16 +386,22 @@ export class BotCallbackService { return; } - // `!lastAssistantContent` lets whitespace-only strings ("\n", " ") through; - // those collapse to empty text downstream and get rejected by Telegram as - // "message text is empty", silently losing the reply. Trim before testing. - if (!lastAssistantContent?.trim()) { - log('handleCompletion: no lastAssistantContent, skipping'); + // Skip only when there's nothing at all to send. An image/file-only reply + // (no text, but attachments present) is still a valid reply and must go + // through — silently dropping it would mean an agent that answered with + // just a generated image gets shown nothing on the user side. + // + // For the text leg: `!lastAssistantContent` lets whitespace-only strings + // ("\n", " ") through; those collapse to empty text downstream and get + // rejected by Telegram as "message text is empty", silently losing the + // reply. Trim before testing. + const hasText = !!lastAssistantContent?.trim(); + const hasAttachments = !!attachments?.length; + if (!hasText && !hasAttachments) { + log('handleCompletion: no lastAssistantContent and no attachments, skipping'); return; } - const msgBody = renderFinalReply(lastAssistantContent); - const stats: UsageStats = { elapsedMs: body.duration, llmCalls: body.llmCalls ?? 0, @@ -389,21 +410,44 @@ export class BotCallbackService { totalTokens: body.totalTokens ?? 0, }; - const formattedBody = client.formatMarkdown?.(msgBody) ?? msgBody; - const finalText = client.formatReply?.(formattedBody, stats) ?? formattedBody; - const chunks = splitMessage(finalText, charLimit); - - if (chunks.length === 0) { - log('handleCompletion: all chunks empty after formatting, skipping send'); - return; + // Build the chunk list. Empty text → a single empty chunk so the + // attachment-only path still drives `deliverFirstChunk` once. + let chunks: string[]; + if (hasText) { + const msgBody = renderFinalReply(lastAssistantContent!); + const formattedBody = client.formatMarkdown?.(msgBody) ?? msgBody; + const finalText = client.formatReply?.(formattedBody, stats) ?? formattedBody; + chunks = splitMessage(finalText, charLimit); + if (chunks.length === 0) { + log('handleCompletion: all chunks empty after formatting, skipping send'); + // Even with no text we still want to deliver the attachments. + if (!hasAttachments) return; + chunks = ['']; + } + } else { + chunks = ['']; } - await this.deliverFirstChunk(messenger, progressMessageId, chunks[0], canEdit); + // Attach outbound attachments to the *last* chunk only so we don't send + // the same image/file once per chunk. + const lastIndex = chunks.length - 1; + const firstChunkAttachments = lastIndex === 0 ? attachments : undefined; + + await this.deliverFirstChunk( + messenger, + progressMessageId, + chunks[0], + canEdit, + firstChunkAttachments, + ); // Each remaining chunk gets its own try/catch so a single transient failure // (rate-limit, network blip) doesn't drop everything that follows. for (let i = 1; i < chunks.length; i++) { try { - await messenger.createMessage(chunks[i]); + const isLast = i === lastIndex; + await messenger.createMessage( + isLast && attachments?.length ? { attachments, content: chunks[i] } : chunks[i], + ); } catch (error) { log('handleCompletion: failed to send chunk %d: %O', i, error); } @@ -421,17 +465,20 @@ export class BotCallbackService { progressMessageId: string, text: string, canEdit: boolean, + attachments?: BotMessageAttachment[], ): Promise { + const payload = attachments && attachments.length > 0 ? { attachments, content: text } : text; + if (canEdit && progressMessageId) { try { - await messenger.editMessage(progressMessageId, text); + await messenger.editMessage(progressMessageId, payload); return; } catch (error) { log('handleCompletion: editMessage failed, falling back to createMessage: %O', error); } } try { - await messenger.createMessage(text); + await messenger.createMessage(payload); } catch (error) { log('handleCompletion: createMessage fallback failed: %O', error); } diff --git a/src/server/services/bot/BotMessageRouter.ts b/src/server/services/bot/BotMessageRouter.ts index 7046f6bf5d..25042ecc02 100644 --- a/src/server/services/bot/BotMessageRouter.ts +++ b/src/server/services/bot/BotMessageRouter.ts @@ -21,6 +21,7 @@ import { peekPairingRequest, releasePairingClaim, } from './dmPairingStore'; +import { submitBotFeedback } from './feedbackSubmit'; import { type BotPlatformRuntimeContext, type BotReplyLocale, @@ -53,6 +54,7 @@ import { renderDmPairing, renderDmRejected, renderError, + renderFeedbackSubmitted, renderGroupRejected, renderInlineError, renderSenderRejected, @@ -114,6 +116,22 @@ interface CommandContext { * surface only the ID, not a friendly label. */ authorUserName?: string; post: (text: string) => Promise; + /** + * Post a reply visible only to the invoker. + * + * Set only on native-slash paths (Slack / Discord) where chat-sdk knows + * the interaction context — Slack uses native ephemeral, Discord uses an + * ephemeral interaction response, both fall back to a DM if needed + * (`fallbackToDM: true`). Undefined on text-based platforms (Telegram, + * Feishu) — handlers should fall back to `post` there since those + * platforms have no per-user ephemeral primitive. + * + * Used by `/feedback` so the user's feedback text and the bot's + * confirmation don't leak into the public channel; other commands keep + * using `post` to preserve the existing channel-visible behaviour + * (e.g. `/new` resetting is useful for everyone to see). + */ + postEphemeral?: (text: string) => Promise; /** Locale to use for any system-generated reply text. Plumbed in by the * caller — text-based commands derive it per-message via the platform's * `extractAuthorLocale`, native slash commands fall back to the platform @@ -1623,6 +1641,47 @@ export class BotMessageRouter { }, name: 'approve', }, + { + description: 'Send feedback directly to the LobeHub team (no AI reply)', + // Declaring the argument so Discord/Slack surface a `/feedback ` + // prompt instead of registering the command as zero-arg (see the + // `options` comment on the BotCommand interface). + options: [ + { + description: 'Your feedback message', + name: 'message', + required: true, + }, + ], + handler: async (ctx) => { + log('command /feedback: agent=%s, platform=%s', agentId, platform); + // Prefer the ephemeral channel when available (Slack / Discord + // native slash) so the user's feedback content and the bot's + // confirmation never leak into the public channel. Text-based + // platforms (Telegram, Feishu) have no ephemeral primitive, so + // we fall back to a regular post — those platforms are DM-first + // anyway, so the privacy concern is much smaller. + const reply = ctx.postEphemeral ?? ctx.post; + const body = ctx.args.trim(); + if (!body) { + await reply(renderCommandReply('cmdFeedbackUsage', ctx.replyLocale)); + return; + } + const result = await submitBotFeedback(serverDB, { + applicationId, + body, + platform, + threadId: ctx.threadId, + userId, + }); + if (!result.success) { + await reply(renderCommandReply('cmdFeedbackError', ctx.replyLocale)); + return; + } + await reply(renderFeedbackSubmitted(result.issueUrl, ctx.replyLocale)); + }, + name: 'feedback', + }, ]; } @@ -1696,6 +1755,16 @@ export class BotMessageRouter { authorUserId: authorLike.userId, authorUserName: authorLike.userName, post: (text) => event.channel.post(text), + // Wire chat-sdk's `postEphemeral` so commands that want a private + // reply (e.g. `/feedback`) can opt in. `fallbackToDM: true` so + // Discord — which has no channel-level ephemeral outside of an + // interaction response — still delivers privately by DMing the + // user instead of broadcasting to the channel. Wrapped here, not + // in the handler, so commands stay platform-agnostic. + postEphemeral: event.user + ? (text: string) => + event.channel.postEphemeral(event.user!, text, { fallbackToDM: true }) + : undefined, // Native slash-command events don't carry a Chat SDK Message, so // there's no per-sender locale field to read; use the channel // default. Telegram/Feishu/etc. dispatch via the text-based path diff --git a/src/server/services/bot/__tests__/BotCallbackService.test.ts b/src/server/services/bot/__tests__/BotCallbackService.test.ts index 063f5de0dd..597c83fb86 100644 --- a/src/server/services/bot/__tests__/BotCallbackService.test.ts +++ b/src/server/services/bot/__tests__/BotCallbackService.test.ts @@ -697,6 +697,171 @@ describe('BotCallbackService', () => { await expect(service.handleCallback(body)).resolves.toBeUndefined(); }); + + // ==================== Attachments ==================== + + it('should pass attachments through to messenger when present on single-chunk reply', async () => { + const body = makeBody({ + attachments: [ + { + fetchUrl: 'https://cdn.example.com/foo.png', + mimeType: 'image/png', + name: 'foo.png', + type: 'image', + }, + ], + lastAssistantContent: 'Here is the image you asked for.', + reason: 'completed', + type: 'completion', + }); + + await service.handleCallback(body); + + expect(mockEditMessage).toHaveBeenCalledWith( + 'progress-msg-1', + expect.objectContaining({ + attachments: [ + expect.objectContaining({ + fetchUrl: 'https://cdn.example.com/foo.png', + type: 'image', + }), + ], + content: expect.stringContaining('Here is the image you asked for.'), + }), + ); + }); + + it('should only attach to the last chunk in a multi-chunk reply', async () => { + const longContent = 'A'.repeat(2000) + '\n\n' + 'B'.repeat(2000); + const body = makeBody({ + attachments: [{ fetchUrl: 'https://cdn.example.com/bar.png', type: 'image' }], + lastAssistantContent: longContent, + reason: 'completed', + type: 'completion', + }); + + await service.handleCallback(body); + + // First chunk goes through editMessage as a plain string — no attachments. + expect(mockEditMessage).toHaveBeenCalledTimes(1); + const firstCallArg = mockEditMessage.mock.calls[0][1]; + expect(typeof firstCallArg).toBe('string'); + + // Last chunk goes through createMessage with the attachments. + const lastCreateArg = mockCreateMessage.mock.calls.at(-1)?.[0]; + expect(lastCreateArg).toMatchObject({ + attachments: [{ fetchUrl: 'https://cdn.example.com/bar.png', type: 'image' }], + }); + }); + + it('should fall back to createMessage with attachments when edit fails', async () => { + mockEditMessage.mockRejectedValueOnce(new Error('edit failed')); + + const body = makeBody({ + attachments: [{ data: 'aGVsbG8=', mimeType: 'image/png', type: 'image' }], + lastAssistantContent: 'reply', + reason: 'completed', + type: 'completion', + }); + + await service.handleCallback(body); + + expect(mockCreateMessage).toHaveBeenCalledWith( + expect.objectContaining({ + attachments: [{ data: 'aGVsbG8=', mimeType: 'image/png', type: 'image' }], + content: expect.stringContaining('reply'), + }), + ); + }); + + // Regression for Codex P1: image-only final assistant turn must still + // ship the attachments, instead of being silently dropped because there + // is no `lastAssistantContent.trim()`. + it('should deliver attachments even when reply text is empty', async () => { + const body = makeBody({ + attachments: [{ fetchUrl: 'https://cdn.example.com/only.png', type: 'image' }], + lastAssistantContent: '', + reason: 'completed', + type: 'completion', + }); + + await service.handleCallback(body); + + // Either edit or create — but the call MUST carry the attachments. + const editCalls = mockEditMessage.mock.calls; + const createCalls = mockCreateMessage.mock.calls; + const allCalls = [...editCalls.map((c) => c[1]), ...createCalls.map((c) => c[0])]; + expect( + allCalls.some( + (arg) => + arg && + typeof arg === 'object' && + 'attachments' in arg && + (arg as any).attachments?.[0]?.fetchUrl === 'https://cdn.example.com/only.png', + ), + ).toBe(true); + }); + + it('should still skip when there is neither text nor attachments', async () => { + const body = makeBody({ + lastAssistantContent: ' \n ', + reason: 'completed', + type: 'completion', + }); + + await service.handleCallback(body); + + expect(mockEditMessage).not.toHaveBeenCalled(); + expect(mockCreateMessage).not.toHaveBeenCalled(); + }); + + it('should still ship attachments when reply text is whitespace-only', async () => { + // Whitespace text alone collapses to empty downstream and would be + // dropped, but the attachment-only path must still deliver the image. + const body = makeBody({ + attachments: [{ fetchUrl: 'https://cdn.example.com/ws.png', type: 'image' }], + lastAssistantContent: '\n\n ', + reason: 'completed', + type: 'completion', + }); + + await service.handleCallback(body); + + const editCalls = mockEditMessage.mock.calls; + const createCalls = mockCreateMessage.mock.calls; + const allCalls = [...editCalls.map((c) => c[1]), ...createCalls.map((c) => c[0])]; + expect( + allCalls.some( + (arg) => + arg && + typeof arg === 'object' && + 'attachments' in arg && + (arg as any).attachments?.[0]?.fetchUrl === 'https://cdn.example.com/ws.png', + ), + ).toBe(true); + }); + + it('should not summarize topic title for attachment-only reply', async () => { + // Attachment-only reply has no assistant text, so the LLM summarizer + // has no body to work with. `summarizeTopicTitle` already guards on + // `!lastAssistantContent`; this regression-locks that contract. + mockFindById.mockResolvedValue({ title: null }); + + const body = makeBody({ + attachments: [{ fetchUrl: 'https://cdn.example.com/x.png', type: 'image' }], + reason: 'completed', + topicId: 'topic-1', + type: 'completion', + userId: 'user-1', + userPrompt: 'Draw something', + }); + + await service.handleCallback(body); + + await new Promise((r) => setTimeout(r, 50)); + expect(mockGenerateTopicTitle).not.toHaveBeenCalled(); + expect(mockTopicUpdate).not.toHaveBeenCalled(); + }); }); // ==================== Message splitting ==================== diff --git a/src/server/services/bot/feedbackSubmit.ts b/src/server/services/bot/feedbackSubmit.ts new file mode 100644 index 0000000000..e86d1d6fb9 --- /dev/null +++ b/src/server/services/bot/feedbackSubmit.ts @@ -0,0 +1,99 @@ +import debug from 'debug'; +import { eq } from 'drizzle-orm'; + +import { users } from '@/database/schemas'; +import type { LobeChatDatabase } from '@/database/type'; +import { MarketService } from '@/server/services/market'; + +const log = debug('lobe-server:bot:feedback'); + +/** + * Maximum number of characters used to derive a feedback title from the + * leading line of the user's message. The web `FeedbackModal` caps title + * input at 200 chars (`src/components/FeedbackModal/index.tsx`), so the bot + * path stays in the same envelope so downstream tooling can treat the two + * sources interchangeably. + */ +const TITLE_MAX_LENGTH = 80; + +const truncateTitle = (raw: string): string => { + const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? ''; + if (firstLine.length <= TITLE_MAX_LENGTH) return firstLine; + return `${firstLine.slice(0, TITLE_MAX_LENGTH - 1).trim()}…`; +}; + +export interface BotFeedbackSubmitOptions { + applicationId?: string; + /** Raw text after the `/feedback` command (already trimmed by the caller). */ + body: string; + platform: string; + /** Stable platform conversation id for traceability — included in + * `clientInfo` so operators can correlate feedback with the originating + * thread without exposing it in the user-facing message. */ + threadId?: string; + /** LobeHub user id. Required — feedback always carries identity so it can + * be tied back to the account / workspace. */ + userId: string; +} + +export interface BotFeedbackSubmitResult { + issueUrl?: string; + success: boolean; +} + +/** + * Submit a `/feedback` slash-command body via the same `MarketService` + * pipeline the web `FeedbackModal` uses. Bot webhook contexts have no + * OAuth access token, so we authenticate via the trusted-client token by + * looking up the user's email/name from `users` and passing it as + * `userInfo` (Market SDK signs the request server-side). + * + * Returns `success: false` on any failure (DB error, Market error). The + * caller renders the user-facing reply via `renderFeedbackSubmitted` / + * `renderCommandReply('cmdFeedbackError')`. + */ +export async function submitBotFeedback( + serverDB: LobeChatDatabase, + options: BotFeedbackSubmitOptions, +): Promise { + const { applicationId, body, platform, threadId, userId } = options; + + try { + const user = await serverDB.query.users.findFirst({ + columns: { email: true, fullName: true }, + where: eq(users.id, userId), + }); + + const marketService = new MarketService({ + userInfo: { + email: user?.email ?? undefined, + name: user?.fullName ?? undefined, + userId, + }, + }); + + const title = truncateTitle(body) || `Bot feedback from ${platform}`; + const footerParts = [`via ${platform} bot`]; + if (applicationId) footerParts.push(`app: ${applicationId}`); + if (threadId) footerParts.push(`thread: ${threadId}`); + const result = await marketService.submitFeedback({ + clientInfo: { + url: applicationId ? `bot://${platform}/${applicationId}` : `bot://${platform}`, + }, + email: user?.email ?? undefined, + message: `${body}\n\n---\n_Submitted ${footerParts.join(' · ')}_`, + title, + }); + + return { issueUrl: result?.issueUrl, success: true }; + } catch (error) { + log( + 'submitBotFeedback failed: platform=%s, applicationId=%s, userId=%s, error=%O', + platform, + applicationId, + userId, + error, + ); + return { success: false }; + } +} diff --git a/src/server/services/bot/platforms/discord/client.ts b/src/server/services/bot/platforms/discord/client.ts index a93764132c..d733a554d6 100644 --- a/src/server/services/bot/platforms/discord/client.ts +++ b/src/server/services/bot/platforms/discord/client.ts @@ -14,6 +14,7 @@ import { type BotPlatformRuntimeContext, type BotProviderConfig, ClientFactory, + messengerContentText, type PlatformClient, type PlatformMessenger, type UsageStats, @@ -228,8 +229,12 @@ class DiscordGatewayClient implements PlatformClient { const discord = this.discord; return { addReaction: (messageId, emoji) => discord.createReaction(channelId, messageId, emoji), - createMessage: (content) => discord.createMessage(channelId, content).then(() => {}), - editMessage: (messageId, content) => discord.editMessage(channelId, messageId, content), + // Attachments are silently dropped for now — Discord outbound media + // is its own follow-up; reply text still ships. + createMessage: (content) => + discord.createMessage(channelId, messengerContentText(content)).then(() => {}), + editMessage: (messageId, content) => + discord.editMessage(channelId, messageId, messengerContentText(content)), removeReaction: (messageId, emoji) => discord.removeOwnReaction(channelId, messageId, emoji), replaceReaction: async (messageId, prevEmoji, nextEmoji) => { if (prevEmoji === nextEmoji) return; diff --git a/src/server/services/bot/platforms/feishu/client.ts b/src/server/services/bot/platforms/feishu/client.ts index 398c7d937d..a68b52724f 100644 --- a/src/server/services/bot/platforms/feishu/client.ts +++ b/src/server/services/bot/platforms/feishu/client.ts @@ -20,6 +20,7 @@ import { type BotPlatformRuntimeContext, type BotProviderConfig, ClientFactory, + messengerContentText, type PlatformClient, type PlatformMessenger, type UsageStats, @@ -62,8 +63,12 @@ function createMessenger( const chatId = extractChatId(platformThreadId); return { addReaction: (messageId, emoji) => api.addReaction(messageId, emoji).then(() => {}), - createMessage: (content) => api.sendMessage(chatId, content).then(() => {}), - editMessage: (messageId, content) => api.editMessage(messageId, content).then(() => {}), + // Attachments are silently dropped for now — Lark/Feishu outbound media + // is its own follow-up; reply text still ships. + createMessage: (content) => + api.sendMessage(chatId, messengerContentText(content)).then(() => {}), + editMessage: (messageId, content) => + api.editMessage(messageId, messengerContentText(content)).then(() => {}), // Feishu / Lark currently expose no authenticated removeReaction endpoint. // Callers should treat this as a best-effort no-op — step swaps will stack // additions rather than clear the previous emoji. diff --git a/src/server/services/bot/platforms/index.ts b/src/server/services/bot/platforms/index.ts index 9461cd657c..9ec9e580d5 100644 --- a/src/server/services/bot/platforms/index.ts +++ b/src/server/services/bot/platforms/index.ts @@ -47,12 +47,14 @@ export { } from './const'; export { PlatformRegistry } from './registry'; export type { + BotMessageAttachment, BotPlatformRedisClient, BotPlatformRuntimeContext, BotProviderConfig, ConnectionMode, ExtractFilesResult, FieldSchema, + MessengerContent, PlatformClient, PlatformDefinition, PlatformDocumentation, @@ -61,7 +63,7 @@ export type { UsageStats, ValidationResult, } from './types'; -export { ClientFactory } from './types'; +export { ClientFactory, messengerContentText } from './types'; export type { ProviderConfigInput, ResolvedBotProviderConfig } from './utils'; export { buildRuntimeKey, diff --git a/src/server/services/bot/platforms/line/client.ts b/src/server/services/bot/platforms/line/client.ts index 0e71b7a832..2de21d4fe8 100644 --- a/src/server/services/bot/platforms/line/client.ts +++ b/src/server/services/bot/platforms/line/client.ts @@ -20,6 +20,7 @@ import { type BotPlatformRuntimeContext, type BotProviderConfig, ClientFactory, + messengerContentText, type PlatformClient, type PlatformMessenger, type UsageStats, @@ -166,14 +167,16 @@ class LineWebhookClient implements PlatformClient { getMessenger(platformThreadId: string): PlatformMessenger { const { id: recipient, type } = decodeThread(platformThreadId); return { + // Attachments are silently dropped for now — LINE outbound media is + // its own follow-up; reply text still ships. createMessage: async (content) => { - await this.api.pushText(recipient, content); + await this.api.pushText(recipient, messengerContentText(content)); }, // LINE does not support editing — `supportsMessageEdit: false` makes the // bridge skip the per-step progress edit, but we still implement this // path so any unexpected caller falls back to a fresh push. editMessage: async (_messageId, content) => { - await this.api.pushText(recipient, content); + await this.api.pushText(recipient, messengerContentText(content)); }, removeReaction: () => Promise.resolve(), triggerTyping: async () => { diff --git a/src/server/services/bot/platforms/qq/client.ts b/src/server/services/bot/platforms/qq/client.ts index 108c7a5dbd..2b7cdc871b 100644 --- a/src/server/services/bot/platforms/qq/client.ts +++ b/src/server/services/bot/platforms/qq/client.ts @@ -15,6 +15,7 @@ import { type BotPlatformRuntimeContext, type BotProviderConfig, ClientFactory, + messengerContentText, type PlatformClient, type PlatformMessenger, type UsageStats, @@ -265,10 +266,13 @@ class QQGatewayClient implements PlatformClient { const targetId = extractChatId(platformThreadId); const threadType = extractThreadType(platformThreadId); return { - createMessage: (content) => sendQQMessage(api, threadType, targetId, content), + // Attachments are silently dropped for now — QQ outbound media is its + // own follow-up; reply text still ships. + createMessage: (content) => + sendQQMessage(api, threadType, targetId, messengerContentText(content)), editMessage: (_messageId, content) => // QQ does not support editing — send a new message as fallback - sendQQMessage(api, threadType, targetId, content), + sendQQMessage(api, threadType, targetId, messengerContentText(content)), // QQ Bot API doesn't support reactions or typing removeReaction: () => Promise.resolve(), }; @@ -365,8 +369,12 @@ class QQWebhookClient implements PlatformClient { const targetId = extractChatId(platformThreadId); const threadType = extractThreadType(platformThreadId); return { - createMessage: (content) => sendQQMessage(api, threadType, targetId, content), - editMessage: (_messageId, content) => sendQQMessage(api, threadType, targetId, content), + // Attachments are silently dropped for now — QQ outbound media is its + // own follow-up; reply text still ships. + createMessage: (content) => + sendQQMessage(api, threadType, targetId, messengerContentText(content)), + editMessage: (_messageId, content) => + sendQQMessage(api, threadType, targetId, messengerContentText(content)), removeReaction: () => Promise.resolve(), }; } diff --git a/src/server/services/bot/platforms/slack/client.ts b/src/server/services/bot/platforms/slack/client.ts index 446745f7b1..b8b9cb8487 100644 --- a/src/server/services/bot/platforms/slack/client.ts +++ b/src/server/services/bot/platforms/slack/client.ts @@ -13,6 +13,7 @@ import { type BotPlatformRuntimeContext, type BotProviderConfig, ClientFactory, + messengerContentText, type PlatformClient, type PlatformMessenger, type UsageStats, @@ -50,11 +51,16 @@ function createMessenger(config: BotProviderConfig, platformThreadId: string): P return { addReaction: (messageId, emoji) => slack.addReaction(channelId, messageId, emoji), - createMessage: (content) => - threadTs - ? slack.postMessageInThread(channelId, threadTs, content).then(() => {}) - : slack.postMessage(channelId, content).then(() => {}), - editMessage: (messageId, content) => slack.updateMessage(channelId, messageId, content), + // Attachments are silently dropped for now — Slack outbound media is + // its own follow-up; reply text still ships. + createMessage: (content) => { + const text = messengerContentText(content); + return threadTs + ? slack.postMessageInThread(channelId, threadTs, text).then(() => {}) + : slack.postMessage(channelId, text).then(() => {}); + }, + editMessage: (messageId, content) => + slack.updateMessage(channelId, messageId, messengerContentText(content)), removeReaction: (messageId, emoji) => slack.removeReaction(channelId, messageId, emoji), replaceReaction: async (messageId, prevEmoji, nextEmoji) => { if (prevEmoji === nextEmoji) return; diff --git a/src/server/services/bot/platforms/telegram/client.ts b/src/server/services/bot/platforms/telegram/client.ts index 41e9529167..3909cbfa6f 100644 --- a/src/server/services/bot/platforms/telegram/client.ts +++ b/src/server/services/bot/platforms/telegram/client.ts @@ -14,6 +14,7 @@ import { type BotProviderConfig, ClientFactory, type ExtractFilesResult, + messengerContentText, type PlatformClient, type PlatformMessenger, type UsageStats, @@ -175,9 +176,16 @@ class TelegramWebhookClient implements PlatformClient { return { addReaction: (messageId, emoji) => telegram.setMessageReaction(chatId, parseTelegramMessageId(messageId), emoji), - createMessage: (content) => telegram.sendMessage(chatId, content).then(() => {}), + // Attachments are silently dropped — Telegram outbound media support + // is tracked in its own follow-up; reply text still ships. + createMessage: (content) => + telegram.sendMessage(chatId, messengerContentText(content)).then(() => {}), editMessage: (messageId, content) => - telegram.editMessageText(chatId, parseTelegramMessageId(messageId), content), + telegram.editMessageText( + chatId, + parseTelegramMessageId(messageId), + messengerContentText(content), + ), removeReaction: (messageId) => telegram.removeMessageReaction(chatId, parseTelegramMessageId(messageId)), // Telegram replaces the whole reaction list in one call — one API diff --git a/src/server/services/bot/platforms/types.ts b/src/server/services/bot/platforms/types.ts index 5e24969822..3d9d8efc1a 100644 --- a/src/server/services/bot/platforms/types.ts +++ b/src/server/services/bot/platforms/types.ts @@ -79,6 +79,42 @@ export interface FieldSchema { visibleWhen?: { field: string; value: unknown }; } +// --------------- Bot Message Attachment --------------- + +/** + * JSON-safe attachment carried through the bot callback path + * (agent runtime → webhook body → BotCallbackService → PlatformMessenger). + * + * Either `data` (base64-encoded bytes) or `fetchUrl` (remote URL the + * messenger can fetch) must be set. Prefer `fetchUrl` when possible to keep + * webhook payload sizes manageable. + */ +export interface BotMessageAttachment { + /** Base64-encoded bytes. Used when no fetchable URL exists. */ + data?: string; + /** Remote URL the messenger can GET to retrieve the bytes. */ + fetchUrl?: string; + mimeType?: string; + name?: string; + type: 'image' | 'file' | 'video' | 'audio'; +} + +/** + * Input accepted by `PlatformMessenger.createMessage` / `editMessage`. + * + * Plain string is the legacy form; object form carries optional attachments. + * Platforms that don't support attachments treat the object form as + * `{ content }` and silently drop attachments. + */ +export type MessengerContent = string | { attachments?: BotMessageAttachment[]; content?: string }; + +/** + * Helper for messenger implementations that don't (yet) support attachments: + * coerces `MessengerContent` to its text payload. + */ +export const messengerContentText = (input: MessengerContent): string => + typeof input === 'string' ? input : (input.content ?? ''); + // --------------- Platform Messenger --------------- /** @@ -90,8 +126,8 @@ export interface PlatformMessenger { * can omit this). Callers must no-op on platforms that don't implement it. */ addReaction?: (messageId: string, emoji: string) => Promise; - createMessage: (content: string) => Promise; - editMessage: (messageId: string, content: string) => Promise; + createMessage: (content: MessengerContent) => Promise; + editMessage: (messageId: string, content: MessengerContent) => Promise; removeReaction: (messageId: string, emoji: string) => Promise; /** * Transition the bot's reaction on a message from `prevEmoji` to diff --git a/src/server/services/bot/platforms/wechat/client.test.ts b/src/server/services/bot/platforms/wechat/client.test.ts index be23f2fba5..d6061c1c85 100644 --- a/src/server/services/bot/platforms/wechat/client.test.ts +++ b/src/server/services/bot/platforms/wechat/client.test.ts @@ -3,19 +3,46 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; const mockCreateWechatAdapter = vi.hoisted(() => vi.fn()); const mockGetUpdates = vi.hoisted(() => vi.fn()); const mockStartTyping = vi.hoisted(() => vi.fn()); +const mockSendMessage = vi.hoisted(() => vi.fn().mockResolvedValue({ ret: 0 })); +const mockSendItem = vi.hoisted(() => vi.fn().mockResolvedValue({ ret: 0 })); +const mockUploadCdnMedia = vi.hoisted(() => + vi.fn().mockResolvedValue({ + aesKey: 'aes-key', + cipherSize: 128, + encryptQueryParam: 'enc-param', + }), +); const mockDownloadMediaFromRawMessage = vi.hoisted(() => vi.fn()); const MessageState = vi.hoisted(() => ({ FINISH: 2 })); const MessageType = vi.hoisted(() => ({ BOT: 2, USER: 1 })); +const MessageItemType = vi.hoisted(() => ({ + FILE: 4, + IMAGE: 1, + TEXT: 0, + VIDEO: 3, + VOICE: 2, +})); +const WechatUploadMediaType = vi.hoisted(() => ({ + FILE: 4, + IMAGE: 1, + VIDEO: 3, + VOICE: 2, +})); vi.mock('@lobechat/chat-adapter-wechat', () => ({ createWechatAdapter: mockCreateWechatAdapter, downloadMediaFromRawMessage: mockDownloadMediaFromRawMessage, + MessageItemType, MessageState, MessageType, WechatApiClient: vi.fn().mockImplementation(() => ({ getUpdates: mockGetUpdates, + sendItem: mockSendItem, + sendMessage: mockSendMessage, startTyping: mockStartTyping, + uploadCdnMedia: mockUploadCdnMedia, })), + WechatUploadMediaType, })); const { WechatClientFactory } = await import('./client'); @@ -315,4 +342,128 @@ describe('WechatGatewayClient', () => { ).rejects.toThrow('helper crashed'); }); }); + + describe('messenger.createMessage with attachments', () => { + const createClient = () => + new WechatClientFactory().createClient( + { + applicationId: 'wechat-app', + credentials: { botId: 'bot-id', botToken: 'bot-token' }, + platform: 'wechat', + settings: {}, + }, + { appUrl: 'https://example.com', redisClient: runtimeRedis as any }, + ); + + it('forwards inline base64 image attachments to uploadCdnMedia + sendItem', async () => { + const client = createClient(); + const messenger = client.getMessenger('wechat:p2p:user-1@im.wechat'); + + // Pre-seed an in-memory context token; the adapter caches per-user. + runtimeRedis.get.mockResolvedValueOnce('ctx-from-redis'); + + await messenger.createMessage({ + attachments: [ + { + data: Buffer.from('image-bytes').toString('base64'), + mimeType: 'image/png', + name: 'foo.png', + type: 'image', + }, + ], + content: 'Here you go', + }); + + // Text leg goes through the standard sendMessage call. + expect(mockSendMessage).toHaveBeenCalledWith( + 'user-1@im.wechat', + 'Here you go', + 'ctx-from-redis', + ); + // Attachment leg: upload bytes, then send a media item. + expect(mockUploadCdnMedia).toHaveBeenCalledWith( + 'user-1@im.wechat', + WechatUploadMediaType.IMAGE, + expect.any(Buffer), + ); + expect(mockSendItem).toHaveBeenCalledWith( + 'user-1@im.wechat', + expect.objectContaining({ + image_item: expect.objectContaining({ + media: expect.objectContaining({ + aes_key: 'aes-key', + encrypt_query_param: 'enc-param', + }), + }), + type: MessageItemType.IMAGE, + }), + 'ctx-from-redis', + ); + }); + + it('fetches and uploads attachments delivered as fetchUrl', async () => { + const client = createClient(); + const messenger = client.getMessenger('wechat:p2p:user-2@im.wechat'); + + runtimeRedis.get.mockResolvedValueOnce('ctx-2'); + const fetchMock = vi.mocked(fetch); + fetchMock.mockResolvedValueOnce( + new Response(new Uint8Array([1, 2, 3, 4]), { + headers: { 'Content-Type': 'image/png' }, + status: 200, + }) as any, + ); + + await messenger.createMessage({ + attachments: [ + { + fetchUrl: 'https://cdn.example.com/pic.png', + name: 'pic.png', + type: 'image', + }, + ], + content: 'pic', + }); + + expect(fetchMock).toHaveBeenCalledWith('https://cdn.example.com/pic.png', expect.any(Object)); + expect(mockUploadCdnMedia).toHaveBeenCalledTimes(1); + expect(mockSendItem).toHaveBeenCalledTimes(1); + }); + + it('continues sending remaining attachments when one fails', async () => { + const client = createClient(); + const messenger = client.getMessenger('wechat:p2p:user-3@im.wechat'); + + runtimeRedis.get.mockResolvedValueOnce('ctx-3'); + mockUploadCdnMedia.mockRejectedValueOnce(new Error('upload failed')).mockResolvedValueOnce({ + aesKey: 'aes-2', + cipherSize: 64, + encryptQueryParam: 'enc-2', + }); + + await messenger.createMessage({ + attachments: [ + { data: Buffer.from('a').toString('base64'), type: 'image' }, + { data: Buffer.from('b').toString('base64'), type: 'image' }, + ], + content: '', + }); + + // Two upload attempts, one sendItem success after the first failure. + expect(mockUploadCdnMedia).toHaveBeenCalledTimes(2); + expect(mockSendItem).toHaveBeenCalledTimes(1); + }); + + it('accepts plain string content (legacy form) and skips attachment path', async () => { + const client = createClient(); + const messenger = client.getMessenger('wechat:p2p:user-4@im.wechat'); + + runtimeRedis.get.mockResolvedValueOnce('ctx-4'); + await messenger.createMessage('text only'); + + expect(mockSendMessage).toHaveBeenCalledWith('user-4@im.wechat', 'text only', 'ctx-4'); + expect(mockUploadCdnMedia).not.toHaveBeenCalled(); + expect(mockSendItem).not.toHaveBeenCalled(); + }); + }); }); diff --git a/src/server/services/bot/platforms/wechat/client.ts b/src/server/services/bot/platforms/wechat/client.ts index 76399d8d91..07cc1310b2 100644 --- a/src/server/services/bot/platforms/wechat/client.ts +++ b/src/server/services/bot/platforms/wechat/client.ts @@ -20,12 +20,15 @@ import { type BotPlatformRuntimeContext, type BotProviderConfig, ClientFactory, + type MessengerContent, + messengerContentText, type PlatformClient, type PlatformMessenger, type UsageStats, type ValidationResult, } from '../types'; import { formatUsageStats } from '../utils'; +import { sendWechatAttachments } from './sendAttachments'; const log = debug('bot-platform:wechat:bot'); @@ -392,15 +395,25 @@ class WechatGatewayClient implements PlatformClient { return ''; }; + const sendMessengerContent = async (input: MessengerContent): Promise => { + const text = messengerContentText(input); + const attachments = typeof input === 'string' ? undefined : input.attachments; + const token = await resolveToken(); + if (text.trim()) { + await this.api.sendMessage(targetId, text, token); + } + if (attachments?.length) { + await sendWechatAttachments(this.api, targetId, attachments, token); + } + }; + return { createMessage: async (content) => { - const token = await resolveToken(); - await this.api.sendMessage(targetId, content, token); + await sendMessengerContent(content); }, editMessage: async (_messageId, content) => { // WeChat doesn't support editing — send a new message - const token = await resolveToken(); - await this.api.sendMessage(targetId, content, token); + await sendMessengerContent(content); }, removeReaction: () => Promise.resolve(), triggerTyping: async () => { diff --git a/src/server/services/bot/platforms/wechat/sendAttachments.ts b/src/server/services/bot/platforms/wechat/sendAttachments.ts new file mode 100644 index 0000000000..58988aad3e --- /dev/null +++ b/src/server/services/bot/platforms/wechat/sendAttachments.ts @@ -0,0 +1,158 @@ +import type { MessageItem, WechatApiClient } from '@lobechat/chat-adapter-wechat'; +import { MessageItemType, WechatUploadMediaType } from '@lobechat/chat-adapter-wechat'; +import debug from 'debug'; + +const log = debug('bot-platform:wechat:send-attachments'); + +/** + * Shared JSON-safe attachment shape used on the WeChat outbound path. + * Either `data` (base64-encoded bytes) or `fetchUrl` (remote URL) must be + * set; `fetchUrl` is preferred so we don't blow up webhook payloads. + * + * Kept in sync with `BotMessageAttachment` (bot/platforms/types.ts) and + * `SendMessageAttachment` (@lobechat/builtin-tool-message); both flow into + * this helper through different entry points (agent reply callback vs. the + * Messager `sendMessage` tool / TRPC / CLI). + */ +export interface WechatOutboundAttachment { + data?: string; + fetchUrl?: string; + mimeType?: string; + name?: string; + type: 'image' | 'file' | 'video' | 'audio'; +} + +const mapAttachmentTypeToUploadMediaType = ( + type: WechatOutboundAttachment['type'], +): WechatUploadMediaType => { + switch (type) { + case 'image': { + return WechatUploadMediaType.IMAGE; + } + case 'video': { + return WechatUploadMediaType.VIDEO; + } + case 'audio': { + return WechatUploadMediaType.VOICE; + } + case 'file': + default: { + return WechatUploadMediaType.FILE; + } + } +}; + +/** + * Materialize an attachment's bytes from `data` (base64) or `fetchUrl` (HTTP + * GET, 15s timeout). Returns undefined if neither source resolves. + */ +const loadAttachmentBuffer = async ( + attachment: WechatOutboundAttachment, +): Promise => { + if (attachment.data) { + try { + return Buffer.from(attachment.data, 'base64'); + } catch (error) { + log('loadAttachmentBuffer: failed to decode base64: %O', error); + } + } + if (attachment.fetchUrl) { + try { + const response = await fetch(attachment.fetchUrl, { + signal: AbortSignal.timeout(15_000), + }); + if (response.ok) { + return Buffer.from(await response.arrayBuffer()); + } + log('loadAttachmentBuffer: HTTP %d for %s', response.status, attachment.fetchUrl); + } catch (error) { + log('loadAttachmentBuffer: fetch failed for %s: %O', attachment.fetchUrl, error); + } + } + return undefined; +}; + +const buildMediaItemFromUpload = ( + mediaType: WechatUploadMediaType, + cdnMedia: { aes_key: string; encrypt_query_param: string; encrypt_type: 1 }, + uploadResult: { cipherSize: number }, + attachment: WechatOutboundAttachment, + bufferLength: number, +): MessageItem => { + switch (mediaType) { + case WechatUploadMediaType.IMAGE: { + return { + image_item: { media: cdnMedia }, + type: MessageItemType.IMAGE, + }; + } + case WechatUploadMediaType.VIDEO: { + return { + type: MessageItemType.VIDEO, + video_item: { media: cdnMedia, video_size: uploadResult.cipherSize }, + }; + } + case WechatUploadMediaType.VOICE: { + return { + type: MessageItemType.VOICE, + voice_item: { media: cdnMedia }, + }; + } + case WechatUploadMediaType.FILE: + default: { + return { + file_item: { + file_name: attachment.name, + len: String(bufferLength), + media: cdnMedia, + }, + type: MessageItemType.FILE, + }; + } + } +}; + +/** + * Upload + send each attachment as its own iLink sendmessage call (per + * protocol §6.7, one MessageItem per request). Single-attachment failures + * are logged and skipped so the rest still ship — mirroring the chat-adapter + * adapter's per-item try/catch. + */ +export const sendWechatAttachments = async ( + api: WechatApiClient, + toUserId: string, + attachments: WechatOutboundAttachment[], + contextToken: string, +): Promise => { + for (const attachment of attachments) { + try { + const buffer = await loadAttachmentBuffer(attachment); + if (!buffer) { + log('sendWechatAttachments: skipping attachment without resolvable bytes'); + continue; + } + const mediaType = mapAttachmentTypeToUploadMediaType(attachment.type); + const uploadResult = await api.uploadCdnMedia(toUserId, mediaType, buffer); + const cdnMedia = { + aes_key: uploadResult.aesKey, + encrypt_query_param: uploadResult.encryptQueryParam, + encrypt_type: 1 as const, + }; + const item = buildMediaItemFromUpload( + mediaType, + cdnMedia, + uploadResult, + attachment, + buffer.length, + ); + await api.sendItem(toUserId, item, contextToken); + } catch (error) { + log( + 'sendWechatAttachments: failed to send %s attachment "%s": %O', + attachment.type, + attachment.name ?? '(unnamed)', + error, + ); + } + } +}; diff --git a/src/server/services/bot/platforms/wechat/service.test.ts b/src/server/services/bot/platforms/wechat/service.test.ts new file mode 100644 index 0000000000..b122cf3cbf --- /dev/null +++ b/src/server/services/bot/platforms/wechat/service.test.ts @@ -0,0 +1,142 @@ +// @vitest-environment node +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +const MessageItemType = vi.hoisted(() => ({ + FILE: 4, + IMAGE: 1, + TEXT: 0, + VIDEO: 3, + VOICE: 2, +})); +const WechatUploadMediaType = vi.hoisted(() => ({ + FILE: 4, + IMAGE: 1, + VIDEO: 3, + VOICE: 2, +})); + +vi.mock('@lobechat/chat-adapter-wechat', () => ({ + MessageItemType, + WechatUploadMediaType, +})); + +const mockRedisGet = vi.hoisted(() => vi.fn().mockResolvedValue(null)); +vi.mock('@/server/modules/AgentRuntime/redis', () => ({ + getAgentRuntimeRedisClient: () => ({ get: mockRedisGet }), +})); + +const { WechatMessageService } = await import('./service'); + +const makeApi = () => ({ + sendItem: vi.fn().mockResolvedValue({ ret: 0 }), + sendMessage: vi.fn().mockResolvedValue({ ret: 0 }), + uploadCdnMedia: vi.fn().mockResolvedValue({ + aesKey: 'aes-key', + cipherSize: 64, + encryptQueryParam: 'enc-param', + }), +}); + +describe('WechatMessageService.sendMessage', () => { + beforeEach(() => { + vi.clearAllMocks(); + vi.stubGlobal('fetch', vi.fn()); + mockRedisGet.mockResolvedValue(null); + }); + + it('forwards text via api.sendMessage', async () => { + const api = makeApi(); + const service = new WechatMessageService(api as any, 'app-1'); + + await service.sendMessage({ + channelId: 'user-1@im.wechat', + content: 'hello', + platform: 'wechat', + }); + + expect(api.sendMessage).toHaveBeenCalledWith('user-1@im.wechat', 'hello', ''); + expect(api.uploadCdnMedia).not.toHaveBeenCalled(); + expect(api.sendItem).not.toHaveBeenCalled(); + }); + + it('uploads + sends attachments as separate iLink items (text + image)', async () => { + const api = makeApi(); + mockRedisGet.mockResolvedValueOnce('ctx-1'); + const service = new WechatMessageService(api as any, 'app-1'); + + await service.sendMessage({ + attachments: [ + { + data: Buffer.from('image-bytes').toString('base64'), + mimeType: 'image/png', + name: 'foo.png', + type: 'image', + }, + ], + channelId: 'user-1@im.wechat', + content: 'here you go', + platform: 'wechat', + }); + + expect(api.sendMessage).toHaveBeenCalledWith('user-1@im.wechat', 'here you go', 'ctx-1'); + expect(api.uploadCdnMedia).toHaveBeenCalledWith( + 'user-1@im.wechat', + WechatUploadMediaType.IMAGE, + expect.any(Buffer), + ); + expect(api.sendItem).toHaveBeenCalledWith( + 'user-1@im.wechat', + expect.objectContaining({ + image_item: expect.objectContaining({ + media: expect.objectContaining({ + aes_key: 'aes-key', + encrypt_query_param: 'enc-param', + }), + }), + type: MessageItemType.IMAGE, + }), + 'ctx-1', + ); + }); + + it('skips the text leg when content is empty but still sends attachments', async () => { + const api = makeApi(); + const service = new WechatMessageService(api as any, 'app-1'); + + await service.sendMessage({ + attachments: [ + { data: Buffer.from('pdf-bytes').toString('base64'), name: 'a.pdf', type: 'file' }, + ], + channelId: 'user-2@im.wechat', + content: '', + platform: 'wechat', + }); + + expect(api.sendMessage).not.toHaveBeenCalled(); + expect(api.uploadCdnMedia).toHaveBeenCalledTimes(1); + expect(api.sendItem).toHaveBeenCalledTimes(1); + }); + + it('fetches attachments delivered as fetchUrl', async () => { + const api = makeApi(); + const service = new WechatMessageService(api as any, 'app-1'); + const fetchMock = vi.mocked(fetch); + fetchMock.mockResolvedValueOnce( + new Response(new Uint8Array([9, 9, 9, 9]), { + headers: { 'Content-Type': 'image/png' }, + status: 200, + }) as any, + ); + + await service.sendMessage({ + attachments: [{ fetchUrl: 'https://cdn.example.com/pic.png', type: 'image' }], + channelId: 'user-3@im.wechat', + content: '', + platform: 'wechat', + }); + + expect(fetchMock).toHaveBeenCalledWith('https://cdn.example.com/pic.png', expect.any(Object)); + expect(api.uploadCdnMedia).toHaveBeenCalledTimes(1); + expect(api.sendItem).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/server/services/bot/platforms/wechat/service.ts b/src/server/services/bot/platforms/wechat/service.ts index ec3944e63a..2c6ed2ff6b 100644 --- a/src/server/services/bot/platforms/wechat/service.ts +++ b/src/server/services/bot/platforms/wechat/service.ts @@ -40,6 +40,8 @@ import { getAgentRuntimeRedisClient } from '@/server/modules/AgentRuntime/redis' import type { MessageRuntimeService } from '@/server/services/toolExecution/serverRuntimes/message/adapters/types'; import { PlatformUnsupportedError } from '@/server/services/toolExecution/serverRuntimes/message/PlatformUnsupportedError'; +import { sendWechatAttachments } from './sendAttachments'; + /** * WeChat iLink Bot message adapter. * @@ -84,7 +86,12 @@ export class WechatMessageService implements MessageRuntimeService { sendMessage = async (params: SendMessageParams): Promise => { const contextToken = await this.resolveContextToken(params.channelId); - await this.api.sendMessage(params.channelId, params.content, contextToken); + if (params.content) { + await this.api.sendMessage(params.channelId, params.content, contextToken); + } + if (params.attachments?.length) { + await sendWechatAttachments(this.api, params.channelId, params.attachments, contextToken); + } return { channelId: params.channelId, platform: 'wechat', diff --git a/src/server/services/bot/replyTemplate.ts b/src/server/services/bot/replyTemplate.ts index e8535311cc..5ea53a7b58 100644 --- a/src/server/services/bot/replyTemplate.ts +++ b/src/server/services/bot/replyTemplate.ts @@ -219,6 +219,10 @@ type SystemStrings = { cmdApproveSuccess: (label: string) => string; cmdApproveUnknownCode: string; cmdApproveUsage: string; + cmdFeedbackError: string; + cmdFeedbackSubmitted: string; + cmdFeedbackSubmittedWithLink: (issueUrl: string) => string; + cmdFeedbackUsage: string; cmdNewReset: string; cmdStopNotActive: string; cmdStopRequested: string; @@ -264,6 +268,12 @@ const SYSTEM_STRINGS: Partial> = { cmdApproveSuccess: (label) => `Approved ${label}.`, cmdApproveUnknownCode: 'That pairing code is unknown or has expired.', cmdApproveUsage: 'Usage: `/approve `', + cmdFeedbackError: "Couldn't send your feedback right now. Please try again in a moment.", + cmdFeedbackSubmitted: 'Thanks — your feedback has been sent to the LobeHub team.', + cmdFeedbackSubmittedWithLink: (issueUrl) => + `Thanks — your feedback has been sent to the LobeHub team. Tracked at: ${issueUrl}`, + cmdFeedbackUsage: + 'Usage: `/feedback ` — sends feedback directly to the LobeHub team (no AI reply).', cmdNewReset: 'Conversation reset. Your next message will start a new topic.', cmdStopNotActive: 'No active execution to stop.', cmdStopRequested: 'Stop requested.', @@ -318,6 +328,12 @@ const SYSTEM_STRINGS: Partial> = { cmdApproveSuccess: (label) => `已审批 ${label}。`, cmdApproveUnknownCode: '该配对码不存在或已过期。', cmdApproveUsage: '用法:`/approve <配对码>`', + cmdFeedbackError: '发送反馈失败,请稍后再试。', + cmdFeedbackSubmitted: '已收到,感谢反馈,已转交 LobeHub 团队。', + cmdFeedbackSubmittedWithLink: (issueUrl) => + `已收到,感谢反馈,已转交 LobeHub 团队。跟踪链接:${issueUrl}`, + cmdFeedbackUsage: + '用法:`/feedback <你的反馈内容>` —— 反馈会直达 LobeHub 团队,不会触发 AI 回复。', cmdNewReset: '对话已重置,下一条消息会开启新话题。', cmdStopNotActive: '当前没有正在执行的任务可以停止。', cmdStopRequested: '已发出停止请求。', @@ -467,6 +483,9 @@ export type CommandReplyKey = | 'cmdApproveNotOwner' | 'cmdApproveUnknownCode' | 'cmdApproveUsage' + | 'cmdFeedbackError' + | 'cmdFeedbackSubmitted' + | 'cmdFeedbackUsage' | 'cmdNewReset' | 'cmdStopNotActive' | 'cmdStopRequested' @@ -491,6 +510,17 @@ export function renderApproveSuccess(label: string, lng?: BotReplyLocale): strin return getSystemStrings(lng).cmdApproveSuccess(label); } +/** + * Render the `/feedback` success reply. When the feedback backend returns a + * tracked issue URL, surface it so the user knows where to follow up — for + * Slack / Discord that surface autolinks the URL, on Telegram it remains + * tappable in monospace. + */ +export function renderFeedbackSubmitted(issueUrl?: string, lng?: BotReplyLocale): string { + const strings = getSystemStrings(lng); + return issueUrl ? strings.cmdFeedbackSubmittedWithLink(issueUrl) : strings.cmdFeedbackSubmitted; +} + /** * Render the system message a stranger sees after their first DM when the * bot is in pairing mode. Variants: diff --git a/src/server/services/messenger/MessengerRouter.ts b/src/server/services/messenger/MessengerRouter.ts index 9f6b62f765..cb4b4c241c 100644 --- a/src/server/services/messenger/MessengerRouter.ts +++ b/src/server/services/messenger/MessengerRouter.ts @@ -20,8 +20,13 @@ import { getAgentRuntimeRedisClient } from '@/server/modules/AgentRuntime/redis' import { AiAgentService } from '@/server/services/aiAgent'; import { AgentBridgeService } from '@/server/services/bot/AgentBridgeService'; import { buildBotContext } from '@/server/services/bot/buildBotContext'; +import { submitBotFeedback } from '@/server/services/bot/feedbackSubmit'; import type { PlatformClient } from '@/server/services/bot/platforms'; -import { renderInlineError } from '@/server/services/bot/replyTemplate'; +import { + renderCommandReply, + renderFeedbackSubmitted, + renderInlineError, +} from '@/server/services/bot/replyTemplate'; import { getInstallationStore } from './installations'; import type { InstallationCredentials } from './installations/types'; @@ -93,6 +98,19 @@ interface MessengerCommand { description: string; handler: (ctx: MessengerCommandContext) => Promise; name: string; + /** + * Native slash-command argument schema for platforms that require + * arguments to be declared up-front (Discord, Slack). Without this, + * Discord registers the command as zero-arg — clicking it from the + * slash menu fires the handler with no value field shown to the user. + * Mirrors `BotCommand.options` in `BotMessageRouter` so command authors + * use the same shape across both routers. + */ + options?: Array<{ + description: string; + name: string; + required?: boolean; + }>; } const HELP_TEXT = [ @@ -101,6 +119,7 @@ const HELP_TEXT = [ '• /agents — list your agents and switch the active one', '• /new — start a new conversation', '• /stop — stop the current execution', + '• /feedback — send feedback to the LobeHub team (no AI reply)', ].join('\n'); /** @@ -323,7 +342,17 @@ export class MessengerRouter { if (client.registerBotCommands) { client .registerBotCommands( - this.commands.map((cmd) => ({ command: cmd.name, description: cmd.description })), + this.commands.map((cmd) => ({ + command: cmd.name, + description: cmd.description, + // Forward the option schema so Discord/Slack surface required + // arguments (e.g. `/feedback message:`) in the slash picker. + // Without this, the command registers as zero-arg and the + // user can fire it without providing the required text — the + // handler then sees empty `args` and falls back to the usage + // hint. Mirrors `BotMessageRouter.createAndRegisterBot`. + options: cmd.options, + })), ) .catch((error) => log('registerBotCommands failed for %s: %O', creds.installationKey, error), @@ -813,6 +842,45 @@ export class MessengerRouter { }, name: 'stop', }, + { + description: 'Send feedback directly to the LobeHub team (no AI reply)', + // Declaring the argument so Discord/Slack surface a `/feedback ` + // prompt; without it the slash picker registers the command as zero-arg + // and the user can't enter feedback text from the picker UI. + options: [ + { + description: 'Your feedback message', + name: 'message', + required: true, + }, + ], + handler: async (ctx) => { + // Feedback is tied to a LobeHub account so the team can follow up; + // an unbound user has no email/identity to attach. Mirror the + // `/new` / `/stop` "you need to /start" guard for consistency. + if (!ctx.link) { + await ctx.reply('You need to /start to bind your account first.'); + return; + } + const body = ctx.args.trim(); + if (!body) { + await ctx.reply(renderCommandReply('cmdFeedbackUsage')); + return; + } + const result = await submitBotFeedback(ctx.serverDB, { + body, + platform: ctx.platform, + threadId: ctx.thread?.id ?? ctx.chatId, + userId: ctx.link.userId, + }); + if (!result.success) { + await ctx.reply(renderCommandReply('cmdFeedbackError')); + return; + } + await ctx.reply(renderFeedbackSubmitted(result.issueUrl)); + }, + name: 'feedback', + }, { description: 'Show usage', handler: async (ctx) => { From 7144e9de28b6dbb087510d9917b200bfc0bc7449 Mon Sep 17 00:00:00 2001 From: YuTengjing Date: Wed, 20 May 2026 01:16:54 +0800 Subject: [PATCH 039/224] =?UTF-8?q?=F0=9F=90=9B=20fix:=20resolve=20desktop?= =?UTF-8?q?=20visual=20media=20urls=20(#14989)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/builtin-tool-lobe-agent/package.json | 3 +- .../src/client/executor/index.ts | 5 +- .../executor/resolveVisualMediaUris.test.ts | 147 ++++++++++++++++++ .../client/executor/resolveVisualMediaUris.ts | 58 +++++++ packages/database/src/models/file.ts | 3 +- .../routers/lambda/__tests__/file.test.ts | 110 +++++++++++++ src/server/routers/lambda/file.ts | 46 +++++- .../services/file/__tests__/index.test.ts | 60 ++++++- src/server/services/file/index.ts | 27 +++- 9 files changed, 452 insertions(+), 7 deletions(-) create mode 100644 packages/builtin-tool-lobe-agent/src/client/executor/resolveVisualMediaUris.test.ts create mode 100644 packages/builtin-tool-lobe-agent/src/client/executor/resolveVisualMediaUris.ts diff --git a/packages/builtin-tool-lobe-agent/package.json b/packages/builtin-tool-lobe-agent/package.json index 5b8aa146f2..02fa3ce1bb 100644 --- a/packages/builtin-tool-lobe-agent/package.json +++ b/packages/builtin-tool-lobe-agent/package.json @@ -14,7 +14,8 @@ }, "dependencies": { "@lobechat/const": "workspace:*", - "@lobechat/prompts": "workspace:*" + "@lobechat/prompts": "workspace:*", + "@lobechat/utils": "workspace:*" }, "devDependencies": { "@lobechat/types": "workspace:*" diff --git a/packages/builtin-tool-lobe-agent/src/client/executor/index.ts b/packages/builtin-tool-lobe-agent/src/client/executor/index.ts index d1dc87a8c9..166244aa83 100644 --- a/packages/builtin-tool-lobe-agent/src/client/executor/index.ts +++ b/packages/builtin-tool-lobe-agent/src/client/executor/index.ts @@ -35,6 +35,7 @@ import { type PlanRuntimeService, } from './PlanRuntime'; import { getTodosFromContext } from './planTodoHelper'; +import { resolveClientVisualMediaPayloadItems } from './resolveVisualMediaUris'; const PLAN_DOC_TYPE = 'agent/plan'; @@ -289,6 +290,8 @@ class LobeAgentExecutor extends BaseExecutor { }; } + const payloadItems = await resolveClientVisualMediaPayloadItems({ selectedRefs, selectedUrls }); + let content = ''; let error: { message?: string } | undefined; let usage: unknown; @@ -299,7 +302,7 @@ class LobeAgentExecutor extends BaseExecutor { max_tokens: 2000, messages: [ { - content: buildAnalyzeVisualMediaContent(selectedItems, params.question, { + content: buildAnalyzeVisualMediaContent(payloadItems, params.question, { includeFallbackInstruction: true, includeFileSummary: true, }), diff --git a/packages/builtin-tool-lobe-agent/src/client/executor/resolveVisualMediaUris.test.ts b/packages/builtin-tool-lobe-agent/src/client/executor/resolveVisualMediaUris.test.ts new file mode 100644 index 0000000000..6279a9170a --- /dev/null +++ b/packages/builtin-tool-lobe-agent/src/client/executor/resolveVisualMediaUris.test.ts @@ -0,0 +1,147 @@ +import { imageUrlToBase64 } from '@lobechat/utils/imageToBase64'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import type { VisualFileItem } from '../../visualMedia'; +import { + resolveClientVisualMediaPayloadItems, + resolveClientVisualMediaUris, +} from './resolveVisualMediaUris'; + +vi.mock('@lobechat/utils/imageToBase64', () => ({ + imageUrlToBase64: vi.fn(), +})); + +const createVisualItem = (item: Partial): VisualFileItem => ({ + description: 'test.png', + localRef: 'image_1', + name: 'test.png', + ref: 'msg_1.image_1', + type: 'image', + uri: 'https://example.com/test.png', + ...item, +}); + +describe('resolveClientVisualMediaUris', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should convert desktop local visual media URLs to data URLs', async () => { + vi.mocked(imageUrlToBase64) + .mockResolvedValueOnce({ + base64: 'image-base64', + mimeType: 'image/png', + }) + .mockResolvedValueOnce({ + base64: 'video-base64', + mimeType: 'video/mp4', + }); + + const localImage = createVisualItem({ + name: 'local.png', + uri: 'http://127.0.0.1:3210/uploads/local.png', + }); + const localVideo = createVisualItem({ + name: 'local.mp4', + type: 'video', + uri: 'http://127.0.0.1:3210/uploads/local.mp4', + }); + const remoteImage = createVisualItem({ + name: 'remote.png', + uri: 'https://example.com/remote.png', + }); + const dataImage = createVisualItem({ + name: 'inline.png', + uri: 'data:image/png;base64,inline-base64', + }); + + const result = await resolveClientVisualMediaUris([ + localImage, + localVideo, + remoteImage, + dataImage, + ]); + + expect(result).toEqual([ + { + ...localImage, + uri: 'data:image/png;base64,image-base64', + }, + { + ...localVideo, + uri: 'data:video/mp4;base64,video-base64', + }, + remoteImage, + dataImage, + ]); + expect(imageUrlToBase64).toHaveBeenCalledTimes(2); + expect(imageUrlToBase64).toHaveBeenNthCalledWith(1, 'http://127.0.0.1:3210/uploads/local.png'); + expect(imageUrlToBase64).toHaveBeenNthCalledWith(2, 'http://127.0.0.1:3210/uploads/local.mp4'); + }); + + it('should reject desktop local URLs when fetched MIME type does not match the item type', async () => { + vi.mocked(imageUrlToBase64).mockResolvedValue({ + base64: 'not-found', + mimeType: 'text/plain', + }); + + const localImage = createVisualItem({ + name: 'missing.png', + uri: 'http://127.0.0.1:3210/uploads/missing.png', + }); + + await expect(resolveClientVisualMediaUris([localImage])).rejects.toThrow( + 'Unable to read image attachment "missing.png": expected image/* MIME type, received text/plain.', + ); + }); + + it('should reject desktop local video URLs when fetched MIME type is an image', async () => { + vi.mocked(imageUrlToBase64).mockResolvedValue({ + base64: 'poster', + mimeType: 'image/png', + }); + + const localVideo = createVisualItem({ + name: 'clip.mp4', + type: 'video', + uri: 'http://127.0.0.1:3210/uploads/clip.mp4', + }); + + await expect(resolveClientVisualMediaUris([localVideo])).rejects.toThrow( + 'Unable to read video attachment "clip.mp4": expected video/* MIME type, received image/png.', + ); + }); + + it('should only convert attachment refs when building visual media payload items', async () => { + vi.mocked(imageUrlToBase64).mockResolvedValue({ + base64: 'attachment-base64', + mimeType: 'image/png', + }); + + const localAttachment = createVisualItem({ + name: 'attachment.png', + uri: 'http://127.0.0.1:3210/uploads/attachment.png', + }); + const directLocalUrl = createVisualItem({ + localRef: 'url_1', + name: 'direct.png', + ref: 'url_1', + uri: 'http://127.0.0.1:3210/private/direct.png', + }); + + const result = await resolveClientVisualMediaPayloadItems({ + selectedRefs: [localAttachment], + selectedUrls: [directLocalUrl], + }); + + expect(result).toEqual([ + { + ...localAttachment, + uri: 'data:image/png;base64,attachment-base64', + }, + directLocalUrl, + ]); + expect(imageUrlToBase64).toHaveBeenCalledTimes(1); + expect(imageUrlToBase64).toHaveBeenCalledWith('http://127.0.0.1:3210/uploads/attachment.png'); + }); +}); diff --git a/packages/builtin-tool-lobe-agent/src/client/executor/resolveVisualMediaUris.ts b/packages/builtin-tool-lobe-agent/src/client/executor/resolveVisualMediaUris.ts new file mode 100644 index 0000000000..327ff58da0 --- /dev/null +++ b/packages/builtin-tool-lobe-agent/src/client/executor/resolveVisualMediaUris.ts @@ -0,0 +1,58 @@ +import { imageUrlToBase64 } from '@lobechat/utils/imageToBase64'; +import { parseDataUri } from '@lobechat/utils/uriParser'; +import { isDesktopLocalStaticServerUrl } from '@lobechat/utils/url'; + +import type { VisualFileItem } from '../../visualMedia'; + +interface ResolveClientVisualMediaPayloadItemsParams { + selectedRefs: VisualFileItem[]; + selectedUrls: VisualFileItem[]; +} + +const VISUAL_MEDIA_MIME_TYPE_PREFIXES = { + image: 'image/', + video: 'video/', +} as const satisfies Record; + +const assertExpectedVisualMediaMimeType = (item: VisualFileItem, mimeType: string) => { + const expectedPrefix = VISUAL_MEDIA_MIME_TYPE_PREFIXES[item.type]; + const normalizedMimeType = mimeType.trim().toLowerCase(); + + if (normalizedMimeType.startsWith(expectedPrefix)) return; + + throw new TypeError( + `Unable to read ${item.type} attachment "${item.name}": expected ${expectedPrefix}* MIME type, received ${normalizedMimeType || 'unknown'}.`, + ); +}; + +/** + * Desktop attachments are exposed through a 127.0.0.1 static file server. + * Convert those URLs in the client before sending a remote visual request; + * otherwise the server sees its own localhost and SSRF protection blocks it. + */ +export const resolveClientVisualMediaUris = async ( + items: VisualFileItem[], +): Promise => + Promise.all( + items.map(async (item) => { + const { type } = parseDataUri(item.uri); + + if (type !== 'url' || !isDesktopLocalStaticServerUrl(item.uri)) return item; + + const { base64, mimeType } = await imageUrlToBase64(item.uri); + assertExpectedVisualMediaMimeType(item, mimeType); + + return { + ...item, + uri: `data:${mimeType};base64,${base64}`, + }; + }), + ); + +export const resolveClientVisualMediaPayloadItems = async ({ + selectedRefs, + selectedUrls, +}: ResolveClientVisualMediaPayloadItemsParams): Promise => [ + ...(await resolveClientVisualMediaUris(selectedRefs)), + ...selectedUrls, +]; diff --git a/packages/database/src/models/file.ts b/packages/database/src/models/file.ts index 56c3a6bed7..ae81f774c7 100644 --- a/packages/database/src/models/file.ts +++ b/packages/database/src/models/file.ts @@ -95,8 +95,9 @@ export class FileModel { updateGlobalFile = async ( hashId: string, data: Partial>, + trx?: Transaction, ) => { - return this.db.update(globalFiles).set(data).where(eq(globalFiles.hashId, hashId)); + return (trx ?? this.db).update(globalFiles).set(data).where(eq(globalFiles.hashId, hashId)); }; checkHash = async (hash: string) => { diff --git a/src/server/routers/lambda/__tests__/file.test.ts b/src/server/routers/lambda/__tests__/file.test.ts index 86328f903e..f30f47b8d6 100644 --- a/src/server/routers/lambda/__tests__/file.test.ts +++ b/src/server/routers/lambda/__tests__/file.test.ts @@ -29,6 +29,7 @@ function createCallerWithCtx(partialCtx: any = {}) { query: vi.fn().mockResolvedValue([]), delete: vi.fn().mockResolvedValue(undefined), deleteMany: vi.fn().mockResolvedValue([]), + updateGlobalFile: vi.fn().mockResolvedValue(undefined), clear: vi.fn().mockResolvedValue({} as any), }; @@ -128,6 +129,7 @@ const mockFileModelDeleteMany = vi.fn(); const mockFileModelFindById = vi.fn(); const mockFileModelFindByIds = vi.fn(); const mockFileModelQuery = vi.fn(); +const mockFileModelUpdateGlobalFile = vi.fn(); const mockFileModelClear = vi.fn(); vi.mock('@/database/models/file', () => ({ @@ -139,6 +141,7 @@ vi.mock('@/database/models/file', () => ({ findById: mockFileModelFindById, findByIds: mockFileModelFindByIds, query: mockFileModelQuery, + updateGlobalFile: mockFileModelUpdateGlobalFile, clear: mockFileModelClear, })), })); @@ -215,6 +218,47 @@ describe('fileRouter', () => { ctx.fileModel.checkHash.mockResolvedValue(undefined); await expect(caller.checkFileHash({ hash: 'test-hash' })).resolves.toBeUndefined(); }); + + it('should return existing hash when the stored object is still available', async () => { + const checkResult = { + isExist: true, + metadata: { path: 'files/existing.png' }, + url: 'files/existing.png', + }; + mockFileModelCheckHash.mockResolvedValue(checkResult); + mockFileServiceGetFileMetadata.mockResolvedValue({ + contentLength: 100, + contentType: 'image/png', + }); + + await expect(caller.checkFileHash({ hash: 'test-hash' })).resolves.toEqual(checkResult); + + expect(mockFileServiceGetFileMetadata).toHaveBeenCalledWith('files/existing.png'); + }); + + it('should treat stale hash records as missing when the stored object is unavailable', async () => { + const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); + mockFileModelCheckHash.mockResolvedValue({ + isExist: true, + metadata: { path: 'generations/images/missing_raw.jpg' }, + url: 'generations/images/missing_raw.jpg', + }); + mockFileServiceGetFileMetadata.mockRejectedValue(new Error('NoSuchKey')); + + await expect(caller.checkFileHash({ hash: 'test-hash' })).resolves.toEqual({ + isExist: false, + }); + + expect(mockFileServiceGetFileMetadata).toHaveBeenCalledWith( + 'generations/images/missing_raw.jpg', + ); + expect(consoleSpy).toHaveBeenCalledWith( + 'Failed to verify existing file hash storage object:', + expect.any(Error), + ); + + consoleSpy.mockRestore(); + }); }); describe('createFile', () => { @@ -251,6 +295,72 @@ describe('fileRouter', () => { }); }); + it('should refresh global file metadata when an existing hash points to a missing object', async () => { + mockFileModelCheckHash.mockResolvedValue({ + isExist: true, + metadata: { path: 'old/path.txt' }, + url: 'old/path.txt', + }); + mockFileModelCreate.mockResolvedValue({ id: 'new-file-id' }); + mockFileServiceGetFileMetadata + .mockResolvedValueOnce({ contentLength: 100, contentType: 'text/plain' }) + .mockRejectedValueOnce(new Error('NoSuchKey')); + const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); + + await caller.createFile({ + hash: 'test-hash', + fileType: 'text', + metadata: { path: 'new/path.txt' }, + name: 'test.txt', + size: 100, + url: 'new/path.txt', + }); + + expect(mockFileModelUpdateGlobalFile).toHaveBeenCalledWith( + 'test-hash', + { + metadata: { path: 'new/path.txt' }, + url: 'new/path.txt', + }, + routerMocks.transactionClient, + ); + expect(mockFileModelCreate).toHaveBeenCalledWith( + expect.objectContaining({ fileHash: 'test-hash', url: 'new/path.txt' }), + false, + routerMocks.transactionClient, + ); + consoleSpy.mockRestore(); + }); + + it('should keep the global file pointer when an existing hash object is still available', async () => { + mockFileModelCheckHash.mockResolvedValue({ + isExist: true, + metadata: { path: 'old/path.txt' }, + url: 'old/path.txt', + }); + mockFileModelCreate.mockResolvedValue({ id: 'new-file-id' }); + mockFileServiceGetFileMetadata.mockResolvedValue({ + contentLength: 100, + contentType: 'text/plain', + }); + + await caller.createFile({ + hash: 'test-hash', + fileType: 'text', + metadata: { path: 'new/path.txt' }, + name: 'test.txt', + size: 100, + url: 'new/path.txt', + }); + + expect(mockFileModelUpdateGlobalFile).not.toHaveBeenCalled(); + expect(mockFileModelCreate).toHaveBeenCalledWith( + expect.objectContaining({ fileHash: 'test-hash', url: 'new/path.txt' }), + false, + routerMocks.transactionClient, + ); + }); + it('should run business upload check and file creation in the same transaction', async () => { mockFileModelCheckHash.mockResolvedValue({ isExist: false }); mockFileModelCreate.mockResolvedValue({ id: 'new-file-id' }); diff --git a/src/server/routers/lambda/file.ts b/src/server/routers/lambda/file.ts index 919bf38df8..46529c5863 100644 --- a/src/server/routers/lambda/file.ts +++ b/src/server/routers/lambda/file.ts @@ -102,6 +102,19 @@ const getKnowledgeItemStatusMap = async ( ); }; +const isStoredObjectAvailable = async (fileService: FileService, url: string): Promise => { + try { + // Hash records can outlive their backing object, for example when generated + // assets are cleaned up but the global hash row remains. Treat stale rows as + // missing so the client uploads a fresh copy instead of reusing a dead key. + await fileService.getFileMetadata(url); + return true; + } catch (error) { + console.error('Failed to verify existing file hash storage object:', error); + return false; + } +}; + const fileProcedure = authedProcedure.use(serverDatabase).use(async (opts) => { const { ctx } = opts; @@ -123,7 +136,13 @@ export const fileRouter = router({ .use(checkFileStorageUsage) .input(z.object({ hash: z.string() })) .mutation(async ({ ctx, input }) => { - return ctx.fileModel.checkHash(input.hash); + const existingFile = await ctx.fileModel.checkHash(input.hash); + const existingHashUrl = existingFile?.isExist ? existingFile.url : undefined; + if (!existingHashUrl) return existingFile; + + const isStorageAvailable = await isStoredObjectAvailable(ctx.fileService, existingHashUrl); + + return isStorageAvailable ? existingFile : { isExist: false }; }), createFile: fileProcedure @@ -135,7 +154,8 @@ export const fileRouter = router({ }), ) .mutation(async ({ ctx, input }) => { - const { isExist } = await ctx.fileModel.checkHash(input.hash!); + const existingFile = await ctx.fileModel.checkHash(input.hash!); + const { isExist } = existingFile; // Resolve parentId if it's a slug let resolvedParentId = input.parentId; @@ -177,6 +197,28 @@ export const fileRouter = router({ userId: ctx.userId, }); + let shouldRefreshGlobalFile = false; + if (isExist && existingFile.url && existingFile.url !== input.url) { + shouldRefreshGlobalFile = !(await isStoredObjectAvailable( + ctx.fileService, + existingFile.url, + )); + } + + if (shouldRefreshGlobalFile) { + // A user may re-upload the same bytes after the old object key was + // removed. Keep the global hash pointer on the newly uploaded object so + // future dedup checks do not resolve back to the stale key. + await ctx.fileModel.updateGlobalFile( + input.hash!, + { + metadata: input.metadata, + url: input.url, + }, + trx, + ); + } + return ctx.fileModel.create( { fileHash: input.hash, diff --git a/src/server/services/file/__tests__/index.test.ts b/src/server/services/file/__tests__/index.test.ts index 848022e7fe..7bbcbd3e87 100644 --- a/src/server/services/file/__tests__/index.test.ts +++ b/src/server/services/file/__tests__/index.test.ts @@ -24,6 +24,7 @@ vi.mock('../impls', () => ({ deleteFiles: vi.fn(), getFileContent: vi.fn(), getFileByteArray: vi.fn(), + getFileMetadata: vi.fn(), createPreSignedUrl: vi.fn(), createPreSignedUrlForPreview: vi.fn(), uploadContent: vi.fn(), @@ -381,7 +382,7 @@ describe('FileService', () => { }); it('should not insert to global files when hash already exists', async () => { - mockFileModel.checkHash.mockResolvedValue({ isExist: true }); + mockFileModel.checkHash.mockResolvedValue({ isExist: true, url: 'files/test.txt' }); mockFileModel.create.mockResolvedValue({ id: 'file-id' }); await service.createFileRecord({ @@ -398,6 +399,63 @@ describe('FileService', () => { }), false, // insertToGlobalFiles = false when hash exists ); + expect(mockFileModel.updateGlobalFile).not.toHaveBeenCalled(); + }); + + it('should update global file metadata when an existing hash points to a missing object', async () => { + mockFileModel.checkHash.mockResolvedValue({ isExist: true, url: 'old/path.txt' }); + mockFileModel.create.mockResolvedValue({ id: 'file-id' }); + vi.mocked(service['impl'].getFileMetadata).mockRejectedValue(new Error('NoSuchKey')); + const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); + + await service.createFileRecord({ + fileHash: 'existing-hash', + fileType: 'text/plain', + metadata: { dirname: 'new', filename: 'test.txt', path: 'new/path.txt' }, + name: 'test.txt', + size: 100, + url: 'new/path.txt', + }); + + expect(mockFileModel.updateGlobalFile).toHaveBeenCalledWith('existing-hash', { + metadata: { dirname: 'new', filename: 'test.txt', path: 'new/path.txt' }, + url: 'new/path.txt', + }); + expect(mockFileModel.create).toHaveBeenCalledWith( + expect.objectContaining({ + fileHash: 'existing-hash', + url: 'new/path.txt', + }), + false, + ); + consoleSpy.mockRestore(); + }); + + it('should keep global file metadata when the existing hash object is still available', async () => { + mockFileModel.checkHash.mockResolvedValue({ isExist: true, url: 'old/path.txt' }); + mockFileModel.create.mockResolvedValue({ id: 'file-id' }); + vi.mocked(service['impl'].getFileMetadata).mockResolvedValue({ + contentLength: 100, + contentType: 'text/plain', + }); + + await service.createFileRecord({ + fileHash: 'existing-hash', + fileType: 'text/plain', + metadata: { dirname: 'new', filename: 'test.txt', path: 'new/path.txt' }, + name: 'test.txt', + size: 100, + url: 'new/path.txt', + }); + + expect(mockFileModel.updateGlobalFile).not.toHaveBeenCalled(); + expect(mockFileModel.create).toHaveBeenCalledWith( + expect.objectContaining({ + fileHash: 'existing-hash', + url: 'new/path.txt', + }), + false, + ); }); }); diff --git a/src/server/services/file/index.ts b/src/server/services/file/index.ts index fe79682410..8e6633bfa6 100644 --- a/src/server/services/file/index.ts +++ b/src/server/services/file/index.ts @@ -119,6 +119,16 @@ export class FileService { return this.impl.uploadBuffer(key, buffer, contentType); } + private async isStoredFileAvailable(url: string): Promise { + try { + await this.getFileMetadata(url); + return true; + } catch (error) { + console.error('Failed to verify existing file hash storage object:', error); + return false; + } + } + /** * Create file record (common method) * Automatically handles globalFiles deduplication logic @@ -137,7 +147,22 @@ export class FileService { url: string; }): Promise<{ fileId: string; url: string }> { // Check if hash already exists in globalFiles - const { isExist } = await this.fileModel.checkHash(params.fileHash); + const existingFile = await this.fileModel.checkHash(params.fileHash); + const { isExist } = existingFile; + + let shouldRefreshGlobalFile = false; + if (isExist && existingFile.url && existingFile.url !== params.url) { + shouldRefreshGlobalFile = !(await this.isStoredFileAvailable(existingFile.url)); + } + + if (shouldRefreshGlobalFile) { + // Keep global hash dedup usable when the same file is uploaded again to a + // fresh object key after the previous storage object has been removed. + await this.fileModel.updateGlobalFile(params.fileHash, { + metadata: params.metadata, + url: params.url, + }); + } // Create database record // If hash doesn't exist, also create globalFiles record From 3bcf6a8d72773091e7f1c9295412c67e4423b1e7 Mon Sep 17 00:00:00 2001 From: Arvin Xu Date: Wed, 20 May 2026 10:27:35 +0800 Subject: [PATCH 040/224] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(agent-set?= =?UTF-8?q?tings):=20consolidate=20Chat=20tab=20into=20Params=20popover,?= =?UTF-8?q?=20drop=20dead=20auto-topic=20feature=20(#14885)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🔥 chore(agent-config): drop dead enableAutoCreateTopic feature Drop enableAutoCreateTopic + autoCreateTopicThreshold end-to-end. No business code consumed these fields anymore — only types, defaults, locale copy, UI form items, agent-builder LLM prompts, and test fixtures kept the dead config alive. Sweep: - types & zod schema (LobeAgentChatConfig, AgentChatConfigSchema, openapi) - DEFAULT_AGENT_CHAT_CONFIG constant - locale keys in default + 18 translations - agent-builder system prompts & tool manifests - AgentChat form items (auto-topic switch + threshold slider) - test fixtures & integration tests (replaced sample boolean key in parser tests with enableHistoryCount) - docs/self-hosting env-var examples - settings.test snapshot dataImporter JSON fixtures keep the legacy keys on purpose — they simulate historical user exports and the zod schema strips unknowns. Co-Authored-By: Claude Opus 4.7 (1M context) * ✨ feat(chat-input): move inputTemplate + autoScroll into Params popover Surface the User Input Preprocessing template (inputTemplate) and Auto-scroll During AI Response toggle (enableAutoScrollOnStreaming) in the chat-input Params popover, alongside compression / history / max_tokens. Drop the matching form items from AgentChat — the popover is now the single entry point for these two agent-level preferences. ControlRow's action prop becomes optional so inputTemplate can render as a label + TextArea without a Switch. Co-Authored-By: Claude Opus 4.7 (1M context) * 🔥 refactor(agent-settings): drop AgentChat tab in favor of Params popover Remove the now-redundant Chat Preferences tab from agent settings: - delete src/features/AgentSetting/AgentChat/ - drop ChatSettingsTabs.Chat enum and its three registrations (useCategory, AgentSettingsContent, profile Content) - drop agentTab.chat locale key in default + 18 translations - drop MessagesSquare / MessagesSquareIcon imports that became unused History/compression/auto-scroll/inputTemplate already live in the chat-input Params popover, so this tab carried no unique functionality. Co-Authored-By: Claude Opus 4.7 (1M context) * ✨ feat(chat-input): surface enableStreaming + reasoning_effort + disabledParams in Params popover Bring the Model tab's controls into the chat-input Params popover so the popover can become the single entry point for agent-level params. - enableStreaming Switch at the top of Advanced (treats undefined as on, matching `chatConfig.enableStreaming !== false` in chat service) - reasoning_effort row after max_tokens (Select tied to chatConfig.enableReasoningEffort / params.reasoning_effort, matching the agentConfigResolver gating) - per-model disabledParams filter on the 4 sampling sliders (e.g. Claude Opus 4.7 hides temperature/top_p), via aiModelSelectors.modelDisabledParams - max_tokens defaults to 4096 on toggle-on (parity with AgentModal), matching the AgentModal UX - drop the !enableAgentMode gate on Advanced so agent-mode users still reach the model params once the Model tab is gone Co-Authored-By: Claude Opus 4.7 (1M context) * 🔥 refactor(agent-settings): drop AgentModal tab in favor of Params popover Now that the chat-input Params popover surfaces enableStreaming, reasoning_effort, the 4 sampling params (model-aware via disabledParams), and max_tokens, the Model Settings tab carries no unique behavior. Remove it: - delete src/features/AgentSetting/AgentModal/ (index + ModelSelect) - drop ChatSettingsTabs.Modal enum and its three registrations (useCategory, AgentSettingsContent, profile Content) - drop agentTab.modal locale key in default + 18 translations - drop BrainCog / BrainIcon imports that became unused - simplify the profile Content inbox-default fallback to Opening (Content menu no longer carried Modal at all) settingModel.* locale keys are kept — Controls still reads them. Co-Authored-By: Claude Opus 4.7 (1M context) * 🐛 fix(chat-input): keep !enableAgentMode gate on Advanced sampling params Walk back the gate removal from the prior commit. Agent mode is meant to manage temperature / top_p / penalties / reasoning_effort itself; exposing user overrides there contradicts the design. - Move enableStreaming out of Advanced into the common section so it stays visible in both modes (streaming is a UI behavior, not a sampling param). - Re-wrap the SectionHeader + sampling sliders + max_tokens + reasoning_effort with `{!enableAgentMode && (...)}`, restoring the prior visibility rule. Co-Authored-By: Claude Opus 4.7 (1M context) --------- Co-authored-by: Claude Opus 4.7 (1M context) --- .../environment-variables/basic.mdx | 24 +- .../environment-variables/basic.zh-CN.mdx | 24 +- locales/ar/setting.json | 6 - locales/bg-BG/setting.json | 6 - locales/de-DE/setting.json | 6 - locales/en-US/setting.json | 6 - locales/es-ES/setting.json | 6 - locales/fa-IR/setting.json | 6 - locales/fr-FR/setting.json | 6 - locales/it-IT/setting.json | 6 - locales/ja-JP/setting.json | 6 - locales/ko-KR/setting.json | 6 - locales/nl-NL/setting.json | 6 - locales/pl-PL/setting.json | 6 - locales/pt-BR/setting.json | 6 - locales/ru-RU/setting.json | 6 - locales/tr-TR/setting.json | 6 - locales/vi-VN/setting.json | 6 - locales/zh-CN/setting.json | 6 - locales/zh-TW/setting.json | 6 - .../src/agents/agent-builder/systemRole.ts | 2 - .../src/manifest.ts | 2 +- .../src/systemRole.ts | 3 - .../src/manifest.ts | 2 +- packages/const/src/settings/agent.ts | 2 - .../__tests__/nightlyReview.test.ts | 16 +- .../__tests__/reviewContext.test.ts | 10 +- .../deprecated/__tests__/index.test.ts | 6 +- packages/openapi/src/types/agent.type.ts | 2 - packages/openapi/src/types/chat.type.ts | 2 - packages/types/src/agent/chatConfig.ts | 4 - .../AgentCategory/useCategory.tsx | 12 +- src/features/AgentSetting/AgentChat/index.tsx | 97 ----- .../AgentSetting/AgentModal/ModelSelect.tsx | 24 -- .../AgentSetting/AgentModal/index.tsx | 374 ------------------ .../AgentSetting/AgentSettingsContent.tsx | 4 - .../ChatInput/ActionBar/Params/Controls.tsx | 103 ++++- src/features/Conversation/store.test.ts | 5 +- src/locales/default/setting.ts | 8 - .../features/AgentSettings/Content.tsx | 14 +- .../globalConfig/parseDefaultAgent.test.ts | 8 +- .../aiAgent/execAgent.integration.test.ts | 2 +- .../multiRoundTools.integration.test.ts | 2 +- .../routers/lambda/config/index.test.ts | 8 +- src/services/chat/chat.test.ts | 1 - .../chat/mecha/modelParamsResolver.test.ts | 1 - src/store/global/initialState.ts | 2 - .../__snapshots__/settings.test.ts.snap | 4 - 48 files changed, 152 insertions(+), 724 deletions(-) delete mode 100644 src/features/AgentSetting/AgentChat/index.tsx delete mode 100644 src/features/AgentSetting/AgentModal/ModelSelect.tsx delete mode 100644 src/features/AgentSetting/AgentModal/index.tsx diff --git a/docs/self-hosting/environment-variables/basic.mdx b/docs/self-hosting/environment-variables/basic.mdx index 4a6447ee34..264799414c 100644 --- a/docs/self-hosting/environment-variables/basic.mdx +++ b/docs/self-hosting/environment-variables/basic.mdx @@ -52,18 +52,18 @@ When using the `turn` mode, the API Keys will be retrieved in a polling manner a The `DEFAULT_AGENT_CONFIG` is used to configure the default settings for the LobeHub default agent. It supports various data types and structures, including key-value pairs, nested fields, array values, and more. The table below provides detailed information on the configuration options, examples, and corresponding explanations for the `DEFAULT_AGENT_CONFIG` environment variable: -| Configuration Type | Example | Explanation | -| ----------------------- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ | -| Basic Key-Value Pair | `model=gpt-4` | Set the model to `gpt-4`. | -| Nested Field | `tts.sttLocale=en-US` | Set the language locale for the text-to-speech service to `en-US`. | -| Array | `plugins=search-engine,lobe-image-designer` | Enable the `search-engine` and `lobe-image-designer` plugins. | -| Chinese Comma | `plugins=search-engine,lobe-image-designer` | Same as above, demonstrating support for Chinese comma separation. | -| Multiple Configurations | `model=glm-4;provider=zhipu` | Set the model to `glm-4` and the model provider to `zhipu`. | -| Numeric Value | `params.max_tokens=300`, `chatConfig.historyCount=5` | Set the maximum tokens to `300`, Set the number of historical messages to 5. | -| Boolean Value | `chatConfig.enableAutoCreateTopic=true`, `chatConfig.enableCompressThreshold=true`, `chatConfig.enableHistoryCount=true` | Enable automatic topic creation, History length compression threshold, number of historical records. | -| Special Characters | `inputTemplate="Hello; I am a bot;"` | Set the input template to `Hello; I am a bot;`. | -| Error Handling | `model=gpt-4;maxToken` | Ignore invalid entry `maxToken` and only parse `model=gpt-4`. | -| Value Override | `model=gpt-4;model=gpt-4-1106-preview` | If a key is repeated, use the value that appears last; in this case, the value of `model` is `gpt-4-1106-preview`. | +| Configuration Type | Example | Explanation | +| ----------------------- | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ | +| Basic Key-Value Pair | `model=gpt-4` | Set the model to `gpt-4`. | +| Nested Field | `tts.sttLocale=en-US` | Set the language locale for the text-to-speech service to `en-US`. | +| Array | `plugins=search-engine,lobe-image-designer` | Enable the `search-engine` and `lobe-image-designer` plugins. | +| Chinese Comma | `plugins=search-engine,lobe-image-designer` | Same as above, demonstrating support for Chinese comma separation. | +| Multiple Configurations | `model=glm-4;provider=zhipu` | Set the model to `glm-4` and the model provider to `zhipu`. | +| Numeric Value | `params.max_tokens=300`, `chatConfig.historyCount=5` | Set the maximum tokens to `300`, Set the number of historical messages to 5. | +| Boolean Value | `chatConfig.enableHistoryCount=true`, `chatConfig.enableCompressThreshold=true`, `chatConfig.enableStreaming=true` | Enable history length limit, history length compression threshold, and streaming output. | +| Special Characters | `inputTemplate="Hello; I am a bot;"` | Set the input template to `Hello; I am a bot;`. | +| Error Handling | `model=gpt-4;maxToken` | Ignore invalid entry `maxToken` and only parse `model=gpt-4`. | +| Value Override | `model=gpt-4;model=gpt-4-1106-preview` | If a key is repeated, use the value that appears last; in this case, the value of `model` is `gpt-4-1106-preview`. | Further reading: diff --git a/docs/self-hosting/environment-variables/basic.zh-CN.mdx b/docs/self-hosting/environment-variables/basic.zh-CN.mdx index 146963766e..8e7d7f1145 100644 --- a/docs/self-hosting/environment-variables/basic.zh-CN.mdx +++ b/docs/self-hosting/environment-variables/basic.zh-CN.mdx @@ -49,18 +49,18 @@ LobeHub 在部署时提供了一些额外的配置项,你可以使用环境变 `DEFAULT_AGENT_CONFIG` 用于配置 LobeHub 默认助理的默认配置。它支持多种数据类型和结构,包括键值对、嵌套字段、数组值等。下表详细说明了 `DEFAULT_AGENT_CONFIG` 环境变量的配置项、示例以及相应解释: -| 配置项类型 | 示例 | 解释 | -| ----- | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | -| 基本键值对 | `model=gpt-4` | 设置模型为 `gpt-4`。 | -| 嵌套字段 | `tts.sttLocale=en-US` | 设置文本到语音服务的语言区域为 `en-US`。 | -| 数组 | `plugins=search-engine,lobe-image-designer` | 启用 `search-engine` 和 `lobe-image-designer` 插件。 | -| 中文逗号 | `plugins=search-engine,lobe-image-designer` | 同上,演示支持中文逗号分隔。 | -| 多个配置项 | `model=glm-4;provider=zhipu` | 设置模型为 `glm-4` 且模型服务商为 `zhipu`。 | -| 数字值 | `params.max_tokens=300`, `chatConfig.historyCount=5` | 设置最大令牌数为 `300`,设置历史消息条数为 5。 | -| 布尔值 | `chatConfig.enableAutoCreateTopic=true`,`chatConfig.enableCompressThreshold=true`, `chatConfig.enableHistoryCount=true` | 启用自动创建主题,历史长度压缩阈值,历史记录条数。 | -| 特殊字符 | `inputTemplate="Hello; I am a bot;"` | 设置输入模板为 `Hello; I am a bot;`。 | -| 错误处理 | `model=gpt-4;maxToken` | 忽略无效条目 `maxToken`,仅解析出 `model=gpt-4`。 | -| 值覆盖 | `model=gpt-4;model=gpt-4-1106-preview` | 如果键重复,使用最后一次出现的值,此处 `model` 的值为 `gpt-4-1106-preview`。 | +| 配置项类型 | 示例 | 解释 | +| ----- | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | +| 基本键值对 | `model=gpt-4` | 设置模型为 `gpt-4`。 | +| 嵌套字段 | `tts.sttLocale=en-US` | 设置文本到语音服务的语言区域为 `en-US`。 | +| 数组 | `plugins=search-engine,lobe-image-designer` | 启用 `search-engine` 和 `lobe-image-designer` 插件。 | +| 中文逗号 | `plugins=search-engine,lobe-image-designer` | 同上,演示支持中文逗号分隔。 | +| 多个配置项 | `model=glm-4;provider=zhipu` | 设置模型为 `glm-4` 且模型服务商为 `zhipu`。 | +| 数字值 | `params.max_tokens=300`, `chatConfig.historyCount=5` | 设置最大令牌数为 `300`,设置历史消息条数为 5。 | +| 布尔值 | `chatConfig.enableHistoryCount=true`,`chatConfig.enableCompressThreshold=true`, `chatConfig.enableStreaming=true` | 启用历史消息数量限制,历史长度压缩阈值,流式输出。 | +| 特殊字符 | `inputTemplate="Hello; I am a bot;"` | 设置输入模板为 `Hello; I am a bot;`。 | +| 错误处理 | `model=gpt-4;maxToken` | 忽略无效条目 `maxToken`,仅解析出 `model=gpt-4`。 | +| 值覆盖 | `model=gpt-4;model=gpt-4-1106-preview` | 如果键重复,使用最后一次出现的值,此处 `model` 的值为 `gpt-4-1106-preview`。 | 相关阅读: diff --git a/locales/ar/setting.json b/locales/ar/setting.json index 5a1f59ea18..697b9f2e20 100644 --- a/locales/ar/setting.json +++ b/locales/ar/setting.json @@ -180,10 +180,8 @@ "agentSkillModal.url.title": "استيراد من رابط", "agentSkillModal.url.urlPlaceholder": "https://example.com/path/to/SKILL.md", "agentSkillTag": "مهارة الوكيل", - "agentTab.chat": "تفضيلات المحادثة", "agentTab.documents": "المستندات", "agentTab.meta": "معلومات الوكيل", - "agentTab.modal": "إعدادات النموذج", "agentTab.opening": "إعدادات البداية", "agentTab.plugin": "إعدادات المهارات", "agentTab.prompt": "ملف تعريف الوكيل", @@ -531,15 +529,11 @@ "settingAppearance.primaryColor.desc": "لون السمة المخصص", "settingAppearance.primaryColor.title": "لون السمة", "settingAppearance.title": "مظهر التطبيق", - "settingChat.autoCreateTopicThreshold.desc": "إنشاء موضوع تلقائيًا عند تجاوز عدد الرسائل الحالية هذه القيمة", - "settingChat.autoCreateTopicThreshold.title": "حد الرسائل", "settingChat.chatStyleType.title": "نمط نافذة الدردشة", "settingChat.chatStyleType.type.chat": "وضع المحادثة", "settingChat.chatStyleType.type.docs": "وضع الصفحة", "settingChat.compressThreshold.desc": "عند تجاوز عدد الرسائل غير المضغوطة هذه القيمة، سيتم تطبيق الضغط", "settingChat.compressThreshold.title": "حد ضغط طول سجل الرسائل", - "settingChat.enableAutoCreateTopic.desc": "ما إذا كان سيتم إنشاء موضوع تلقائيًا أثناء المحادثة، فعال فقط في المواضيع المؤقتة", - "settingChat.enableAutoCreateTopic.title": "إنشاء موضوع تلقائيًا", "settingChat.enableAutoScrollOnStreaming.desc": "تجاوز الإعداد العام لهذا المساعد", "settingChat.enableAutoScrollOnStreaming.title": "التمرير التلقائي أثناء استجابة الذكاء الاصطناعي", "settingChat.enableCompressHistory.title": "تمكين التلخيص التلقائي لسجل الدردشة", diff --git a/locales/bg-BG/setting.json b/locales/bg-BG/setting.json index e04c1602e4..a9a752ed84 100644 --- a/locales/bg-BG/setting.json +++ b/locales/bg-BG/setting.json @@ -180,10 +180,8 @@ "agentSkillModal.url.title": "Импортиране от URL", "agentSkillModal.url.urlPlaceholder": "https://example.com/път/до/SKILL.md", "agentSkillTag": "Умение на агент", - "agentTab.chat": "Предпочитания за чат", "agentTab.documents": "Документи", "agentTab.meta": "Информация за агента", - "agentTab.modal": "Настройки на модела", "agentTab.opening": "Начални настройки", "agentTab.plugin": "Настройки на уменията", "agentTab.prompt": "Профил на агента", @@ -531,15 +529,11 @@ "settingAppearance.primaryColor.desc": "Персонализиран основен цвят на темата", "settingAppearance.primaryColor.title": "Цвят на темата", "settingAppearance.title": "Външен вид на приложението", - "settingChat.autoCreateTopicThreshold.desc": "Автоматично създаване на тема, когато броят на съобщенията надвиши тази стойност", - "settingChat.autoCreateTopicThreshold.title": "Праг за съобщения", "settingChat.chatStyleType.title": "Стил на чат прозореца", "settingChat.chatStyleType.type.chat": "Режим на разговор", "settingChat.chatStyleType.type.docs": "Режим на страница", "settingChat.compressThreshold.desc": "Когато историята на съобщенията надвиши тази стойност, ще се приложи компресия", "settingChat.compressThreshold.title": "Праг за компресия на историята", - "settingChat.enableAutoCreateTopic.desc": "Автоматично създаване на тема по време на разговор (валидно само за временни теми)", - "settingChat.enableAutoCreateTopic.title": "Автоматично създаване на тема", "settingChat.enableAutoScrollOnStreaming.desc": "Замени глобалната настройка за този асистент", "settingChat.enableAutoScrollOnStreaming.title": "Автоматично превъртане по време на отговор от ИИ", "settingChat.enableCompressHistory.title": "Активирай автоматично обобщаване на историята", diff --git a/locales/de-DE/setting.json b/locales/de-DE/setting.json index 446d86406b..80d6c3dc25 100644 --- a/locales/de-DE/setting.json +++ b/locales/de-DE/setting.json @@ -180,10 +180,8 @@ "agentSkillModal.url.title": "Aus URL importieren", "agentSkillModal.url.urlPlaceholder": "https://example.com/path/to/SKILL.md", "agentSkillTag": "Agenten-Skill", - "agentTab.chat": "Chat-Einstellungen", "agentTab.documents": "Dokumente", "agentTab.meta": "Agenteninformationen", - "agentTab.modal": "Modelleinstellungen", "agentTab.opening": "Startnachricht", "agentTab.plugin": "Fähigkeitseinstellungen", "agentTab.prompt": "Agentenprofil", @@ -531,15 +529,11 @@ "settingAppearance.primaryColor.desc": "Benutzerdefinierte Themenfarbe", "settingAppearance.primaryColor.title": "Themenfarbe", "settingAppearance.title": "Anwendungsdarstellung", - "settingChat.autoCreateTopicThreshold.desc": "Ein Thema wird automatisch erstellt, wenn die aktuelle Nachrichtenanzahl diesen Wert überschreitet", - "settingChat.autoCreateTopicThreshold.title": "Nachrichtenschwelle", "settingChat.chatStyleType.title": "Chatfenster-Stil", "settingChat.chatStyleType.type.chat": "Konversationsmodus", "settingChat.chatStyleType.type.docs": "Seitenmodus", "settingChat.compressThreshold.desc": "Wenn die unkomprimierte Nachrichtenhistorie diesen Wert überschreitet, wird eine Komprimierung angewendet", "settingChat.compressThreshold.title": "Komprimierungsschwelle für Nachrichtenhistorie", - "settingChat.enableAutoCreateTopic.desc": "Ob während des Gesprächs automatisch ein Thema erstellt wird, nur bei temporären Themen wirksam", - "settingChat.enableAutoCreateTopic.title": "Automatische Themaerstellung", "settingChat.enableAutoScrollOnStreaming.desc": "Globale Einstellung für diesen Assistenten überschreiben", "settingChat.enableAutoScrollOnStreaming.title": "Automatisches Scrollen während der KI-Antwort", "settingChat.enableCompressHistory.title": "Automatische Zusammenfassung der Chat-Historie aktivieren", diff --git a/locales/en-US/setting.json b/locales/en-US/setting.json index 62152cec42..5aa1ef6c43 100644 --- a/locales/en-US/setting.json +++ b/locales/en-US/setting.json @@ -180,8 +180,6 @@ "agentSkillModal.url.title": "Import from URL", "agentSkillModal.url.urlPlaceholder": "https://example.com/path/to/SKILL.md", "agentSkillTag": "Agent Skill", - "agentTab.chat": "Chat Preferences", - "agentTab.modal": "Model Settings", "agentTab.opening": "Opening Settings", "agentTab.plugin": "Skill Settings", "agentTab.prompt": "Agent Profile", @@ -529,15 +527,11 @@ "settingAppearance.primaryColor.desc": "Custom theme color", "settingAppearance.primaryColor.title": "Theme Color", "settingAppearance.title": "Application Appearance", - "settingChat.autoCreateTopicThreshold.desc": "Automatically create a topic when the current message count exceeds this value", - "settingChat.autoCreateTopicThreshold.title": "Message Threshold", "settingChat.chatStyleType.title": "Chat Window Style", "settingChat.chatStyleType.type.chat": "Conversation Mode", "settingChat.chatStyleType.type.docs": "Page Mode", "settingChat.compressThreshold.desc": "When the uncompressed history messages exceed this value, compression will be applied", "settingChat.compressThreshold.title": "History Message Length Compression Threshold", - "settingChat.enableAutoCreateTopic.desc": "Whether to automatically create a topic during the conversation, only effective in temporary topics", - "settingChat.enableAutoCreateTopic.title": "Auto Create Topic", "settingChat.enableAutoScrollOnStreaming.desc": "Override global setting for this assistant", "settingChat.enableAutoScrollOnStreaming.title": "Auto-scroll During AI Response", "settingChat.enableCompressHistory.title": "Enable Automatic Summary of Chat History", diff --git a/locales/es-ES/setting.json b/locales/es-ES/setting.json index 620ab63579..e295b00d04 100644 --- a/locales/es-ES/setting.json +++ b/locales/es-ES/setting.json @@ -180,10 +180,8 @@ "agentSkillModal.url.title": "Importar desde URL", "agentSkillModal.url.urlPlaceholder": "https://example.com/path/to/SKILL.md", "agentSkillTag": "Habilidad del Agente", - "agentTab.chat": "Preferencias de Chat", "agentTab.documents": "Documentos", "agentTab.meta": "Información del Agente", - "agentTab.modal": "Configuración del Modelo", "agentTab.opening": "Configuración de Inicio", "agentTab.plugin": "Configuración de Habilidades", "agentTab.prompt": "Perfil del Agente", @@ -531,15 +529,11 @@ "settingAppearance.primaryColor.desc": "Color de tema personalizado", "settingAppearance.primaryColor.title": "Color del Tema", "settingAppearance.title": "Apariencia de la Aplicación", - "settingChat.autoCreateTopicThreshold.desc": "Crea automáticamente un tema cuando el número de mensajes actuales exceda este valor", - "settingChat.autoCreateTopicThreshold.title": "Límite de Mensajes", "settingChat.chatStyleType.title": "Estilo de Ventana de Chat", "settingChat.chatStyleType.type.chat": "Modo Conversación", "settingChat.chatStyleType.type.docs": "Modo Página", "settingChat.compressThreshold.desc": "Cuando los mensajes históricos sin comprimir excedan este valor, se aplicará compresión", "settingChat.compressThreshold.title": "Límite de Compresión de Mensajes Históricos", - "settingChat.enableAutoCreateTopic.desc": "Si se debe crear automáticamente un tema durante la conversación, solo efectivo en temas temporales", - "settingChat.enableAutoCreateTopic.title": "Crear Tema Automáticamente", "settingChat.enableAutoScrollOnStreaming.desc": "Anular la configuración global para este asistente", "settingChat.enableAutoScrollOnStreaming.title": "Desplazamiento automático durante la respuesta de la IA", "settingChat.enableCompressHistory.title": "Activar Resumen Automático del Historial de Chat", diff --git a/locales/fa-IR/setting.json b/locales/fa-IR/setting.json index 588d51ec68..7c6e752b30 100644 --- a/locales/fa-IR/setting.json +++ b/locales/fa-IR/setting.json @@ -180,10 +180,8 @@ "agentSkillModal.url.title": "وارد کردن از URL", "agentSkillModal.url.urlPlaceholder": "https://example.com/path/to/SKILL.md", "agentSkillTag": "مهارت عامل", - "agentTab.chat": "تنظیمات گفتگو", "agentTab.documents": "اسناد", "agentTab.meta": "اطلاعات عامل", - "agentTab.modal": "تنظیمات مدل", "agentTab.opening": "تنظیمات آغازین", "agentTab.plugin": "تنظیمات مهارت", "agentTab.prompt": "پروفایل عامل", @@ -531,15 +529,11 @@ "settingAppearance.primaryColor.desc": "رنگ تم سفارشی", "settingAppearance.primaryColor.title": "رنگ تم", "settingAppearance.title": "ظاهر برنامه", - "settingChat.autoCreateTopicThreshold.desc": "هنگامی که تعداد پیام‌ها از این مقدار بیشتر شود، موضوع جدیدی به‌طور خودکار ایجاد می‌شود", - "settingChat.autoCreateTopicThreshold.title": "آستانه تعداد پیام", "settingChat.chatStyleType.title": "سبک پنجره گفتگو", "settingChat.chatStyleType.type.chat": "حالت مکالمه", "settingChat.chatStyleType.type.docs": "حالت صفحه", "settingChat.compressThreshold.desc": "وقتی تعداد پیام‌های تاریخچه فشرده‌نشده از این مقدار بیشتر شود، فشرده‌سازی اعمال می‌شود", "settingChat.compressThreshold.title": "آستانه فشرده‌سازی پیام‌های تاریخچه", - "settingChat.enableAutoCreateTopic.desc": "آیا در طول مکالمه موضوع به‌طور خودکار ایجاد شود، فقط در موضوعات موقت مؤثر است", - "settingChat.enableAutoCreateTopic.title": "ایجاد خودکار موضوع", "settingChat.enableAutoScrollOnStreaming.desc": "نادیده گرفتن تنظیمات کلی برای این دستیار", "settingChat.enableAutoScrollOnStreaming.title": "پیمایش خودکار هنگام پاسخ‌دهی هوش مصنوعی", "settingChat.enableCompressHistory.title": "فعال‌سازی خلاصه‌سازی خودکار تاریخچه گفتگو", diff --git a/locales/fr-FR/setting.json b/locales/fr-FR/setting.json index c420a3e6a9..549878200c 100644 --- a/locales/fr-FR/setting.json +++ b/locales/fr-FR/setting.json @@ -180,10 +180,8 @@ "agentSkillModal.url.title": "Importer depuis une URL", "agentSkillModal.url.urlPlaceholder": "https://exemple.com/chemin/vers/SKILL.md", "agentSkillTag": "Compétence d'agent", - "agentTab.chat": "Préférences de discussion", "agentTab.documents": "Documents", "agentTab.meta": "Informations de l'agent", - "agentTab.modal": "Paramètres du modèle", "agentTab.opening": "Paramètres d'ouverture", "agentTab.plugin": "Paramètres des compétences", "agentTab.prompt": "Profil de l'agent", @@ -531,15 +529,11 @@ "settingAppearance.primaryColor.desc": "Couleur de thème personnalisée", "settingAppearance.primaryColor.title": "Couleur du thème", "settingAppearance.title": "Apparence de l’application", - "settingChat.autoCreateTopicThreshold.desc": "Créer automatiquement un sujet lorsque le nombre de messages dépasse cette valeur", - "settingChat.autoCreateTopicThreshold.title": "Seuil de messages", "settingChat.chatStyleType.title": "Style de la fenêtre de chat", "settingChat.chatStyleType.type.chat": "Mode conversation", "settingChat.chatStyleType.type.docs": "Mode page", "settingChat.compressThreshold.desc": "Lorsque le nombre de messages non compressés dépasse cette valeur, une compression est appliquée", "settingChat.compressThreshold.title": "Seuil de compression de l’historique", - "settingChat.enableAutoCreateTopic.desc": "Créer automatiquement un sujet pendant la conversation (valable uniquement pour les sujets temporaires)", - "settingChat.enableAutoCreateTopic.title": "Création automatique de sujet", "settingChat.enableAutoScrollOnStreaming.desc": "Remplacer le paramètre global pour cet assistant", "settingChat.enableAutoScrollOnStreaming.title": "Défilement automatique pendant la réponse de l'IA", "settingChat.enableCompressHistory.title": "Activer le résumé automatique de l’historique", diff --git a/locales/it-IT/setting.json b/locales/it-IT/setting.json index 861c3886ae..8efe3d550d 100644 --- a/locales/it-IT/setting.json +++ b/locales/it-IT/setting.json @@ -180,10 +180,8 @@ "agentSkillModal.url.title": "Importa da URL", "agentSkillModal.url.urlPlaceholder": "https://example.com/path/to/SKILL.md", "agentSkillTag": "Skill dell'Agente", - "agentTab.chat": "Preferenze Chat", "agentTab.documents": "Documenti", "agentTab.meta": "Informazioni Agente", - "agentTab.modal": "Impostazioni Modello", "agentTab.opening": "Impostazioni Iniziali", "agentTab.plugin": "Impostazioni Abilità", "agentTab.prompt": "Profilo Agente", @@ -531,15 +529,11 @@ "settingAppearance.primaryColor.desc": "Colore tema personalizzato", "settingAppearance.primaryColor.title": "Colore Tema", "settingAppearance.title": "Aspetto Applicazione", - "settingChat.autoCreateTopicThreshold.desc": "Crea automaticamente un argomento quando il numero di messaggi supera questo valore", - "settingChat.autoCreateTopicThreshold.title": "Soglia Messaggi", "settingChat.chatStyleType.title": "Stile Finestra Chat", "settingChat.chatStyleType.type.chat": "Modalità Conversazione", "settingChat.chatStyleType.type.docs": "Modalità Pagina", "settingChat.compressThreshold.desc": "Quando i messaggi della cronologia non compressi superano questo valore, verrà applicata la compressione", "settingChat.compressThreshold.title": "Soglia di Compressione Cronologia", - "settingChat.enableAutoCreateTopic.desc": "Se abilitato, crea automaticamente un argomento durante la conversazione. Valido solo per argomenti temporanei", - "settingChat.enableAutoCreateTopic.title": "Creazione Automatica Argomento", "settingChat.enableAutoScrollOnStreaming.desc": "Ignora l'impostazione globale per questo assistente", "settingChat.enableAutoScrollOnStreaming.title": "Scorrimento automatico durante la risposta dell'IA", "settingChat.enableCompressHistory.title": "Abilita Riepilogo Automatico Cronologia", diff --git a/locales/ja-JP/setting.json b/locales/ja-JP/setting.json index a887fc1b7e..a781c5fcfd 100644 --- a/locales/ja-JP/setting.json +++ b/locales/ja-JP/setting.json @@ -180,10 +180,8 @@ "agentSkillModal.url.title": "URLからインポート", "agentSkillModal.url.urlPlaceholder": "https://example.com/path/to/SKILL.md", "agentSkillTag": "エージェントスキル", - "agentTab.chat": "チャット設定", "agentTab.documents": "ドキュメント", "agentTab.meta": "メタ情報", - "agentTab.modal": "モーダル設定", "agentTab.opening": "オープニング設定", "agentTab.plugin": "スキル設定", "agentTab.prompt": "プロンプト設定", @@ -531,15 +529,11 @@ "settingAppearance.primaryColor.desc": "テーマカラーのカスタマイズ", "settingAppearance.primaryColor.title": "テーマカラー", "settingAppearance.title": "アプリの外観", - "settingChat.autoCreateTopicThreshold.desc": "現在のメッセージ数がこの値を超えると、トピックが自動的に作成されます", - "settingChat.autoCreateTopicThreshold.title": "メッセージ閾値", "settingChat.chatStyleType.title": "チャットウィンドウのスタイル", "settingChat.chatStyleType.type.chat": "チャットモード", "settingChat.chatStyleType.type.docs": "ドキュメントモード", "settingChat.compressThreshold.desc": "圧縮されていない過去のメッセージがこの値を超えると、圧縮されます", "settingChat.compressThreshold.title": "過去メッセージの長さの圧縮閾値", - "settingChat.enableAutoCreateTopic.desc": "会話中に自動的にトピックを作成するかどうか。一時的なトピックのみ有効です", - "settingChat.enableAutoCreateTopic.title": "自動的にトピックを作成する", "settingChat.enableAutoScrollOnStreaming.desc": "このアシスタントに対してグローバル設定を上書きします", "settingChat.enableAutoScrollOnStreaming.title": "AI応答中の自動スクロール", "settingChat.enableCompressHistory.title": "履歴メッセージの自動要約を有効にする", diff --git a/locales/ko-KR/setting.json b/locales/ko-KR/setting.json index 4f87199bf5..7e3e696053 100644 --- a/locales/ko-KR/setting.json +++ b/locales/ko-KR/setting.json @@ -180,10 +180,8 @@ "agentSkillModal.url.title": "URL에서 가져오기", "agentSkillModal.url.urlPlaceholder": "https://example.com/path/to/SKILL.md", "agentSkillTag": "에이전트 스킬", - "agentTab.chat": "채팅 환경설정", "agentTab.documents": "문서", "agentTab.meta": "도우미 정보", - "agentTab.modal": "모델 설정", "agentTab.opening": "시작 설정", "agentTab.plugin": "기능 설정", "agentTab.prompt": "역할 프롬프트", @@ -531,15 +529,11 @@ "settingAppearance.primaryColor.desc": "테마 색상 사용자 정의", "settingAppearance.primaryColor.title": "테마 색상", "settingAppearance.title": "응용 프로그램 외관", - "settingChat.autoCreateTopicThreshold.desc": "현재 메시지 수가이 값 이상이면 자동으로 주제가 생성됩니다", - "settingChat.autoCreateTopicThreshold.title": "메시지 임계값", "settingChat.chatStyleType.title": "채팅 창 스타일", "settingChat.chatStyleType.type.chat": "대화 모드", "settingChat.chatStyleType.type.docs": "문서 모드", "settingChat.compressThreshold.desc": "압축되지 않은 이전 메시지가이 값 이상이면 압축됩니다", "settingChat.compressThreshold.title": "이전 메시지 길이 압축 임계값", - "settingChat.enableAutoCreateTopic.desc": "대화 중에 자동으로 주제를 만들지 여부를 설정합니다. 일시적인 주제에서만 작동합니다", - "settingChat.enableAutoCreateTopic.title": "자동 주제 생성 활성화", "settingChat.enableAutoScrollOnStreaming.desc": "이 어시스턴트에 대한 전역 설정을 재정의합니다", "settingChat.enableAutoScrollOnStreaming.title": "AI 응답 중 자동 스크롤", "settingChat.enableCompressHistory.title": "역사 메시지 자동 요약 활성화", diff --git a/locales/nl-NL/setting.json b/locales/nl-NL/setting.json index 796cd2da55..68127a18ce 100644 --- a/locales/nl-NL/setting.json +++ b/locales/nl-NL/setting.json @@ -180,10 +180,8 @@ "agentSkillModal.url.title": "Importeren via URL", "agentSkillModal.url.urlPlaceholder": "https://voorbeeld.com/pad/naar/SKILL.md", "agentSkillTag": "Agentvaardigheid", - "agentTab.chat": "Chatvoorkeuren", "agentTab.documents": "Documenten", "agentTab.meta": "Agentinformatie", - "agentTab.modal": "Modelinstellingen", "agentTab.opening": "Startinstellingen", "agentTab.plugin": "Vaardigheidsinstellingen", "agentTab.prompt": "Agentprofiel", @@ -531,15 +529,11 @@ "settingAppearance.primaryColor.desc": "Aangepaste themakleur", "settingAppearance.primaryColor.title": "Themakleur", "settingAppearance.title": "Uiterlijk van de applicatie", - "settingChat.autoCreateTopicThreshold.desc": "Maak automatisch een onderwerp aan wanneer het aantal berichten deze waarde overschrijdt", - "settingChat.autoCreateTopicThreshold.title": "Berichtendrempel", "settingChat.chatStyleType.title": "Stijl van chatvenster", "settingChat.chatStyleType.type.chat": "Gespreksmodus", "settingChat.chatStyleType.type.docs": "Paginamodus", "settingChat.compressThreshold.desc": "Wanneer het aantal niet-gecomprimeerde berichten deze waarde overschrijdt, wordt compressie toegepast", "settingChat.compressThreshold.title": "Drempel voor compressie van berichtgeschiedenis", - "settingChat.enableAutoCreateTopic.desc": "Of er automatisch een onderwerp wordt aangemaakt tijdens het gesprek, alleen van toepassing op tijdelijke onderwerpen", - "settingChat.enableAutoCreateTopic.title": "Automatisch onderwerp aanmaken", "settingChat.enableAutoScrollOnStreaming.desc": "Overschrijf de globale instelling voor deze assistent", "settingChat.enableAutoScrollOnStreaming.title": "Automatisch scrollen tijdens AI-reactie", "settingChat.enableCompressHistory.title": "Automatische samenvatting van chatgeschiedenis inschakelen", diff --git a/locales/pl-PL/setting.json b/locales/pl-PL/setting.json index c79e194450..f6f2897fd7 100644 --- a/locales/pl-PL/setting.json +++ b/locales/pl-PL/setting.json @@ -180,10 +180,8 @@ "agentSkillModal.url.title": "Importuj z URL", "agentSkillModal.url.urlPlaceholder": "https://example.com/sciezka/do/SKILL.md", "agentSkillTag": "Umiejętność agenta", - "agentTab.chat": "Preferencje czatu", "agentTab.documents": "Dokumenty", "agentTab.meta": "Informacje o agencie", - "agentTab.modal": "Ustawienia modelu", "agentTab.opening": "Ustawienia rozpoczęcia", "agentTab.plugin": "Ustawienia umiejętności", "agentTab.prompt": "Profil agenta", @@ -531,15 +529,11 @@ "settingAppearance.primaryColor.desc": "Dostosuj kolor motywu", "settingAppearance.primaryColor.title": "Kolor motywu", "settingAppearance.title": "Wygląd aplikacji", - "settingChat.autoCreateTopicThreshold.desc": "Automatycznie utwórz temat, gdy liczba wiadomości przekroczy tę wartość", - "settingChat.autoCreateTopicThreshold.title": "Próg wiadomości", "settingChat.chatStyleType.title": "Styl okna czatu", "settingChat.chatStyleType.type.chat": "Tryb konwersacji", "settingChat.chatStyleType.type.docs": "Tryb strony", "settingChat.compressThreshold.desc": "Gdy liczba niekompresowanych wiadomości przekroczy tę wartość, zostanie zastosowana kompresja", "settingChat.compressThreshold.title": "Próg kompresji historii wiadomości", - "settingChat.enableAutoCreateTopic.desc": "Czy automatycznie tworzyć temat podczas rozmowy (dotyczy tylko tematów tymczasowych)", - "settingChat.enableAutoCreateTopic.title": "Automatyczne tworzenie tematu", "settingChat.enableAutoScrollOnStreaming.desc": "Zastąp ustawienie globalne dla tego asystenta", "settingChat.enableAutoScrollOnStreaming.title": "Auto-przewijanie podczas odpowiedzi AI", "settingChat.enableCompressHistory.title": "Włącz automatyczne podsumowanie historii czatu", diff --git a/locales/pt-BR/setting.json b/locales/pt-BR/setting.json index c9f548f36a..158cb7fb27 100644 --- a/locales/pt-BR/setting.json +++ b/locales/pt-BR/setting.json @@ -180,10 +180,8 @@ "agentSkillModal.url.title": "Importar de URL", "agentSkillModal.url.urlPlaceholder": "https://exemplo.com/caminho/para/SKILL.md", "agentSkillTag": "Habilidade do Agente", - "agentTab.chat": "Preferências de Conversa", "agentTab.documents": "Documentos", "agentTab.meta": "Informações do Agente", - "agentTab.modal": "Configurações do Modelo", "agentTab.opening": "Configurações de Abertura", "agentTab.plugin": "Configurações de Habilidades", "agentTab.prompt": "Perfil do Agente", @@ -531,15 +529,11 @@ "settingAppearance.primaryColor.desc": "Cor do tema personalizada", "settingAppearance.primaryColor.title": "Cor do Tema", "settingAppearance.title": "Aparência do Aplicativo", - "settingChat.autoCreateTopicThreshold.desc": "Cria automaticamente um tópico quando o número atual de mensagens exceder este valor", - "settingChat.autoCreateTopicThreshold.title": "Limite de Mensagens", "settingChat.chatStyleType.title": "Estilo da Janela de Chat", "settingChat.chatStyleType.type.chat": "Modo Conversa", "settingChat.chatStyleType.type.docs": "Modo Página", "settingChat.compressThreshold.desc": "Quando o histórico de mensagens não compactado exceder este valor, a compactação será aplicada", "settingChat.compressThreshold.title": "Limite de Compactação do Histórico", - "settingChat.enableAutoCreateTopic.desc": "Define se um tópico será criado automaticamente durante a conversa, válido apenas para tópicos temporários", - "settingChat.enableAutoCreateTopic.title": "Criar Tópico Automaticamente", "settingChat.enableAutoScrollOnStreaming.desc": "Substituir a configuração global para este assistente", "settingChat.enableAutoScrollOnStreaming.title": "Rolagem automática durante resposta da IA", "settingChat.enableCompressHistory.title": "Ativar Resumo Automático do Histórico", diff --git a/locales/ru-RU/setting.json b/locales/ru-RU/setting.json index a2df860e9e..e7c4ce4dfb 100644 --- a/locales/ru-RU/setting.json +++ b/locales/ru-RU/setting.json @@ -180,10 +180,8 @@ "agentSkillModal.url.title": "Импорт из URL", "agentSkillModal.url.urlPlaceholder": "https://example.com/path/to/SKILL.md", "agentSkillTag": "Навык агента", - "agentTab.chat": "Настройки чата", "agentTab.documents": "Документы", "agentTab.meta": "Информация об агенте", - "agentTab.modal": "Настройки модели", "agentTab.opening": "Начальные настройки", "agentTab.plugin": "Настройки навыков", "agentTab.prompt": "Профиль агента", @@ -531,15 +529,11 @@ "settingAppearance.primaryColor.desc": "Пользовательский цвет темы", "settingAppearance.primaryColor.title": "Цвет темы", "settingAppearance.title": "Внешний вид приложения", - "settingChat.autoCreateTopicThreshold.desc": "Автоматически создавать тему, когда количество сообщений превышает это значение", - "settingChat.autoCreateTopicThreshold.title": "Порог сообщений", "settingChat.chatStyleType.title": "Стиль окна чата", "settingChat.chatStyleType.type.chat": "Режим диалога", "settingChat.chatStyleType.type.docs": "Режим страницы", "settingChat.compressThreshold.desc": "Если количество несжатых сообщений превышает это значение, будет применено сжатие", "settingChat.compressThreshold.title": "Порог сжатия истории сообщений", - "settingChat.enableAutoCreateTopic.desc": "Автоматически создавать тему во время беседы (только для временных тем)", - "settingChat.enableAutoCreateTopic.title": "Автосоздание темы", "settingChat.enableAutoScrollOnStreaming.desc": "Переопределить глобальную настройку для этого ассистента", "settingChat.enableAutoScrollOnStreaming.title": "Автопрокрутка во время ответа ИИ", "settingChat.enableCompressHistory.title": "Включить автоматическое резюмирование истории чата", diff --git a/locales/tr-TR/setting.json b/locales/tr-TR/setting.json index 3c2fa190bf..e0f13c17c9 100644 --- a/locales/tr-TR/setting.json +++ b/locales/tr-TR/setting.json @@ -180,10 +180,8 @@ "agentSkillModal.url.title": "URL'den İçe Aktar", "agentSkillModal.url.urlPlaceholder": "https://example.com/path/to/SKILL.md", "agentSkillTag": "Ajan Beceri", - "agentTab.chat": "Sohbet Tercihleri", "agentTab.documents": "Belgeler", "agentTab.meta": "Temsilci Bilgisi", - "agentTab.modal": "Model Ayarları", "agentTab.opening": "Açılış Ayarları", "agentTab.plugin": "Yetenek Ayarları", "agentTab.prompt": "Temsilci Profili", @@ -531,15 +529,11 @@ "settingAppearance.primaryColor.desc": "Özel tema rengi", "settingAppearance.primaryColor.title": "Tema Rengi", "settingAppearance.title": "Uygulama Görünümü", - "settingChat.autoCreateTopicThreshold.desc": "Mevcut mesaj sayısı bu değeri aştığında otomatik olarak konu oluştur", - "settingChat.autoCreateTopicThreshold.title": "Mesaj Eşiği", "settingChat.chatStyleType.title": "Sohbet Penceresi Stili", "settingChat.chatStyleType.type.chat": "Konuşma Modu", "settingChat.chatStyleType.type.docs": "Sayfa Modu", "settingChat.compressThreshold.desc": "Sıkıştırılmamış geçmiş mesajlar bu değeri aşarsa sıkıştırma uygulanır", "settingChat.compressThreshold.title": "Geçmiş Mesaj Uzunluğu Sıkıştırma Eşiği", - "settingChat.enableAutoCreateTopic.desc": "Sohbet sırasında otomatik konu oluşturulsun mu, yalnızca geçici konularda geçerlidir", - "settingChat.enableAutoCreateTopic.title": "Otomatik Konu Oluştur", "settingChat.enableAutoScrollOnStreaming.desc": "Bu asistan için genel ayarı geçersiz kıl", "settingChat.enableAutoScrollOnStreaming.title": "Yapay Zeka Yanıtı Sırasında Otomatik Kaydırma", "settingChat.enableCompressHistory.title": "Sohbet Geçmişini Otomatik Özetle", diff --git a/locales/vi-VN/setting.json b/locales/vi-VN/setting.json index 0542d1c8c4..72d62f82f6 100644 --- a/locales/vi-VN/setting.json +++ b/locales/vi-VN/setting.json @@ -180,10 +180,8 @@ "agentSkillModal.url.title": "Nhập từ URL", "agentSkillModal.url.urlPlaceholder": "https://example.com/path/to/SKILL.md", "agentSkillTag": "Kỹ năng Đại lý", - "agentTab.chat": "Tùy chọn trò chuyện", "agentTab.documents": "Tài liệu", "agentTab.meta": "Thông tin Agent", - "agentTab.modal": "Cài đặt Mô hình", "agentTab.opening": "Cài đặt Mở đầu", "agentTab.plugin": "Cài đặt Kỹ năng", "agentTab.prompt": "Hồ sơ Agent", @@ -531,15 +529,11 @@ "settingAppearance.primaryColor.desc": "Tùy chỉnh màu chủ đề", "settingAppearance.primaryColor.title": "Màu chủ đề", "settingAppearance.title": "Giao diện ứng dụng", - "settingChat.autoCreateTopicThreshold.desc": "Tự động tạo chủ đề khi số lượng tin nhắn hiện tại vượt quá giá trị này", - "settingChat.autoCreateTopicThreshold.title": "Ngưỡng tin nhắn", "settingChat.chatStyleType.title": "Kiểu cửa sổ trò chuyện", "settingChat.chatStyleType.type.chat": "Chế độ hội thoại", "settingChat.chatStyleType.type.docs": "Chế độ trang", "settingChat.compressThreshold.desc": "Khi số lượng tin nhắn chưa nén vượt quá giá trị này, sẽ tiến hành nén", "settingChat.compressThreshold.title": "Ngưỡng độ dài tin nhắn lịch sử để nén", - "settingChat.enableAutoCreateTopic.desc": "Tự động tạo chủ đề trong cuộc trò chuyện, chỉ áp dụng cho chủ đề tạm thời", - "settingChat.enableAutoCreateTopic.title": "Tự động tạo chủ đề", "settingChat.enableAutoScrollOnStreaming.desc": "Ghi đè cài đặt toàn cục cho trợ lý này", "settingChat.enableAutoScrollOnStreaming.title": "Tự động cuộn khi AI phản hồi", "settingChat.enableCompressHistory.title": "Bật tóm tắt lịch sử trò chuyện tự động", diff --git a/locales/zh-CN/setting.json b/locales/zh-CN/setting.json index dd4ed96538..9bca81e73f 100644 --- a/locales/zh-CN/setting.json +++ b/locales/zh-CN/setting.json @@ -180,8 +180,6 @@ "agentSkillModal.url.title": "从 URL 导入", "agentSkillModal.url.urlPlaceholder": "https://example.com/path/to/SKILL.md", "agentSkillTag": "Agent 技能", - "agentTab.chat": "对话偏好", - "agentTab.modal": "模型设置", "agentTab.opening": "开场设置", "agentTab.plugin": "技能设置", "agentTab.prompt": "助理档案", @@ -529,15 +527,11 @@ "settingAppearance.primaryColor.desc": "自定义主题色", "settingAppearance.primaryColor.title": "主题色", "settingAppearance.title": "应用外观", - "settingChat.autoCreateTopicThreshold.desc": "当前消息数超过设定该值后,将自动创建话题", - "settingChat.autoCreateTopicThreshold.title": "消息阈值", "settingChat.chatStyleType.title": "聊天窗口样式", "settingChat.chatStyleType.type.chat": "对话模式", "settingChat.chatStyleType.type.docs": "文档模式", "settingChat.compressThreshold.desc": "当未压缩的历史消息超过该值时,将进行压缩", "settingChat.compressThreshold.title": "历史消息长度压缩阈值", - "settingChat.enableAutoCreateTopic.desc": "会话过程中是否自动创建话题,仅在临时话题中生效", - "settingChat.enableAutoCreateTopic.title": "自动创建话题", "settingChat.enableAutoScrollOnStreaming.desc": "覆盖此助手的全局设置", "settingChat.enableAutoScrollOnStreaming.title": "AI 回复时自动滚动", "settingChat.enableCompressHistory.title": "开启历史消息自动总结", diff --git a/locales/zh-TW/setting.json b/locales/zh-TW/setting.json index c09a5e6a7b..055a995a7c 100644 --- a/locales/zh-TW/setting.json +++ b/locales/zh-TW/setting.json @@ -180,10 +180,8 @@ "agentSkillModal.url.title": "從 URL 匯入", "agentSkillModal.url.urlPlaceholder": "https://example.com/path/to/SKILL.md", "agentSkillTag": "代理技能", - "agentTab.chat": "聊天偏好", "agentTab.documents": "文件", "agentTab.meta": "助手資訊", - "agentTab.modal": "模型設定", "agentTab.opening": "開場設定", "agentTab.plugin": "外掛程式設定", "agentTab.prompt": "角色設定", @@ -531,15 +529,11 @@ "settingAppearance.primaryColor.desc": "自訂主題色", "settingAppearance.primaryColor.title": "主題色", "settingAppearance.title": "應用外觀", - "settingChat.autoCreateTopicThreshold.desc": "當前訊息數超過設定值後,將自動建立話題", - "settingChat.autoCreateTopicThreshold.title": "訊息閾值", "settingChat.chatStyleType.title": "聊天視窗樣式", "settingChat.chatStyleType.type.chat": "對話模式", "settingChat.chatStyleType.type.docs": "文件模式", "settingChat.compressThreshold.desc": "當未壓縮的歷史訊息超過該值時,將進行壓縮", "settingChat.compressThreshold.title": "歷史訊息長度壓縮閾值", - "settingChat.enableAutoCreateTopic.desc": "對話過程中是否自動建立話題,僅在臨時話題中生效", - "settingChat.enableAutoCreateTopic.title": "自動建立話題", "settingChat.enableAutoScrollOnStreaming.desc": "覆蓋此助理的全域設定", "settingChat.enableAutoScrollOnStreaming.title": "AI 回應時自動捲動", "settingChat.enableCompressHistory.title": "開啟歷史消息自動總結", diff --git a/packages/builtin-agents/src/agents/agent-builder/systemRole.ts b/packages/builtin-agents/src/agents/agent-builder/systemRole.ts index 4980cf2896..cf783adcae 100644 --- a/packages/builtin-agents/src/agents/agent-builder/systemRole.ts +++ b/packages/builtin-agents/src/agents/agent-builder/systemRole.ts @@ -51,8 +51,6 @@ You have access to tools that can read and modify agent configurations: **Chat Configuration (chatConfig):** - historyCount: Number of previous messages to include in context (default: 20). Higher values provide more context but increase token usage. - enableHistoryCount: Whether to limit history (default: true) -- enableAutoCreateTopic: Automatically create topics based on conversation (default: true) -- autoCreateTopicThreshold: Messages before auto-creating topic (default: 2) - enableCompressHistory: Compress long conversation history to save tokens (default: true) - enableStreaming: Stream responses in real-time (default: true) - enableReasoning: Enable reasoning/thinking mode for supported models (default: false) diff --git a/packages/builtin-tool-agent-builder/src/manifest.ts b/packages/builtin-tool-agent-builder/src/manifest.ts index e1715a60a7..26b7f3e27b 100644 --- a/packages/builtin-tool-agent-builder/src/manifest.ts +++ b/packages/builtin-tool-agent-builder/src/manifest.ts @@ -87,7 +87,7 @@ export const AgentBuilderManifest: BuiltinToolManifest = { properties: { chatConfig: { description: - 'Chat configuration settings (historyCount, enableHistoryCount, enableAutoCreateTopic, autoCreateTopicThreshold, enableCompressHistory, enableStreaming, enableReasoning)', + 'Chat configuration settings (historyCount, enableHistoryCount, enableCompressHistory, enableStreaming, enableReasoning)', type: 'object', }, model: { diff --git a/packages/builtin-tool-agent-builder/src/systemRole.ts b/packages/builtin-tool-agent-builder/src/systemRole.ts index 5ddb63fdd3..f50b44fbd4 100644 --- a/packages/builtin-tool-agent-builder/src/systemRole.ts +++ b/packages/builtin-tool-agent-builder/src/systemRole.ts @@ -73,7 +73,6 @@ When showing configuration to users, use semantic, user-friendly names instead o | top_p | Sampling Range | 采样范围 | | frequency_penalty | Reduce Repetition | 减少重复 | | presence_penalty | Topic Diversity | 话题多样性 | -| autoCreateTopicThreshold | Auto-topic Threshold | 自动话题阈值 | Always adapt to user's language. Use natural descriptions, not raw field names. @@ -137,8 +136,6 @@ Always adapt to user's language. Use natural descriptions, not raw field names. **Chat Configuration (chatConfig)** - Conversation behavior settings: - historyCount: Number of previous messages to include in context (default: 20) - enableHistoryCount: Whether to limit history (default: true) -- enableAutoCreateTopic: Automatically create topics based on conversation (default: true) -- autoCreateTopicThreshold: Messages before auto-creating topic (default: 2) - enableCompressHistory: Compress long conversation history to save tokens (default: true) - enableStreaming: Stream responses in real-time (default: true) - enableReasoning: Enable reasoning/thinking mode for supported models (default: false) diff --git a/packages/builtin-tool-group-agent-builder/src/manifest.ts b/packages/builtin-tool-group-agent-builder/src/manifest.ts index bdca2d316a..725065bfb4 100644 --- a/packages/builtin-tool-group-agent-builder/src/manifest.ts +++ b/packages/builtin-tool-group-agent-builder/src/manifest.ts @@ -325,7 +325,7 @@ export const GroupAgentBuilderManifest: BuiltinToolManifest = { properties: { chatConfig: { description: - 'Chat configuration settings (historyCount, enableHistoryCount, enableAutoCreateTopic, etc.)', + 'Chat configuration settings (historyCount, enableHistoryCount, enableCompressHistory, etc.)', type: 'object', }, model: { diff --git a/packages/const/src/settings/agent.ts b/packages/const/src/settings/agent.ts index 3b6979f248..51cc5c7697 100644 --- a/packages/const/src/settings/agent.ts +++ b/packages/const/src/settings/agent.ts @@ -24,9 +24,7 @@ export const DEFAULT_AGENT_SEARCH_FC_MODEL = { }; export const DEFAULT_AGENT_CHAT_CONFIG: LobeAgentChatConfig = { - autoCreateTopicThreshold: 2, enableAgentMode: true, - enableAutoCreateTopic: true, enableCompressHistory: true, enableContextCompression: true, enableHistoryCount: false, diff --git a/packages/database/src/models/agentSignal/__tests__/nightlyReview.test.ts b/packages/database/src/models/agentSignal/__tests__/nightlyReview.test.ts index 3dba1c1ac9..fb071d2a5b 100644 --- a/packages/database/src/models/agentSignal/__tests__/nightlyReview.test.ts +++ b/packages/database/src/models/agentSignal/__tests__/nightlyReview.test.ts @@ -95,9 +95,7 @@ describe('AgentSignalNightlyReviewModel', () => { describe('listActiveAgentTargets', () => { const chatConfigForSelfIteration = (enabled?: boolean) => - enabled === undefined - ? { autoCreateTopicThreshold: 2 } - : { autoCreateTopicThreshold: 2, selfIteration: { enabled } }; + enabled === undefined ? {} : { selfIteration: { enabled } }; const seedNightlyCapabilityTargets = async (caseName: string, blockedEnabled?: boolean) => { await serverDB.insert(users).values({ id: enabledUserId }); @@ -205,38 +203,38 @@ describe('AgentSignalNightlyReviewModel', () => { .insert(agents) .values([ { - chatConfig: { autoCreateTopicThreshold: 2, selfIteration: { enabled: true } }, + chatConfig: { selfIteration: { enabled: true } }, id: 'nightly-active-agent', title: 'Active agent', userId: enabledUserId, }, { - chatConfig: { autoCreateTopicThreshold: 2, selfIteration: { enabled: true } }, + chatConfig: { selfIteration: { enabled: true } }, id: 'nightly-legacy-agent', title: 'Legacy agent', userId: enabledUserId, }, { - chatConfig: { autoCreateTopicThreshold: 2, selfIteration: { enabled: true } }, + chatConfig: { selfIteration: { enabled: true } }, id: 'nightly-inactive-agent', title: 'Inactive agent', userId: enabledUserId, }, { - chatConfig: { autoCreateTopicThreshold: 2, selfIteration: { enabled: false } }, + chatConfig: { selfIteration: { enabled: false } }, id: 'nightly-disabled-agent', title: 'Disabled agent', userId: enabledUserId, }, { - chatConfig: { autoCreateTopicThreshold: 2, selfIteration: { enabled: true } }, + chatConfig: { selfIteration: { enabled: true } }, id: 'nightly-virtual-agent', title: 'Virtual agent', userId: enabledUserId, virtual: true, }, { - chatConfig: { autoCreateTopicThreshold: 2, selfIteration: { enabled: true } }, + chatConfig: { selfIteration: { enabled: true } }, id: 'nightly-other-user-agent', title: 'Other user', userId: otherUserId, diff --git a/packages/database/src/models/agentSignal/__tests__/reviewContext.test.ts b/packages/database/src/models/agentSignal/__tests__/reviewContext.test.ts index 21ae3d8979..182af0a31d 100644 --- a/packages/database/src/models/agentSignal/__tests__/reviewContext.test.ts +++ b/packages/database/src/models/agentSignal/__tests__/reviewContext.test.ts @@ -77,14 +77,14 @@ describe('AgentSignalReviewContextModel', () => { await serverDB.insert(users).values({ id: userId }); await serverDB.insert(agents).values([ { - chatConfig: { autoCreateTopicThreshold: 2, selfIteration: { enabled: true } }, + chatConfig: { selfIteration: { enabled: true } }, id: 'agent-signal-review-context-enabled', slug: 'custom-agent', title: 'Custom enabled', userId, }, { - chatConfig: { autoCreateTopicThreshold: 2, selfIteration: { enabled: false } }, + chatConfig: { selfIteration: { enabled: false } }, id: 'agent-signal-review-context-disabled', slug: 'custom-disabled', title: 'Custom disabled', @@ -108,7 +108,7 @@ describe('AgentSignalReviewContextModel', () => { await serverDB.insert(users).values({ id: userId }); await serverDB.insert(agents).values([ { - chatConfig: { autoCreateTopicThreshold: 2, selfIteration: { enabled: true } }, + chatConfig: { selfIteration: { enabled: true } }, id: agentId, title: 'Review Context Agent', userId, @@ -210,7 +210,7 @@ describe('AgentSignalReviewContextModel', () => { await serverDB.insert(users).values({ id: userId }); await serverDB.insert(agents).values([ { - chatConfig: { autoCreateTopicThreshold: 2, selfIteration: { enabled: true } }, + chatConfig: { selfIteration: { enabled: true } }, id: agentId, title: 'Review Context Agent', userId, @@ -293,7 +293,7 @@ describe('AgentSignalReviewContextModel', () => { await serverDB.insert(users).values({ id: userId }); await serverDB.insert(agents).values([ { - chatConfig: { autoCreateTopicThreshold: 2, selfIteration: { enabled: true } }, + chatConfig: { selfIteration: { enabled: true } }, id: agentId, title: 'Review Context Agent', userId, diff --git a/packages/database/src/repositories/dataImporter/deprecated/__tests__/index.test.ts b/packages/database/src/repositories/dataImporter/deprecated/__tests__/index.test.ts index 50877fa230..d98ad8c198 100644 --- a/packages/database/src/repositories/dataImporter/deprecated/__tests__/index.test.ts +++ b/packages/database/src/repositories/dataImporter/deprecated/__tests__/index.test.ts @@ -345,7 +345,7 @@ describe('DataImporter', () => { where: eq(sessions.clientId, 'session1'), }); const session1Agent = await serverDB.query.agentsToSessions.findFirst({ - where: eq(agentsToSessions.sessionId, session1?.id!), + where: eq(agentsToSessions.sessionId, session1!.id), with: { agent: true }, }); @@ -355,7 +355,7 @@ describe('DataImporter', () => { where: eq(sessions.clientId, 'session2'), }); const session2Agent = await serverDB.query.agentsToSessions.findFirst({ - where: eq(agentsToSessions.sessionId, session2?.id!), + where: eq(agentsToSessions.sessionId, session2!.id), with: { agent: true }, }); @@ -885,8 +885,6 @@ describe('DataImporter', () => { voice: { openai: 'alloy' }, }, chatConfig: { - autoCreateTopicThreshold: 2, - enableAutoCreateTopic: true, historyCount: 1, }, openingQuestions: ['Question 1', 'Question 2'], diff --git a/packages/openapi/src/types/agent.type.ts b/packages/openapi/src/types/agent.type.ts index e58f9dcc75..2b291c7eda 100644 --- a/packages/openapi/src/types/agent.type.ts +++ b/packages/openapi/src/types/agent.type.ts @@ -27,10 +27,8 @@ export const CreateAgentRequestSchema = z.object({ avatar: z.string().nullish(), chatConfig: z .object({ - autoCreateTopicThreshold: z.number(), disableContextCaching: z.boolean().nullish(), displayMode: z.enum(['chat', 'docs']).nullish(), - enableAutoCreateTopic: z.boolean().nullish(), enableCompressHistory: z.boolean().nullish(), enableHistoryCount: z.boolean().nullish(), enableMaxTokens: z.boolean().nullish(), diff --git a/packages/openapi/src/types/chat.type.ts b/packages/openapi/src/types/chat.type.ts index 54c09f1a89..9585fc705b 100644 --- a/packages/openapi/src/types/chat.type.ts +++ b/packages/openapi/src/types/chat.type.ts @@ -95,10 +95,8 @@ export const MessageGenerationParamsSchema = z.object({ agentId: z.string().nullish(), chatConfig: z .object({ - autoCreateTopicThreshold: z.number().nullish(), disableContextCaching: z.boolean().nullish(), displayMode: z.enum(['chat', 'docs']).nullish(), - enableAutoCreateTopic: z.boolean().nullish(), enableCompressHistory: z.boolean().nullish(), enableHistoryCount: z.boolean().nullish(), enableMaxTokens: z.boolean().nullish(), diff --git a/packages/types/src/agent/chatConfig.ts b/packages/types/src/agent/chatConfig.ts index d0404298ec..7afa479c99 100644 --- a/packages/types/src/agent/chatConfig.ts +++ b/packages/types/src/agent/chatConfig.ts @@ -25,7 +25,6 @@ export interface AgentSelfIterationChatConfig { } export interface LobeAgentChatConfig extends AgentMemoryChatConfig, AgentSelfIterationChatConfig { - autoCreateTopicThreshold: number; codexMaxReasoningEffort?: 'low' | 'medium' | 'high' | 'xhigh'; /** * Model ID to use for generating compression summaries @@ -49,7 +48,6 @@ export interface LobeAgentChatConfig extends AgentMemoryChatConfig, AgentSelfIte * Treat undefined as `true` — agent mode is the default. */ enableAgentMode?: boolean; - enableAutoCreateTopic?: boolean; /** * Whether to auto-scroll during AI streaming output * undefined = use global setting @@ -199,7 +197,6 @@ export const SelfIterationChatConfigSchema = z.object({ export const AgentChatConfigSchema = z .object({ - autoCreateTopicThreshold: z.number().default(2), codexMaxReasoningEffort: z.enum(['low', 'medium', 'high', 'xhigh']).optional(), deepseekV4ReasoningEffort: z.enum(['none', 'high', 'max']).optional(), compressionModelId: z.string().optional(), @@ -207,7 +204,6 @@ export const AgentChatConfigSchema = z effort: z.enum(['low', 'medium', 'high', 'max']).optional(), enableAdaptiveThinking: z.boolean().optional(), enableAgentMode: z.boolean().optional(), - enableAutoCreateTopic: z.boolean().optional(), enableAutoScrollOnStreaming: z.boolean().optional(), enableCompressHistory: z.boolean().optional(), enableContextCompression: z.boolean().optional(), diff --git a/src/features/AgentSetting/AgentCategory/useCategory.tsx b/src/features/AgentSetting/AgentCategory/useCategory.tsx index 61e91034e6..cd9a15436b 100644 --- a/src/features/AgentSetting/AgentCategory/useCategory.tsx +++ b/src/features/AgentSetting/AgentCategory/useCategory.tsx @@ -1,6 +1,6 @@ import { Icon } from '@lobehub/ui'; import { type MenuItemType } from 'antd/es/menu/interface'; -import { Activity, Bot, BrainCog, Handshake, MessagesSquare, Mic2 } from 'lucide-react'; +import { Activity, Bot, Handshake, Mic2 } from 'lucide-react'; import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -33,16 +33,6 @@ export const useCategory = ({ mobile }: UseCategoryOptions = {}) => { key: ChatSettingsTabs.Opening, label: t('agentTab.opening'), }) as MenuItemType, - { - icon: , - key: ChatSettingsTabs.Chat, - label: t('agentTab.chat'), - }, - { - icon: , - key: ChatSettingsTabs.Modal, - label: t('agentTab.modal'), - }, enableAgentSelfIteration && { icon: , key: ChatSettingsTabs.SelfIteration, diff --git a/src/features/AgentSetting/AgentChat/index.tsx b/src/features/AgentSetting/AgentChat/index.tsx deleted file mode 100644 index cf8d6d3603..0000000000 --- a/src/features/AgentSetting/AgentChat/index.tsx +++ /dev/null @@ -1,97 +0,0 @@ -'use client'; - -import type { FormGroupItemType } from '@lobehub/ui'; -import { Form, SliderWithInput, TextArea } from '@lobehub/ui'; -import { Switch } from 'antd'; -import isEqual from 'fast-deep-equal'; -import { memo } from 'react'; -import { useTranslation } from 'react-i18next'; - -import { FORM_STYLE } from '@/const/layoutTokens'; - -import { selectors, useStore } from '../store'; - -const AgentChat = memo(() => { - const { t } = useTranslation('setting'); - const [form] = Form.useForm(); - const updateConfig = useStore((s) => s.setChatConfig); - const config = useStore(selectors.currentChatConfig, isEqual); - - const chat: FormGroupItemType = { - children: [ - { - children: