@autobe/agent 0.7.3 → 0.9.0

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 (100) hide show
  1. package/lib/AutoBeAgent.d.ts +183 -12
  2. package/lib/AutoBeAgent.js +249 -65
  3. package/lib/AutoBeAgent.js.map +1 -1
  4. package/lib/constants/AutoBeSystemPromptConstant.d.ts +5 -4
  5. package/lib/constants/AutoBeSystemPromptConstant.js.map +1 -1
  6. package/lib/context/AutoBeContext.d.ts +2 -2
  7. package/lib/factory/index.d.ts +0 -1
  8. package/lib/factory/index.js +0 -1
  9. package/lib/factory/index.js.map +1 -1
  10. package/lib/index.mjs +1024 -663
  11. package/lib/index.mjs.map +1 -1
  12. package/lib/orchestrate/analyze/AutoBeAnalyzeAgent.js +7 -8
  13. package/lib/orchestrate/analyze/AutoBeAnalyzeAgent.js.map +1 -1
  14. package/lib/orchestrate/analyze/orchestrateAnalyze.js +2 -5
  15. package/lib/orchestrate/analyze/orchestrateAnalyze.js.map +1 -1
  16. package/lib/orchestrate/interface/orchestrateInterface.js +1 -1
  17. package/lib/orchestrate/interface/orchestrateInterface.js.map +1 -1
  18. package/lib/orchestrate/interface/orchestrateInterfaceComplement.js +6 -8
  19. package/lib/orchestrate/interface/orchestrateInterfaceComplement.js.map +1 -1
  20. package/lib/orchestrate/interface/orchestrateInterfaceComponents.js +9 -6
  21. package/lib/orchestrate/interface/orchestrateInterfaceComponents.js.map +1 -1
  22. package/lib/orchestrate/interface/orchestrateInterfaceEndpoints.js +3 -1
  23. package/lib/orchestrate/interface/orchestrateInterfaceEndpoints.js.map +1 -1
  24. package/lib/orchestrate/interface/orchestrateInterfaceOperations.js +5 -8
  25. package/lib/orchestrate/interface/orchestrateInterfaceOperations.js.map +1 -1
  26. package/lib/orchestrate/prisma/orchestratePrisma.js +1 -1
  27. package/lib/orchestrate/prisma/orchestratePrisma.js.map +1 -1
  28. package/lib/orchestrate/prisma/orchestratePrismaComponent.js +5 -1
  29. package/lib/orchestrate/prisma/orchestratePrismaComponent.js.map +1 -1
  30. package/lib/orchestrate/prisma/orchestratePrismaCorrect.js +3 -6
  31. package/lib/orchestrate/prisma/orchestratePrismaCorrect.js.map +1 -1
  32. package/lib/orchestrate/prisma/orchestratePrismaSchema.js +11 -7
  33. package/lib/orchestrate/prisma/orchestratePrismaSchema.js.map +1 -1
  34. package/lib/orchestrate/prisma/transformPrismaCorrectHistories.js +1 -1
  35. package/lib/orchestrate/prisma/transformPrismaCorrectHistories.js.map +1 -1
  36. package/lib/orchestrate/test/orchestrateTest.js +4 -8
  37. package/lib/orchestrate/test/orchestrateTest.js.map +1 -1
  38. package/lib/orchestrate/test/orchestrateTestCorrect.d.ts +2 -2
  39. package/lib/orchestrate/test/orchestrateTestCorrect.js +90 -60
  40. package/lib/orchestrate/test/orchestrateTestCorrect.js.map +1 -1
  41. package/lib/orchestrate/test/orchestrateTestProgress.d.ts +3 -2
  42. package/lib/orchestrate/test/orchestrateTestProgress.js +75 -50
  43. package/lib/orchestrate/test/orchestrateTestProgress.js.map +1 -1
  44. package/lib/orchestrate/test/orchestrateTestScenario.d.ts +1 -1
  45. package/lib/orchestrate/test/orchestrateTestScenario.js +617 -208
  46. package/lib/orchestrate/test/orchestrateTestScenario.js.map +1 -1
  47. package/lib/orchestrate/test/structures/IAutoBeTestScenarioApplication.d.ts +123 -0
  48. package/lib/orchestrate/test/structures/IAutoBeTestScenarioApplication.js +3 -0
  49. package/lib/orchestrate/test/structures/IAutoBeTestScenarioApplication.js.map +1 -0
  50. package/lib/orchestrate/test/transformTestCorrectHistories.d.ts +2 -1
  51. package/lib/orchestrate/test/transformTestCorrectHistories.js +14 -10
  52. package/lib/orchestrate/test/transformTestCorrectHistories.js.map +1 -1
  53. package/lib/orchestrate/test/transformTestProgressHistories.d.ts +7 -1
  54. package/lib/orchestrate/test/transformTestProgressHistories.js +20 -20
  55. package/lib/orchestrate/test/transformTestProgressHistories.js.map +1 -1
  56. package/lib/orchestrate/test/transformTestScenarioHistories.d.ts +1 -2
  57. package/lib/orchestrate/test/transformTestScenarioHistories.js +1 -77
  58. package/lib/orchestrate/test/transformTestScenarioHistories.js.map +1 -1
  59. package/lib/structures/IAutoBeConfig.d.ts +48 -10
  60. package/lib/structures/IAutoBeProps.d.ts +87 -0
  61. package/lib/structures/IAutoBeVendor.d.ts +64 -22
  62. package/lib/utils/backoffRetry.d.ts +7 -0
  63. package/lib/utils/backoffRetry.js +73 -0
  64. package/lib/utils/backoffRetry.js.map +1 -0
  65. package/lib/utils/enforceToolCall.d.ts +3 -0
  66. package/lib/utils/enforceToolCall.js +13 -0
  67. package/lib/utils/enforceToolCall.js.map +1 -0
  68. package/lib/utils/types/BackoffOptions.d.ts +12 -0
  69. package/lib/utils/types/BackoffOptions.js +3 -0
  70. package/lib/utils/types/BackoffOptions.js.map +1 -0
  71. package/package.json +5 -5
  72. package/src/AutoBeAgent.ts +252 -52
  73. package/src/constants/AutoBeSystemPromptConstant.ts +5 -4
  74. package/src/context/AutoBeContext.ts +7 -2
  75. package/src/factory/index.ts +0 -1
  76. package/src/orchestrate/analyze/AutoBeAnalyzeAgent.ts +5 -10
  77. package/src/orchestrate/analyze/orchestrateAnalyze.ts +2 -6
  78. package/src/orchestrate/interface/orchestrateInterface.ts +1 -1
  79. package/src/orchestrate/interface/orchestrateInterfaceComplement.ts +12 -11
  80. package/src/orchestrate/interface/orchestrateInterfaceComponents.ts +7 -6
  81. package/src/orchestrate/interface/orchestrateInterfaceEndpoints.ts +2 -1
  82. package/src/orchestrate/interface/orchestrateInterfaceOperations.ts +4 -9
  83. package/src/orchestrate/prisma/orchestratePrisma.ts +1 -0
  84. package/src/orchestrate/prisma/orchestratePrismaComponent.ts +4 -1
  85. package/src/orchestrate/prisma/orchestratePrismaCorrect.ts +6 -7
  86. package/src/orchestrate/prisma/orchestratePrismaSchema.ts +10 -7
  87. package/src/orchestrate/test/orchestrateTest.ts +6 -13
  88. package/src/orchestrate/test/orchestrateTestCorrect.ts +127 -78
  89. package/src/orchestrate/test/orchestrateTestProgress.ts +88 -47
  90. package/src/orchestrate/test/orchestrateTestScenario.ts +194 -105
  91. package/src/orchestrate/test/structures/IAutoBeTestScenarioApplication.ts +132 -0
  92. package/src/orchestrate/test/transformTestCorrectHistories.ts +14 -10
  93. package/src/orchestrate/test/transformTestProgressHistories.ts +25 -22
  94. package/src/orchestrate/test/transformTestScenarioHistories.ts +0 -79
  95. package/src/structures/IAutoBeConfig.ts +48 -10
  96. package/src/structures/IAutoBeProps.ts +91 -0
  97. package/src/structures/IAutoBeVendor.ts +64 -22
  98. package/src/utils/backoffRetry.ts +84 -0
  99. package/src/utils/enforceToolCall.ts +13 -0
  100. package/src/utils/types/BackoffOptions.ts +15 -0
@@ -1,31 +1,39 @@
1
1
  import { IAgenticaController, MicroAgentica } from "@agentica/core";
2
- import { AutoBeTest, AutoBeTestProgressEvent } from "@autobe/interface";
3
- import { ILlmApplication, ILlmSchema } from "@samchon/openapi";
2
+ import {
3
+ AutoBeOpenApi,
4
+ AutoBeTestScenarioEvent,
5
+ AutoBeTestWriteEvent,
6
+ } from "@autobe/interface";
7
+ import {
8
+ ILlmApplication,
9
+ ILlmSchema,
10
+ OpenApiTypeChecker,
11
+ } from "@samchon/openapi";
4
12
  import { IPointer } from "tstl";
5
13
  import typia from "typia";
6
14
 
7
15
  import { AutoBeContext } from "../../context/AutoBeContext";
8
16
  import { assertSchemaModel } from "../../context/assertSchemaModel";
17
+ import { enforceToolCall } from "../../utils/enforceToolCall";
9
18
  import { transformTestProgressHistories } from "./transformTestProgressHistories";
10
19
 
11
20
  export async function orchestrateTestProgress<Model extends ILlmSchema.Model>(
12
21
  ctx: AutoBeContext<Model>,
13
- scenarios: AutoBeTest.Scenario[],
14
- ): Promise<AutoBeTestProgressEvent[]> {
22
+ scenarios: AutoBeTestScenarioEvent.IScenario[],
23
+ ): Promise<AutoBeTestWriteEvent[]> {
15
24
  const start: Date = new Date();
16
25
  let complete: number = 0;
17
26
 
18
- const events: AutoBeTestProgressEvent[] = await Promise.all(
27
+ const events: AutoBeTestWriteEvent[] = await Promise.all(
19
28
  /**
20
- * Generate test code for each scenario. Maps through scenarios array to
21
- * create individual test code implementations. Each scenario is processed
22
- * to generate corresponding test code and progress events.
29
+ * Generate test code for each scenario. Maps through plans array to create
30
+ * individual test code implementations. Each scenario is processed to
31
+ * generate corresponding test code and progress events.
23
32
  */
24
33
  scenarios.map(async (scenario) => {
25
34
  const code: ICreateTestCodeProps = await process(ctx, scenario);
26
-
27
- const event: AutoBeTestProgressEvent = {
28
- type: "testProgress",
35
+ const event: AutoBeTestWriteEvent = {
36
+ type: "testWrite",
29
37
  created_at: start.toISOString(),
30
38
  filename: `${code.domain}/${scenario.functionName}.ts`,
31
39
  content: code.content,
@@ -53,27 +61,20 @@ export async function orchestrateTestProgress<Model extends ILlmSchema.Model>(
53
61
  */
54
62
  async function process<Model extends ILlmSchema.Model>(
55
63
  ctx: AutoBeContext<Model>,
56
- scenario: AutoBeTest.Scenario,
64
+ scenario: AutoBeTestScenarioEvent.IScenario,
57
65
  ): Promise<ICreateTestCodeProps> {
58
66
  const pointer: IPointer<ICreateTestCodeProps | null> = {
59
67
  value: null,
60
68
  };
61
-
62
- const apiFiles = Object.entries(ctx.state().interface?.files ?? {})
63
- .filter(([filename]) => {
64
- return filename.startsWith("src/api/");
65
- })
66
- .reduce<Record<string, string>>((acc, [filename, content]) => {
67
- return Object.assign(acc, { [filename]: content });
68
- }, {});
69
-
70
- const dtoFiles = Object.entries(ctx.state().interface?.files ?? {})
71
- .filter(([filename]) => {
72
- return filename.startsWith("src/api/structures/");
73
- })
74
- .reduce<Record<string, string>>((acc, [filename, content]) => {
75
- return Object.assign(acc, { [filename]: content });
76
- }, {});
69
+ const document: AutoBeOpenApi.IDocument = filterDocument(
70
+ scenario,
71
+ ctx.state().interface!.document,
72
+ );
73
+ const files: [string, string][] = Object.entries(
74
+ await ctx.compiler.interface.compile(document),
75
+ );
76
+ const filter = (prefix: string) =>
77
+ Object.fromEntries(files.filter(([key]) => key.startsWith(prefix)));
77
78
 
78
79
  const agentica = new MicroAgentica({
79
80
  model: ctx.model,
@@ -81,7 +82,12 @@ async function process<Model extends ILlmSchema.Model>(
81
82
  config: {
82
83
  ...(ctx.config ?? {}),
83
84
  },
84
- histories: transformTestProgressHistories(apiFiles, dtoFiles),
85
+ histories: transformTestProgressHistories({
86
+ scenario: scenario,
87
+ dto: filter("src/api/structures"),
88
+ sdk: filter("src/api/functional"),
89
+ e2e: filter("test/features"),
90
+ }),
85
91
  controllers: [
86
92
  createApplication({
87
93
  model: ctx.model,
@@ -90,26 +96,61 @@ async function process<Model extends ILlmSchema.Model>(
90
96
  },
91
97
  }),
92
98
  ],
99
+ tokenUsage: ctx.usage(),
93
100
  });
101
+ enforceToolCall(agentica);
94
102
 
95
- agentica.on("request", async (event) => {
96
- if (event.body.tools) event.body.tool_choice = "required";
97
- });
98
-
99
- await agentica.conversate(
100
- [
101
- "Create test code for below scenario:",
102
- "",
103
- "```json",
104
- JSON.stringify(scenario, null, 2),
105
- "```",
106
- ].join("\n"),
107
- );
108
-
103
+ await agentica.conversate("Create e2e test functions.");
109
104
  if (pointer.value === null) throw new Error("Failed to create test code.");
110
105
  return pointer.value;
111
106
  }
112
107
 
108
+ export function filterDocument(
109
+ scenario: AutoBeTestScenarioEvent.IScenario,
110
+ document: AutoBeOpenApi.IDocument,
111
+ ): AutoBeOpenApi.IDocument {
112
+ const operations: AutoBeOpenApi.IOperation[] = document.operations.filter(
113
+ (op) => {
114
+ if (
115
+ scenario.endpoint.method === op.method &&
116
+ scenario.endpoint.path === op.path
117
+ ) {
118
+ return true;
119
+ } else if (
120
+ scenario.dependencies.some(
121
+ (dp) =>
122
+ dp.endpoint.method === op.method && dp.endpoint.path === op.path,
123
+ )
124
+ ) {
125
+ return true;
126
+ }
127
+ },
128
+ );
129
+ const components: AutoBeOpenApi.IComponents = {
130
+ schemas: {},
131
+ };
132
+ const visit = (typeName: string) => {
133
+ OpenApiTypeChecker.visit({
134
+ components: document.components,
135
+ schema: { $ref: `#/components/schemas/${typeName}` },
136
+ closure: (s) => {
137
+ if (OpenApiTypeChecker.isReference(s)) {
138
+ const key: string = s.$ref.split("/").pop()!;
139
+ components.schemas[key] = document.components.schemas[key];
140
+ }
141
+ },
142
+ });
143
+ };
144
+ for (const op of operations) {
145
+ if (op.requestBody) visit(op.requestBody.typeName);
146
+ if (op.responseBody) visit(op.responseBody.typeName);
147
+ }
148
+ return {
149
+ operations,
150
+ components,
151
+ };
152
+ }
153
+
113
154
  function createApplication<Model extends ILlmSchema.Model>(props: {
114
155
  model: Model;
115
156
  build: (next: ICreateTestCodeProps) => void;
@@ -181,7 +222,7 @@ interface ICreateTestCodeProps {
181
222
  * #### Execution Strategy
182
223
  *
183
224
  * - Outline step-by-step test execution flow
184
- * - Plan error handling and exception scenarios
225
+ * - Plan error handling and exception plans
185
226
  * - Define cleanup and teardown procedures
186
227
  * - Identify dependencies and prerequisites
187
228
  *
@@ -191,12 +232,12 @@ interface ICreateTestCodeProps {
191
232
  * 1. Prepare valid article data with required fields
192
233
  * 2. Execute POST request to create article
193
234
  * 3. Validate response structure and data integrity
194
- * 4. Test error scenarios (missing fields, invalid data)
235
+ * 4. Test error plans (missing fields, invalid data)
195
236
  * 5. Verify database state changes
196
- * 6. Reconsider the plan if it doesn't follow the Test Generation
237
+ * 6. Reconsider the scenario if it doesn't follow the Test Generation
197
238
  * Guildelines.
198
239
  */
199
- plan: string;
240
+ scenario: string;
200
241
 
201
242
  /**
202
243
  * Functional domain classification for test organization.
@@ -1,150 +1,253 @@
1
- import { IAgenticaController, MicroAgentica } from "@agentica/core";
2
- import { AutoBeOpenApi, AutoBeTest } from "@autobe/interface";
3
- import { AutoBeTestScenarioEvent } from "@autobe/interface/src/events/AutoBeTestScenarioEvent";
4
- import { ILlmApplication, ILlmSchema } from "@samchon/openapi";
1
+ import {
2
+ IAgenticaController,
3
+ IAgenticaHistoryJson,
4
+ MicroAgentica,
5
+ } from "@agentica/core";
6
+ import { AutoBeOpenApi } from "@autobe/interface";
7
+ import { AutoBeTestScenarioEvent } from "@autobe/interface";
8
+ import { ILlmApplication, ILlmSchema, IValidation } from "@samchon/openapi";
5
9
  import { IPointer } from "tstl";
6
10
  import typia from "typia";
11
+ import { v4 } from "uuid";
7
12
 
13
+ import { AutoBeSystemPromptConstant } from "../../constants/AutoBeSystemPromptConstant";
8
14
  import { AutoBeContext } from "../../context/AutoBeContext";
9
15
  import { assertSchemaModel } from "../../context/assertSchemaModel";
10
- import { transformTestScenarioHistories } from "./transformTestScenarioHistories";
16
+ import { divideArray } from "../../utils/divideArray";
17
+ import { enforceToolCall } from "../../utils/enforceToolCall";
18
+ import { IAutoBeTestScenarioApplication } from "./structures/IAutoBeTestScenarioApplication";
11
19
 
12
20
  export async function orchestrateTestScenario<Model extends ILlmSchema.Model>(
13
21
  ctx: AutoBeContext<Model>,
14
22
  ): Promise<AutoBeTestScenarioEvent> {
15
- const files = Object.entries(ctx.state().interface?.files ?? {})
16
- .filter(([filename]) => {
17
- return filename.startsWith("test/features/api/");
18
- })
19
- .reduce<Record<string, string>>((acc, [filename, content]) => {
20
- return Object.assign(acc, { [filename]: content });
21
- }, {});
22
-
23
23
  const operations = ctx.state().interface?.document.operations ?? [];
24
- const endpoints: Omit<AutoBeOpenApi.IOperation, "specification">[] =
25
- operations.map((it) => {
26
- return {
27
- method: it.method,
28
- path: it.path,
29
- summary: it.summary,
30
- description: it.description,
31
- parameters: it.parameters,
32
- requestBody: it.requestBody,
33
- responseBody: it.responseBody,
34
- };
35
- });
24
+ if (operations.length === 0) {
25
+ throw new Error(
26
+ "Cannot write test scenarios because these are no operations.",
27
+ );
28
+ }
36
29
 
37
- const start: Date = new Date();
38
-
39
- let completed: number = 0;
40
-
41
- const scenarios: AutoBeTest.IScenario[][] = await Promise.all(
42
- endpoints.map(async (endpoint, i, arr) => {
43
- const endponits = arr.filter((_el, j) => i !== j);
44
- const rows: AutoBeTest.IScenario[] = await process(
45
- ctx,
46
- endpoint,
47
- endponits,
48
- files,
49
- );
50
- ctx.dispatch({
51
- type: "testScenario",
52
- scenarios: rows,
53
- total: rows.flatMap((el) => el.scenarios).length,
54
- step: ctx.state().test?.step ?? 0,
55
- completed,
56
- created_at: start.toISOString(),
57
- });
58
- return rows;
59
- }),
60
- );
30
+ const exclude: IAutoBeTestScenarioApplication.IScenarioGroup[] = [];
31
+ let include: AutoBeOpenApi.IOperation[] = Array.from(operations);
32
+
33
+ do {
34
+ const matrix = divideArray({ array: include, capacity: 30 });
35
+
36
+ await Promise.all(
37
+ matrix.map(async (_include) => {
38
+ exclude.push(
39
+ ...(await execute(
40
+ ctx,
41
+ operations,
42
+ _include,
43
+ exclude.map((x) => x.endpoint),
44
+ )),
45
+ );
46
+ }),
47
+ );
48
+
49
+ include = include.filter((op) => {
50
+ if (
51
+ exclude.some(
52
+ (pg) =>
53
+ pg.endpoint.method === op.method && pg.endpoint.path === op.path,
54
+ )
55
+ ) {
56
+ return false;
57
+ }
58
+ return true;
59
+ });
60
+ } while (include.length > 0);
61
61
 
62
62
  return {
63
63
  type: "testScenario",
64
- scenarios: scenarios.flat(),
65
- total: scenarios.flat().flatMap((el) => el.scenarios).length,
66
- step: ctx.state().test?.step ?? 0,
67
- completed,
68
- created_at: start.toISOString(),
69
- };
64
+ step: ctx.state().analyze?.step ?? 0,
65
+ scenarios: exclude.flatMap((pg) => {
66
+ return pg.scenarios.map((plan) => {
67
+ return {
68
+ endpoint: pg.endpoint,
69
+ draft: plan.draft,
70
+ functionName: plan.functionName,
71
+ dependencies: plan.dependsOn,
72
+ } satisfies AutoBeTestScenarioEvent.IScenario;
73
+ });
74
+ }),
75
+ created_at: new Date().toISOString(),
76
+ } as AutoBeTestScenarioEvent;
70
77
  }
71
78
 
72
- async function process<Model extends ILlmSchema.Model>(
79
+ const execute = async <Model extends ILlmSchema.Model>(
73
80
  ctx: AutoBeContext<Model>,
74
- endpoint: AutoBeOpenApi.IEndpoint,
75
- endpoints: AutoBeOpenApi.IEndpoint[],
76
- files: Record<string, string>,
77
- ): Promise<AutoBeTest.IScenario[]> {
78
- const pointer: IPointer<AutoBeTest.IScenario[] | null> = {
79
- value: null,
81
+ ops: AutoBeOpenApi.IOperation[],
82
+ include: Pick<AutoBeOpenApi.IOperation, "method" | "path">[],
83
+ exclude: Pick<AutoBeOpenApi.IOperation, "method" | "path">[],
84
+ ) => {
85
+ const pointer: IPointer<IAutoBeTestScenarioApplication.IScenarioGroup[]> = {
86
+ value: [],
80
87
  };
81
-
82
- const agentica = new MicroAgentica({
88
+ const agentica: MicroAgentica<Model> = new MicroAgentica({
83
89
  model: ctx.model,
84
90
  vendor: ctx.vendor,
85
91
  config: {
86
- ...(ctx.config ?? { locale: "en-US" }),
87
- systemPrompt: {
88
- describe: () => {
89
- return "Answer only 'completion' or 'failure'.";
90
- },
92
+ ...(ctx.config ?? {}),
93
+ executor: {
94
+ describe: null,
91
95
  },
92
96
  },
93
97
  tokenUsage: ctx.usage(),
94
- histories: [
95
- ...transformTestScenarioHistories(ctx.state(), endpoints, files),
96
- ],
98
+ histories: createHistoryProperties(ops, include, exclude),
97
99
  controllers: [
98
100
  createApplication({
99
101
  model: ctx.model,
100
102
  build: (next) => {
101
- pointer.value = next.scenarios;
103
+ pointer.value ??= [];
104
+ pointer.value.push(...next.scenarioGroups);
102
105
  },
103
106
  }),
104
107
  ],
105
108
  });
109
+ enforceToolCall(agentica);
106
110
 
107
- agentica.on("request", async (event) => {
108
- if (event.body.tools) event.body.tool_choice = "required";
109
- });
111
+ await agentica.conversate(`create test scenarios.`);
112
+ if (pointer.value.length === 0) {
113
+ throw new Error("Failed to create test plans.");
114
+ }
115
+
116
+ return pointer.value;
117
+ };
110
118
 
111
- await agentica.conversate(
112
- [
113
- "Make User Scenarios for below endpoint:",
119
+ const createHistoryProperties = (
120
+ operations: AutoBeOpenApi.IOperation[],
121
+ include: Pick<AutoBeOpenApi.IOperation, "method" | "path">[],
122
+ exclude: Pick<AutoBeOpenApi.IOperation, "method" | "path">[],
123
+ ) => [
124
+ {
125
+ id: v4(),
126
+ created_at: new Date().toISOString(),
127
+ type: "systemMessage",
128
+ text: AutoBeSystemPromptConstant.TEST_SCENARIO,
129
+ } satisfies IAgenticaHistoryJson.ISystemMessage,
130
+ {
131
+ id: v4(),
132
+ created_at: new Date().toISOString(),
133
+ type: "systemMessage",
134
+ text: [
135
+ "Below are the full operations. Please refer to this.",
136
+ "Your role is to draft all test cases for each given Operation.",
137
+ "It is also permissible to write multiple test codes on a single endpoint.",
138
+ "However, rather than meaningless tests, business logic tests should be written and an E2E test situation should be assumed.",
114
139
  "",
115
140
  "```json",
116
- JSON.stringify(endpoint, null, 2),
141
+ JSON.stringify(
142
+ operations.map((el) => ({
143
+ path: el.path,
144
+ method: el.method,
145
+ summary: el.summary,
146
+ })),
147
+ ),
117
148
  "```",
118
149
  ].join("\n"),
119
- );
120
-
121
- if (pointer.value === null) throw new Error("Failed to make scenarios.");
122
- return pointer.value;
123
- }
150
+ } satisfies IAgenticaHistoryJson.ISystemMessage,
151
+ {
152
+ id: v4(),
153
+ created_at: new Date().toISOString(),
154
+ type: "systemMessage",
155
+ text: [
156
+ "# Included in Test Plan",
157
+ include
158
+ .map((el) => `- ${el.method.toUpperCase()}: ${el.path}`)
159
+ .join("\n"),
160
+ "",
161
+ "# Excluded from Test Plan",
162
+ "These are the endpoints that have already been used in test codes generated as part of a plan group.",
163
+ "These endpoints do not need to be tested again.",
164
+ "However, it is allowed to reference or depend on these endpoints when writing test codes for other purposes.",
165
+ exclude
166
+ .map((el) => `- ${el.method.toUpperCase()}: ${el.path}`)
167
+ .join("\n"),
168
+ ].join("\n"),
169
+ } satisfies IAgenticaHistoryJson.ISystemMessage,
170
+ ];
124
171
 
125
172
  function createApplication<Model extends ILlmSchema.Model>(props: {
126
173
  model: Model;
127
- build: (next: IMakeScenarioProps) => void;
174
+ build: (next: IAutoBeTestScenarioApplication.IProps) => void;
128
175
  }): IAgenticaController.IClass<Model> {
129
176
  assertSchemaModel(props.model);
130
177
 
131
178
  const application: ILlmApplication<Model> = collection[
132
179
  props.model
133
180
  ] as unknown as ILlmApplication<Model>;
181
+
182
+ application.functions[0].validate = (next: unknown): IValidation => {
183
+ const result: IValidation<IAutoBeTestScenarioApplication.IProps> =
184
+ typia.validate<IAutoBeTestScenarioApplication.IProps>(next);
185
+ if (result.success === false) return result;
186
+
187
+ const errors: IValidation.IError[] = [];
188
+ result.data.scenarioGroups.forEach((pg, i, arr) => {
189
+ arr.forEach((target, j) => {
190
+ if (
191
+ i !== j &&
192
+ target.endpoint.method === pg.endpoint.method &&
193
+ target.endpoint.path === pg.endpoint.path
194
+ ) {
195
+ if (
196
+ !errors.some(
197
+ (el) =>
198
+ el.path !== `planGroups[${j}].path` &&
199
+ el.value !== target.endpoint.path,
200
+ )
201
+ ) {
202
+ errors.push({
203
+ path: `planGroups[${j}].path`,
204
+ expected: `planGroup's {method + path} cannot duplicated.`,
205
+ value: target.endpoint.path,
206
+ });
207
+ }
208
+
209
+ if (
210
+ !errors.some(
211
+ (el) =>
212
+ el.path !== `planGroups[${j}].method` &&
213
+ el.value !== target.endpoint.method,
214
+ )
215
+ ) {
216
+ errors.push({
217
+ path: `planGroups[${j}].method`,
218
+ expected: `planGroup's {method + path} cannot duplicated.`,
219
+ value: target.endpoint.method,
220
+ });
221
+ }
222
+ }
223
+ });
224
+ });
225
+
226
+ if (errors.length !== 0) {
227
+ console.log(JSON.stringify(errors, null, 2), "errors");
228
+ return {
229
+ success: false,
230
+ errors,
231
+ data: next,
232
+ };
233
+ }
234
+
235
+ return result;
236
+ };
134
237
  return {
135
238
  protocol: "class",
136
- name: "Make User Scenarios",
239
+ name: "Make test plans",
137
240
  application,
138
241
  execute: {
139
242
  makeScenario: (next) => {
140
243
  props.build(next);
141
244
  },
142
- } satisfies IApplication,
245
+ } satisfies IAutoBeTestScenarioApplication,
143
246
  };
144
247
  }
145
248
 
146
249
  const claude = typia.llm.application<
147
- IApplication,
250
+ IAutoBeTestScenarioApplication,
148
251
  "claude",
149
252
  {
150
253
  reference: true;
@@ -152,7 +255,7 @@ const claude = typia.llm.application<
152
255
  >();
153
256
  const collection = {
154
257
  chatgpt: typia.llm.application<
155
- IApplication,
258
+ IAutoBeTestScenarioApplication,
156
259
  "chatgpt",
157
260
  { reference: true }
158
261
  >(),
@@ -160,19 +263,5 @@ const collection = {
160
263
  llama: claude,
161
264
  deepseek: claude,
162
265
  "3.1": claude,
163
- "3.0": typia.llm.application<IApplication, "3.0">(),
266
+ "3.0": typia.llm.application<IAutoBeTestScenarioApplication, "3.0">(),
164
267
  };
165
-
166
- interface IApplication {
167
- /**
168
- * Make user scenarios for the given endpoints.
169
- *
170
- * @param props Properties containing the endpoints and user scenarios.
171
- */
172
- makeScenario(props: IMakeScenarioProps): void;
173
- }
174
-
175
- interface IMakeScenarioProps {
176
- /** Array of user scenarios. */
177
- scenarios: AutoBeTest.IScenario[];
178
- }