@yrpri/api 9.0.98 → 9.0.100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/agents/assistants/baseAssistant.js +24 -9
  2. package/agents/assistants/modes/agentDirectConnection.js +2 -17
  3. package/agents/assistants/modes/agentSelectionMode.js +5 -8
  4. package/agents/assistants/modes/tools/agentConnectionTools.js +326 -0
  5. package/agents/assistants/modes/tools/agentTools.js +3 -1
  6. package/agents/assistants/modes/tools/loginTools.js +1 -1
  7. package/agents/assistants/modes/tools/models/agents.js +9 -6
  8. package/agents/assistants/modes/tools/models/subscriptions.js +23 -4
  9. package/agents/assistants/modes/tools/navigationTools.js +95 -21
  10. package/agents/assistants/modes/tools/subscriptionTools.js +1 -4
  11. package/agents/assistants/modes/tools/workflowConversationTools.js +326 -0
  12. package/agents/assistants/modes/tools/workflowConverstationTools.js +112 -0
  13. package/agents/assistants/modes/tools/workflowTools.js +112 -0
  14. package/agents/assistants/voiceAssistant.js +3 -3
  15. package/agents/controllers/assistantsController.js +60 -2
  16. package/agents/managers/newAiModelSetup.js +63 -0
  17. package/agents/managers/notificationAgentQueueManager.js +6 -1
  18. package/agents/managers/workflowConversationManager.js +79 -0
  19. package/agents/managers/workflowManager.js +76 -0
  20. package/agents/models/agentProduct.js +37 -24
  21. package/agents/models/agentProductRun.js +9 -0
  22. package/agents/models/testData/setupEvolyAgentProductConfig.js +41 -40
  23. package/agents/models/workflow.js +53 -0
  24. package/agents/models/workflowConversation.js +53 -0
  25. package/agents/models/workflowConverstation.js +53 -0
  26. package/controllers/users.cjs +1 -0
  27. package/migrations/zzzzzzz_create_trees.cjs +81 -0
  28. package/package.json +1 -1
@@ -0,0 +1,112 @@
1
+ import { BaseAssistantTools } from "./baseTools.js";
2
+ export class WorkflowTools extends BaseAssistantTools {
3
+ constructor(assistant) {
4
+ super(assistant);
5
+ }
6
+ get show_running_workflow_conversations() {
7
+ return {
8
+ name: "show_running_workflow_conversations",
9
+ description: "Display running workflow conversations",
10
+ type: "function",
11
+ parameters: {
12
+ type: "object",
13
+ properties: {},
14
+ required: []
15
+ },
16
+ handler: this.showRunningWorkflowsHandler.bind(this)
17
+ };
18
+ }
19
+ async showRunningWorkflowsHandler(params) {
20
+ params = this.assistant.getCleanedParams(params);
21
+ console.log(`handler: show_running_workflows: ${JSON.stringify(params, null, 2)}`);
22
+ try {
23
+ const html = `<yp-workflow-widget-small running="true"></yp-workflow-widget-small>`;
24
+ return {
25
+ success: true,
26
+ html,
27
+ uniqueToken: "runningWorkflows",
28
+ data: { message: "Running workflows displayed successfully" },
29
+ metadata: { timestamp: new Date().toISOString() }
30
+ };
31
+ }
32
+ catch (error) {
33
+ const errorMessage = error instanceof Error ? error.message : "Error displaying running workflows";
34
+ console.error(`Error in show_running_workflows: ${errorMessage}`);
35
+ return {
36
+ success: false,
37
+ error: errorMessage
38
+ };
39
+ }
40
+ }
41
+ get show_all_workflow_conversations() {
42
+ return {
43
+ name: "show_all_workflow_conversations",
44
+ description: "Display all workflow conversations",
45
+ type: "function",
46
+ parameters: {
47
+ type: "object",
48
+ properties: {},
49
+ required: []
50
+ },
51
+ handler: this.showAllWorkflowsHandler.bind(this)
52
+ };
53
+ }
54
+ async showAllWorkflowsHandler(params) {
55
+ params = this.assistant.getCleanedParams(params);
56
+ console.log(`handler: show_all_workflows: ${JSON.stringify(params, null, 2)}`);
57
+ try {
58
+ const html = `<yp-workflow-widget-small all="true"></yp-workflow-widget-small>`;
59
+ return {
60
+ success: true,
61
+ html,
62
+ uniqueToken: "allWorkflows",
63
+ data: { message: "All workflows displayed successfully" },
64
+ metadata: { timestamp: new Date().toISOString() }
65
+ };
66
+ }
67
+ catch (error) {
68
+ const errorMessage = error instanceof Error ? error.message : "Error displaying all workflows";
69
+ console.error(`Error in show_all_workflows: ${errorMessage}`);
70
+ return {
71
+ success: false,
72
+ error: errorMessage
73
+ };
74
+ }
75
+ }
76
+ get connect_to_workflow_conversation() {
77
+ return {
78
+ name: "connect_to_workflow_conversation",
79
+ description: "Connect to an existing workflow conversation",
80
+ type: "function",
81
+ parameters: {
82
+ type: "object",
83
+ properties: {
84
+ workflowId: { type: "number" }
85
+ },
86
+ required: ["workflowId"]
87
+ },
88
+ handler: this.connectToWorkflowHandler.bind(this)
89
+ };
90
+ }
91
+ async connectToWorkflowHandler(params) {
92
+ params = this.assistant.getCleanedParams(params);
93
+ console.log(`handler: connect_to_workflow: ${JSON.stringify(params, null, 2)}`);
94
+ try {
95
+ const { workflowId } = params;
96
+ return {
97
+ success: true,
98
+ data: { message: `Connected to workflow ${workflowId} successfully` },
99
+ uniqueToken: `connectWorkflow_${workflowId}`,
100
+ metadata: { timestamp: new Date().toISOString() }
101
+ };
102
+ }
103
+ catch (error) {
104
+ const errorMessage = error instanceof Error ? error.message : "Error connecting to workflow conversation";
105
+ console.error(`Error in connect_to_workflow_conversation: ${errorMessage}`);
106
+ return {
107
+ success: false,
108
+ error: errorMessage
109
+ };
110
+ }
111
+ }
112
+ }
@@ -82,7 +82,7 @@ export class YpBaseChatBotWithVoice extends YpBaseChatBot {
82
82
  throw new Error("No subscription plan found");
83
83
  }
84
84
  if (subscriptionPlan?.AgentProduct.name) {
85
- this.exitMessageFromDirectAgentConversation = `Welcome the user back from their conversation with the ${subscriptionPlan.AgentProduct.name}. (it happened on a seperate channel). Now help the user with agent selection and subscription management.`;
85
+ this.exitMessageFromDirectAgentConversation = `Welcome the user back from their conversation with the ${subscriptionPlan.AgentProduct.name}. (it happened on a seperate channel). Now help the user with agent selection`;
86
86
  }
87
87
  this.sendToClient("assistant", "", "clear_audio_buffer");
88
88
  if (this.directAgentVoiceConnection) {
@@ -132,7 +132,7 @@ export class YpBaseChatBotWithVoice extends YpBaseChatBot {
132
132
  this.directAgentVoiceConnection = undefined;
133
133
  this.parentAssistant.memory.chatLog.push({
134
134
  sender: "user",
135
- message: "Thank you for the information, I would now like to speak to the main assistant about selecting agents or managing subscriptions",
135
+ message: "Thank you for the information, I would now like to speak to the main assistant about selecting agents",
136
136
  });
137
137
  await this.parentAssistant.saveMemory();
138
138
  }
@@ -189,7 +189,7 @@ export class YpBaseChatBotWithVoice extends YpBaseChatBot {
189
189
  await this.handleSpeechStopped();
190
190
  break;
191
191
  case "response.cancelled":
192
- console.log("-------------------MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMZZZZZZZZZZZZZZ>>>>>>>>>>>>>>>>>>> response.cancelled");
192
+ console.log("response.cancelled");
193
193
  this.isWaitingOnCancelResponseCompleted = false;
194
194
  break;
195
195
  case "conversation.item.input_audio_transcription.completed":
@@ -14,6 +14,7 @@ import { YpDiscount } from "../models/discount.js";
14
14
  import { sequelize } from "@policysynth/agents/dbModels/index.js";
15
15
  import { NotificationAgentQueueManager } from "../managers/notificationAgentQueueManager.js";
16
16
  import { AgentQueueManager } from "@policysynth/agents/operations/agentQueueManager.js";
17
+ import { WorkflowConversationManager } from "../managers/workflowConversationManager.js";
17
18
  const models = {
18
19
  YpAgentProduct,
19
20
  YpAgentProductBundle,
@@ -65,7 +66,7 @@ export class AssistantController {
65
66
  }
66
67
  const markdownContent = match[1];
67
68
  const htmlContent = await marked(markdownContent);
68
- const docxBuffer = await HTMLtoDOCX(htmlContent);
69
+ const docxBuffer = (await HTMLtoDOCX(htmlContent));
69
70
  console.debug(`docxBuffer: ${docxBuffer.length}`);
70
71
  res.setHeader("Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
71
72
  res.setHeader("Content-disposition", 'attachment; filename="converted.docx"');
@@ -121,7 +122,7 @@ export class AssistantController {
121
122
  const subscription = await YpSubscription.findOne({
122
123
  where: {
123
124
  id: subscriptionId,
124
- user_id: req.user.id
125
+ user_id: req.user.id,
125
126
  },
126
127
  });
127
128
  if (!subscription) {
@@ -335,8 +336,62 @@ export class AssistantController {
335
336
  res.send({});
336
337
  }
337
338
  };
339
+ // New API endpoints for workflow management
340
+ this.getRunningWorkflowConversations = async (req, res) => {
341
+ if (!req.user || !req.user.id) {
342
+ return res.status(401).json({ error: "Unauthorized" });
343
+ }
344
+ try {
345
+ const workflows = await this.workflowConversationManager.getRunningWorkflowConversationsForUser(req.user.id);
346
+ res.status(200).json({
347
+ success: true,
348
+ data: { workflows },
349
+ message: "Running workflows retrieved successfully",
350
+ });
351
+ }
352
+ catch (error) {
353
+ console.error("Error retrieving running workflows:", error);
354
+ res.status(500).json({ error: error.message });
355
+ }
356
+ };
357
+ this.getAllWorkflowConversations = async (req, res) => {
358
+ if (!req.user || !req.user.id) {
359
+ return res.status(401).json({ error: "Unauthorized" });
360
+ }
361
+ try {
362
+ const workflows = await this.workflowConversationManager.getWorkflowConversationsForUser(req.user.id);
363
+ res.status(200).json({
364
+ success: true,
365
+ data: { workflows },
366
+ message: "All workflows retrieved successfully",
367
+ });
368
+ }
369
+ catch (error) {
370
+ console.error("Error retrieving all workflows:", error);
371
+ res.status(500).json({ error: error.message });
372
+ }
373
+ };
374
+ this.connectToWorkflowConversation = async (req, res) => {
375
+ if (!req.user || !req.user.id) {
376
+ return res.status(401).json({ error: "Unauthorized" });
377
+ }
378
+ try {
379
+ const { workflowConversationId, connectionData } = req.body;
380
+ const updatedWorkflowConversation = await this.workflowConversationManager.connectToWorkflowConversation(workflowConversationId, connectionData || {});
381
+ res.status(200).json({
382
+ success: true,
383
+ data: updatedWorkflowConversation,
384
+ message: `Connected to workflow conversation ${workflowConversationId} successfully`,
385
+ });
386
+ }
387
+ catch (error) {
388
+ console.error("Error connecting to workflow conversation:", error);
389
+ res.status(500).json({ error: error.message });
390
+ }
391
+ };
338
392
  this.wsClients = wsClients;
339
393
  this.agentQueueManager = new AgentQueueManager();
394
+ this.workflowConversationManager = new WorkflowConversationManager();
340
395
  this.initializeRoutes();
341
396
  this.initializeModels();
342
397
  }
@@ -355,6 +410,9 @@ export class AssistantController {
355
410
  this.router.post("/:groupId/:agentId/stopCurrentWorkflowStep", this.stopCurrentWorkflowStep.bind(this));
356
411
  this.router.put("/:groupId/:agentId/:runId/advanceOrStopWorkflow", this.advanceOrStopCurrentWorkflowStep.bind(this));
357
412
  this.router.get("/:groupId/:agentId/getDocxReport", auth.can("view domain"), this.getDocxReport.bind(this));
413
+ this.router.get("/:domainId/workflowConversations/running", auth.can("view domain"), this.getRunningWorkflowConversations.bind(this));
414
+ this.router.get("/:domainId/workflowConversations/all", auth.can("view domain"), this.getAllWorkflowConversations.bind(this));
415
+ this.router.put("/:domainId/workflowConversations/connect", auth.can("view domain"), this.connectToWorkflowConversation.bind(this));
358
416
  }
359
417
  async getLastStatusMessageFromDB(agentId) {
360
418
  const status = await this.agentQueueManager.getAgentStatus(agentId);
@@ -84,6 +84,37 @@ export class NewAiModelSetup {
84
84
  console.log("Anthropic model already exists: Anthropic Sonnet 3.5");
85
85
  }
86
86
  }
87
+ static async seedAnthropic37Models(userId) {
88
+ const anthropicSonnet = await PsAiModel.findOne({
89
+ where: { name: "Anthropic Sonnet 3.7" },
90
+ });
91
+ const anthropicSonnetConfig = {
92
+ type: PsAiModelType.TextReasoning,
93
+ modelSize: PsAiModelSize.Medium,
94
+ provider: "anthropic",
95
+ prices: {
96
+ costInTokensPerMillion: 3,
97
+ costOutTokensPerMillion: 15,
98
+ currency: "USD",
99
+ },
100
+ maxTokensOut: 8000,
101
+ defaultTemperature: 0.7,
102
+ model: "claude-3-7-sonnet-20250219",
103
+ active: true,
104
+ };
105
+ if (!anthropicSonnet) {
106
+ const createdModel = await PsAiModel.create({
107
+ name: "Anthropic Sonnet 3.7",
108
+ organization_id: 1,
109
+ user_id: userId,
110
+ configuration: anthropicSonnetConfig,
111
+ });
112
+ console.log("Created Anthropic model:", createdModel);
113
+ }
114
+ else {
115
+ console.log("Anthropic model already exists: Anthropic Sonnet 3.7");
116
+ }
117
+ }
87
118
  /**
88
119
  * Seeds OpenAI models.
89
120
  * This currently creates several models including GPT-4o, GPT-4o Mini, o1 Mini,
@@ -276,6 +307,35 @@ export class NewAiModelSetup {
276
307
  else {
277
308
  console.log("OpenAI model already exists: o3 mini");
278
309
  }
310
+ const openAiGpt45 = await PsAiModel.findOne({
311
+ where: { name: "GPT-4.5 Preview" },
312
+ });
313
+ const openAiGpt45Config = {
314
+ type: PsAiModelType.Text,
315
+ modelSize: PsAiModelSize.Large,
316
+ provider: "openai",
317
+ prices: {
318
+ costInTokensPerMillion: 75,
319
+ costOutTokensPerMillion: 150,
320
+ currency: "USD",
321
+ },
322
+ maxTokensOut: 100000,
323
+ defaultTemperature: 0.7,
324
+ model: "gpt-4.5-preview",
325
+ active: true,
326
+ };
327
+ if (!openAiGpt45) {
328
+ await PsAiModel.create({
329
+ name: "GPT-4.5 Preview",
330
+ organization_id: 1,
331
+ user_id: userId,
332
+ configuration: openAiGpt45Config,
333
+ });
334
+ console.log("Created OpenAI model: GPT-4.5 Preview");
335
+ }
336
+ else {
337
+ console.log("OpenAI model already exists: GPT-4.5 Preview updating");
338
+ }
279
339
  }
280
340
  /**
281
341
  * Seeds Google models.
@@ -380,6 +440,7 @@ export class NewAiModelSetup {
380
440
  static async seedAiModels(userId) {
381
441
  try {
382
442
  await NewAiModelSetup.seedAnthropicModels(userId);
443
+ await NewAiModelSetup.seedAnthropic37Models(userId);
383
444
  await NewAiModelSetup.seedOpenAiModels(userId);
384
445
  await NewAiModelSetup.seedGoogleModels(userId);
385
446
  // Optionally, seed the top-level agent class if it does not exist.
@@ -461,8 +522,10 @@ export class NewAiModelSetup {
461
522
  // Explicitly type envKey as a key of apiKeys
462
523
  const modelsMapping = [
463
524
  { name: "Anthropic Sonnet 3.5", envKey: "ANTHROPIC_CLAUDE_API_KEY" },
525
+ { name: "Anthropic Sonnet 3.7", envKey: "ANTHROPIC_CLAUDE_API_KEY" },
464
526
  { name: "GPT-4o", envKey: "OPENAI_API_KEY" },
465
527
  { name: "GPT-4o Mini", envKey: "OPENAI_API_KEY" },
528
+ { name: "GPT-4.5 Preview", envKey: "OPENAI_API_KEY" },
466
529
  { name: "o1 Preview", envKey: "OPENAI_API_KEY" },
467
530
  { name: "o1 Mini", envKey: "OPENAI_API_KEY" },
468
531
  { name: "Gemini 1.5 Pro 2", envKey: "GEMINI_API_KEY" },
@@ -27,13 +27,18 @@ export class NotificationAgentQueueManager extends AgentQueueManager {
27
27
  console.log("NotificationAgentQueueManager: Sending notification", agentRunId, updatedWorkflow);
28
28
  const wsClient = this.wsClients.get(wsClientId);
29
29
  if (wsClient) {
30
+ const currentWorkflowStep = updatedWorkflow?.steps[updatedWorkflow?.currentStepIndex];
30
31
  wsClient.send(JSON.stringify({
31
32
  type: "updated_workflow",
32
33
  action,
33
34
  status,
34
35
  result,
35
36
  agentRunId,
36
- updatedWorkflow: { workflow: updatedWorkflow, status: status },
37
+ updatedWorkflow: {
38
+ workflow: updatedWorkflow,
39
+ status: status,
40
+ currentWorkflowStep,
41
+ },
37
42
  }));
38
43
  }
39
44
  else {
@@ -0,0 +1,79 @@
1
+ import { YpWorkflowConversation } from "../models/workflowConversation.js";
2
+ export class WorkflowConversationManager {
3
+ constructor() { }
4
+ async createWorkflowConversation(data) {
5
+ try {
6
+ const workflow = await YpWorkflowConversation.create({
7
+ agentProductId: data.agentProductId,
8
+ userId: data.userId || null,
9
+ configuration: data.configuration || {},
10
+ });
11
+ return workflow;
12
+ }
13
+ catch (error) {
14
+ throw new Error(`Error creating workflow conversation: ${error.message}`);
15
+ }
16
+ }
17
+ async getWorkflowConversation(workflowConversationId) {
18
+ try {
19
+ const workflowConversation = await YpWorkflowConversation.findByPk(workflowConversationId);
20
+ return workflowConversation;
21
+ }
22
+ catch (error) {
23
+ throw new Error(`Error retrieving workflow conversation: ${error.message}`);
24
+ }
25
+ }
26
+ async updateWorkflowConversation(workflowConversationId, updates) {
27
+ try {
28
+ const workflowConversation = await YpWorkflowConversation.findByPk(workflowConversationId);
29
+ if (!workflowConversation) {
30
+ throw new Error("Workflow conversation not found");
31
+ }
32
+ Object.assign(workflowConversation, updates);
33
+ await workflowConversation.save();
34
+ return workflowConversation;
35
+ }
36
+ catch (error) {
37
+ throw new Error(`Error updating workflow conversation: ${error.message}`);
38
+ }
39
+ }
40
+ async connectToWorkflowConversation(workflowConversationId, connectionData) {
41
+ try {
42
+ const workflowConversation = await YpWorkflowConversation.findByPk(workflowConversationId);
43
+ if (!workflowConversation) {
44
+ throw new Error("Workflow conversation not found");
45
+ }
46
+ workflowConversation.configuration = {
47
+ ...workflowConversation.configuration,
48
+ ...connectionData,
49
+ };
50
+ await workflowConversation.save();
51
+ return workflowConversation;
52
+ }
53
+ catch (error) {
54
+ throw new Error(`Error connecting to workflow: ${error.message}`);
55
+ }
56
+ }
57
+ async getWorkflowConversationsForUser(userId) {
58
+ try {
59
+ const workflowConversations = await YpWorkflowConversation.findAll({ where: { userId } });
60
+ return workflowConversations;
61
+ }
62
+ catch (error) {
63
+ throw new Error(`Error retrieving workflow conversations for user ${userId}: ${error.message}`);
64
+ }
65
+ }
66
+ async getRunningWorkflowConversationsForUser(userId) {
67
+ try {
68
+ const allWorkflowConversations = await this.getWorkflowConversationsForUser(userId);
69
+ const runningWorkflowConversations = allWorkflowConversations.filter((workflowConversation) => {
70
+ return (workflowConversation.configuration &&
71
+ workflowConversation.configuration.running === true);
72
+ });
73
+ return runningWorkflowConversations;
74
+ }
75
+ catch (error) {
76
+ throw new Error(`Error retrieving running workflows for user ${userId}: ${error.message}`);
77
+ }
78
+ }
79
+ }
@@ -0,0 +1,76 @@
1
+ import { YpWorkflowConversation } from "../models/workflowConverstation.js";
2
+ export class WorkflowManager {
3
+ constructor() { }
4
+ async createWorkflow(data) {
5
+ try {
6
+ const workflow = await YpWorkflowConversation.create({
7
+ agentProductId: data.agentProductId,
8
+ userId: data.userId || null,
9
+ configuration: data.configuration || {},
10
+ });
11
+ return workflow;
12
+ }
13
+ catch (error) {
14
+ throw new Error(`Error creating workflow: ${error.message}`);
15
+ }
16
+ }
17
+ async getWorkflow(workflowId) {
18
+ try {
19
+ const workflow = await YpWorkflowConversation.findByPk(workflowId);
20
+ return workflow;
21
+ }
22
+ catch (error) {
23
+ throw new Error(`Error retrieving workflow: ${error.message}`);
24
+ }
25
+ }
26
+ async updateWorkflow(workflowId, updates) {
27
+ try {
28
+ const workflow = await YpWorkflowConversation.findByPk(workflowId);
29
+ if (!workflow) {
30
+ throw new Error("Workflow not found");
31
+ }
32
+ Object.assign(workflow, updates);
33
+ await workflow.save();
34
+ return workflow;
35
+ }
36
+ catch (error) {
37
+ throw new Error(`Error updating workflow: ${error.message}`);
38
+ }
39
+ }
40
+ async connectToWorkflow(workflowId, connectionData) {
41
+ try {
42
+ const workflow = await YpWorkflowConversation.findByPk(workflowId);
43
+ if (!workflow) {
44
+ throw new Error("Workflow not found");
45
+ }
46
+ workflow.configuration = { ...workflow.configuration, ...connectionData };
47
+ await workflow.save();
48
+ return workflow;
49
+ }
50
+ catch (error) {
51
+ throw new Error(`Error connecting to workflow: ${error.message}`);
52
+ }
53
+ }
54
+ async getWorkflowsForUser(userId) {
55
+ try {
56
+ const workflows = await YpWorkflowConversation.findAll({ where: { userId } });
57
+ return workflows;
58
+ }
59
+ catch (error) {
60
+ throw new Error(`Error retrieving workflows for user ${userId}: ${error.message}`);
61
+ }
62
+ }
63
+ async getRunningWorkflowsForUser(userId) {
64
+ try {
65
+ const allWorkflows = await this.getWorkflowsForUser(userId);
66
+ const runningWorkflows = allWorkflows.filter((workflow) => {
67
+ return (workflow.configuration &&
68
+ workflow.configuration.running === true);
69
+ });
70
+ return runningWorkflows;
71
+ }
72
+ catch (error) {
73
+ throw new Error(`Error retrieving running workflows for user ${userId}: ${error.message}`);
74
+ }
75
+ }
76
+ }
@@ -33,9 +33,13 @@ YpAgentProduct.init({
33
33
  type: DataTypes.INTEGER,
34
34
  allowNull: false,
35
35
  },
36
+ parent_agent_product_id: {
37
+ type: DataTypes.INTEGER,
38
+ allowNull: true,
39
+ },
36
40
  configuration: {
37
41
  type: DataTypes.JSONB,
38
- allowNull: false
42
+ allowNull: false,
39
43
  },
40
44
  status: {
41
45
  type: DataTypes.JSONB,
@@ -54,12 +58,13 @@ YpAgentProduct.init({
54
58
  },
55
59
  }, {
56
60
  sequelize,
57
- tableName: 'agent_products',
61
+ tableName: "agent_products",
58
62
  indexes: [
59
- { fields: ['uuid'], unique: true },
60
- { fields: ['user_id'] },
61
- { fields: ['group_id'] },
62
- { fields: ['domain_id'] },
63
+ { fields: ["uuid"], unique: true },
64
+ { fields: ["user_id"] },
65
+ { fields: ["group_id"] },
66
+ { fields: ["domain_id"] },
67
+ { fields: ["parent_agent_product_id"] },
63
68
  ],
64
69
  timestamps: true,
65
70
  underscored: true,
@@ -67,37 +72,45 @@ YpAgentProduct.init({
67
72
  YpAgentProduct.associate = (models) => {
68
73
  console.log(`YpAgentProduct.associate`);
69
74
  YpAgentProduct.belongsTo(models.YpSubscriptionUser, {
70
- foreignKey: 'user_id',
71
- as: 'User'
75
+ foreignKey: "user_id",
76
+ as: "User",
72
77
  });
73
78
  YpAgentProduct.belongsTo(models.Group, {
74
- foreignKey: 'group_id',
75
- as: 'Group'
79
+ foreignKey: "group_id",
80
+ as: "Group",
76
81
  });
77
82
  YpAgentProduct.hasOne(models.YpAgentProductBundle, {
78
- foreignKey: 'agent_bundle_id',
79
- as: 'AgentBundle'
83
+ foreignKey: "agent_bundle_id",
84
+ as: "AgentBundle",
80
85
  });
81
86
  YpAgentProduct.hasMany(models.YpAgentProductBoosterPurchase, {
82
- foreignKey: 'agent_product_id',
83
- as: 'BoosterPurchases',
87
+ foreignKey: "agent_product_id",
88
+ as: "BoosterPurchases",
84
89
  });
85
90
  YpAgentProduct.hasMany(models.YpSubscription, {
86
- foreignKey: 'agent_product_id',
87
- as: 'Subscriptions',
91
+ foreignKey: "agent_product_id",
92
+ as: "Subscriptions",
88
93
  });
89
94
  YpAgentProduct.hasMany(models.YpSubscriptionPlan, {
90
- foreignKey: 'agent_product_id',
91
- as: 'SubscriptionPlans',
95
+ foreignKey: "agent_product_id",
96
+ as: "SubscriptionPlans",
92
97
  });
93
98
  YpAgentProduct.hasMany(models.YpAgentProductRun, {
94
- foreignKey: 'agent_product_id',
95
- as: 'Runs',
99
+ foreignKey: "agent_product_id",
100
+ as: "Runs",
96
101
  });
97
102
  YpAgentProduct.belongsToMany(models.YpAgentProductBundle, {
98
- through: 'agent_product_bundles_products',
99
- foreignKey: 'agent_product_id',
100
- otherKey: 'agent_product_bundle_id',
101
- as: 'AgentBundles'
103
+ through: "agent_product_bundles_products",
104
+ foreignKey: "agent_product_id",
105
+ otherKey: "agent_product_bundle_id",
106
+ as: "AgentBundles",
107
+ });
108
+ YpAgentProduct.belongsTo(YpAgentProduct, {
109
+ as: "ParentAgentProduct",
110
+ foreignKey: "parent_agent_product_id",
111
+ });
112
+ YpAgentProduct.hasMany(YpAgentProduct, {
113
+ as: "ChildAgentProducts",
114
+ foreignKey: "parent_agent_product_id",
102
115
  });
103
116
  };
@@ -10,6 +10,7 @@ YpAgentProductRun.init({
10
10
  start_time: { type: DataTypes.DATE, allowNull: false, defaultValue: DataTypes.NOW },
11
11
  end_time: { type: DataTypes.DATE, allowNull: true },
12
12
  duration: { type: DataTypes.INTEGER, allowNull: true },
13
+ parent_agent_product_run_id: { type: DataTypes.INTEGER, allowNull: true },
13
14
  status: {
14
15
  type: DataTypes.STRING,
15
16
  allowNull: false,
@@ -40,4 +41,12 @@ YpAgentProductRun.associate = (models) => {
40
41
  foreignKey: 'subscription_id',
41
42
  as: 'Subscription',
42
43
  });
44
+ YpAgentProductRun.belongsTo(models.YpAgentProductRun, {
45
+ foreignKey: 'parent_agent_product_run_id',
46
+ as: 'ParentAgentProduct',
47
+ });
48
+ YpAgentProductRun.hasMany(models.YpAgentProductRun, {
49
+ foreignKey: 'parent_agent_product_run_id',
50
+ as: 'ChildAgentProducts',
51
+ });
43
52
  };