mirror of
https://github.com/lobehub/lobe-chat.git
synced 2025-12-20 01:12:52 +08:00
♻️ refactor: move utils to separate package (#8889)
* move utils * move utils * move utils * update * update * update * update * update * refactor to clean the tests * fix release workflow * fix tests * fix tests * fix tests * fix tests * fix tests * fix tests * try to fix client db migration issue * fix tests
This commit is contained in:
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: pgvector/pgvector:pg17
|
||||
image: paradedb/paradedb:latest
|
||||
env:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
options: >-
|
||||
@@ -38,8 +38,10 @@ jobs:
|
||||
- name: Lint
|
||||
run: bun run lint
|
||||
|
||||
- name: Test Server Coverage
|
||||
run: bun run test-server:coverage
|
||||
- uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Test Database Coverage
|
||||
run: pnpm --filter @lobechat/database test
|
||||
env:
|
||||
DATABASE_TEST_URL: postgresql://postgres:postgres@localhost:5432/postgres
|
||||
DATABASE_DRIVER: node
|
||||
@@ -48,8 +50,8 @@ jobs:
|
||||
S3_PUBLIC_DOMAIN: https://example.com
|
||||
APP_URL: https://home.com
|
||||
|
||||
- name: Test App Coverage
|
||||
run: bun run test-app:coverage
|
||||
- name: Test App
|
||||
run: bun run test-app
|
||||
|
||||
- name: Release
|
||||
run: bun run release
|
||||
|
||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
package: [file-loaders, prompts, model-runtime, web-crawler, electron-server-ipc]
|
||||
package: [file-loaders, prompts, model-runtime, web-crawler, electron-server-ipc, utils]
|
||||
|
||||
name: Test package ${{ matrix.package }}
|
||||
|
||||
|
||||
@@ -104,6 +104,19 @@ table ai_providers {
|
||||
}
|
||||
}
|
||||
|
||||
table api_keys {
|
||||
id integer [pk, not null]
|
||||
name varchar(256) [not null]
|
||||
key varchar(256) [not null, unique]
|
||||
enabled boolean [default: true]
|
||||
expires_at "timestamp with time zone"
|
||||
last_used_at "timestamp with time zone"
|
||||
user_id text [not null]
|
||||
accessed_at "timestamp with time zone" [not null, default: `now()`]
|
||||
created_at "timestamp with time zone" [not null, default: `now()`]
|
||||
updated_at "timestamp with time zone" [not null, default: `now()`]
|
||||
}
|
||||
|
||||
table async_tasks {
|
||||
id uuid [pk, not null, default: `gen_random_uuid()`]
|
||||
type text
|
||||
@@ -702,6 +715,7 @@ table rbac_roles {
|
||||
description text
|
||||
is_system boolean [not null, default: false]
|
||||
is_active boolean [not null, default: true]
|
||||
metadata jsonb [default: `{}`]
|
||||
accessed_at "timestamp with time zone" [not null, default: `now()`]
|
||||
created_at "timestamp with time zone" [not null, default: `now()`]
|
||||
updated_at "timestamp with time zone" [not null, default: `now()`]
|
||||
|
||||
@@ -139,12 +139,14 @@
|
||||
"@icons-pack/react-simple-icons": "9.6.0",
|
||||
"@khmyznikov/pwa-install": "0.3.9",
|
||||
"@langchain/community": "^0.3.50",
|
||||
"@lobechat/const": "workspace:*",
|
||||
"@lobechat/database": "workspace:*",
|
||||
"@lobechat/electron-client-ipc": "workspace:*",
|
||||
"@lobechat/electron-server-ipc": "workspace:*",
|
||||
"@lobechat/file-loaders": "workspace:*",
|
||||
"@lobechat/model-runtime": "workspace:*",
|
||||
"@lobechat/prompts": "workspace:*",
|
||||
"@lobechat/utils": "workspace:*",
|
||||
"@lobechat/web-crawler": "workspace:*",
|
||||
"@lobehub/analytics": "^1.6.0",
|
||||
"@lobehub/charts": "^2.0.0",
|
||||
@@ -360,8 +362,7 @@
|
||||
"unified": "^11.0.5",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"vite": "^5.4.19",
|
||||
"vitest": "^3.2.4",
|
||||
"vitest-canvas-mock": "^0.3.3"
|
||||
"vitest": "^3.2.4"
|
||||
},
|
||||
"packageManager": "pnpm@10.14.0",
|
||||
"publishConfig": {
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
export * from './locale';
|
||||
export * from './message';
|
||||
export * from './settings';
|
||||
export * from './version';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { ModelProviderCard } from '@/types/llm';
|
||||
import { ModelProviderCard } from '@/types/index';
|
||||
|
||||
import { genUserLLMConfig } from './genUserLLMConfig';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ModelProvider } from '@lobechat/model-runtime';
|
||||
import { UserModelProviderConfig } from '@lobechat/types';
|
||||
|
||||
import * as ProviderCards from '@/config/modelProviders';
|
||||
import { ModelProviderCard } from '@/types/llm';
|
||||
import { UserModelProviderConfig } from '@/types/user/settings';
|
||||
|
||||
export const genUserLLMConfig = (specificConfig: Record<any, any>): UserModelProviderConfig => {
|
||||
return Object.keys(ModelProvider).reduce((config, providerKey) => {
|
||||
@@ -9,8 +9,6 @@ import { DEFAULT_SYSTEM_AGENT_CONFIG } from './systemAgent';
|
||||
import { DEFAULT_TOOL_CONFIG } from './tool';
|
||||
import { DEFAULT_TTS_CONFIG } from './tts';
|
||||
|
||||
export const COOKIE_CACHE_DAYS = 30;
|
||||
|
||||
export * from './agent';
|
||||
export * from './hotkey';
|
||||
export * from './llm';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ModelProvider } from '@lobechat/model-runtime';
|
||||
|
||||
import { genUserLLMConfig } from '@/utils/genUserLLMConfig';
|
||||
import { genUserLLMConfig } from './genUserLLMConfig';
|
||||
|
||||
export const DEFAULT_LLM_CONFIG = genUserLLMConfig({
|
||||
lmstudio: {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
CREATE TABLE "oauth_handoffs" (
|
||||
CREATE TABLE IF NOT EXISTS "oauth_handoffs" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"client" varchar(50) NOT NULL,
|
||||
"payload" jsonb NOT NULL,
|
||||
|
||||
@@ -12,5 +12,5 @@ CREATE TABLE IF NOT EXISTS "api_keys" (
|
||||
CONSTRAINT "api_keys_key_unique" UNIQUE("key")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "rbac_roles" ADD COLUMN "metadata" jsonb DEFAULT '{}'::jsonb;--> statement-breakpoint
|
||||
ALTER TABLE "rbac_roles" ADD COLUMN IF NOT EXISTS "metadata" jsonb DEFAULT '{}'::jsonb;--> statement-breakpoint
|
||||
ALTER TABLE "api_keys" ADD CONSTRAINT "api_keys_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"@electric-sql/pglite": "^0.2.17",
|
||||
"@lobechat/const": "workspace:*",
|
||||
"@lobechat/types": "workspace:*",
|
||||
"@lobechat/utils": "workspace:*",
|
||||
"dayjs": "^1.11.13",
|
||||
"drizzle-orm": "^0.44.4",
|
||||
"nanoid": "^5.1.5",
|
||||
|
||||
@@ -544,10 +544,20 @@
|
||||
},
|
||||
{
|
||||
"sql": [
|
||||
"CREATE TABLE \"oauth_handoffs\" (\n\t\"id\" text PRIMARY KEY NOT NULL,\n\t\"client\" varchar(50) NOT NULL,\n\t\"payload\" jsonb NOT NULL,\n\t\"accessed_at\" timestamp with time zone DEFAULT now() NOT NULL,\n\t\"created_at\" timestamp with time zone DEFAULT now() NOT NULL,\n\t\"updated_at\" timestamp with time zone DEFAULT now() NOT NULL\n);\n"
|
||||
"CREATE TABLE IF NOT EXISTS \"oauth_handoffs\" (\n\t\"id\" text PRIMARY KEY NOT NULL,\n\t\"client\" varchar(50) NOT NULL,\n\t\"payload\" jsonb NOT NULL,\n\t\"accessed_at\" timestamp with time zone DEFAULT now() NOT NULL,\n\t\"created_at\" timestamp with time zone DEFAULT now() NOT NULL,\n\t\"updated_at\" timestamp with time zone DEFAULT now() NOT NULL\n);\n"
|
||||
],
|
||||
"bps": true,
|
||||
"folderMillis": 1752567402506,
|
||||
"hash": "8ba3ae52ed72e8aad1623dbcf47ca26a8406ebffc6d5284abff94ea994b59c04"
|
||||
"hash": "83c410b18ef5c8667b4bdfd7880ef7db4c3278d826e6e87e9d3e05dde67fe8e1"
|
||||
},
|
||||
{
|
||||
"sql": [
|
||||
"CREATE TABLE IF NOT EXISTS \"api_keys\" (\n\t\"id\" integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name \"api_keys_id_seq\" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),\n\t\"name\" varchar(256) NOT NULL,\n\t\"key\" varchar(256) NOT NULL,\n\t\"enabled\" boolean DEFAULT true,\n\t\"expires_at\" timestamp with time zone,\n\t\"last_used_at\" timestamp with time zone,\n\t\"user_id\" text NOT NULL,\n\t\"accessed_at\" timestamp with time zone DEFAULT now() NOT NULL,\n\t\"created_at\" timestamp with time zone DEFAULT now() NOT NULL,\n\t\"updated_at\" timestamp with time zone DEFAULT now() NOT NULL,\n\tCONSTRAINT \"api_keys_key_unique\" UNIQUE(\"key\")\n);\n",
|
||||
"\nALTER TABLE \"rbac_roles\" ADD COLUMN IF NOT EXISTS \"metadata\" jsonb DEFAULT '{}'::jsonb;",
|
||||
"\nALTER TABLE \"api_keys\" ADD CONSTRAINT \"api_keys_user_id_users_id_fk\" FOREIGN KEY (\"user_id\") REFERENCES \"public\".\"users\"(\"id\") ON DELETE cascade ON UPDATE no action;\n"
|
||||
],
|
||||
"bps": true,
|
||||
"folderMillis": 1753201379817,
|
||||
"hash": "fe5c0d7c2768189771c42ef93693fc1d58586b468c4bdde7fb6f2dc58cc9931c"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -23,7 +23,7 @@ describe('TableViewerRepo', () => {
|
||||
it('should return all tables with counts', async () => {
|
||||
const result = await repo.getAllTables();
|
||||
|
||||
expect(result.length).toEqual(59);
|
||||
expect(result.length).toEqual(60);
|
||||
expect(result[0]).toEqual({ name: 'agents', count: 0, type: 'BASE TABLE' });
|
||||
});
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ export default defineConfig({
|
||||
alias: {
|
||||
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
||||
'@/const': resolve(__dirname, '../const/src'),
|
||||
'@/utils/errorResponse': resolve(__dirname, '../../src/utils/errorResponse'),
|
||||
'@/utils': resolve(__dirname, '../utils/src'),
|
||||
'@/database': resolve(__dirname, '../database/src'),
|
||||
'@/types': resolve(__dirname, '../types/src'),
|
||||
'@': resolve(__dirname, '../../src'),
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-bedrock-runtime": "^3.862.0",
|
||||
"@lobechat/types": "workspace:*",
|
||||
"@lobechat/utils": "workspace:*",
|
||||
"debug": "^4.4.1",
|
||||
"openai": "^4.104.0"
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { createBflImage } from './createImage';
|
||||
import { BflStatusResponse } from './types';
|
||||
|
||||
// Mock external dependencies
|
||||
vi.mock('@/utils/imageToBase64', () => ({
|
||||
vi.mock('@lobechat/utils', () => ({
|
||||
imageUrlToBase64: vi.fn(),
|
||||
}));
|
||||
|
||||
@@ -188,7 +188,7 @@ describe('createBflImage', () => {
|
||||
it('should convert single imageUrl to image_prompt base64', async () => {
|
||||
// Arrange
|
||||
const { parseDataUri } = await import('../utils/uriParser');
|
||||
const { imageUrlToBase64 } = await import('@/utils/imageToBase64');
|
||||
const { imageUrlToBase64 } = await import('@lobechat/utils');
|
||||
const { asyncifyPolling } = await import('../utils/asyncifyPolling');
|
||||
|
||||
const mockParseDataUri = vi.mocked(parseDataUri);
|
||||
@@ -291,7 +291,7 @@ describe('createBflImage', () => {
|
||||
it('should convert multiple imageUrls for Kontext models', async () => {
|
||||
// Arrange
|
||||
const { parseDataUri } = await import('../utils/uriParser');
|
||||
const { imageUrlToBase64 } = await import('@/utils/imageToBase64');
|
||||
const { imageUrlToBase64 } = await import('@lobechat/utils');
|
||||
const { asyncifyPolling } = await import('../utils/asyncifyPolling');
|
||||
|
||||
const mockParseDataUri = vi.mocked(parseDataUri);
|
||||
@@ -351,7 +351,7 @@ describe('createBflImage', () => {
|
||||
it('should limit imageUrls to maximum 4 images', async () => {
|
||||
// Arrange
|
||||
const { parseDataUri } = await import('../utils/uriParser');
|
||||
const { imageUrlToBase64 } = await import('@/utils/imageToBase64');
|
||||
const { imageUrlToBase64 } = await import('@lobechat/utils');
|
||||
const { asyncifyPolling } = await import('../utils/asyncifyPolling');
|
||||
|
||||
const mockParseDataUri = vi.mocked(parseDataUri);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { imageUrlToBase64 } from '@lobechat/utils';
|
||||
import createDebug from 'debug';
|
||||
|
||||
import { RuntimeImageGenParamsValue } from '@/libs/standard-parameters/index';
|
||||
import { imageUrlToBase64 } from '@/utils/imageToBase64';
|
||||
|
||||
import { AgentRuntimeErrorType } from '../error';
|
||||
import { CreateImagePayload, CreateImageResponse } from '../types/image';
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// @vitest-environment edge-runtime
|
||||
import { GenerateContentResponse, Tool } from '@google/genai';
|
||||
import * as imageToBase64Module from '@lobechat/utils';
|
||||
import OpenAI from 'openai';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { OpenAIChatMessage } from '@/libs/model-runtime';
|
||||
import { CreateImagePayload } from '@/libs/model-runtime/types/image';
|
||||
import { ChatStreamPayload } from '@/types/openai/chat';
|
||||
import * as imageToBase64Module from '@/utils/imageToBase64';
|
||||
|
||||
import * as debugStreamModule from '../utils/debugStream';
|
||||
import { LobeGoogleAI } from './index';
|
||||
|
||||
@@ -9,9 +9,7 @@ import {
|
||||
Type as SchemaType,
|
||||
ThinkingConfig,
|
||||
} from '@google/genai';
|
||||
|
||||
import { imageUrlToBase64 } from '@/utils/imageToBase64';
|
||||
import { safeParseJSON } from '@/utils/safeParseJSON';
|
||||
import { imageUrlToBase64, safeParseJSON } from '@lobechat/utils';
|
||||
|
||||
import { LobeRuntimeAI } from '../BaseAI';
|
||||
import { AgentRuntimeErrorType } from '../error';
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { ChatModelCard } from '@lobechat/types';
|
||||
import { Ollama, Tool } from 'ollama/browser';
|
||||
import { ClientOptions } from 'openai';
|
||||
|
||||
import { ModelRequestOptions, OpenAIChatMessage } from '@/libs/model-runtime';
|
||||
import { ChatModelCard } from '@/types/llm';
|
||||
import { createErrorResponse } from '@/utils/errorResponse';
|
||||
|
||||
import { LobeRuntimeAI } from '../BaseAI';
|
||||
@@ -13,6 +12,8 @@ import {
|
||||
Embeddings,
|
||||
EmbeddingsPayload,
|
||||
ModelProvider,
|
||||
ModelRequestOptions,
|
||||
OpenAIChatMessage,
|
||||
PullModelParams,
|
||||
} from '../types';
|
||||
import { AgentRuntimeError } from '../utils/createError';
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { imageUrlToBase64 } from '@lobechat/utils';
|
||||
import { OpenAI } from 'openai';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { imageUrlToBase64 } from '@/utils/imageToBase64';
|
||||
|
||||
import { OpenAIChatMessage, UserMessageContentPart } from '../types/chat';
|
||||
import {
|
||||
buildAnthropicBlock,
|
||||
@@ -20,7 +19,7 @@ vi.mock('./uriParser', () => ({
|
||||
type: 'base64',
|
||||
}),
|
||||
}));
|
||||
vi.mock('@/utils/imageToBase64');
|
||||
vi.mock('@lobechat/utils');
|
||||
|
||||
describe('anthropicHelpers', () => {
|
||||
describe('buildAnthropicBlock', () => {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
import { imageUrlToBase64 } from '@lobechat/utils';
|
||||
import OpenAI from 'openai';
|
||||
|
||||
import { imageUrlToBase64 } from '@/utils/imageToBase64';
|
||||
|
||||
import { OpenAIChatMessage, UserMessageContentPart } from '../types';
|
||||
import { parseDataUri } from './uriParser';
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { getModelPropertyWithFallback } from '@lobechat/utils';
|
||||
import dayjs from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import createDebug from 'debug';
|
||||
@@ -7,7 +8,6 @@ import { Stream } from 'openai/streaming';
|
||||
import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels';
|
||||
import { RuntimeImageGenParamsValue } from '@/libs/standard-parameters/index';
|
||||
import type { ChatModelCard } from '@/types/llm';
|
||||
import { getModelPropertyWithFallback } from '@/utils/getFallbackModelProperty';
|
||||
|
||||
import { LobeRuntimeAI } from '../../BaseAI';
|
||||
import { AgentRuntimeErrorType, ILobeAgentRuntimeErrorType } from '../../error';
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { imageUrlToBase64 } from '@lobechat/utils';
|
||||
import OpenAI from 'openai';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { imageUrlToBase64 } from '@/utils/imageToBase64';
|
||||
|
||||
import {
|
||||
convertImageUrlToFile,
|
||||
convertMessageContent,
|
||||
@@ -12,7 +11,7 @@ import {
|
||||
import { parseDataUri } from './uriParser';
|
||||
|
||||
// 模拟依赖
|
||||
vi.mock('@/utils/imageToBase64');
|
||||
vi.mock('@lobechat/utils');
|
||||
vi.mock('./uriParser');
|
||||
|
||||
describe('convertMessageContent', () => {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { imageUrlToBase64 } from '@lobechat/utils';
|
||||
import OpenAI, { toFile } from 'openai';
|
||||
|
||||
import { disableStreamModels, systemToUserModels } from '@/const/models';
|
||||
import { ChatStreamPayload, OpenAIChatMessage } from '@/libs/model-runtime';
|
||||
import { imageUrlToBase64 } from '@/utils/imageToBase64';
|
||||
|
||||
import { parseDataUri } from './uriParser';
|
||||
|
||||
@@ -103,7 +103,7 @@ export const convertOpenAIResponseInputs = async (
|
||||
export const pruneReasoningPayload = (payload: ChatStreamPayload) => {
|
||||
const shouldStream = !disableStreamModels.has(payload.model);
|
||||
const { stream_options, ...cleanedPayload } = payload as any;
|
||||
|
||||
|
||||
return {
|
||||
...cleanedPayload,
|
||||
frequency_penalty: 0,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { InvokeModelWithResponseStreamResponse } from '@aws-sdk/client-bedrock-runtime';
|
||||
|
||||
import { nanoid } from '@/utils/uuid';
|
||||
import { nanoid } from '@lobechat/utils';
|
||||
|
||||
import { ChatStreamCallbacks } from '../../../types';
|
||||
import { transformAnthropicStream } from '../anthropic';
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { InvokeModelWithResponseStreamResponse } from '@aws-sdk/client-bedrock-runtime';
|
||||
import * as uuidModule from '@lobechat/utils';
|
||||
import { Readable } from 'stream';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import * as uuidModule from '@/utils/uuid';
|
||||
|
||||
import { AWSBedrockLlamaStream } from './llama';
|
||||
|
||||
describe('AWSBedrockLlamaStream', () => {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { InvokeModelWithResponseStreamResponse } from '@aws-sdk/client-bedrock-runtime';
|
||||
|
||||
import { nanoid } from '@/utils/uuid';
|
||||
import { nanoid } from '@lobechat/utils';
|
||||
|
||||
import { ChatStreamCallbacks } from '../../../types';
|
||||
import {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { GenerateContentResponse } from '@google/genai';
|
||||
import * as uuidModule from '@lobechat/utils';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import * as uuidModule from '@/utils/uuid';
|
||||
|
||||
import { GoogleGenerativeAIStream } from './google-ai';
|
||||
|
||||
describe('GoogleGenerativeAIStream', () => {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { GenerateContentResponse } from '@google/genai';
|
||||
import { nanoid } from '@lobechat/utils';
|
||||
|
||||
import errorLocale from '@/locales/default/error';
|
||||
import { ModelTokensUsage } from '@/types/message';
|
||||
import { GroundingSearch } from '@/types/search';
|
||||
import { nanoid } from '@/utils/uuid';
|
||||
|
||||
import { ChatStreamCallbacks } from '../../types';
|
||||
import {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import * as uuidModule from '@lobechat/utils';
|
||||
import { ChatResponse } from 'ollama/browser';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import * as uuidModule from '@/utils/uuid';
|
||||
|
||||
import { OllamaStream } from './ollama';
|
||||
|
||||
describe('OllamaStream', () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { nanoid } from '@lobechat/utils';
|
||||
import { ChatResponse } from 'ollama/browser';
|
||||
|
||||
import { ChatStreamCallbacks } from '@/libs/model-runtime';
|
||||
import { nanoid } from '@/utils/uuid';
|
||||
|
||||
import {
|
||||
StreamContext,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { nanoid, safeParseJSON } from '@lobechat/utils';
|
||||
|
||||
import { CitationItem, ModelSpeed, ModelTokensUsage } from '@/types/message';
|
||||
import { safeParseJSON } from '@/utils/safeParseJSON';
|
||||
import { nanoid } from '@/utils/uuid';
|
||||
|
||||
import { AgentRuntimeErrorType } from '../../error';
|
||||
import { parseToolCalls } from '../../helpers';
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as uuidModule from '@lobechat/utils';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import * as uuidModule from '@/utils/uuid';
|
||||
|
||||
import { VertexAIStream } from './vertex-ai';
|
||||
|
||||
describe('VertexAIStream', () => {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { GenerateContentResponse } from '@google/genai';
|
||||
import { nanoid } from '@lobechat/utils';
|
||||
|
||||
import { ModelTokensUsage } from '@/types/message';
|
||||
import { GroundingSearch } from '@/types/search';
|
||||
import { nanoid } from '@/utils/uuid';
|
||||
|
||||
import { type GoogleAIStreamOptions } from './google-ai';
|
||||
import {
|
||||
|
||||
@@ -7,6 +7,8 @@ export default defineConfig({
|
||||
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
||||
'@/libs/model-runtime': resolve(__dirname, './src'),
|
||||
'@/types': resolve(__dirname, '../types/src'),
|
||||
'@/utils/errorResponse': resolve(__dirname, '../../src/utils/errorResponse'),
|
||||
'@/utils': resolve(__dirname, '../utils/src'),
|
||||
'@/const': resolve(__dirname, '../const/src'),
|
||||
'@': resolve(__dirname, '../../src'),
|
||||
/* eslint-enable */
|
||||
|
||||
@@ -4,8 +4,12 @@ export * from './clientDB';
|
||||
export * from './eval';
|
||||
export * from './fetch';
|
||||
export * from './knowledgeBase';
|
||||
export * from './llm';
|
||||
export * from './message';
|
||||
export * from './user';
|
||||
export * from './user/settings';
|
||||
// FIXME: I think we need a refactor for the "openai" types
|
||||
// it more likes the UI message payload
|
||||
export * from './openai/chat';
|
||||
export * from './trace';
|
||||
export * from './zustand';
|
||||
|
||||
17
packages/utils/package.json
Normal file
17
packages/utils/package.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "@lobechat/utils",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"main": "./src/index.ts",
|
||||
"scripts": {
|
||||
"test": "vitest",
|
||||
"test:coverage": "vitest --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@lobechat/const": "workspace:*",
|
||||
"@lobechat/types": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vitest-canvas-mock": "^0.3.3"
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
import dayjs from 'dayjs';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { COOKIE_CACHE_DAYS } from '@/const/settings';
|
||||
|
||||
import { setCookie } from './cookie';
|
||||
import { COOKIE_CACHE_DAYS, setCookie } from './cookie';
|
||||
|
||||
describe('setCookie', () => {
|
||||
// Mock document.cookie since we're in a test environment
|
||||
@@ -1,6 +1,6 @@
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { COOKIE_CACHE_DAYS } from '@/const/settings';
|
||||
export const COOKIE_CACHE_DAYS = 30;
|
||||
|
||||
export const setCookie = (
|
||||
key: string,
|
||||
@@ -1,9 +1,9 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { VARIABLE_GENERATORS, parsePlaceholderVariablesMessages } from './parserPlaceholder';
|
||||
import { parsePlaceholderVariablesMessages } from './parserPlaceholder';
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock('@/utils/uuid', () => ({
|
||||
vi.mock('../uuid', () => ({
|
||||
uuid: () => 'mocked-uuid-12345',
|
||||
}));
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
import { template } from 'lodash-es';
|
||||
|
||||
import { uuid } from '@/utils/uuid';
|
||||
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { userProfileSelectors } from '@/store/user/selectors';
|
||||
|
||||
import { uuid } from '../uuid';
|
||||
|
||||
const placeholderVariablesRegex = /{{(.*?)}}/g;
|
||||
|
||||
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
||||
export const VARIABLE_GENERATORS = {
|
||||
/**
|
||||
* 时间类模板变量
|
||||
*
|
||||
* | Value | Example |
|
||||
* |-------|---------|
|
||||
* | `{{date}}` | 12/25/2023 |
|
||||
* | `{{datetime}}` | 12/25/2023, 2:30:45 PM |
|
||||
* | `{{day}}` | 25 |
|
||||
* | `{{hour}}` | 14 |
|
||||
* | `{{iso}}` | 2023-12-25T14:30:45.123Z |
|
||||
* | `{{locale}}` | zh-CN |
|
||||
* | `{{minute}}` | 30 |
|
||||
* | `{{month}}` | 12 |
|
||||
* | `{{second}}` | 45 |
|
||||
* | `{{time}}` | 2:30:45 PM |
|
||||
* | `{{timestamp}}` | 1703538645123 |
|
||||
* | `{{timezone}}` | America/New_York |
|
||||
* | `{{weekday}}` | Monday |
|
||||
* | `{{year}}` | 2023 |
|
||||
*
|
||||
*/
|
||||
* 时间类模板变量
|
||||
*
|
||||
* | Value | Example |
|
||||
* |-------|---------|
|
||||
* | `{{date}}` | 12/25/2023 |
|
||||
* | `{{datetime}}` | 12/25/2023, 2:30:45 PM |
|
||||
* | `{{day}}` | 25 |
|
||||
* | `{{hour}}` | 14 |
|
||||
* | `{{iso}}` | 2023-12-25T14:30:45.123Z |
|
||||
* | `{{locale}}` | zh-CN |
|
||||
* | `{{minute}}` | 30 |
|
||||
* | `{{month}}` | 12 |
|
||||
* | `{{second}}` | 45 |
|
||||
* | `{{time}}` | 2:30:45 PM |
|
||||
* | `{{timestamp}}` | 1703538645123 |
|
||||
* | `{{timezone}}` | America/New_York |
|
||||
* | `{{weekday}}` | Monday |
|
||||
* | `{{year}}` | 2023 |
|
||||
*
|
||||
*/
|
||||
date: () => new Date().toLocaleDateString(),
|
||||
datetime: () => new Date().toLocaleString(),
|
||||
day: () => new Date().getDate().toString().padStart(2, '0'),
|
||||
@@ -46,65 +46,71 @@ export const VARIABLE_GENERATORS = {
|
||||
year: () => new Date().getFullYear().toString(),
|
||||
|
||||
/**
|
||||
* 用户信息类模板变量
|
||||
*
|
||||
* | Value | Example |
|
||||
* |-------|---------|
|
||||
* | `{{email}}` | demo@lobehub.com |
|
||||
* | `{{nickname}}` | 社区版用户 |
|
||||
* | `{{username}}` | LobeChat |
|
||||
*
|
||||
*/
|
||||
* 用户信息类模板变量
|
||||
*
|
||||
* | Value | Example |
|
||||
* |-------|---------|
|
||||
* | `{{email}}` | demo@lobehub.com |
|
||||
* | `{{nickname}}` | 社区版用户 |
|
||||
* | `{{username}}` | LobeChat |
|
||||
*
|
||||
*/
|
||||
email: () => userProfileSelectors.email(useUserStore.getState()) ?? '',
|
||||
nickname: () => userProfileSelectors.nickName(useUserStore.getState()) ?? '',
|
||||
username: () => userProfileSelectors.displayUserName(useUserStore.getState()) ?? userProfileSelectors.fullName(useUserStore.getState()) ?? '',
|
||||
username: () =>
|
||||
userProfileSelectors.displayUserName(useUserStore.getState()) ??
|
||||
userProfileSelectors.fullName(useUserStore.getState()) ??
|
||||
'',
|
||||
|
||||
/**
|
||||
* 随机值类模板变量
|
||||
*
|
||||
* | Value | Example |
|
||||
* |-------|---------|
|
||||
* | `{{random}}` | 100041 |
|
||||
* | `{{random_bool}}` | true |
|
||||
* | `{{random_float}}` | 76.02 |
|
||||
* | `{{random_hex}}` | de0dbd |
|
||||
* | `{{random_int}}` | 68 |
|
||||
* | `{{random_string}}` | wqn9zfrqe7h |
|
||||
*
|
||||
*/
|
||||
* 随机值类模板变量
|
||||
*
|
||||
* | Value | Example |
|
||||
* |-------|---------|
|
||||
* | `{{random}}` | 100041 |
|
||||
* | `{{random_bool}}` | true |
|
||||
* | `{{random_float}}` | 76.02 |
|
||||
* | `{{random_hex}}` | de0dbd |
|
||||
* | `{{random_int}}` | 68 |
|
||||
* | `{{random_string}}` | wqn9zfrqe7h |
|
||||
*
|
||||
*/
|
||||
random: () => Math.floor(Math.random() * 1_000_000 + 1).toString(),
|
||||
random_bool: () => (Math.random() > 0.5 ? 'true' : 'false'),
|
||||
random_float: () => (Math.random() * 100).toFixed(2),
|
||||
random_hex: () => Math.floor(Math.random() * 16_777_215).toString(16).padStart(6, '0'),
|
||||
random_hex: () =>
|
||||
Math.floor(Math.random() * 16_777_215)
|
||||
.toString(16)
|
||||
.padStart(6, '0'),
|
||||
random_int: () => Math.floor(Math.random() * 100 + 1).toString(),
|
||||
random_string: () => Math.random().toString(36).slice(2, 15),
|
||||
random_digit: () => Math.floor(Math.random() * 10).toString(),
|
||||
|
||||
/**
|
||||
* UUID 类模板变量
|
||||
*
|
||||
* | Value | Example |
|
||||
* |-------|---------|
|
||||
* | `{{uuid}}` | dd90b35-669f-4e87-beb8-ac6877f6995d |
|
||||
* | `{{uuid_short}}` | dd90b35 |
|
||||
*
|
||||
*/
|
||||
* UUID 类模板变量
|
||||
*
|
||||
* | Value | Example |
|
||||
* |-------|---------|
|
||||
* | `{{uuid}}` | dd90b35-669f-4e87-beb8-ac6877f6995d |
|
||||
* | `{{uuid_short}}` | dd90b35 |
|
||||
*
|
||||
*/
|
||||
uuid: () => uuid(),
|
||||
uuid_short: () => uuid().split('-')[0],
|
||||
|
||||
/**
|
||||
* 平台类模板变量
|
||||
*
|
||||
* | Value | Example |
|
||||
* |-------|---------|
|
||||
* | `{{language}}` | zh-CN |
|
||||
* | `{{platform}}` | MacIntel |
|
||||
* | `{{user_agent}}` | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 Edg/132.0.0.0 |
|
||||
*
|
||||
*/
|
||||
language: () => typeof navigator !== 'undefined' ? navigator.language : '',
|
||||
platform: () => typeof navigator !== 'undefined' ? navigator.platform : '',
|
||||
user_agent: () => typeof navigator !== 'undefined' ? navigator.userAgent : '',
|
||||
* 平台类模板变量
|
||||
*
|
||||
* | Value | Example |
|
||||
* |-------|---------|
|
||||
* | `{{language}}` | zh-CN |
|
||||
* | `{{platform}}` | MacIntel |
|
||||
* | `{{user_agent}}` | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 Edg/132.0.0.0 |
|
||||
*
|
||||
*/
|
||||
language: () => (typeof navigator !== 'undefined' ? navigator.language : ''),
|
||||
platform: () => (typeof navigator !== 'undefined' ? navigator.platform : ''),
|
||||
user_agent: () => (typeof navigator !== 'undefined' ? navigator.userAgent : ''),
|
||||
} as Record<string, () => string>;
|
||||
|
||||
/**
|
||||
@@ -114,7 +120,7 @@ export const VARIABLE_GENERATORS = {
|
||||
*/
|
||||
const extractPlaceholderVariables = (text: string): string[] => {
|
||||
const matches = [...text.matchAll(placeholderVariablesRegex)];
|
||||
return matches.map(m => m[1].trim());
|
||||
return matches.map((m) => m[1].trim());
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -132,7 +138,7 @@ export const parsePlaceholderVariables = (text: string, depth = 2): string => {
|
||||
const variables = Object.fromEntries(
|
||||
extractPlaceholderVariables(result)
|
||||
.map((key) => [key, VARIABLE_GENERATORS[key]?.()])
|
||||
.filter(([, value]) => value !== undefined)
|
||||
.filter(([, value]) => value !== undefined),
|
||||
);
|
||||
|
||||
const replaced = template(result, { interpolate: placeholderVariablesRegex })(variables);
|
||||
@@ -153,7 +159,7 @@ export const parsePlaceholderVariables = (text: string, depth = 2): string => {
|
||||
* @returns 处理后的消息数组
|
||||
*/
|
||||
export const parsePlaceholderVariablesMessages = (messages: any[]): any[] =>
|
||||
messages.map(message => {
|
||||
messages.map((message) => {
|
||||
if (!message?.content) return message;
|
||||
|
||||
const { content } = message;
|
||||
@@ -167,11 +173,9 @@ export const parsePlaceholderVariablesMessages = (messages: any[]): any[] =>
|
||||
if (Array.isArray(content)) {
|
||||
return {
|
||||
...message,
|
||||
content: content.map(item =>
|
||||
item?.type === 'text'
|
||||
? { ...item, text: parsePlaceholderVariables(item.text) }
|
||||
: item
|
||||
)
|
||||
content: content.map((item) =>
|
||||
item?.type === 'text' ? { ...item, text: parsePlaceholderVariables(item.text) } : item,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import dayjs from 'dayjs';
|
||||
import { beforeAll, describe, expect, it } from 'vitest';
|
||||
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { ChatTopic } from '@/types/topic';
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import compressImage from './compressImage';
|
||||
|
||||
const getContextSpy = vi.spyOn(global.HTMLCanvasElement.prototype, 'getContext');
|
||||
@@ -1,9 +1,8 @@
|
||||
import { MESSAGE_CANCEL_FLAT } from '@lobechat/const';
|
||||
import { ChatMessageError } from '@lobechat/types';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { MESSAGE_CANCEL_FLAT } from '@/const/message';
|
||||
import { ChatMessageError } from '@/types/message';
|
||||
import { sleep } from '@/utils/sleep';
|
||||
|
||||
import { sleep } from '../../sleep';
|
||||
import { FetchEventSourceInit } from '../fetchEventSource';
|
||||
import { fetchEventSource } from '../fetchEventSource';
|
||||
import { fetchSSE } from '../fetchSSE';
|
||||
@@ -15,8 +15,8 @@ import {
|
||||
} from '@/types/message';
|
||||
import { ChatImageChunk } from '@/types/message/image';
|
||||
import { GroundingSearch } from '@/types/search';
|
||||
import { nanoid } from '@/utils/uuid';
|
||||
|
||||
import { nanoid } from '../uuid';
|
||||
import { fetchEventSource } from './fetchEventSource';
|
||||
import { getMessageError } from './parseError';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { vi } from 'vitest';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { getModelPropertyWithFallback } from './getFallbackModelProperty';
|
||||
|
||||
5
packages/utils/src/index.ts
Normal file
5
packages/utils/src/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export * from './client/cookie';
|
||||
export * from './getFallbackModelProperty';
|
||||
export * from './imageToBase64';
|
||||
export * from './safeParseJSON';
|
||||
export * from './uuid';
|
||||
@@ -2,7 +2,8 @@ import { resolveAcceptLanguage } from 'resolve-accept-language';
|
||||
|
||||
import { DEFAULT_LANG } from '@/const/locale';
|
||||
import { Locales, locales, normalizeLocale } from '@/locales/resources';
|
||||
import { RouteVariants } from '@/utils/server/routeVariants';
|
||||
|
||||
import { RouteVariants } from './server/routeVariants';
|
||||
|
||||
export const getAntdLocale = async (lang?: string) => {
|
||||
let normalLang: any = normalizeLocale(lang);
|
||||
@@ -1,6 +1,4 @@
|
||||
import { expect } from 'vitest';
|
||||
|
||||
import { AIChatModelCard } from '@/types/aiModel';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { mergeArrayById } from './merge';
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { produce } from 'immer';
|
||||
|
||||
import { AiFullModelCard, AiModelType } from '@/types/aiModel';
|
||||
import { getModelPropertyWithFallback } from '@/utils/getFallbackModelProperty';
|
||||
import { merge } from '@/utils/merge';
|
||||
|
||||
import { getModelPropertyWithFallback } from './getFallbackModelProperty';
|
||||
import { merge } from './merge';
|
||||
|
||||
/**
|
||||
* Parse model string to add or remove models.
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { isArc, isSonomaOrLaterSafari } from './platform';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user