mirror of
https://github.com/docmost/docmost.git
synced 2025-11-18 01:51:12 +10:00
refine
This commit is contained in:
@ -30,7 +30,6 @@
|
||||
"test:e2e": "jest --config test/jest-e2e.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/amazon-bedrock": "^3.0.35",
|
||||
"@ai-sdk/azure": "^2.0.47",
|
||||
"@ai-sdk/google": "^2.0.18",
|
||||
"@ai-sdk/openai": "^2.0.46",
|
||||
|
||||
@ -22,4 +22,12 @@ export class UpdateWorkspaceDto extends PartialType(CreateWorkspaceDto) {
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
restrictApiToAdmins: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
aiSearch: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
generativeAi: boolean;
|
||||
}
|
||||
|
||||
@ -312,6 +312,30 @@ export class WorkspaceService {
|
||||
delete updateWorkspaceDto.restrictApiToAdmins;
|
||||
}
|
||||
|
||||
if (typeof updateWorkspaceDto.aiSearch !== 'undefined') {
|
||||
await this.workspaceRepo.updateAiSettings(
|
||||
workspaceId,
|
||||
'aiSearch',
|
||||
updateWorkspaceDto.aiSearch,
|
||||
);
|
||||
|
||||
// to enable this
|
||||
// we need to check if pgvector and embeddings table exists
|
||||
|
||||
delete updateWorkspaceDto.aiSearch;
|
||||
// if true, send to ai queue
|
||||
// if false, send to delete embeddings
|
||||
}
|
||||
|
||||
if (typeof updateWorkspaceDto.generativeAi !== 'undefined') {
|
||||
await this.workspaceRepo.updateAiSettings(
|
||||
workspaceId,
|
||||
'generativeAi',
|
||||
updateWorkspaceDto.generativeAi,
|
||||
);
|
||||
delete updateWorkspaceDto.generativeAi;
|
||||
}
|
||||
|
||||
await this.workspaceRepo.updateWorkspace(updateWorkspaceDto, workspaceId);
|
||||
|
||||
const workspace = await this.workspaceRepo.findById(workspaceId, {
|
||||
|
||||
@ -26,18 +26,15 @@ export class PageListener {
|
||||
if (this.isTypesense()) {
|
||||
await this.searchQueue.add(QueueJob.PAGE_CREATED, { pageIds });
|
||||
}
|
||||
if (this.environmentService.isAISearchEnabled()) {
|
||||
await this.aiQueue.add(QueueJob.PAGE_CREATED, { pageIds });
|
||||
}
|
||||
|
||||
await this.aiQueue.add(QueueJob.PAGE_CREATED, { pageIds });
|
||||
}
|
||||
|
||||
@OnEvent(EventName.PAGE_UPDATED)
|
||||
async handlePageUpdated(event: PageEvent) {
|
||||
const { pageIds } = event;
|
||||
|
||||
if (this.isTypesense()) {
|
||||
await this.searchQueue.add(QueueJob.PAGE_UPDATED, { pageIds });
|
||||
}
|
||||
await this.searchQueue.add(QueueJob.PAGE_UPDATED, { pageIds });
|
||||
}
|
||||
|
||||
@OnEvent(EventName.PAGE_DELETED)
|
||||
@ -68,9 +65,7 @@ export class PageListener {
|
||||
await this.searchQueue.add(QueueJob.PAGE_RESTORED, { pageIds });
|
||||
}
|
||||
|
||||
if (this.environmentService.isAISearchEnabled()) {
|
||||
await this.aiQueue.add(QueueJob.PAGE_RESTORED, { pageIds });
|
||||
}
|
||||
await this.aiQueue.add(QueueJob.PAGE_RESTORED, { pageIds });
|
||||
}
|
||||
|
||||
isTypesense(): boolean {
|
||||
|
||||
@ -27,9 +27,7 @@ export class SpaceListener {
|
||||
await this.searchQueue.add(QueueJob.SPACE_DELETED, { spaceId });
|
||||
}
|
||||
|
||||
if (this.environmentService.isAISearchEnabled()) {
|
||||
await this.aiQueue.add(QueueJob.SPACE_DELETED, { spaceId });
|
||||
}
|
||||
await this.aiQueue.add(QueueJob.SPACE_DELETED, { spaceId });
|
||||
}
|
||||
|
||||
isTypesense(): boolean {
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
import { type Kysely } from 'kysely';
|
||||
|
||||
export async function up(db: Kysely<any>): Promise<void> {
|
||||
await db.schema
|
||||
.alterTable('workspaces')
|
||||
.addColumn('enable_ai', 'boolean', (col) => col.defaultTo(false))
|
||||
.addColumn('enable_ai_search', 'boolean', (col) => col.defaultTo(false))
|
||||
.execute();
|
||||
}
|
||||
|
||||
export async function down(db: Kysely<any>): Promise<void> {
|
||||
await db.schema.alterTable('workspaces').dropColumn('enable_ai').execute();
|
||||
await db.schema.alterTable('workspaces').dropColumn('enable_ai_search').execute();
|
||||
}
|
||||
@ -175,4 +175,22 @@ export class WorkspaceRepo {
|
||||
.returning(this.baseFields)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
async updateAiSettings(
|
||||
workspaceId: string,
|
||||
prefKey: string,
|
||||
prefValue: string | boolean,
|
||||
) {
|
||||
return this.db
|
||||
.updateTable('workspaces')
|
||||
.set({
|
||||
settings: sql`COALESCE(settings, '{}'::jsonb)
|
||||
|| jsonb_build_object('ai', COALESCE(settings->'ai', '{}'::jsonb)
|
||||
|| jsonb_build_object('${sql.raw(prefKey)}', ${sql.lit(prefValue)}))`,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where('id', '=', workspaceId)
|
||||
.returning(this.baseFields)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
}
|
||||
|
||||
Submodule apps/server/src/ee updated: 05b042cbb0...f7ff3ff80d
@ -10,6 +10,10 @@ export class EnvironmentService {
|
||||
return this.configService.get<string>('NODE_ENV', 'development');
|
||||
}
|
||||
|
||||
isDevelopment(): boolean {
|
||||
return this.getNodeEnv() === 'development';
|
||||
}
|
||||
|
||||
getAppUrl(): string {
|
||||
const rawUrl =
|
||||
this.configService.get<string>('APP_URL') ||
|
||||
@ -237,23 +241,20 @@ export class EnvironmentService {
|
||||
}
|
||||
|
||||
getAiDriver(): string {
|
||||
return this.configService.get<string>('AI_DRIVER', 'openai');
|
||||
return this.configService.get<string>('AI_DRIVER');
|
||||
}
|
||||
|
||||
getAiEmbeddingModel(): string {
|
||||
return this.configService.get<string>(
|
||||
'AI_EMBEDDING_MODEL',
|
||||
'text-embedding-3-small',
|
||||
);
|
||||
return this.configService.get<string>('AI_EMBEDDING_MODEL');
|
||||
}
|
||||
|
||||
getAiCompletionModel(): string {
|
||||
return this.configService.get<string>('AI_COMPLETION_MODEL', 'gpt-4o-mini');
|
||||
return this.configService.get<string>('AI_COMPLETION_MODEL');
|
||||
}
|
||||
|
||||
getAiEmbeddingDimension(): number {
|
||||
return parseInt(
|
||||
this.configService.get<string>('AI_EMBEDDING_DIMENSION', '1536'),
|
||||
this.configService.get<string>('AI_EMBEDDING_DIMENSION'),
|
||||
10,
|
||||
);
|
||||
}
|
||||
@ -266,8 +267,8 @@ export class EnvironmentService {
|
||||
return this.configService.get<string>('OPENAI_API_URL');
|
||||
}
|
||||
|
||||
getGoogleAiApiKey(): string {
|
||||
return this.configService.get<string>('GOOGLE_AI_API_KEY');
|
||||
getGeminiApiKey(): string {
|
||||
return this.configService.get<string>('GEMINI_API_KEY');
|
||||
}
|
||||
|
||||
getOllamaApiUrl(): string {
|
||||
@ -276,27 +277,4 @@ export class EnvironmentService {
|
||||
'http://localhost:11434',
|
||||
);
|
||||
}
|
||||
|
||||
getAwsAccessKeyId(): string {
|
||||
return this.configService.get<string>('AWS_ACCESS_KEY_ID');
|
||||
}
|
||||
|
||||
getAwsSecretAccessKey(): string {
|
||||
return this.configService.get<string>('AWS_SECRET_ACCESS_KEY');
|
||||
}
|
||||
|
||||
getAwsBedrockRegion(): string {
|
||||
return this.configService.get<string>('AWS_BEDROCK_REGION');
|
||||
}
|
||||
|
||||
isAIEnabled(): string {
|
||||
return this.configService.get<string>('ENABLE_AI');
|
||||
}
|
||||
|
||||
isAISearchEnabled(): boolean {
|
||||
const config = this.configService
|
||||
.get<string>('AI_SEARCH', 'false')
|
||||
.toLowerCase();
|
||||
return config === 'true';
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,6 +93,7 @@ export class EnvironmentVariables {
|
||||
|
||||
@IsOptional()
|
||||
@ValidateIf((obj) => obj.SEARCH_DRIVER === 'typesense')
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
TYPESENSE_API_KEY: string;
|
||||
|
||||
@ -101,6 +102,53 @@ export class EnvironmentVariables {
|
||||
@IsISO6391()
|
||||
@IsString()
|
||||
TYPESENSE_LOCALE: string;
|
||||
|
||||
@IsOptional()
|
||||
@ValidateIf((obj) => obj.AI_DRIVER)
|
||||
@IsIn(['openai', 'gemini', 'ollama'])
|
||||
@IsString()
|
||||
AI_DRIVER: string;
|
||||
|
||||
@IsOptional()
|
||||
@ValidateIf((obj) => obj.AI_DRIVER)
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
AI_EMBEDDING_MODEL: string;
|
||||
|
||||
@IsOptional()
|
||||
@ValidateIf((obj) => obj.AI_EMBEDDING_DIMENSION)
|
||||
@IsIn(['768', '1024', '1536'])
|
||||
@IsString()
|
||||
AI_EMBEDDING_DIMENSION: string;
|
||||
|
||||
|
||||
@IsOptional()
|
||||
@ValidateIf((obj) => obj.AI_DRIVER)
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
AI_COMPLETION_MODEL: string;
|
||||
|
||||
@IsOptional()
|
||||
@ValidateIf((obj) => obj.AI_DRIVER && obj.AI_DRIVER === 'openai')
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
OPENAI_API_KEY: string;
|
||||
|
||||
@IsOptional()
|
||||
@ValidateIf((obj) => obj.AI_DRIVER && obj.OPENAI_API_URL && obj.AI_DRIVER === 'openai')
|
||||
@IsUrl({ protocols: ['http', 'https'], require_tld: false })
|
||||
OPENAI_API_URL: string;
|
||||
|
||||
@IsOptional()
|
||||
@ValidateIf((obj) => obj.AI_DRIVER && obj.AI_DRIVER === 'gemini')
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
GEMINI_API_KEY: string;
|
||||
|
||||
@IsOptional()
|
||||
@ValidateIf((obj) => obj.AI_DRIVER && obj.AI_DRIVER === 'ollama')
|
||||
@IsUrl({ protocols: ['http', 'https'], require_tld: false })
|
||||
OLLAMA_API_URL: string;
|
||||
}
|
||||
|
||||
export function validate(config: Record<string, any>) {
|
||||
|
||||
@ -53,6 +53,7 @@ export enum QueueJob {
|
||||
WORKSPACE_CREATED = 'workspace-created',
|
||||
WORKSPACE_SPACE_UPDATED = 'workspace-updated',
|
||||
WORKSPACE_DELETED = 'workspace-deleted',
|
||||
WORKSPACE_DELETE_EMBEDDINGS = 'workspace-delete-embeddings',
|
||||
|
||||
GENERATE_PAGE_EMBEDDINGS = 'generate-page-embeddings',
|
||||
DELETE_PAGE_EMBEDDINGS = 'delete-page-embeddings',
|
||||
|
||||
Reference in New Issue
Block a user