Skip to content

Commit 461c4d3

Browse files
authored
Merge pull request #1623 from line/feat/implement-ai-field
feat: implement ai field
2 parents c495df8 + bb40c53 commit 461c4d3

File tree

260 files changed

+14010
-2597
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

260 files changed

+14010
-2597
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,16 @@ For more detailed setup instructions, refer to the [Getting Started](#getting-st
4848
| ![Image 1](./assets/01-feedback-tag.png) | ![Image 2](./assets/02-Issue-Kanban.png) |
4949
| ------------------------------------------- | ----------------------------------------- |
5050
| ![Image 3](./assets/03-issue-tracker.png) | ![Image 4](./assets/04-single-signon.png) |
51-
| ![Image 3](./assets/05-role-management.png) | ![Image 4](./assets/06-dashboard.png) |
51+
| ![Image 5](./assets/05-role-management.png) | ![Image 6](./assets/06-dashboard.png) |
5252

5353
- **Feedback Tag**: You can assign tags to each feedback to categorize them by topic.
5454
- **Kanban Mode**: Experience the advantage of organizing and visualizing issue groups efficiently with Kanban mode.
5555
- **Issue Tracker**: The Issue feature has a status indicator that lets you use it as a simple issue tracker. You can also link each issue to a ticket in your own issue tracker system.
5656
- **Single Sign-on**: Authentication offers OAuth to accommodate enterprise-level single sign-on (SSO) requirements.
5757
- **Role Management**: Role Based Access Control (RBAC)
5858
- **Dashboard**: Dashboard help you visualize statistical data about your feedback and issues so you can learn about them at a glance.
59+
- **🤖 AI Field**: Write custom prompts to get AI-powered analysis of your feedback data. Examples include summarization, translation, keyword extraction, and sentiment analysis.
60+
- **🤖 AI Issue Recommandation**: Get intelligent issue recommendations based on feedback analysis using AI-powered insights.
5961

6062
## Getting Started
6163

apps/api/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
"@nestjs/testing": "^11.1.5",
8989
"@swc-node/jest": "^1.8.13",
9090
"@swc/cli": "0.7.8",
91-
"@swc/core": "^1.13.3",
91+
"@swc/core": "^1.4.16",
9292
"@swc/helpers": "^0.5.17",
9393
"@types/bcrypt": "^6.0.0",
9494
"@types/express": "^5.0.3",

apps/api/src/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import { FieldModule } from './domains/admin/channel/field/field.module';
4141
import { OptionModule } from './domains/admin/channel/option/option.module';
4242
import { FeedbackModule } from './domains/admin/feedback/feedback.module';
4343
import { HistoryModule } from './domains/admin/history/history.module';
44+
import { AIModule } from './domains/admin/project/ai/ai.module';
4445
import { ApiKeyModule } from './domains/admin/project/api-key/api-key.module';
4546
import { CategoryModule } from './domains/admin/project/category/category.module';
4647
import { IssueTrackerModule } from './domains/admin/project/issue-tracker/issue-tracker.module';
@@ -83,6 +84,7 @@ export const domainModules = [
8384
FeedbackIssueStatisticsModule,
8485
APIModule,
8586
SchedulerLockModule,
87+
AIModule,
8688
] as (typeof AuthModule)[];
8789

8890
@Module({

apps/web/src/shared/types/svg.d.ts renamed to apps/api/src/common/enums/ai-prompt-status.enum.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,9 @@
1313
* License for the specific language governing permissions and limitations
1414
* under the License.
1515
*/
16-
declare module '*.svg' {
17-
import type * as React from 'react';
1816

19-
export const ReactComponent: React.FunctionComponent<
20-
React.ComponentProps<'svg'> & { title?: string }
21-
>;
17+
export enum AIPromptStatusEnum {
18+
success = 'success',
19+
error = 'error',
20+
loading = 'loading',
2221
}

packages/ufb-tailwindcss/src/components/toaster.css renamed to apps/api/src/common/enums/ai-providers.enum.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,8 @@
1313
* License for the specific language governing permissions and limitations
1414
* under the License.
1515
*/
16-
.toaster {
17-
}
18-
19-
.toast {
20-
@apply !absolute !left-0;
21-
}
2216

23-
.toast-close {
24-
@apply order-1;
17+
export enum AIProvidersEnum {
18+
OPEN_AI = 'OPEN_AI',
19+
GEMINI = 'GEMINI',
2520
}

apps/api/src/common/enums/field-format.enum.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export enum FieldFormatEnum {
2121
multiSelect = 'multiSelect',
2222
date = 'date',
2323
images = 'images',
24+
aiField = 'aiField',
2425
}
2526

2627
export function isSelectFieldFormat(type: FieldFormatEnum) {
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/**
2+
* Copyright 2025 LY Corporation
3+
*
4+
* LY Corporation licenses this file to you under the Apache License,
5+
* version 2.0 (the "License"); you may not use this file except in compliance
6+
* with the License. You may obtain a copy of the License at:
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
import type { MigrationInterface, QueryRunner } from 'typeorm';
18+
19+
export class AddAIFieldTables1747019250371 implements MigrationInterface {
20+
name = 'AddAIFieldTables1747019250371';
21+
22+
public async up(queryRunner: QueryRunner): Promise<void> {
23+
await queryRunner.query(
24+
`CREATE TABLE \`ai_integrations\` (
25+
\`id\` int NOT NULL AUTO_INCREMENT,
26+
\`project_id\` int NOT NULL UNIQUE,
27+
\`provider\` enum('OPEN_AI', 'GEMINI') NOT NULL,
28+
\`api_key\` varchar(255) NOT NULL,
29+
\`endpoint_url\` varchar(255) DEFAULT NULL,
30+
\`system_prompt\` text NOT NULL,
31+
\`token_threshold\` int NULL DEFAULT NULL,
32+
\`notification_threshold\` float NULL DEFAULT NULL,
33+
\`created_at\` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
34+
\`updated_at\` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
35+
\`deleted_at\` DATETIME(6) DEFAULT NULL,
36+
PRIMARY KEY (\`id\`)
37+
) ENGINE=InnoDB`,
38+
);
39+
await queryRunner.query(
40+
`ALTER TABLE \`ai_integrations\` ADD CONSTRAINT \`FK_project_id\` FOREIGN KEY (\`project_id\`) REFERENCES \`projects\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION`,
41+
);
42+
await queryRunner.query(
43+
`CREATE TABLE \`ai_field_templates\` (
44+
\`id\` int NOT NULL AUTO_INCREMENT,
45+
\`project_id\` int NOT NULL,
46+
\`title\` varchar(255) NOT NULL DEFAULT '',
47+
\`prompt\` text NOT NULL,
48+
\`model\` varchar(255) NULL DEFAULT NULL,
49+
\`temperature\` float NOT NULL DEFAULT 0.7,
50+
\`created_at\` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
51+
\`updated_at\` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
52+
\`deleted_at\` DATETIME(6) DEFAULT NULL,
53+
PRIMARY KEY (\`id\`)
54+
) ENGINE=InnoDB`,
55+
);
56+
await queryRunner.query(
57+
`ALTER TABLE \`ai_field_templates\`
58+
ADD CONSTRAINT \`FK_ai_field_templates_project_id\`
59+
FOREIGN KEY (\`project_id\`) REFERENCES \`projects\`(\`id\`)
60+
ON DELETE CASCADE ON UPDATE NO ACTION`,
61+
);
62+
await queryRunner.query(
63+
`CREATE TABLE \`ai_usages\` (
64+
\`id\` int NOT NULL AUTO_INCREMENT,
65+
\`created_at\` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
66+
\`updated_at\` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
67+
\`deleted_at\` DATETIME(6) DEFAULT NULL,
68+
\`year\` int NOT NULL,
69+
\`month\` int NOT NULL,
70+
\`day\` int NOT NULL,
71+
\`category\` enum('AI_FIELD', 'ISSUE_RECOMMEND') NOT NULL,
72+
\`provider\` enum('OPEN_AI', 'GEMINI') NOT NULL,
73+
\`used_tokens\` int NOT NULL,
74+
\`project_id\` int NOT NULL,
75+
PRIMARY KEY (\`id\`),
76+
FOREIGN KEY (\`project_id\`) REFERENCES \`projects\`(\`id\`) ON DELETE CASCADE
77+
) ENGINE=InnoDB`,
78+
);
79+
}
80+
81+
public async down(queryRunner: QueryRunner): Promise<void> {
82+
await queryRunner.query(`DROP TABLE \`ai_usages\``);
83+
await queryRunner.query(
84+
`ALTER TABLE \`ai_field_templates\` DROP FOREIGN KEY \`FK_ai_field_templates_project_id\``,
85+
);
86+
await queryRunner.query(`DROP TABLE \`ai_field_templates\``);
87+
await queryRunner.query(
88+
`ALTER TABLE \`ai_integrations\` DROP FOREIGN KEY \`FK_project_id\``,
89+
);
90+
await queryRunner.query(`DROP TABLE \`ai_integrations\``);
91+
}
92+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* Copyright 2025 LY Corporation
3+
*
4+
* LY Corporation licenses this file to you under the Apache License,
5+
* version 2.0 (the "License"); you may not use this file except in compliance
6+
* with the License. You may obtain a copy of the License at:
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
import type { MigrationInterface, QueryRunner } from 'typeorm';
18+
19+
export class AddAiFieldType1747717939594 implements MigrationInterface {
20+
name = 'AddAiFieldType1747717939594';
21+
22+
public async up(queryRunner: QueryRunner): Promise<void> {
23+
await queryRunner.query(
24+
`ALTER TABLE \`fields\` CHANGE \`format\` \`format\` enum ('text', 'keyword', 'number', 'select', 'multiSelect', 'date', 'images', 'aiField') NOT NULL`,
25+
);
26+
await queryRunner.query(
27+
`ALTER TABLE \`fields\` ADD \`ai_field_template_id\` int NULL DEFAULT NULL AFTER \`order\``,
28+
);
29+
await queryRunner.query(
30+
`ALTER TABLE \`fields\` ADD CONSTRAINT \`FK_fields_ai_field_template_id\` FOREIGN KEY (\`ai_field_template_id\`) REFERENCES \`ai_field_templates\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION`,
31+
);
32+
await queryRunner.query(
33+
`ALTER TABLE \`fields\` ADD \`ai_field_target_keys\` json NULL DEFAULT NULL AFTER \`ai_field_template_id\``,
34+
);
35+
await queryRunner.query(
36+
`ALTER TABLE \`fields\` ADD \`ai_field_auto_processing\` boolean NULL DEFAULT NULL AFTER \`ai_field_target_keys\``,
37+
);
38+
}
39+
40+
public async down(queryRunner: QueryRunner): Promise<void> {
41+
await queryRunner.query(
42+
`ALTER TABLE \`fields\` DROP COLUMN \`ai_field_auto_processing\``,
43+
);
44+
await queryRunner.query(
45+
`ALTER TABLE \`fields\` DROP COLUMN \`ai_field_target_keys\``,
46+
);
47+
await queryRunner.query(
48+
`ALTER TABLE \`fields\` DROP FOREIGN KEY \`FK_fields_ai_field_template_id\``,
49+
);
50+
await queryRunner.query(
51+
`ALTER TABLE \`fields\` DROP COLUMN \`ai_field_template_id\``,
52+
);
53+
await queryRunner.query(
54+
`ALTER TABLE \`fields\` CHANGE \`format\` \`format\` enum ('text', 'keyword', 'number', 'select', 'multiSelect', 'date', 'images') NOT NULL`,
55+
);
56+
}
57+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* Copyright 2025 LY Corporation
3+
*
4+
* LY Corporation licenses this file to you under the Apache License,
5+
* version 2.0 (the "License"); you may not use this file except in compliance
6+
* with the License. You may obtain a copy of the License at:
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
import type { MigrationInterface, QueryRunner } from 'typeorm';
17+
18+
export class AddAiIssueTables1751434107382 implements MigrationInterface {
19+
name = 'AddAiIssueTables1751434107382';
20+
21+
public async up(queryRunner: QueryRunner): Promise<void> {
22+
await queryRunner.query(
23+
`CREATE TABLE \`ai_issue_templates\` (
24+
\`id\` int NOT NULL AUTO_INCREMENT,
25+
\`channel_id\` int NOT NULL,
26+
\`target_field_keys\` json NOT NULL,
27+
\`prompt\` text NOT NULL,
28+
\`is_enabled\` tinyint NOT NULL DEFAULT 1,
29+
\`model\` varchar(255) NULL DEFAULT NULL,
30+
\`temperature\` float NOT NULL DEFAULT 0.7,
31+
\`data_reference_amount\` int NOT NULL DEFAULT 3,
32+
\`created_at\` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
33+
\`updated_at\` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
34+
\`deleted_at\` DATETIME(6) DEFAULT NULL,
35+
PRIMARY KEY (\`id\`)
36+
) ENGINE=InnoDB`,
37+
);
38+
await queryRunner.query(
39+
`ALTER TABLE \`ai_issue_templates\`
40+
ADD CONSTRAINT \`FK_ai_issue_templates_channel_id\`
41+
FOREIGN KEY (\`channel_id\`) REFERENCES \`channels\`(\`id\`)
42+
ON DELETE CASCADE ON UPDATE NO ACTION`,
43+
);
44+
}
45+
46+
public async down(queryRunner: QueryRunner): Promise<void> {
47+
await queryRunner.query(
48+
`ALTER TABLE \`ai_issue_templates\` DROP FOREIGN KEY \`FK_ai_issue_templates_channel_id\``,
49+
);
50+
await queryRunner.query(`DROP TABLE \`ai_issue_templates\``);
51+
}
52+
}

0 commit comments

Comments
 (0)