refactor: 重构项目结构和代码组织
This commit is contained in:
@@ -1,13 +0,0 @@
|
||||
// server/src/app.module.ts
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true, // 全局可用,无需在每个模块导入
|
||||
envFilePath: '.env', // 指定 .env 文件路径
|
||||
}),
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
0
server/src/controllers/character.controller.ts
Normal file
0
server/src/controllers/character.controller.ts
Normal file
0
server/src/controllers/chat.controller.ts
Normal file
0
server/src/controllers/chat.controller.ts
Normal file
0
server/src/controllers/import-export.controller.ts
Normal file
0
server/src/controllers/import-export.controller.ts
Normal file
0
server/src/controllers/llm.controller.ts
Normal file
0
server/src/controllers/llm.controller.ts
Normal file
0
server/src/core/config/config.service.ts
Normal file
0
server/src/core/config/config.service.ts
Normal file
0
server/src/core/di/tokens.ts
Normal file
0
server/src/core/di/tokens.ts
Normal file
0
server/src/core/filters/http-exception.filter.ts
Normal file
0
server/src/core/filters/http-exception.filter.ts
Normal file
0
server/src/core/interceptors/logging.interceptor.ts
Normal file
0
server/src/core/interceptors/logging.interceptor.ts
Normal file
0
server/src/core/utils/helpers.ts
Normal file
0
server/src/core/utils/helpers.ts
Normal file
0
server/src/llm/ai-sdk.service.ts
Normal file
0
server/src/llm/ai-sdk.service.ts
Normal file
0
server/src/llm/llm.module.ts
Normal file
0
server/src/llm/llm.module.ts
Normal file
0
server/src/llm/providers/claude.provider.ts
Normal file
0
server/src/llm/providers/claude.provider.ts
Normal file
0
server/src/llm/providers/local.provider.ts
Normal file
0
server/src/llm/providers/local.provider.ts
Normal file
0
server/src/llm/providers/openai.provider.ts
Normal file
0
server/src/llm/providers/openai.provider.ts
Normal file
0
server/src/llm/tools/tool-registry.ts
Normal file
0
server/src/llm/tools/tool-registry.ts
Normal file
@@ -1,10 +0,0 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
app.enableCors(); // Allow cross-origin
|
||||
await app.listen(3000);
|
||||
console.log(`Application is running on: ${await app.getUrl()}`);
|
||||
}
|
||||
bootstrap();
|
||||
|
||||
0
server/src/modules/character.module.ts
Normal file
0
server/src/modules/character.module.ts
Normal file
0
server/src/modules/chat.module.ts
Normal file
0
server/src/modules/chat.module.ts
Normal file
@@ -1,182 +0,0 @@
|
||||
/**
|
||||
* Chat Adapter
|
||||
* Converts between SillyTavern JSONL format and internal format
|
||||
*/
|
||||
|
||||
import type {
|
||||
STChatMetadata,
|
||||
STChatMessage,
|
||||
STChatSession,
|
||||
} from '@shared/sillytavern/chat.types';
|
||||
import type {
|
||||
ExtendedChatMetadata,
|
||||
ExtendedChatMessage,
|
||||
ExtendedChatSession,
|
||||
DynamicHeader,
|
||||
} from '@shared/extended/chat-ext.types';
|
||||
import type { InternalChatSession } from '@shared/adapters/import-export.types';
|
||||
|
||||
export class ChatAdapter {
|
||||
/**
|
||||
* Import from SillyTavern JSONL format
|
||||
*/
|
||||
importFromST(jsonlContent: string): InternalChatSession {
|
||||
const lines = jsonlContent.trim().split('\n');
|
||||
if (lines.length === 0) {
|
||||
throw new Error('Empty JSONL content');
|
||||
}
|
||||
|
||||
// Parse metadata (first line)
|
||||
const metadataLine = JSON.parse(lines[0]) as STChatMetadata;
|
||||
const internalMetadata = this.convertMetadata(metadataLine);
|
||||
|
||||
// Parse messages (subsequent lines)
|
||||
const messages: ExtendedChatMessage[] = [];
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
if (lines[i].trim()) {
|
||||
const stMessage = JSON.parse(lines[i]) as STChatMessage;
|
||||
messages.push(this.convertMessage(stMessage));
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: this.generateId(),
|
||||
characterId: this.generateId(), // Will be set by caller
|
||||
metadata: internalMetadata,
|
||||
messages,
|
||||
createdAt: new Date(metadataLine.create_date),
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Export to SillyTavern JSONL format
|
||||
*/
|
||||
exportToST(chat: InternalChatSession): string {
|
||||
const lines: string[] = [];
|
||||
|
||||
// Convert metadata to ST format
|
||||
const stMetadata = this.convertMetadataToST(chat.metadata);
|
||||
lines.push(JSON.stringify(stMetadata));
|
||||
|
||||
// Convert messages to ST format
|
||||
for (const message of chat.messages) {
|
||||
const stMessage = this.convertMessageToST(message);
|
||||
lines.push(JSON.stringify(stMessage));
|
||||
}
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse JSONL to structured ST data
|
||||
*/
|
||||
parseJSONL(jsonlContent: string): STChatSession {
|
||||
const lines = jsonlContent.trim().split('\n');
|
||||
if (lines.length === 0) {
|
||||
throw new Error('Empty JSONL content');
|
||||
}
|
||||
|
||||
const metadata = JSON.parse(lines[0]) as STChatMetadata;
|
||||
const messages: STChatMessage[] = [];
|
||||
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
if (lines[i].trim()) {
|
||||
messages.push(JSON.parse(lines[i]) as STChatMessage);
|
||||
}
|
||||
}
|
||||
|
||||
return { metadata, messages };
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert structured ST data to JSONL
|
||||
*/
|
||||
toJSONL(session: STChatSession): string {
|
||||
const lines: string[] = [JSON.stringify(session.metadata)];
|
||||
|
||||
for (const message of session.messages) {
|
||||
lines.push(JSON.stringify(message));
|
||||
}
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert ST metadata to internal metadata
|
||||
*/
|
||||
private convertMetadata(stMetadata: STChatMetadata): ExtendedChatMetadata {
|
||||
return {
|
||||
characterName: stMetadata.character_name,
|
||||
dynamicHeaders: [], // Default empty, will be populated by user
|
||||
characterDescription: '', // Will be set from character card
|
||||
characterAvatar: undefined,
|
||||
aiCharacterName: stMetadata.character_name,
|
||||
creatorNotes: '',
|
||||
firstMessages: [],
|
||||
systemPrompt: '',
|
||||
extensions: stMetadata.chat_metadata || {},
|
||||
createdAt: new Date(stMetadata.create_date),
|
||||
updatedAt: new Date(),
|
||||
tags: [],
|
||||
category: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert internal metadata to ST metadata
|
||||
*/
|
||||
private convertMetadataToST(metadata: ExtendedChatMetadata): STChatMetadata {
|
||||
return {
|
||||
user_name: 'User', // Default, can be customized
|
||||
character_name: metadata.characterName,
|
||||
create_date: metadata.createdAt.toISOString(),
|
||||
chat_metadata: metadata.extensions,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert ST message to internal message
|
||||
*/
|
||||
private convertMessage(stMessage: STChatMessage): ExtendedChatMessage {
|
||||
return {
|
||||
senderName: stMessage.name,
|
||||
senderRole: stMessage.is_user ? 'user' : 'assistant',
|
||||
mes: stMessage.mes,
|
||||
swipes: stMessage.extra?.swipes,
|
||||
reasoning: stMessage.extra?.reasoning,
|
||||
image: stMessage.extra?.image,
|
||||
audio: stMessage.extra?.audio,
|
||||
timestamp: new Date(stMessage.send_date),
|
||||
tokens: undefined,
|
||||
model: stMessage.extra?.model,
|
||||
dynamicData: {},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert internal message to ST message
|
||||
*/
|
||||
private convertMessageToST(message: ExtendedChatMessage): STChatMessage {
|
||||
return {
|
||||
name: message.senderName,
|
||||
is_user: message.senderRole === 'user',
|
||||
send_date: message.timestamp.getTime(),
|
||||
mes: message.mes,
|
||||
extra: {
|
||||
swipes: message.swipes,
|
||||
reasoning: message.reasoning,
|
||||
image: message.image,
|
||||
audio: message.audio,
|
||||
model: message.model,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate unique ID
|
||||
*/
|
||||
private generateId(): string {
|
||||
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
/**
|
||||
* Adapter Usage Examples
|
||||
* Demonstrates how to use the adapters for import/export
|
||||
*/
|
||||
|
||||
import { ChatAdapter } from '../chat/adapters/chat.adapter';
|
||||
import { WorldInfoAdapter } from '../world-info/adapters/world-info.adapter';
|
||||
import { PresetAdapter } from '../preset/adapters/preset.adapter';
|
||||
|
||||
// ============================================
|
||||
// Example 1: Chat Import/Export
|
||||
// ============================================
|
||||
|
||||
export function chatExample() {
|
||||
const adapter = new ChatAdapter();
|
||||
|
||||
// Sample SillyTavern JSONL content
|
||||
const jsonlContent = `{"user_name":"User","character_name":"Assistant","create_date":"2024-01-01T12:00:00.000Z"}
|
||||
{"name":"User","is_user":true,"send_date":1704115200000,"mes":"你好!"}
|
||||
{"name":"Assistant","is_user":false,"send_date":1704115210000,"mes":"你好!有什么可以帮助你的吗?"}`;
|
||||
|
||||
// Import from ST format
|
||||
const internalChat = adapter.importFromST(jsonlContent);
|
||||
console.log('Imported chat:', internalChat.metadata.characterName);
|
||||
console.log('Message count:', internalChat.messages.length);
|
||||
|
||||
// Export back to ST format
|
||||
const exportedJsonl = adapter.exportToST(internalChat);
|
||||
console.log('Exported JSONL length:', exportedJsonl.length);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Example 2: World Info with Unified Triggers
|
||||
// ============================================
|
||||
|
||||
export function worldInfoExample() {
|
||||
const adapter = new WorldInfoAdapter();
|
||||
|
||||
// Sample ST World Info
|
||||
const stWorldInfo = {
|
||||
name: '奇幻世界',
|
||||
description: '一个魔法世界',
|
||||
entries: [
|
||||
{
|
||||
key: ['魔法', '法术'],
|
||||
content: '这个世界充满了魔法能量。',
|
||||
constant: false,
|
||||
selective: false,
|
||||
order: 1,
|
||||
position: 'after_char' as const,
|
||||
use_regex: false,
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
key: [],
|
||||
content: '这是一个永恒的背景设定。',
|
||||
constant: true, // Permanent trigger
|
||||
selective: false,
|
||||
order: 0,
|
||||
position: 'before_char' as const,
|
||||
use_regex: false,
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Import from ST format (converts to unified triggers)
|
||||
const extendedWorldInfo = adapter.importFromST(stWorldInfo);
|
||||
console.log('World Info:', extendedWorldInfo.name);
|
||||
console.log('Entry 1 triggers:', extendedWorldInfo.entries[0].triggers);
|
||||
|
||||
// Export back to ST format (converts triggers back to keys + constant)
|
||||
const exportedST = adapter.exportToST(extendedWorldInfo);
|
||||
console.log('Exported entries:', exportedST.entries.length);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Example 3: Preset with Position Anchors
|
||||
// ============================================
|
||||
|
||||
export function presetExample() {
|
||||
const adapter = new PresetAdapter();
|
||||
|
||||
// Sample ST Preset
|
||||
const stPreset = {
|
||||
name: '创意写作预设',
|
||||
temp: 0.9,
|
||||
top_p: 0.95,
|
||||
top_k: 50,
|
||||
rep_pen: 1.05,
|
||||
maxContextTokens: 8192,
|
||||
streaming: true,
|
||||
description: '用于创意写作的预设配置',
|
||||
};
|
||||
|
||||
// Import from ST format
|
||||
const extendedPreset = adapter.importFromST(stPreset);
|
||||
console.log('Preset:', extendedPreset.name);
|
||||
console.log('Temperature:', extendedPreset.temperature);
|
||||
console.log('Entries count:', extendedPreset.entries.length);
|
||||
console.log('Entry positions:', extendedPreset.entries.map((e) => e.position));
|
||||
|
||||
// Export back to ST format
|
||||
const exportedST = adapter.exportToST(extendedPreset);
|
||||
console.log('Exported preset:', exportedST.name);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Run examples
|
||||
// ============================================
|
||||
|
||||
if (require.main === module) {
|
||||
console.log('=== Chat Example ===');
|
||||
chatExample();
|
||||
|
||||
console.log('\n=== World Info Example ===');
|
||||
worldInfoExample();
|
||||
|
||||
console.log('\n=== Preset Example ===');
|
||||
presetExample();
|
||||
}
|
||||
0
server/src/modules/llm.module.ts
Normal file
0
server/src/modules/llm.module.ts
Normal file
@@ -1,94 +0,0 @@
|
||||
/**
|
||||
* Preset Adapter
|
||||
* Converts between SillyTavern preset format and extended format with position anchors
|
||||
*/
|
||||
|
||||
import type { STPreset } from '@shared/sillytavern/preset.types';
|
||||
import type { ExtendedPreset, PresetEntry, PresetPosition } from '@shared/extended/preset-ext.types';
|
||||
|
||||
export class PresetAdapter {
|
||||
/**
|
||||
* Import from SillyTavern format
|
||||
*/
|
||||
importFromST(stPreset: STPreset): ExtendedPreset {
|
||||
return {
|
||||
id: this.generateId(),
|
||||
name: stPreset.name,
|
||||
description: stPreset.description,
|
||||
temperature: stPreset.temp || 0.7,
|
||||
maxReplyLength: 500, // Default
|
||||
topP: stPreset.top_p || 0.9,
|
||||
topK: stPreset.top_k || 40,
|
||||
repetitionPenalty: stPreset.rep_pen || 1.1,
|
||||
entryOrder: [], // Will be populated by user
|
||||
maxContextTokens: stPreset.maxContextTokens || 4096,
|
||||
streaming: stPreset.streaming || false,
|
||||
createdAt: new Date(stPreset.created_at || Date.now()),
|
||||
updatedAt: new Date(stPreset.updated_at || Date.now()),
|
||||
entries: this.createDefaultEntries(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Export to SillyTavern format
|
||||
*/
|
||||
exportToST(extendedPreset: ExtendedPreset): STPreset {
|
||||
return {
|
||||
name: extendedPreset.name,
|
||||
description: extendedPreset.description,
|
||||
temp: extendedPreset.temperature,
|
||||
top_p: extendedPreset.topP,
|
||||
top_k: extendedPreset.topK,
|
||||
rep_pen: extendedPreset.repetitionPenalty,
|
||||
maxContextTokens: extendedPreset.maxContextTokens,
|
||||
streaming: extendedPreset.streaming,
|
||||
created_at: extendedPreset.createdAt.getTime(),
|
||||
updated_at: extendedPreset.updatedAt.getTime(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create default preset entries with fixed positions
|
||||
*/
|
||||
private createDefaultEntries(): PresetEntry[] {
|
||||
const positions: PresetPosition[] = [
|
||||
'system',
|
||||
'world_info_before',
|
||||
'persona_description',
|
||||
'char_description',
|
||||
'world_info_after',
|
||||
'history',
|
||||
'user_input',
|
||||
];
|
||||
|
||||
return positions.map((position, index) => ({
|
||||
name: this.getPositionName(position),
|
||||
context: '',
|
||||
position,
|
||||
order: index,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get human-readable name for position
|
||||
*/
|
||||
private getPositionName(position: PresetPosition): string {
|
||||
const names: Record<PresetPosition, string> = {
|
||||
system: '系统提示词',
|
||||
world_info_before: '世界书(前置)',
|
||||
persona_description: '角色设定描述',
|
||||
char_description: '角色详细描述',
|
||||
world_info_after: '世界书(后置)',
|
||||
history: '聊天记录',
|
||||
user_input: '用户输入',
|
||||
};
|
||||
return names[position];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate unique ID
|
||||
*/
|
||||
private generateId(): string {
|
||||
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
}
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
/**
|
||||
* World Info Adapter
|
||||
* Converts between SillyTavern format and extended format with unified triggers
|
||||
*/
|
||||
|
||||
import type { STWorldInfo, STWorldInfoEntry } from '@shared/sillytavern/world.types';
|
||||
import type {
|
||||
ExtendedWorldInfo,
|
||||
ExtendedWorldInfoEntry,
|
||||
TriggerConfig,
|
||||
} from '@shared/extended/world-ext.types';
|
||||
|
||||
export class WorldInfoAdapter {
|
||||
/**
|
||||
* Import from SillyTavern format
|
||||
*/
|
||||
importFromST(stWorldInfo: STWorldInfo): ExtendedWorldInfo {
|
||||
return {
|
||||
id: this.generateId(),
|
||||
name: stWorldInfo.name,
|
||||
description: stWorldInfo.description,
|
||||
entries: stWorldInfo.entries.map((entry) => this.importEntryFromST(entry)),
|
||||
metadata: {
|
||||
entryCount: stWorldInfo.entries.length,
|
||||
categories: [],
|
||||
tags: [],
|
||||
},
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Export to SillyTavern format
|
||||
*/
|
||||
exportToST(extendedWorldInfo: ExtendedWorldInfo): STWorldInfo {
|
||||
return {
|
||||
name: extendedWorldInfo.name,
|
||||
description: extendedWorldInfo.description,
|
||||
entries: extendedWorldInfo.entries.map((entry) => this.exportEntryToST(entry)),
|
||||
metadata: {
|
||||
created_at: extendedWorldInfo.createdAt.getTime(),
|
||||
updated_at: extendedWorldInfo.updatedAt.getTime(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert unified trigger config to ST format (keys + constant)
|
||||
*/
|
||||
triggersToST(triggers: TriggerConfig): { keys: string[]; constant: boolean } {
|
||||
// If permanent trigger is enabled, set constant = true
|
||||
if (triggers.permanent.enabled) {
|
||||
return {
|
||||
keys: [],
|
||||
constant: true,
|
||||
};
|
||||
}
|
||||
|
||||
// Otherwise, use keyword triggers
|
||||
if (triggers.keyword.enabled) {
|
||||
return {
|
||||
keys: triggers.keyword.keywords,
|
||||
constant: false,
|
||||
};
|
||||
}
|
||||
|
||||
// Default: no triggers
|
||||
return {
|
||||
keys: [],
|
||||
constant: false,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert ST format (keys + constant) to unified trigger config
|
||||
*/
|
||||
triggersFromST(keys: string[], constant: boolean): TriggerConfig {
|
||||
if (constant) {
|
||||
// Permanent trigger
|
||||
return {
|
||||
permanent: { enabled: true },
|
||||
keyword: {
|
||||
enabled: false,
|
||||
keywords: [],
|
||||
useRegex: false,
|
||||
matchWholeWords: false,
|
||||
caseSensitive: false,
|
||||
},
|
||||
rag: { enabled: false, databaseName: '' },
|
||||
variable: {
|
||||
enabled: false,
|
||||
variableA: '',
|
||||
operator: '=',
|
||||
},
|
||||
};
|
||||
} else if (keys && keys.length > 0) {
|
||||
// Keyword trigger
|
||||
return {
|
||||
permanent: { enabled: false },
|
||||
keyword: {
|
||||
enabled: true,
|
||||
keywords: keys,
|
||||
useRegex: false,
|
||||
matchWholeWords: true,
|
||||
caseSensitive: false,
|
||||
},
|
||||
rag: { enabled: false, databaseName: '' },
|
||||
variable: {
|
||||
enabled: false,
|
||||
variableA: '',
|
||||
operator: '=',
|
||||
},
|
||||
};
|
||||
} else {
|
||||
// No triggers
|
||||
return {
|
||||
permanent: { enabled: false },
|
||||
keyword: {
|
||||
enabled: false,
|
||||
keywords: [],
|
||||
useRegex: false,
|
||||
matchWholeWords: false,
|
||||
caseSensitive: false,
|
||||
},
|
||||
rag: { enabled: false, databaseName: '' },
|
||||
variable: {
|
||||
enabled: false,
|
||||
variableA: '',
|
||||
operator: '=',
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Import single entry from ST format
|
||||
*/
|
||||
private importEntryFromST(stEntry: STWorldInfoEntry): ExtendedWorldInfoEntry {
|
||||
const triggers = this.triggersFromST(stEntry.key, stEntry.constant);
|
||||
|
||||
return {
|
||||
id: stEntry.uid?.toString() || this.generateId(),
|
||||
content: stEntry.content,
|
||||
position: stEntry.position,
|
||||
scanDepth: stEntry.depth || stEntry.scan_depth || 4,
|
||||
priority: stEntry.order,
|
||||
triggers,
|
||||
enabled: stEntry.enabled,
|
||||
category: undefined,
|
||||
tags: [],
|
||||
usageCount: 0,
|
||||
lastUsed: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Export single entry to ST format
|
||||
*/
|
||||
private exportEntryToST(entry: ExtendedWorldInfoEntry): STWorldInfoEntry {
|
||||
const { keys, constant } = this.triggersToST(entry.triggers);
|
||||
|
||||
return {
|
||||
uid: parseInt(entry.id) || undefined,
|
||||
key: keys,
|
||||
content: entry.content,
|
||||
constant,
|
||||
selective: entry.triggers.keyword.enabled && entry.triggers.keyword.keywords.length > 1,
|
||||
order: entry.priority,
|
||||
position: entry.position,
|
||||
use_regex: entry.triggers.keyword.useRegex,
|
||||
match_whole_words: entry.triggers.keyword.matchWholeWords,
|
||||
case_sensitive: entry.triggers.keyword.caseSensitive,
|
||||
depth: entry.scanDepth,
|
||||
enabled: entry.enabled,
|
||||
comment: undefined,
|
||||
display_index: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate unique ID
|
||||
*/
|
||||
private generateId(): string {
|
||||
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
}
|
||||
}
|
||||
0
server/src/persistence/file-system/file.manager.ts
Normal file
0
server/src/persistence/file-system/file.manager.ts
Normal file
0
server/src/persistence/file-system/lock.manager.ts
Normal file
0
server/src/persistence/file-system/lock.manager.ts
Normal file
0
server/src/persistence/interfaces/i-chat-repo.ts
Normal file
0
server/src/persistence/interfaces/i-chat-repo.ts
Normal file
0
server/src/persistence/persistence.module.ts
Normal file
0
server/src/persistence/persistence.module.ts
Normal file
0
server/src/services/character.service.ts
Normal file
0
server/src/services/character.service.ts
Normal file
0
server/src/services/chat.service.ts
Normal file
0
server/src/services/chat.service.ts
Normal file
0
server/src/services/data-migration.service.ts
Normal file
0
server/src/services/data-migration.service.ts
Normal file
0
server/src/services/workflow.service.ts
Normal file
0
server/src/services/workflow.service.ts
Normal file
0
server/src/tools/context-chunker.ts
Normal file
0
server/src/tools/context-chunker.ts
Normal file
0
server/src/tools/prompt-assembler.ts
Normal file
0
server/src/tools/prompt-assembler.ts
Normal file
0
server/src/tools/token-counter.ts
Normal file
0
server/src/tools/token-counter.ts
Normal file
Reference in New Issue
Block a user