Skip to content

Commit

Permalink
Merge pull request #176 from DashHub-ai/feature/add-files-to-organiza…
Browse files Browse the repository at this point in the history
…tion-context

Add files to organization scope project
  • Loading branch information
Mati365 authored Mar 2, 2025
2 parents 81c6f6d + 772b078 commit ba98219
Show file tree
Hide file tree
Showing 31 changed files with 425 additions and 136 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import type { Kysely } from 'kysely';

export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.alterTable('organizations_ai_settings')
.addColumn('project_id', 'integer', col => col.references('projects.id').onDelete('restrict'))
.execute();

const organizations = await db
.selectFrom('organizations')
.select(['id'])
.execute();

const rootUser = await db
.selectFrom('users')
.select(['id'])
.where('role', '=', 'root')
.executeTakeFirst();

if (rootUser) {
for (const [index, organization] of organizations.entries()) {
const project = await db
.insertInto('projects')
.values({
name: `Migrated Organization Project - ${Date.now()}-${index}`,
organization_id: organization.id,
internal: true,
creator_user_id: rootUser.id,
})
.returning(['id'])
.executeTakeFirstOrThrow();

await db
.insertInto('projects_summaries')
.values({
project_id: project.id,
})
.execute();

await db
.insertInto('organizations_ai_settings')
.values({
organization_id: organization.id,
project_id: project.id,
chat_context: null,
})
.onConflict(oc => oc
.column('organization_id')
.doUpdateSet(eb => ({
project_id: eb.ref('excluded.project_id'),
})),
)
.execute();
}
}

await db.schema
.alterTable('organizations_ai_settings')
.alterColumn('project_id', col => col.setNotNull())
.execute();
}

export async function down(db: Kysely<any>): Promise<void> {
const projects = await db
.selectFrom('organizations_ai_settings')
.select(['project_id'])
.where('project_id', 'is not', null)
.execute();

await db.schema
.alterTable('organizations_ai_settings')
.dropColumn('project_id')
.execute();

if (projects.length > 0) {
await db
.deleteFrom('projects_summaries')
.where('project_id', 'in', projects.map(app => app.project_id))
.execute();

await db
.deleteFrom('projects')
.where('id', 'in', projects.map(app => app.project_id))
.execute();
}
}
2 changes: 2 additions & 0 deletions apps/backend/src/migrations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import * as addSearchEnginesTable from './0042-add-search-engines-table';
import * as addWebSearchToMessages from './0043-add-websearch-flag-to-messages';
import * as addPinnedMessages from './0044-add-pinned-messages';
import * as addOrganizationsAISettings from './0045-add-organizations-ai-settings-table';
import * as addProjectsToOrganizationsAISettings from './0046-add-projects-to-organizations-ai-settings';

export const DB_MIGRATIONS = {
'0000-add-users-tables': addUsersTables,
Expand Down Expand Up @@ -92,4 +93,5 @@ export const DB_MIGRATIONS = {
'0043-add-websearch-flag-to-messages': addWebSearchToMessages,
'0044-add-pinned-messages': addPinnedMessages,
'0045-add-organizations-ai-settings-table': addOrganizationsAISettings,
'0046-add-projects-to-organizations-ai-settings': addProjectsToOrganizationsAISettings,
};
4 changes: 2 additions & 2 deletions apps/backend/src/modules/messages/messages.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ export class MessagesService implements WithAuthFirewall<MessagesFirewall> {
);
}),
)),
TE.bindW('mappedContent', ({ message, history }) => pipe(
TE.bindW('mappedContent', ({ message, chat, history }) => pipe(
message.repliedMessage
? createReplyAiMessagePrefix(message.repliedMessage, message.content)
: message.content,
Expand All @@ -176,7 +176,7 @@ export class MessagesService implements WithAuthFirewall<MessagesFirewall> {
prefixedMessage => this.projectsEmbeddingsService.wrapWithEmbeddingContextPrompt({
history,
message: prefixedMessage,
chat: { id: message.chat.id },
chat,
}),
)),
TE.chainW(({ mappedContent, history, message, chat, personalities }) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './organizations-ai-settings.repo';
export * from './organizations-ai-settings.service';
export * from './organizations-ai-settings.tables';
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,39 @@ import { injectable } from 'tsyringe';
import {
AbstractDatabaseRepo,
DatabaseError,
TableId,
TransactionalAttrs,
type TableId,
type TransactionalAttrs,
tryReuseTransactionOrSkip,
} from '~/modules/database';

import type { OrganizationsAISettingsTableInsertRow } from './organizations-ai-settings.tables';

@injectable()
export class OrganizationsAISettingsRepo extends AbstractDatabaseRepo {
getProjectIdByOrganizationIdOrNil = (
{
forwardTransaction,
organizationId,
}: TransactionalAttrs<{ organizationId: TableId; }>,
) => {
const transaction = tryReuseTransactionOrSkip({
db: this.db,
forwardTransaction,
});

return pipe(
transaction(trx =>
trx
.selectFrom('organizations_ai_settings')
.select('project_id as id')
.where('organization_id', '=', organizationId)
.executeTakeFirst(),
),
DatabaseError.tryTask,
TE.map(row => row?.id ?? null),
);
};

getChatContextByOrganizationIdOrNil = (
{
forwardTransaction,
Expand Down Expand Up @@ -42,7 +66,9 @@ export class OrganizationsAISettingsRepo extends AbstractDatabaseRepo {
{
forwardTransaction,
value,
}: TransactionalAttrs<{ value: OrganizationsAISettingsTableInsertRow; }>,
}: TransactionalAttrs<{
value: OrganizationsAISettingsTableInsertRow;
}>,
) => {
const transaction = tryReuseTransactionOrSkip({
db: this.db,
Expand All @@ -56,6 +82,7 @@ export class OrganizationsAISettingsRepo extends AbstractDatabaseRepo {
.values({
organization_id: value.organizationId,
chat_context: value.chatContext || null,
project_id: value.projectId,
})
.onConflict(oc => oc
.column('organization_id')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,46 @@
import { taskEither as TE } from 'fp-ts';
import { pipe } from 'fp-ts/lib/function';
import { inject, injectable } from 'tsyringe';

import { SdkUpsertOrganizationAISettingsInputT } from '@llm/sdk';

import { TableRowWithId } from '../database';
import { ProjectsService } from '../projects';
import { OrganizationsAISettingsRepo } from './organizations-ai-settings.repo';

@injectable()
export class OrganizationsAISettingsService {
constructor(
@inject(OrganizationsAISettingsRepo) private readonly organizationsAISettingsRepo: OrganizationsAISettingsRepo,
@inject(OrganizationsAISettingsRepo) private readonly repo: OrganizationsAISettingsRepo,
@inject(ProjectsService) private readonly projectsService: ProjectsService,
) {}

getChatContextByOrganizationIdOrNil = this.organizationsAISettingsRepo.getChatContextByOrganizationIdOrNil;
getProjectIdByOrganizationIdOrNil = this.repo.getProjectIdByOrganizationIdOrNil;

getChatContextByOrganizationIdOrNil = this.repo.getChatContextByOrganizationIdOrNil;

upsert = ({ chatContext, organization }: SdkUpsertOrganizationAISettingsInputT & { organization: TableRowWithId; }) => pipe(
TE.Do,
TE.bind('project', () => pipe(
this.repo.getProjectIdByOrganizationIdOrNil({
organizationId: organization.id,
}),
TE.chainW((projectId) => {
if (projectId) {
return TE.right({ id: projectId });
}

return this.projectsService.createInternal({
organization,
});
}),
)),
TE.chainW(({ project }) => this.repo.upsert({
value: {
projectId: project.id,
organizationId: organization.id,
chatContext,
},
})),
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@ import type {
NormalizeInsertTableRow,
NormalizeSelectTableRow,
TableId,
TableRowWithIdName,
} from '../database';

export type OrganizationsAISettingsTable = {
organization_id: ColumnType<TableId, TableId, never>;
project_id: ColumnType<TableId, TableId, never>;
chat_context: string | null;
};

export type OrganizationsAISettingsTableRow = NormalizeSelectTableRow<OrganizationsAISettingsTable>;

export type OrganizationsAISettingsTableInsertRow = NormalizeInsertTableRow<OrganizationsAISettingsTable>;

export type OrganizationsAISettingsTableRelationRow = Omit<OrganizationsAISettingsTableRow, 'organizationId'>;
export type OrganizationsAISettingsTableRelationRow =
& Omit<OrganizationsAISettingsTableRow, 'organizationId' | 'projectId'>
& {
project: TableRowWithIdName;
};
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export class OrganizationsEsSearchRepo {
maxNumberOfUsers: source.max_number_of_users ?? 0,
aiSettings: {
chatContext: source.ai_settings?.chat_context ?? null,
project: source.ai_settings?.project,
},
});
}
82 changes: 16 additions & 66 deletions apps/backend/src/modules/organizations/organizations.repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,95 +3,36 @@ import { array as A, taskEither as TE } from 'fp-ts';
import { pipe } from 'fp-ts/lib/function';
import { inject, injectable } from 'tsyringe';

import type { SdkCreateOrganizationInputT } from '@llm/sdk';

import {
createArchiveRecordQuery,
createArchiveRecordsQuery,
createProtectedDatabaseRepo,
createDatabaseRepo,
createUnarchiveRecordQuery,
createUnarchiveRecordsQuery,
DatabaseConnectionRepo,
DatabaseError,
TableId,
type TableId,
type TransactionalAttrs,
tryReuseOrCreateTransaction,
tryReuseTransactionOrSkip,
} from '~/modules/database';

import { OrganizationsAISettingsRepo } from '../organizations-ai-settings';
import { OrganizationTableRowWithRelations } from './organizations.tables';

@injectable()
export class OrganizationsRepo extends createProtectedDatabaseRepo('organizations') {
export class OrganizationsRepo extends createDatabaseRepo('organizations') {
constructor(
@inject(DatabaseConnectionRepo) databaseConnectionRepo: DatabaseConnectionRepo,
@inject(OrganizationsAISettingsRepo) private readonly organizationsAISettingsRepo: OrganizationsAISettingsRepo,
) {
super(databaseConnectionRepo);
}

createIdsIterator = this.baseRepo.createIdsIterator;

archive = createArchiveRecordQuery(this.baseRepo.queryFactoryAttrs);

archiveRecords = createArchiveRecordsQuery(this.baseRepo.queryFactoryAttrs);

unarchive = createUnarchiveRecordQuery(this.baseRepo.queryFactoryAttrs);
archive = createArchiveRecordQuery(this.queryFactoryAttrs);

unarchiveRecords = createUnarchiveRecordsQuery(this.baseRepo.queryFactoryAttrs);
archiveRecords = createArchiveRecordsQuery(this.queryFactoryAttrs);

create = ({ value, forwardTransaction }: TransactionalAttrs<{ value: SdkCreateOrganizationInputT; }>) => {
const transaction = tryReuseOrCreateTransaction({
db: this.db,
forwardTransaction,
});
unarchive = createUnarchiveRecordQuery(this.queryFactoryAttrs);

return transaction(trx => pipe(
this.baseRepo.create({
forwardTransaction: trx,
value: {
name: value.name,
maxNumberOfUsers: value.maxNumberOfUsers,
},
}),
TE.tap(({ id }) => this.organizationsAISettingsRepo.upsert({
forwardTransaction: trx,
value: {
organizationId: id,
chatContext: value.aiSettings.chatContext,
},
})),
));
};

update = ({ id, value, forwardTransaction }: TransactionalAttrs<{
id: TableId;
value: SdkCreateOrganizationInputT;
}>) => {
const transaction = tryReuseOrCreateTransaction({
db: this.db,
forwardTransaction,
});

return transaction(trx => pipe(
this.baseRepo.update({
id,
forwardTransaction: trx,
value: {
name: value.name,
maxNumberOfUsers: value.maxNumberOfUsers,
},
}),
TE.tap(() => this.organizationsAISettingsRepo.upsert({
forwardTransaction: trx,
value: {
organizationId: id,
chatContext: value.aiSettings.chatContext,
},
})),
));
};
unarchiveRecords = createUnarchiveRecordsQuery(this.queryFactoryAttrs);

findWithRelationsByIds = ({ forwardTransaction, ids }: TransactionalAttrs<{ ids: TableId[]; }>) => {
const transaction = tryReuseTransactionOrSkip({ db: this.db, forwardTransaction });
Expand All @@ -103,9 +44,12 @@ export class OrganizationsRepo extends createProtectedDatabaseRepo('organization
.selectFrom(this.table)
.where('organizations.id', 'in', ids)
.leftJoin('organizations_ai_settings', 'organizations_ai_settings.organization_id', 'organizations.id')
.innerJoin('projects', 'projects.id', 'organizations_ai_settings.project_id')
.selectAll('organizations')
.select([
'organizations_ai_settings.chat_context as ai_settings_chat_context',
'projects.id as ai_settings_project_id',
'projects.name as ai_settings_project_name',
])
.limit(ids.length)
.execute(),
Expand All @@ -114,11 +58,17 @@ export class OrganizationsRepo extends createProtectedDatabaseRepo('organization
TE.map(
A.map(({
ai_settings_chat_context: aiSettingsChatContext,
ai_settings_project_id: projectId,
ai_settings_project_name: projectName,
...organization
}): OrganizationTableRowWithRelations => ({
...camelcaseKeys(organization),
aiSettings: {
chatContext: aiSettingsChatContext,
project: {
id: projectId,
name: projectName,
},
},
})),
),
Expand Down
Loading

0 comments on commit ba98219

Please sign in to comment.