💄 style: batch fix eslint violations across packages (#12601)

This commit is contained in:
YuTengjing
2026-03-03 02:19:50 +08:00
committed by GitHub
parent 466f713ca6
commit c1521d2aeb
81 changed files with 230 additions and 221 deletions
@@ -41,7 +41,7 @@ describe('UsageCounter', () => {
const { usage } = UsageCounter.accumulateLLM({
cost: state.cost,
model: 'gpt-4',
modelUsage: modelUsage,
modelUsage,
provider: 'openai',
usage: state.usage,
});
@@ -63,7 +63,7 @@ describe('UsageCounter', () => {
const { cost } = UsageCounter.accumulateLLM({
cost: state.cost,
model: 'gpt-4',
modelUsage: modelUsage,
modelUsage,
provider: 'openai',
usage: state.usage,
});
@@ -183,7 +183,7 @@ describe('UsageCounter', () => {
const { cost } = UsageCounter.accumulateLLM({
cost: state.cost,
model: 'claude-3-5-sonnet-20241022',
modelUsage: modelUsage,
modelUsage,
provider: 'anthropic',
usage: state.usage,
});
@@ -248,7 +248,7 @@ describe('UsageCounter', () => {
const { cost } = UsageCounter.accumulateLLM({
cost: state.cost,
model: 'gpt-4',
modelUsage: modelUsage,
modelUsage,
provider: 'openai',
usage: state.usage,
});
@@ -270,21 +270,21 @@ describe('UsageCounter', () => {
const result1 = UsageCounter.accumulateLLM({
cost: state.cost,
model: 'gpt-4',
modelUsage: modelUsage,
modelUsage,
provider: 'openai',
usage: state.usage,
});
const result2 = UsageCounter.accumulateLLM({
cost: result1.cost,
model: 'gpt-4',
modelUsage: modelUsage,
modelUsage,
provider: 'openai',
usage: result1.usage,
});
const result3 = UsageCounter.accumulateLLM({
cost: result2.cost,
model: 'claude-3-5-sonnet-20241022',
modelUsage: modelUsage,
modelUsage,
provider: 'anthropic',
usage: result2.usage,
});
@@ -56,10 +56,10 @@ export const CallAgentInspector = memo<BuiltinInspectorProps<CallAgentParams>>(
return (
<Flexbox
horizontal
align={'center'}
className={cx(styles.root, isArgumentsStreaming && shinyTextStyles.shinyText)}
gap={8}
horizontal
>
<span className={styles.title}>{t(titleKey)}</span>
{agentMeta && (
@@ -44,10 +44,10 @@ export const CreateAgentInspector = memo<BuiltinInspectorProps<CreateAgentParams
return (
<Flexbox
horizontal
align={'center'}
className={cx(styles.root, isArgumentsStreaming && shinyTextStyles.shinyText)}
gap={8}
horizontal
>
<span className={styles.title}>
{t('builtins.lobe-agent-management.inspector.createAgent.title')}
@@ -57,10 +57,10 @@ export const SearchAgentInspector = memo<BuiltinInspectorProps<SearchAgentParams
return (
<Flexbox
horizontal
align={'center'}
className={cx(styles.root, isArgumentsStreaming && shinyTextStyles.shinyText)}
gap={8}
horizontal
>
<span className={styles.title}>{t(titleKey)}</span>
{keyword && <span className={highlightTextStyles.primary}>{keyword}</span>}
@@ -40,10 +40,10 @@ export const UpdateAgentInspector = memo<BuiltinInspectorProps<UpdateAgentParams
return (
<Flexbox
horizontal
align={'center'}
className={cx(styles.root, isArgumentsStreaming && shinyTextStyles.shinyText)}
gap={8}
horizontal
>
<span className={styles.title}>
{t('builtins.lobe-agent-management.inspector.updateAgent.title')}
@@ -1,7 +1,7 @@
'use client';
import type { BuiltinRenderProps } from '@lobechat/types';
import { Block, Markdown, Tag , Flexbox } from '@lobehub/ui';
import { Block, Flexbox,Markdown, Tag } from '@lobehub/ui';
import { createStaticStyles } from 'antd-style';
import { memo } from 'react';
@@ -62,7 +62,7 @@ export const CreateAgentRender = memo<BuiltinRenderProps<CreateAgentParams>>(({
{plugins && plugins.length > 0 && (
<div className={styles.field}>
<div className={styles.label}>Plugins</div>
<Flexbox gap={4} horizontal wrap={'wrap'}>
<Flexbox horizontal gap={4} wrap={'wrap'}>
{plugins.map((plugin) => (
<Tag key={plugin}>{plugin}</Tag>
))}
@@ -73,7 +73,7 @@ export const SearchAgentRender = memo<BuiltinRenderProps<SearchAgentParams, Sear
return (
<div className={styles.container}>
{agents.map((agent: AgentSearchItem) => (
<Flexbox align={'center'} className={styles.agentItem} gap={12} horizontal key={agent.id}>
<Flexbox horizontal align={'center'} className={styles.agentItem} gap={12} key={agent.id}>
<Avatar
avatar={agent.avatar || DEFAULT_AVATAR}
background={agent.backgroundColor || theme.colorBgContainer}
@@ -82,7 +82,7 @@ export const SearchAgentRender = memo<BuiltinRenderProps<SearchAgentParams, Sear
title={agent.title || undefined}
/>
<Flexbox flex={1} gap={2}>
<Flexbox align={'center'} gap={8} horizontal>
<Flexbox horizontal align={'center'} gap={8}>
<span className={styles.agentTitle}>{agent.title || agent.id}</span>
{agent.isMarket && <span className={styles.marketBadge}>Market</span>}
</Flexbox>
@@ -59,7 +59,7 @@ export const CreateAgentStreaming = memo<BuiltinStreamingProps<CreateAgentParams
{plugins && plugins.length > 0 && (
<div className={styles.field}>
<div className={styles.label}>Plugins</div>
<Flexbox gap={4} horizontal wrap={'wrap'}>
<Flexbox horizontal gap={4} wrap={'wrap'}>
{plugins.map((plugin) => (
<Tag key={plugin}>{plugin}</Tag>
))}
@@ -194,7 +194,7 @@ class AgentManagementExecutor extends BaseExecutor<typeof AgentManagementApiName
// The message.agentId will still be current agent, but metadata stores subAgentId + scope
await get().internal_execAgentRuntime({
context: { ...conversationContext, subAgentId: agentId, scope: 'sub_agent' },
messages: messages,
messages,
parentMessageId: ctx.messageId,
parentMessageType: 'tool',
});
@@ -265,6 +265,11 @@ export interface CallAgentParams {
* If true, execute as an async background task
*/
runAsTask?: boolean;
/**
* If true (and in a group context), skip calling supervisor after agent responds.
* Only relevant when used within agent groups. Default: false
*/
skipCallSupervisor?: boolean;
/**
* Task title (required when runAsTask is true)
*/
@@ -273,11 +278,6 @@ export interface CallAgentParams {
* Timeout in milliseconds for task execution (default: 1800000 = 30 minutes)
*/
timeout?: number;
/**
* If true (and in a group context), skip calling supervisor after agent responds.
* Only relevant when used within agent groups. Default: false
*/
skipCallSupervisor?: boolean;
}
export interface CallAgentState {
@@ -293,12 +293,12 @@ export interface CallAgentState {
* Execution mode
*/
mode: 'speak' | 'task';
/**
* Task ID if running as background task
*/
taskId?: string;
/**
* Whether to skip calling supervisor after agent responds (only relevant in group context)
*/
skipCallSupervisor?: boolean;
/**
* Task ID if running as background task
*/
taskId?: string;
}
@@ -8,8 +8,8 @@ import {
Avatar,
Flexbox,
Icon,
Tooltip,
stopPropagation,
Tooltip,
} from '@lobehub/ui';
import { Input, InputNumber } from 'antd';
import { createStaticStyles, useTheme } from 'antd-style';
@@ -21,7 +21,7 @@ const RenameLocalFile = memo<BuiltinInterventionProps<RenameLocalFileParams>>(({
<Icon icon={ChevronRight} />
<LocalFile name={base} path={filePath} />
</Flexbox>
<Flexbox align="center" gap={8} horizontal>
<Flexbox horizontal align="center" gap={8}>
<Text type="secondary">{base}</Text>
<Icon icon={ArrowRight} />
<Text>{newName}</Text>
@@ -22,16 +22,16 @@ const ExecScript = memo<BuiltinRenderProps<ExecScriptParams, ExecScriptState>>(
<Flexbox className={styles.container} gap={8}>
<Block gap={8} padding={8} variant={'outlined'}>
<Highlighter
wrap
language={'sh'}
showLanguage={false}
style={{ paddingInline: 8 }}
variant={'borderless'}
wrap
>
{args?.command || command || ''}
</Highlighter>
{content && (
<Highlighter language={'text'} showLanguage={false} variant={'filled'} wrap>
<Highlighter wrap language={'text'} showLanguage={false} variant={'filled'}>
{content}
</Highlighter>
)}
@@ -64,12 +64,12 @@ const ReadReference = memo<BuiltinRenderProps<ReadReferenceParams, ReadReference
return (
<Flexbox className={styles.container} gap={8}>
<Flexbox align={'center'} horizontal justify={'space-between'}>
<Text as={'span'} code ellipsis fontSize={12}>
<Flexbox horizontal align={'center'} justify={'space-between'}>
<Text code ellipsis as={'span'} fontSize={12}>
{path}
</Text>
{sizeText && (
<Text as={'span'} code fontSize={12} type={'secondary'}>
<Text code as={'span'} fontSize={12} type={'secondary'}>
{sizeText}
</Text>
)}
@@ -90,11 +90,11 @@ const ReadReference = memo<BuiltinRenderProps<ReadReferenceParams, ReadReference
) : (
<Block padding={0} variant={'outlined'}>
<Highlighter
language={getLanguage(ext)}
showLanguage
wrap
language={getLanguage(ext)}
style={{ maxHeight: 400, overflow: 'auto' }}
variant={'borderless'}
wrap
>
{content}
</Highlighter>
+1 -1
View File
@@ -71,7 +71,7 @@ export interface ReadReferenceParams {
}
export interface ReadReferenceState {
encoding: 'base64' | 'utf-8';
encoding: 'base64' | 'utf8';
fileType: string;
path: string;
size: number;
+1 -1
View File
@@ -1,6 +1,6 @@
import { AgentBuilderManifest } from '@lobechat/builtin-tool-agent-builder';
import { CalculatorManifest } from '@lobechat/builtin-tool-calculator';
import { AgentManagementManifest } from '@lobechat/builtin-tool-agent-management';
import { CalculatorManifest } from '@lobechat/builtin-tool-calculator';
import { CloudSandboxManifest } from '@lobechat/builtin-tool-cloud-sandbox';
import { GroupAgentBuilderManifest } from '@lobechat/builtin-tool-group-agent-builder';
import { GroupManagementManifest } from '@lobechat/builtin-tool-group-management';
@@ -62,7 +62,9 @@ export class ToolNameResolver {
): ChatToolPayload[] {
return toolCalls
.map((toolCall): ChatToolPayload | null => {
let [identifier, apiName, type] = toolCall.function.name.split(PLUGIN_SCHEMA_SEPARATOR);
const [initialIdentifier, apiName, type] =
toolCall.function.name.split(PLUGIN_SCHEMA_SEPARATOR);
let identifier = initialIdentifier;
if (!apiName) return null;
@@ -63,7 +63,7 @@ export class TaskMessageProcessor extends BaseProcessor {
if (!instruction) {
clonedContext.messages[i] = {
...message,
content: content,
content,
role: 'assistant',
};
processedCount++;
@@ -143,7 +143,7 @@ describe('MessageCleanupProcessor', () => {
content: 'Here is the answer',
extraField: 'remove',
id: 'msg5',
reasoning: reasoning,
reasoning,
role: 'assistant',
timestamp: Date.now(),
},
@@ -154,7 +154,7 @@ describe('MessageCleanupProcessor', () => {
expect(result.messages).toHaveLength(1);
expect(result.messages[0]).toEqual({
content: 'Here is the answer',
reasoning: reasoning,
reasoning,
role: 'assistant',
});
});
@@ -5,7 +5,7 @@ import type { PipelineContext } from '../../types';
import { MessageContentProcessor } from '../MessageContent';
vi.mock('@lobechat/utils/imageToBase64', async (importOriginal) => {
const actual = await importOriginal<typeof import('@lobechat/utils/imageToBase64')>();
const actual = await importOriginal<Record<string, unknown>>();
return {
...actual,
imageUrlToBase64: vi.fn().mockResolvedValue({
@@ -18,9 +18,9 @@ export {
PlaceholderVariablesProcessor,
renderPlaceholderTemplate,
} from './PlaceholderVariables';
export { ReactionFeedbackProcessor } from './ReactionFeedback';
export { SupervisorRoleRestoreProcessor } from './SupervisorRoleRestore';
export { TaskMessageProcessor } from './TaskMessage';
export { ReactionFeedbackProcessor } from './ReactionFeedback';
export { TasksFlattenProcessor } from './TasksFlatten';
export { ToolCallProcessor } from './ToolCall';
export { ToolMessageReorder } from './ToolMessageReorder';
@@ -1,8 +1,8 @@
import { describe, expect, it } from 'vitest';
import type { PipelineContext } from '../../types';
import { SkillContextProvider } from '../SkillContextProvider';
import type { SkillMeta } from '../SkillContextProvider';
import { SkillContextProvider } from '../SkillContextProvider';
const createContext = (messages: any[]): PipelineContext => ({
initialState: { messages: [] } as any,
@@ -1,11 +1,11 @@
// @vitest-environment node
import { SkillManifest } from '@lobechat/types';
import type { SkillManifest } from '@lobechat/types';
import { eq } from 'drizzle-orm';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { getTestDB } from '../../core/getTestDB';
import { agentSkills, users } from '../../schemas';
import { LobeChatDatabase } from '../../type';
import type { LobeChatDatabase } from '../../type';
import { AgentSkillModel } from '../agentSkill';
const serverDB: LobeChatDatabase = await getTestDB();
@@ -4,7 +4,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { getTestDB } from '../../core/getTestDB';
import { apiKeys, users } from '../../schemas';
import { LobeChatDatabase } from '../../type';
import type { LobeChatDatabase } from '../../type';
import { ApiKeyModel } from '../apiKey';
const serverDB: LobeChatDatabase = await getTestDB();
@@ -50,7 +50,7 @@ beforeEach(async () => {
]);
await trx.insert(files).values({
id: 'f1',
userId: userId,
userId,
url: 'abc',
name: 'file-1',
fileType: 'image/png',
@@ -27,7 +27,7 @@ beforeEach(async () => {
await trx.insert(sessions).values([{ id: '1', userId }]);
await trx.insert(files).values({
id: 'f1',
userId: userId,
userId,
url: 'abc',
name: 'file-1',
fileType: 'image/png',
@@ -40,7 +40,7 @@ beforeEach(async () => {
]);
await trx.insert(files).values({
id: 'f1',
userId: userId,
userId,
url: 'abc',
name: 'file-1',
fileType: 'image/png',
@@ -72,7 +72,7 @@ describe('MessageModel - queryWithWhere with task messages', () => {
expect(taskMessage.taskDetail).toEqual({
duration: 5000,
status: ThreadStatus.Completed,
threadId: threadId,
threadId,
title: 'Agent Task Execution',
totalCost: 0.05,
totalMessages: 10,
@@ -348,7 +348,7 @@ describe('MessageModel - queryWithWhere with task messages', () => {
expect(taskMessage.taskDetail).toEqual({
duration: undefined,
status: ThreadStatus.Active,
threadId: threadId,
threadId,
title: 'Partial Metadata Thread',
totalCost: undefined,
totalMessages: undefined,
@@ -387,7 +387,7 @@ describe('MessageModel - queryWithWhere with task messages', () => {
expect(taskMessage.taskDetail).toEqual({
duration: undefined,
status: ThreadStatus.Pending,
threadId: threadId,
threadId,
title: undefined,
totalCost: undefined,
totalMessages: undefined,
@@ -47,8 +47,8 @@ describe('TopicModel - Delete', () => {
{ id: 'topic2', sessionId: 'session2', userId: '345' },
]);
await tx.insert(messages).values([
{ id: 'message1', role: 'user', topicId: topicId, userId },
{ id: 'message2', role: 'assistant', topicId: topicId, userId },
{ id: 'message1', role: 'user', topicId, userId },
{ id: 'message2', role: 'assistant', topicId, userId },
{ id: 'message3', role: 'user', topicId: 'topic2', userId: '345' },
]);
});
@@ -11,7 +11,7 @@ import {
} from '../../../schemas';
import { AgentEvalRunModel } from '../run';
let serverDB = await getTestDB();
const serverDB = await getTestDB();
const userId = 'run-test-user';
const userId2 = 'run-test-user-2';
+4 -3
View File
@@ -1,9 +1,10 @@
import { SkillItem, SkillListItem } from '@lobechat/types';
import type { SkillItem, SkillListItem } from '@lobechat/types';
import { merge } from '@lobechat/utils';
import { and, desc, eq, ilike, inArray, or } from 'drizzle-orm';
import { NewAgentSkill, agentSkills } from '../schemas';
import { LobeChatDatabase } from '../type';
import type {NewAgentSkill } from '../schemas';
import { agentSkills } from '../schemas';
import type { LobeChatDatabase } from '../type';
const skillItemColumns = {
content: agentSkills.content,
+3 -2
View File
@@ -2,8 +2,9 @@ import { and, desc, eq } from 'drizzle-orm';
import { generateApiKey, isApiKeyExpired, validateApiKeyFormat } from '@/utils/apiKey';
import { ApiKeyItem, NewApiKeyItem, apiKeys } from '../schemas';
import { LobeChatDatabase } from '../type';
import type { ApiKeyItem,NewApiKeyItem } from '../schemas';
import { apiKeys } from '../schemas';
import type { LobeChatDatabase } from '../type';
type EncryptAPIKeyVaults = (keyVaults: string) => Promise<string>;
type DecryptAPIKeyVaults = (keyVaults: string) => Promise<{ plaintext: string }>;
@@ -1,8 +1,9 @@
import type { RAGEvalDataSetItem } from '@lobechat/types';
import { and, desc, eq } from 'drizzle-orm';
import { NewEvalDatasetsItem, evalDatasets } from '../../schemas';
import { LobeChatDatabase } from '../../type';
import type {NewEvalDatasetsItem } from '../../schemas';
import { evalDatasets } from '../../schemas';
import type { LobeChatDatabase } from '../../type';
export class EvalDatasetModel {
private userId: string;
@@ -1,8 +1,9 @@
import type { EvalDatasetRecordRefFile } from '@lobechat/types';
import { and, eq, inArray } from 'drizzle-orm';
import { NewEvalDatasetRecordsItem, evalDatasetRecords, files } from '../../schemas';
import { LobeChatDatabase } from '../../type';
import type {NewEvalDatasetRecordsItem } from '../../schemas';
import { evalDatasetRecords, files } from '../../schemas';
import type { LobeChatDatabase } from '../../type';
export class EvalDatasetRecordModel {
private userId: string;
@@ -3,13 +3,14 @@ import { EvalEvaluationStatus } from '@lobechat/types';
import type { SQL } from 'drizzle-orm';
import { and, count, desc, eq, inArray } from 'drizzle-orm';
import type {
NewEvalEvaluationItem} from '../../schemas';
import {
NewEvalEvaluationItem,
evalDatasets,
evalEvaluation,
evaluationRecords,
evaluationRecords
} from '../../schemas';
import { LobeChatDatabase } from '../../type';
import type { LobeChatDatabase } from '../../type';
export class EvalEvaluationModel {
private userId: string;
@@ -1,7 +1,8 @@
import { and, eq } from 'drizzle-orm';
import { NewEvaluationRecordsItem, evaluationRecords } from '../../schemas';
import { LobeChatDatabase } from '../../type';
import type {NewEvaluationRecordsItem } from '../../schemas';
import { evaluationRecords } from '../../schemas';
import type { LobeChatDatabase } from '../../type';
export class EvaluationRecordModel {
private userId: string;
+1 -1
View File
@@ -366,7 +366,7 @@ export class SessionModel {
const { id: _, slug: __, ...config } = agent;
return this.create({
config: config,
config,
id: sessionId,
session: {
...session,
@@ -2,12 +2,12 @@
import { beforeEach, describe, expect, it } from 'vitest';
import { getTestDB } from '../../core/getTestDB';
import { NewAgent, agents } from '../../schemas/agent';
import { NewChatGroup, chatGroups, chatGroupsAgents } from '../../schemas/chatGroup';
import { agents } from '../../schemas/agent';
import { chatGroups, chatGroupsAgents } from '../../schemas/chatGroup';
import { agentsToSessions } from '../../schemas/relations';
import { NewSession, NewSessionGroup, sessionGroups, sessions } from '../../schemas/session';
import { sessionGroups, sessions } from '../../schemas/session';
import { users } from '../../schemas/user';
import { LobeChatDatabase } from '../../type';
import type { LobeChatDatabase } from '../../type';
import { HomeRepository } from './index';
const userId = 'home-test-user';
@@ -63,7 +63,7 @@ describe('TopicImporterRepo.importTopic', () => {
it('should restore parentId chain from real exported data', async () => {
const repo = new TopicImporterRepo(serverDB, userId);
const jsonPath = path.join(__dirname, 'fixtures/exported-topic.json');
const fileContent = readFileSync(jsonPath, 'utf-8');
const fileContent = readFileSync(jsonPath, 'utf8');
const exportedData = JSON.parse(fileContent) as ExportedTopic;
const result = await repo.importTopic({
@@ -117,7 +117,7 @@ describe('TopicImporterRepo.importTopic', () => {
it('should preserve plugin and pluginState fields in message_plugins table', async () => {
const repo = new TopicImporterRepo(serverDB, userId);
const jsonPath = path.join(__dirname, 'fixtures/exported-topic.json');
const fileContent = readFileSync(jsonPath, 'utf-8');
const fileContent = readFileSync(jsonPath, 'utf8');
const exportedData = JSON.parse(fileContent) as ExportedTopic;
const result = await repo.importTopic({
@@ -172,7 +172,7 @@ describe('TopicImporterRepo.importTopic', () => {
it('should preserve tools array on assistant messages', async () => {
const repo = new TopicImporterRepo(serverDB, userId);
const jsonPath = path.join(__dirname, 'fixtures/exported-topic.json');
const fileContent = readFileSync(jsonPath, 'utf-8');
const fileContent = readFileSync(jsonPath, 'utf8');
const exportedData = JSON.parse(fileContent) as ExportedTopic;
const result = await repo.importTopic({
@@ -206,7 +206,7 @@ describe('TopicImporterRepo.importTopic', () => {
it('should preserve model and provider fields', async () => {
const repo = new TopicImporterRepo(serverDB, userId);
const jsonPath = path.join(__dirname, 'fixtures/exported-topic.json');
const fileContent = readFileSync(jsonPath, 'utf-8');
const fileContent = readFileSync(jsonPath, 'utf8');
const exportedData = JSON.parse(fileContent) as ExportedTopic;
const result = await repo.importTopic({
@@ -231,7 +231,7 @@ describe('TopicImporterRepo.importTopic', () => {
it('should verify branching is preserved (2 children for root)', async () => {
const repo = new TopicImporterRepo(serverDB, userId);
const jsonPath = path.join(__dirname, 'fixtures/exported-topic.json');
const fileContent = readFileSync(jsonPath, 'utf-8');
const fileContent = readFileSync(jsonPath, 'utf8');
const exportedData = JSON.parse(fileContent) as ExportedTopic;
const result = await repo.importTopic({
@@ -196,8 +196,6 @@ export class ElectronIpcClient {
const request = { id, method, params };
log('Created request with ID: %s', id);
let requestTimeoutId: NodeJS.Timeout;
const cleanupAndResolve = (value: T) => {
clearTimeout(requestTimeoutId);
this.requestQueue.delete(id);
@@ -222,7 +220,7 @@ export class ElectronIpcClient {
this.requestQueue.set(id, { reject: cleanupAndReject, resolve: cleanupAndResolve });
log('Added request to queue, current queue size: %d', this.requestQueue.size);
requestTimeoutId = setTimeout(() => {
const requestTimeoutId: NodeJS.Timeout = setTimeout(() => {
const pendingRequest = this.requestQueue.get(id);
if (pendingRequest) {
// Request is still in queue, indicating timeout
@@ -8,7 +8,7 @@ import { parseDataset } from '../src';
const fixtures = resolve(__dirname, 'fixtures');
describe('parseDataset - CSV', () => {
const csv = readFileSync(resolve(fixtures, 'sample.csv'), 'utf-8');
const csv = readFileSync(resolve(fixtures, 'sample.csv'), 'utf8');
it('should parse CSV with headers', () => {
const result = parseDataset(csv, { format: 'csv' });
@@ -26,7 +26,7 @@ describe('parseDataset - CSV', () => {
});
describe('parseDataset - JSONL', () => {
const jsonl = readFileSync(resolve(fixtures, 'sample.jsonl'), 'utf-8');
const jsonl = readFileSync(resolve(fixtures, 'sample.jsonl'), 'utf8');
it('should parse JSONL', () => {
const result = parseDataset(jsonl, { format: 'jsonl' });
@@ -47,7 +47,7 @@ describe('parseDataset - JSONL', () => {
});
describe('parseDataset - JSON', () => {
const json = readFileSync(resolve(fixtures, 'sample.json'), 'utf-8');
const json = readFileSync(resolve(fixtures, 'sample.json'), 'utf8');
it('should parse JSON array', () => {
const result = parseDataset(json, { format: 'json' });
@@ -65,20 +65,20 @@ describe('parseDataset - JSON', () => {
describe('parseDataset - auto detection', () => {
it('should auto-detect CSV by filename', () => {
const csv = readFileSync(resolve(fixtures, 'sample.csv'), 'utf-8');
const csv = readFileSync(resolve(fixtures, 'sample.csv'), 'utf8');
const result = parseDataset(csv, { filename: 'sample.csv' });
expect(result.format).toBe('csv');
expect(result.headers).toContain('prompt');
});
it('should auto-detect JSONL by filename', () => {
const jsonl = readFileSync(resolve(fixtures, 'sample.jsonl'), 'utf-8');
const jsonl = readFileSync(resolve(fixtures, 'sample.jsonl'), 'utf8');
const result = parseDataset(jsonl, { filename: 'sample.jsonl' });
expect(result.format).toBe('jsonl');
});
it('should auto-detect JSON by content', () => {
const json = readFileSync(resolve(fixtures, 'sample.json'), 'utf-8');
const json = readFileSync(resolve(fixtures, 'sample.json'), 'utf8');
const result = parseDataset(json);
expect(result.format).toBe('json');
});
+1 -1
View File
@@ -1,6 +1,6 @@
import type { DatasetFormat } from './types';
const XLSX_MAGIC = [0x50, 0x4b, 0x03, 0x04]; // PK\x03\x04 (ZIP header)
const XLSX_MAGIC = [0x50, 0x4B, 0x03, 0x04]; // PK\x03\x04 (ZIP header)
export function detectFormat(
input: Buffer | string | Uint8Array,
+4 -4
View File
@@ -16,15 +16,15 @@ export const extract = (output: string, extractor: AnswerExtractor): string => {
const parts = output.split(extractor.delimiter);
if (parts.length < 2) return output;
const segment =
extractor.position === 'first' ? parts[1] : parts[parts.length - 1];
return segment.trim();
extractor.position === 'first' ? parts[1] : parts.at(-1);
return segment!.trim();
}
case 'last-line': {
const lines = output.split('\n').filter((l) => l.trim());
if (lines.length === 0) return output;
const last = lines[lines.length - 1];
return extractor.trim !== false ? last.trim() : last;
const last = lines.at(-1);
return extractor.trim !== false ? last!.trim() : last!;
}
case 'choice-index': {
@@ -91,7 +91,7 @@ export class ExcelLoader implements FileLoaderInterface {
charCount,
lineCount,
metadata: {
sheetName: sheetName,
sheetName,
},
pageContent: tableMarkdown.trim(),
});
@@ -112,7 +112,7 @@ export class PdfLoader implements FileLoaderInterface {
lineCount: 0,
metadata: {
error: `Failed to load or parse PDF file: ${error.message}`,
filePath: filePath,
filePath,
},
pageContent: '',
};
@@ -159,7 +159,7 @@ export class PdfLoader implements FileLoaderInterface {
});
return {
pdfInfo: pdfInfo,
pdfInfo,
// PDF info (Author, Title, etc.)
pdfMetadata: metadata,
// PDF metadata
@@ -93,14 +93,14 @@ export class PptxLoader implements FileLoaderInterface {
const metadata = {
pageCount: slideFiles.length, // Total number of slides found
slideNumber: slideNumber,
slideNumber,
sourceFileName,
};
return {
charCount: slideText.length,
lineCount: lines.length,
metadata: metadata,
metadata,
pageContent: slideText.trim(), // Trim final content
};
} catch (parseError) {
@@ -210,7 +210,7 @@ ${page.pageContent}
metadata: {
error: errorInfo,
pageCount: 0,
sourceFileName: sourceFileName,
sourceFileName,
...(sourceFilePath && { sourceFilePath }), // Add specific path if available
},
pageContent: '', // Error pages have no content
@@ -64,7 +64,7 @@ export class BenchmarkLocomoContextProvider implements MemoryContextProvider<
context: toXml(root),
metadata: {},
sourceId: this.options.sourceId,
userId: userId,
userId,
};
}
}
@@ -118,7 +118,7 @@ export class LobeChatTopicContextProvider implements MemoryContextProvider<
context: topicContext,
metadata: {},
sourceId: this.options.topicId,
userId: userId,
userId,
} satisfies BuiltContext;
}
}
@@ -250,8 +250,8 @@ export class RetrievalUserMemoryContextProvider implements MemoryContextProvider
return {
context: memoryContext,
metadata: {},
sourceId: sourceId,
userId: userId,
sourceId,
userId,
};
}
}
@@ -343,8 +343,8 @@ export class RetrievalUserMemoryIdentitiesProvider implements MemoryContextProvi
return {
context: identityContext,
metadata: {},
sourceId: sourceId,
userId: userId,
sourceId,
userId,
};
}
}
@@ -251,8 +251,8 @@ export class MemoryExtractionService<RO> {
layers: layersToExtract,
outputs,
processedCounts: processedCount,
processedErrorsCount: processedErrorsCount,
processedLayersCount: processedLayersCount,
processedErrorsCount,
processedLayersCount,
};
} catch (error) {
await options?.resultRecorder?.recordFail?.(job, error as Error);
+1 -1
View File
@@ -1,4 +1,4 @@
import { AIChatModelCard } from '../types/aiModel';
import type { AIChatModelCard } from '../types/aiModel';
const straicoModels: AIChatModelCard[] = [];
+1 -1
View File
@@ -9,7 +9,7 @@ describe('model-bank package.json exports should cover all aiModels files', () =
const aiModelsDir = path.resolve(packageRoot, 'src/aiModels');
const packageJsonPath = path.resolve(packageRoot, 'package.json');
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')) as {
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as {
exports?: Record<string, string>;
};
@@ -1,8 +1,8 @@
import type Anthropic from '@anthropic-ai/sdk';
import debug from 'debug';
import type { GenerateObjectOptions, GenerateObjectPayload } from '../../types';
import { buildAnthropicMessages, buildAnthropicTools } from '../contextBuilders/anthropic';
import { GenerateObjectOptions, GenerateObjectPayload } from '../../types';
const log = debug('lobe-model-runtime:anthropic:generate-object');
@@ -66,7 +66,7 @@ export const createAnthropicGenerateObject = async (
messages: anthropicMessages,
model,
system: systemPrompts,
tool_choice: tool_choice,
tool_choice,
tools: finalTools,
},
{ signal: options?.signal },
@@ -317,7 +317,7 @@ export const buildGoogleTool = (tool: ChatCompletionTool): FunctionDeclaration =
name: functionDeclaration.name,
parameters: {
description: parameters?.description,
properties: properties,
properties,
required: parameters?.required,
type: SchemaType.OBJECT,
},
@@ -1,8 +1,9 @@
import { imageUrlToBase64 } from '@lobechat/utils';
import OpenAI, { toFile } from 'openai';
import type OpenAI from 'openai';
import { toFile } from 'openai';
import { disableStreamModels, systemToUserModels } from '../../const/models';
import { ChatStreamPayload, OpenAIChatMessage } from '../../types';
import type { ChatStreamPayload, OpenAIChatMessage } from '../../types';
import { parseDataUri } from '../../utils/uriParser';
type ConvertMessageContentOptions = {
@@ -74,7 +75,7 @@ export const convertOpenAIResponseInputs = async (
messages: OpenAIChatMessage[],
options?: ConvertMessageContentOptions,
) => {
let input: OpenAI.Responses.ResponseInputItem[] = [];
const input: OpenAI.Responses.ResponseInputItem[] = [];
await Promise.all(
messages.map(async (message) => {
// if message has reasoning, add it as a separate reasoning item
@@ -245,7 +245,7 @@ export const AnthropicStream = (
return readableStream
.pipeThrough(
createTokenSpeedCalculator(transformWithPayload, {
enableStreaming: enableStreaming,
enableStreaming,
inputStartAt,
streamStack,
}),
@@ -46,7 +46,8 @@ function desensitizeAccountId(path: string): string {
function desensitizeCloudflareUrl(url: string): string {
const urlObj = new URL(url);
let { protocol, hostname, port, pathname, search } = urlObj;
const { protocol, hostname, port, search } = urlObj;
let { pathname } = urlObj;
if (url.startsWith(DEFAULT_BASE_URL_PREFIX)) {
return `${protocol}//${hostname}${port ? `:${port}` : ''}${desensitizeAccountId(pathname)}${search}`;
} else {
@@ -840,7 +840,7 @@ describe('GoogleGenerativeAIStream', () => {
'id: chat_1',
'event: grounding',
`data: {\"citations\":[{\"favicon\":\"npmjs.com\",\"title\":\"npmjs.com\",\"url\":\"https://vertexaisearch.cloud.google.com/grounding-api-redirect/AbF9wXG1234545\"},{\"favicon\":\"google.dev\",\"title\":\"google.dev\",\"url\":\"https://vertexaisearch.cloud.google.com/grounding-api-redirect/AbF9wXE9288334\"}],\"searchQueries\":[\"sdk latest version\"]}\n`,
`data: {"citations":[{"favicon":"npmjs.com","title":"npmjs.com","url":"https://vertexaisearch.cloud.google.com/grounding-api-redirect/AbF9wXG1234545"},{"favicon":"google.dev","title":"google.dev","url":"https://vertexaisearch.cloud.google.com/grounding-api-redirect/AbF9wXE9288334"}],"searchQueries":["sdk latest version"]}\n`,
// stop
'id: chat_1',
'event: stop',
@@ -928,7 +928,7 @@ describe('GoogleGenerativeAIStream', () => {
[
'id: chat_1',
'event: tool_calls',
'data: [{"function":{"arguments":"{\\"query\\":\\"\\\\\\\"version\\\\\\":\\",\\"repo\\":\\"lobehub/lobe-chat\\",\\"path\\":\\"package.json\\"}","name":"grep____searchGitHub____mcp"},"id":"grep____searchGitHub____mcp_0_abcd1234","index":0,"thoughtSignature":"123","type":"function"}]\n',
'data: [{"function":{"arguments":"{\\"query\\":\\"\\\\\\"version\\\\\\":\\",\\"repo\\":\\"lobehub/lobe-chat\\",\\"path\\":\\"package.json\\"}","name":"grep____searchGitHub____mcp"},"id":"grep____searchGitHub____mcp_0_abcd1234","index":0,"thoughtSignature":"123","type":"function"}]\n',
'id: chat_1',
'event: stop',
@@ -96,7 +96,7 @@ const transformGoogleGenerativeAIStream = (
name: value.name,
},
id: generateToolCallId(index, value.name),
index: index,
index,
thoughtSignature: value.thoughtSignature,
type: 'function',
}),
@@ -328,7 +328,7 @@ export const GoogleGenerativeAIStream = (
return rawStream
.pipeThrough(
createTokenSpeedCalculator(transformWithPayload, {
enableStreaming: enableStreaming,
enableStreaming,
inputStartAt,
streamStack,
}),
@@ -22,7 +22,7 @@ const transformOllamaStream = (chunk: ChatResponse, stack: StreamContext): Strea
name: value.function?.name ?? null,
},
id: generateToolCallId(index, value.function?.name),
index: index,
index,
type: 'function',
})),
id: stack.id,
@@ -45,7 +45,7 @@ const processMarkdownBase64Images = (text: string): { cleanedText: string; urls:
if (!text) return { cleanedText: text, urls: [] };
const urls: string[] = [];
const mdRegex = /!\[[^\]]*]\(\s*(data:image\/[\d+.A-Za-z-]+;base64,[^\s)]+)\s*\)/g;
const mdRegex = /!\[[^\]]*\]\(\s*(data:image\/[\d+.A-Za-z-]+;base64,[^\s)]+)\s*\)/g;
let cleanedText = text;
let m: RegExpExecArray | null;
@@ -605,7 +605,7 @@ export const OpenAIStream = (
.pipeThrough(createFirstErrorHandleTransformer(bizErrorTypeTransformer, payload?.provider))
.pipeThrough(
createTokenSpeedCalculator(transformWithProvider, {
enableStreaming: enableStreaming,
enableStreaming,
inputStartAt,
streamStack,
}),
@@ -219,7 +219,7 @@ export const OpenAIResponsesStream = (
.pipeThrough(createFirstErrorHandleTransformer(bizErrorTypeTransformer, payload?.provider))
.pipeThrough(
createTokenSpeedCalculator(transformWithPayload, {
enableStreaming: enableStreaming,
enableStreaming,
inputStartAt,
streamStack,
}),
@@ -160,7 +160,7 @@ export const QwenAIStream = (
return readableStream
.pipeThrough(
createTokenSpeedCalculator(transformQwenStream, {
enableStreaming: enableStreaming,
enableStreaming,
inputStartAt,
streamStack: streamContext,
}),
@@ -178,7 +178,7 @@ describe('SparkAIStream', () => {
expect(chunks).toEqual([
'id: cha000b0bf9@dx193d1ffa61cb894532\n',
'event: tool_calls\n',
`data: [{\"function\":{\"arguments\":\"{\\\"city\\\":\\\"Shanghai\\\"}\",\"name\":\"realtime-weather____fetchCurrentWeather\"},\"id\":\"call_1\",\"index\":0,\"type\":\"function\"}]\n\n`,
`data: [{"function":{"arguments":"{\\"city\\":\\"Shanghai\\"}","name":"realtime-weather____fetchCurrentWeather"},"id":"call_1","index":0,"type":"function"}]\n\n`,
]);
expect(onToolCallMock).toHaveBeenCalledTimes(1);
@@ -41,14 +41,14 @@ export const convertOpenAIUsage = (
const data = {
acceptedPredictionTokens: usage.completion_tokens_details?.accepted_prediction_tokens,
inputAudioTokens: usage.prompt_tokens_details?.audio_tokens,
inputCacheMissTokens: inputCacheMissTokens,
inputCacheMissTokens,
inputCachedTokens: cachedTokens,
inputCitationTokens: inputCitationTokens,
inputTextTokens: inputTextTokens,
outputAudioTokens: outputAudioTokens,
outputImageTokens: outputImageTokens,
inputCitationTokens,
inputTextTokens,
outputAudioTokens,
outputImageTokens,
outputReasoningTokens: outputReasoning,
outputTextTokens: outputTextTokens,
outputTextTokens,
rejectedPredictionTokens: usage.completion_tokens_details?.rejected_prediction_tokens,
totalInputTokens,
totalOutputTokens: totalOutputTokensNormalized,
@@ -98,17 +98,17 @@ export const convertOpenAIResponseUsage = (
// and potentially filtered out later.
acceptedPredictionTokens: undefined, // Not in ResponseUsage
inputAudioTokens: undefined, // Not in ResponseUsage
inputCacheMissTokens: inputCacheMissTokens,
inputCachedTokens: inputCachedTokens,
inputCacheMissTokens,
inputCachedTokens,
inputCitationTokens: undefined, // Not in ResponseUsage
inputTextTokens: inputTextTokens,
inputTextTokens,
outputAudioTokens: undefined, // Not in ResponseUsage
outputImageTokens: outputImageTokens,
outputReasoningTokens: outputReasoningTokens,
outputTextTokens: outputTextTokens,
outputImageTokens,
outputReasoningTokens,
outputTextTokens,
rejectedPredictionTokens: undefined, // Not in ResponseUsage
totalInputTokens: totalInputTokens,
totalOutputTokens: totalOutputTokens,
totalInputTokens,
totalOutputTokens,
totalTokens: overallTotalTokens,
} satisfies ModelTokensUsage; // This helps ensure all keys of ModelTokensUsage are considered
@@ -237,7 +237,7 @@ export const testProvider = ({
// Expect the chat method to throw an error with InvalidHunyuanAPIKey
expect(e).toEqual({
endpoint: defaultBaseURL,
error: error,
error,
errorType: invalidErrorType,
provider,
});
@@ -266,7 +266,7 @@ describe('LobeInternLMAI - custom features', () => {
// Mock a model with abilities in model-bank
vi.mock('model-bank', async (importOriginal) => {
const actual = await importOriginal<typeof import('model-bank')>();
const actual = await importOriginal<Record<string, unknown>>();
return {
...actual,
LOBE_DEFAULT_MODEL_LIST: [
@@ -303,9 +303,9 @@ export class LobeReplicateAI implements LobeRuntimeAI {
this.debugLog('[Replicate] Final imageUrl:', outputImageUrl);
return {
height: height,
height,
imageUrl: outputImageUrl,
width: width,
width,
};
} catch (error) {
throw this.handleError(error);
@@ -399,7 +399,7 @@ export class LobeReplicateAI implements LobeRuntimeAI {
// Generic error
throw AgentRuntimeError.chat({
endpoint: desensitizedEndpoint,
error: error,
error,
errorType: AgentRuntimeErrorType.ProviderBizError,
provider: this.id,
});
@@ -2,7 +2,7 @@ import { ModelProvider } from 'model-bank';
import { createOpenAICompatibleRuntime } from '../../core/openaiCompatibleFactory';
import { processMultiProviderModelList } from '../../utils/modelParse';
import { StraicoChatModel, StraicoModelsResponse } from './type';
import type { StraicoChatModel, StraicoModelsResponse } from './type';
const formatPrice = (pricing?: { coins?: number; words?: number }) => {
if (!pricing || typeof pricing.coins !== 'number' || typeof pricing.words !== 'number') {
@@ -27,7 +27,7 @@ export const LobeStraicoAI = createOpenAICompatibleRuntime({
baseURL: 'https://api.straico.com/v0',
chatCompletion: {
handlePayload: (payload) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-vars
const { model, ...rest } = payload;
return {
@@ -1,7 +1,7 @@
import createDebug from 'debug';
import { CreateVideoOptions } from '../../../core/openaiCompatibleFactory';
import { CreateVideoPayload, CreateVideoResponse } from '../../../types/video';
import type { CreateVideoOptions } from '../../../core/openaiCompatibleFactory';
import type { CreateVideoPayload, CreateVideoResponse } from '../../../types/video';
const log = createDebug('lobe-video:volcengine');
@@ -1,6 +1,6 @@
import createDebug from 'debug';
import {
import type {
HandleCreateVideoWebhookPayload,
HandleCreateVideoWebhookResult,
} from '../../../types/video';
+1 -1
View File
@@ -1,4 +1,4 @@
import { RuntimeVideoGenParams } from 'model-bank';
import type { RuntimeVideoGenParams } from 'model-bank';
export type CreateVideoPayload = {
callbackUrl?: string;
@@ -73,13 +73,13 @@ export function extractStatusCodeFromError(message: string): {
// Create JSON containing status code and message
const resultJson = {
message: messageContent,
statusCode: statusCode,
statusCode,
statusCodeText: `[${statusCode} ${statusText}]`,
};
return {
errorDetails: resultJson,
prefix: prefix,
prefix,
};
}
}
+50 -49
View File
@@ -1,36 +1,37 @@
import { z } from 'zod';
import { PageSelection, PageSelectionSchema } from './pageSelection';
import type { PageSelection} from './pageSelection';
import { PageSelectionSchema } from './pageSelection';
export interface ModelTokensUsage {
// Prediction tokens
acceptedPredictionTokens?: number;
inputAudioTokens?: number;
// Input tokens breakdown
/**
* user prompt input
*/
// Input cache tokens
inputCachedTokens?: number;
inputCacheMissTokens?: number;
inputWriteCacheTokens?: number;
inputTextTokens?: number;
/**
* user prompt image
*/
inputImageTokens?: number;
inputAudioTokens?: number;
inputCacheMissTokens?: number;
/**
* currently only pplx has citation_tokens
*/
inputCitationTokens?: number;
/**
* user prompt image
*/
inputImageTokens?: number;
inputTextTokens?: number;
inputWriteCacheTokens?: number;
outputAudioTokens?: number;
outputImageTokens?: number;
outputReasoningTokens?: number;
// Output tokens breakdown
outputTextTokens?: number;
outputImageTokens?: number;
outputAudioTokens?: number;
outputReasoningTokens?: number;
// Prediction tokens
acceptedPredictionTokens?: number;
rejectedPredictionTokens?: number;
// Total tokens
@@ -79,8 +80,8 @@ export const ModelPerformanceSchema = z.object({
// ============ Emoji Reaction ============ //
export interface EmojiReaction {
emoji: string;
count: number;
emoji: string;
users: string[];
}
@@ -109,14 +110,6 @@ export interface ModelUsage extends ModelTokensUsage {
}
export interface ModelPerformance {
/**
* tokens per second
*/
tps?: number;
/**
* time to first token (ms)
*/
ttft?: number;
/**
* from output start to output finish (ms)
*/
@@ -125,36 +118,60 @@ export interface ModelPerformance {
* from input start to output finish (ms)
*/
latency?: number;
/**
* tokens per second
*/
tps?: number;
/**
* time to first token (ms)
*/
ttft?: number;
}
export interface MessageMetadata extends ModelUsage, ModelPerformance {
activeBranchIndex?: number;
activeColumn?: boolean;
finishType?: string;
/**
* Message collapse state
* true: collapsed, false/undefined: expanded
*/
collapsed?: boolean;
compare?: boolean;
finishType?: string;
/**
* Tool inspect expanded state
* true: expanded, false/undefined: collapsed
*/
inspectExpanded?: boolean;
compare?: boolean;
usage?: ModelUsage;
performance?: ModelPerformance;
/**
* Task instruction (for role='task' messages)
* The instruction given by supervisor to the agent
* Thread's sourceMessageId links back to this message for status tracking
*/
instruction?: string;
/**
* Flag indicating if message content is multimodal (serialized MessageContentPart[])
*/
isMultimodal?: boolean;
// message content is multimodal, display content in the streaming, won't save to db
tempDisplayContent?: string;
/**
* Flag indicating if message is from the Supervisor agent in group orchestration
* Used by conversation-flow to transform role to 'supervisor' for UI rendering
*/
isSupervisor?: boolean;
/**
* Page selections attached to user message
* Used for Ask AI functionality to persist selection context
*/
pageSelections?: PageSelection[];
performance?: ModelPerformance;
/**
* Flag indicating if message is pinned (excluded from compression)
*/
pinned?: boolean;
/**
* Emoji reactions on this message
*/
reactions?: EmojiReaction[];
/**
* Message scope - indicates the context in which this message was created
* Used by conversation-flow to determine how to handle message grouping and display
@@ -168,24 +185,8 @@ export interface MessageMetadata extends ModelUsage, ModelPerformance {
* Used by callAgent tool (sub_agent) and group orchestration (group modes)
*/
subAgentId?: string;
/**
* Flag indicating if message is pinned (excluded from compression)
*/
pinned?: boolean;
/**
* Task instruction (for role='task' messages)
* The instruction given by supervisor to the agent
* Thread's sourceMessageId links back to this message for status tracking
*/
instruction?: string;
taskTitle?: string;
/**
* Page selections attached to user message
* Used for Ask AI functionality to persist selection context
*/
pageSelections?: PageSelection[];
/**
* Emoji reactions on this message
*/
reactions?: EmojiReaction[];
// message content is multimodal, display content in the streaming, won't save to db
tempDisplayContent?: string;
usage?: ModelUsage;
}
+1 -1
View File
@@ -91,7 +91,7 @@ export interface SkillResourceTreeNode {
export interface SkillResourceContent {
content: string;
encoding: 'utf-8' | 'base64';
encoding: 'utf8' | 'base64';
fileHash: string;
fileType: string;
path: string;
@@ -230,6 +230,10 @@ const TokenDetail = memo<TokenDetailProps>(({ usage, performance, model, provide
<Icon icon={isShowCredit ? BadgeCent : CoinsIcon} />
<AnimatedNumber
duration={1500}
// Force remount when switching between token/credit to prevent unwanted animation
// See: https://github.com/lobehub/lobe-chat/pull/10098
key={isShowCredit ? 'credit' : 'token'}
value={totalCount}
formatter={(value) => {
const roundedValue = Math.round(value);
if (isShortFormat) {
@@ -237,10 +241,6 @@ const TokenDetail = memo<TokenDetailProps>(({ usage, performance, model, provide
}
return new Intl.NumberFormat('en-US').format(roundedValue);
}}
// Force remount when switching between token/credit to prevent unwanted animation
// See: https://github.com/lobehub/lobe-chat/pull/10098
key={isShowCredit ? 'credit' : 'token'}
value={totalCount}
/>
</Center>
</Popover>
@@ -291,10 +291,10 @@ const AddButton = () => {
multiple
id="folder-upload-input"
style={{ display: 'none' }}
onChange={handleFolderUploadWithClose}
type="file"
// @ts-expect-error - webkitdirectory is not in the React types
webkitdirectory=""
onChange={handleFolderUploadWithClose}
/>
<input
accept=".zip"
@@ -35,12 +35,12 @@ export const SuccessState = memo<SuccessStateProps>(
>
<ImageItem
alt={prompt}
preview={{
src: generation.asset!.url,
}}
style={{ height: '100%', width: '100%' }}
// Thumbnail quality is too bad
url={generation.asset!.url}
preview={{
src: generation.asset!.url,
}}
/>
<ActionButtons
showDownload
@@ -42,13 +42,13 @@ const SystemAgentForm = memo(
{
children: (
<ModelSelect
showAbility={false}
// value={value}
onChange={async (props) => {
setLoading(true);
await updateSystemAgent(systemAgentKey, props);
setLoading(false);
}}
showAbility={false}
// value={value}
/>
),
desc: t(`systemAgent.${systemAgentKey}.modelDesc`),
+4 -4
View File
@@ -7,7 +7,7 @@ const mockCreateGlobalFile = vi.fn().mockResolvedValue({ fileHash: 'mock-file-ha
const mockGetFileContentByHash = vi.fn().mockResolvedValue('file content');
const mockGetFileByteArrayByHash = vi
.fn()
.mockResolvedValue(new Uint8Array([0x89, 0x50, 0x4e, 0x47]));
.mockResolvedValue(new Uint8Array([0x89, 0x50, 0x4E, 0x47]));
const mockUploadBuffer = vi.fn().mockResolvedValue({ key: 'mock-key' });
// Mock FileService only (no longer need FileModel)
@@ -215,11 +215,11 @@ describe('SkillResourceService', () => {
expect(result).toEqual({
content: 'file content',
encoding: 'utf-8',
encoding: 'utf8',
fileHash: 'abc123fileHash',
fileType: 'text/plain',
path: 'test.txt',
size: Buffer.byteLength('file content', 'utf-8'),
size: Buffer.byteLength('file content', 'utf8'),
});
expect(mockGetFileContentByHash).toHaveBeenCalledWith('abc123fileHash');
});
@@ -227,7 +227,7 @@ describe('SkillResourceService', () => {
it('should read binary resource content with base64 encoding', async () => {
const service = new SkillResourceService({} as any, 'user-1');
const resources = { 'image.png': { fileHash: 'binaryFileHash', size: 1024 } };
const binaryData = new Uint8Array([0x89, 0x50, 0x4e, 0x47]);
const binaryData = new Uint8Array([0x89, 0x50, 0x4E, 0x47]);
mockGetFileByteArrayByHash.mockResolvedValue(binaryData);
const result = await service.readResource(resources, 'image.png');
+2 -2
View File
@@ -88,11 +88,11 @@ export class SkillResourceService {
return {
content,
encoding: 'utf-8',
encoding: 'utf8',
fileHash: meta.fileHash,
fileType,
path: virtualPath,
size: Buffer.byteLength(content, 'utf-8'),
size: Buffer.byteLength(content, 'utf8'),
};
}