Skip to content

Commit 3565346

Browse files
committed
feat: add assistant skills
1 parent c939488 commit 3565346

File tree

6 files changed

+342
-0
lines changed

6 files changed

+342
-0
lines changed

apps/api/src/chat/chat.module.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,19 @@ import { WorkflowTriggersModule } from '../workflow-triggers/workflow-triggers.m
1717
import { WorkflowsModule } from '../workflows/workflows.module'
1818
import { ContactSubscriptionController } from './controllers/contact-subscription.controller'
1919
import { Assistant, AssistantAuthorizer } from './entities/assistant'
20+
import { AssistantSkill, AssistantSkillAuthorizer } from './entities/assistant-skill'
2021
import { Campaign, CampaignAuthorizer } from './entities/campaign'
2122
import { CampaignMessage } from './entities/campaign-message'
2223
import { Contact, ContactAuthorizer } from './entities/contact'
2324
import { Menu, MenuAuthorizer } from './entities/menu'
2425
import { Order, OrderAuthorizer } from './entities/order'
26+
import { AssistantSkillResolver } from './resolvers/assistant-skill.resolver'
2527
import { AssistantResolver } from './resolvers/assistant.resolver'
2628
import { CampaignResolver } from './resolvers/campaign.resolver'
2729
import { ContactResolver } from './resolvers/contact.resolver'
2830
import { MenuResolver } from './resolvers/menu.resolver'
2931
import { OrderResolver } from './resolvers/order.resolver'
32+
import { AssistantSkillService } from './services/assistant-skill.service'
3033
import { AssistantService } from './services/assistant.service'
3134
import { BroadcastConsumer } from './services/broadcast.consumer'
3235
import { CampaignMessageService } from './services/campaign-message.service'
@@ -62,6 +65,10 @@ import { OrderService } from './services/order.service'
6265
imports: [NestjsQueryTypegooseModule.forFeature([Assistant])],
6366
dtos: [{ DTOClass: Assistant }],
6467
}),
68+
NestjsQueryGraphQLModule.forFeature({
69+
imports: [NestjsQueryTypegooseModule.forFeature([AssistantSkill])],
70+
dtos: [{ DTOClass: AssistantSkill }],
71+
}),
6572
BullModule.registerQueue({
6673
name: 'chatbotMessage',
6774
settings: {
@@ -122,6 +129,7 @@ import { OrderService } from './services/order.service'
122129
MenuResolver,
123130
OrderResolver,
124131
AssistantResolver,
132+
AssistantSkillResolver,
125133

126134
// Services
127135
ContactService,
@@ -130,13 +138,15 @@ import { OrderService } from './services/order.service'
130138
MenuService,
131139
OrderService,
132140
AssistantService,
141+
AssistantSkillService,
133142

134143
// Authorizers
135144
ContactAuthorizer,
136145
CampaignAuthorizer,
137146
MenuAuthorizer,
138147
OrderAuthorizer,
139148
AssistantAuthorizer,
149+
AssistantSkillAuthorizer,
140150

141151
// Consumers
142152
ChatbotConsumer,
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { BaseEntity } from '@app/common/base/base-entity'
2+
import { OwnedAuthorizer } from '@app/common/base/owned.authorizer'
3+
import { OwnedEntity } from '@app/common/decorators/owned-entity.decorator'
4+
import { Reference } from '@app/common/typings/mongodb'
5+
import { Injectable } from '@nestjs/common'
6+
import { Field, ID, InputType, ObjectType } from '@nestjs/graphql'
7+
import { Authorize, FilterableField } from '@ptc-org/nestjs-query-graphql'
8+
import { prop } from '@typegoose/typegoose'
9+
import { GraphQLJSONObject } from 'graphql-type-json'
10+
import { User } from '../../users/entities/user'
11+
import { Assistant } from './assistant'
12+
13+
@Injectable()
14+
export class AssistantSkillAuthorizer extends OwnedAuthorizer<AssistantSkill> {}
15+
16+
@ObjectType()
17+
@OwnedEntity()
18+
@Authorize<AssistantSkill>(AssistantSkillAuthorizer)
19+
export class AssistantSkill extends BaseEntity {
20+
@prop({ ref: User, required: true })
21+
readonly owner!: Reference<User>
22+
23+
@FilterableField(() => ID)
24+
@prop({ ref: 'Assistant', required: true })
25+
readonly assistant!: Reference<Assistant>
26+
27+
@Field()
28+
@prop({ required: true })
29+
name: string
30+
31+
@Field(() => GraphQLJSONObject)
32+
inputs: object
33+
}
34+
35+
@InputType()
36+
export class CreateAssistantSkillInput {
37+
@Field(() => ID)
38+
assistant: Reference<Assistant>
39+
40+
@Field()
41+
name: string
42+
43+
@Field(() => GraphQLJSONObject)
44+
inputs: object
45+
}
46+
47+
@InputType()
48+
export class UpdateAssistantSkillInput {
49+
@Field({ nullable: true })
50+
name: string
51+
52+
@Field(() => GraphQLJSONObject, { nullable: true })
53+
inputs?: object
54+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { BaseResolver } from '@app/common/base/base.resolver'
2+
import { UseGuards, UseInterceptors } from '@nestjs/common'
3+
import { Resolver } from '@nestjs/graphql'
4+
import { Authorizer, AuthorizerInterceptor, InjectAuthorizer } from '@ptc-org/nestjs-query-graphql'
5+
import { GraphqlGuard } from '../../auth/guards/graphql.guard'
6+
import { AssistantSkill, CreateAssistantSkillInput, UpdateAssistantSkillInput } from '../entities/assistant-skill'
7+
import { AssistantSkillService } from '../services/assistant-skill.service'
8+
9+
@Resolver(() => AssistantSkill)
10+
@UseGuards(GraphqlGuard)
11+
@UseInterceptors(AuthorizerInterceptor(AssistantSkill))
12+
export class AssistantSkillResolver extends BaseResolver(AssistantSkill, {
13+
CreateDTOClass: CreateAssistantSkillInput,
14+
UpdateDTOClass: UpdateAssistantSkillInput,
15+
guards: [GraphqlGuard],
16+
}) {
17+
constructor(
18+
protected assistantSkillService: AssistantSkillService,
19+
@InjectAuthorizer(AssistantSkill) readonly authorizer: Authorizer<AssistantSkill>,
20+
) {
21+
super(assistantSkillService)
22+
}
23+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { BaseService } from '@app/common/base/base.service'
2+
import { InjectQueue } from '@nestjs/bull'
3+
import { BadRequestException, Injectable, Logger, NotFoundException } from '@nestjs/common'
4+
import { DeleteOneOptions, UpdateOneOptions } from '@ptc-org/nestjs-query-core'
5+
import { ReturnModelType } from '@typegoose/typegoose'
6+
import { Queue } from 'bull'
7+
import { UpdateResult } from 'mongodb'
8+
import { FilterQuery, UpdateQuery } from 'mongoose'
9+
import { InjectModel } from 'nestjs-typegoose'
10+
import { AssistantSkill } from '../entities/assistant-skill'
11+
import { AssistantService } from './assistant.service'
12+
13+
@Injectable()
14+
export class AssistantSkillService extends BaseService<AssistantSkill> {
15+
protected readonly logger = new Logger(AssistantSkillService.name)
16+
static instance: AssistantSkillService
17+
18+
constructor(
19+
@InjectModel(AssistantSkill) protected readonly model: ReturnModelType<typeof AssistantSkill>,
20+
@InjectQueue('assistants') private assistantsQueue: Queue,
21+
private assistantService: AssistantService,
22+
) {
23+
super(model)
24+
AssistantSkillService.instance = this
25+
}
26+
27+
async createOne(record: Partial<AssistantSkill>): Promise<AssistantSkill> {
28+
if (!record.assistant || !record.owner) {
29+
throw new BadRequestException()
30+
}
31+
32+
// Verify assistant exist and the user has access to it
33+
const assistant = await this.assistantService.findById(record.assistant?.toString())
34+
if (!assistant?.owner || assistant.owner.toString() !== record.owner.toString()) {
35+
throw new NotFoundException(`Assistant ${record.assistant} not found`)
36+
}
37+
38+
return super.createOne(record)
39+
}
40+
41+
async afterCreateOne(assistantSkill: AssistantSkill) {
42+
this.assistantsQueue.add({
43+
type: 'skill-created',
44+
id: assistantSkill._id,
45+
})
46+
}
47+
48+
async updateOne(
49+
id: string,
50+
update: Partial<AssistantSkill>,
51+
opts?: UpdateOneOptions<AssistantSkill> | undefined,
52+
): Promise<AssistantSkill> {
53+
this.assistantsQueue.add({
54+
type: 'skill-updated',
55+
id,
56+
})
57+
return super.updateOne(id, update, opts)
58+
}
59+
60+
updateOneNative(
61+
conditions: FilterQuery<new () => AssistantSkill>,
62+
query: UpdateQuery<new () => AssistantSkill>,
63+
): Promise<UpdateResult> {
64+
this.assistantsQueue.add({
65+
type: 'skill-updated',
66+
id: conditions._id,
67+
})
68+
return super.updateOneNative(conditions, query)
69+
}
70+
71+
async deleteOne(id: string, opts?: DeleteOneOptions<AssistantSkill> | undefined): Promise<AssistantSkill> {
72+
const assistantSkill = await this.findById(id)
73+
if (assistantSkill) {
74+
this.assistantsQueue.add({
75+
type: 'skill-deleted',
76+
id,
77+
assistantId: assistantSkill.assistant,
78+
})
79+
}
80+
return super.deleteOne(id, opts)
81+
}
82+
}

generated/graphql.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,12 @@ export enum AssistantSortFields {
173173
createdAt = "createdAt"
174174
}
175175

176+
export enum AssistantSkillSortFields {
177+
id = "id",
178+
createdAt = "createdAt",
179+
assistant = "assistant"
180+
}
181+
176182
export enum UserDatabaseSortFields {
177183
id = "id",
178184
createdAt = "createdAt"
@@ -592,6 +598,20 @@ export interface AssistantSort {
592598
nulls?: Nullable<SortNulls>;
593599
}
594600

601+
export interface AssistantSkillFilter {
602+
and?: Nullable<AssistantSkillFilter[]>;
603+
or?: Nullable<AssistantSkillFilter[]>;
604+
id?: Nullable<IDFilterComparison>;
605+
createdAt?: Nullable<DateFieldComparison>;
606+
assistant?: Nullable<IDFilterComparison>;
607+
}
608+
609+
export interface AssistantSkillSort {
610+
field: AssistantSkillSortFields;
611+
direction: SortDirection;
612+
nulls?: Nullable<SortNulls>;
613+
}
614+
595615
export interface UserDatabaseFilter {
596616
and?: Nullable<UserDatabaseFilter[]>;
597617
or?: Nullable<UserDatabaseFilter[]>;
@@ -921,6 +941,30 @@ export interface DeleteOneAssistantInput {
921941
id: string;
922942
}
923943

944+
export interface CreateOneAssistantSkillInput {
945+
assistantSkill: CreateAssistantSkillInput;
946+
}
947+
948+
export interface CreateAssistantSkillInput {
949+
assistant: string;
950+
name: string;
951+
inputs: JSONObject;
952+
}
953+
954+
export interface UpdateOneAssistantSkillInput {
955+
id: string;
956+
update: UpdateAssistantSkillInput;
957+
}
958+
959+
export interface UpdateAssistantSkillInput {
960+
name?: Nullable<string>;
961+
inputs?: Nullable<JSONObject>;
962+
}
963+
964+
export interface DeleteOneAssistantSkillInput {
965+
id: string;
966+
}
967+
924968
export interface CreateOneUserDatabaseInput {
925969
userDatabase: CreateUserDatabase;
926970
}
@@ -1425,6 +1469,14 @@ export interface Assistant {
14251469
enabled?: Nullable<boolean>;
14261470
}
14271471

1472+
export interface AssistantSkill {
1473+
id: string;
1474+
createdAt: DateTime;
1475+
assistant: string;
1476+
name: string;
1477+
inputs: JSONObject;
1478+
}
1479+
14281480
export interface Campaign {
14291481
id: string;
14301482
createdAt: DateTime;
@@ -1440,6 +1492,24 @@ export interface Campaign {
14401492
error?: Nullable<string>;
14411493
}
14421494

1495+
export interface AssistantSkillDeleteResponse {
1496+
id?: Nullable<string>;
1497+
createdAt?: Nullable<DateTime>;
1498+
assistant?: Nullable<string>;
1499+
name?: Nullable<string>;
1500+
inputs?: Nullable<JSONObject>;
1501+
}
1502+
1503+
export interface AssistantSkillEdge {
1504+
node: AssistantSkill;
1505+
cursor: ConnectionCursor;
1506+
}
1507+
1508+
export interface AssistantSkillConnection {
1509+
pageInfo: PageInfo;
1510+
edges: AssistantSkillEdge[];
1511+
}
1512+
14431513
export interface AssistantDeleteResponse {
14441514
id?: Nullable<string>;
14451515
createdAt?: Nullable<DateTime>;
@@ -1752,6 +1822,8 @@ export interface IQuery {
17521822
orderSummary(from: DateTime, to: DateTime): OrderSummary | Promise<OrderSummary>;
17531823
assistant(id: string): Assistant | Promise<Assistant>;
17541824
assistants(paging?: Nullable<CursorPaging>, filter?: Nullable<AssistantFilter>, sorting?: Nullable<AssistantSort[]>): AssistantConnection | Promise<AssistantConnection>;
1825+
assistantSkill(id: string): AssistantSkill | Promise<AssistantSkill>;
1826+
assistantSkills(paging?: Nullable<CursorPaging>, filter?: Nullable<AssistantSkillFilter>, sorting?: Nullable<AssistantSkillSort[]>): AssistantSkillConnection | Promise<AssistantSkillConnection>;
17551827
contractSchema(chainId: number, address: string, type: string): ContractSchema | Promise<ContractSchema>;
17561828
asyncSchemas(integrationId: string, accountCredentialId: string, names: string[], inputs?: Nullable<JSONObject>, integrationTriggerId?: Nullable<string>, integrationActionId?: Nullable<string>): AsyncSchema | Promise<AsyncSchema>;
17571829
manyAsyncSchemas(asyncSchemaInputs: JSONObject[]): AsyncSchema | Promise<AsyncSchema>;
@@ -1814,6 +1886,9 @@ export interface IMutation {
18141886
createOneAssistant(input: CreateOneAssistantInput): Assistant | Promise<Assistant>;
18151887
updateOneAssistant(input: UpdateOneAssistantInput): Assistant | Promise<Assistant>;
18161888
deleteOneAssistant(input: DeleteOneAssistantInput): AssistantDeleteResponse | Promise<AssistantDeleteResponse>;
1889+
createOneAssistantSkill(input: CreateOneAssistantSkillInput): AssistantSkill | Promise<AssistantSkill>;
1890+
updateOneAssistantSkill(input: UpdateOneAssistantSkillInput): AssistantSkill | Promise<AssistantSkill>;
1891+
deleteOneAssistantSkill(input: DeleteOneAssistantSkillInput): AssistantSkillDeleteResponse | Promise<AssistantSkillDeleteResponse>;
18171892
createOneUserDatabase(input: CreateOneUserDatabaseInput): UserDatabase | Promise<UserDatabase>;
18181893
updateOneUserDatabase(input: UpdateOneUserDatabaseInput): UserDatabase | Promise<UserDatabase>;
18191894
deleteOneUserDatabase(input: DeleteOneUserDatabaseInput): UserDatabaseDeleteResponse | Promise<UserDatabaseDeleteResponse>;

0 commit comments

Comments
 (0)