Skip to content

Commit 67e0496

Browse files
committed
feat(chatbot): integrate create task, answer question for chatbot
1 parent 8fb4d36 commit 67e0496

3 files changed

Lines changed: 124 additions & 4 deletions

File tree

src/lib/features/chatbot/services/chatbot_services.dart

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:flutter/foundation.dart';
22
import 'package:flutter_dotenv/flutter_dotenv.dart';
33
import 'package:google_generative_ai/google_generative_ai.dart';
4+
import 'package:supabase_flutter/supabase_flutter.dart';
45

56
class ChatBotAssistantService {
67
final String _apiKey = (dotenv.env['GEMINI_API_KEY'] ?? '').trim();
@@ -16,6 +17,37 @@ class ChatBotAssistantService {
1617
_model = GenerativeModel(
1718
apiKey: _apiKey,
1819
model: 'gemini-2.5-flash',
20+
tools: [
21+
Tool(
22+
functionDeclarations: [
23+
FunctionDeclaration(
24+
'create_task_full',
25+
'Tạo một công việc mới. Hãy tự động trích xuất tên công việc, suy luận độ ưu tiên (1-Thấp, 2-Trung bình, 3-Cao) và các thẻ (tags) dựa trên câu nói của người dùng.',
26+
Schema(
27+
SchemaType.object,
28+
properties: {
29+
'title': Schema(
30+
SchemaType.string,
31+
description: 'Tên công việc cần làm',
32+
),
33+
'priority': Schema(
34+
SchemaType.integer,
35+
description:
36+
'Độ ưu tiên: 1 (Thấp), 2 (Trung bình), 3 (Cao). Nếu người dùng không nói rõ, mặc định là 1.',
37+
),
38+
'tags': Schema(
39+
SchemaType.array,
40+
items: Schema(SchemaType.string),
41+
description:
42+
'Danh sách các thẻ phân loại (ví dụ: ["Học tập", "Gấp", "Backend"]). Gửi mảng rỗng [] nếu không có.',
43+
),
44+
},
45+
requiredProperties: ['title', 'priority', 'tags'],
46+
),
47+
),
48+
],
49+
),
50+
],
1951
systemInstruction: Content.system(
2052
'Bạn là một chuyên gia quản lý thời gian và trợ lý năng suất cho ứng dụng Task Management. '
2153
'Nhiệm vụ của bạn là đưa ra lời khuyên ngắn gọn (dưới 100 chữ), thực tế để giúp người dùng '
@@ -36,8 +68,48 @@ class ChatBotAssistantService {
3668
final response = await _chatSession!.sendMessage(
3769
Content.text(userMessage),
3870
);
71+
72+
if (response.functionCalls.isNotEmpty) {
73+
final functionCall = response.functionCalls.first;
74+
if (functionCall.name == 'create_task_full') {
75+
final args = functionCall.args;
76+
final title = args['title'] as String;
77+
final priority = (args['priority'] as num?)?.toInt() ?? 1;
78+
final rawTags = args['tags'] as List<dynamic>? ?? [];
79+
final tags = rawTags.map((e) => e.toString()).toList();
80+
81+
final userId = Supabase.instance.client.auth.currentUser?.id;
82+
if (userId == null) {
83+
return 'Vui lòng đăng nhập để tạo công việc.';
84+
}
85+
86+
final dbResponse = await Supabase.instance.client.rpc(
87+
'create_task_full',
88+
params: {
89+
'p_title': title,
90+
'p_priority': priority,
91+
'p_profile_id': userId,
92+
'p_tag_names': tags,
93+
},
94+
);
95+
96+
final isSuccess = dbResponse['success'] == true;
97+
final functionResponse = await _chatSession!.sendMessage(
98+
Content.functionResponse('create_task_full', {
99+
'status': isSuccess ? 'Thành công' : 'Thất bại',
100+
}),
101+
);
102+
return functionResponse.text ?? 'Đã xử lý xong yêu cầu của bạn!';
103+
}
104+
}
39105
return response.text ?? 'Xin lỗi, trợ lý đang bận xíu. Thử lại sau nhé!';
40106
} catch (e) {
107+
final errorString = e.toString();
108+
if (errorString.contains('503')) {
109+
return 'Bạn đợi vài phút rồi chat lại nhé!';
110+
} else if (errorString.contains('429')) {
111+
return 'Bạn chat nhanh quá! Vui lòng chờ chút';
112+
}
41113
return 'Lỗi kết nối AI: $e';
42114
}
43115
}

src/lib/features/chatbot/view/widgets/chat_header.dart

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,6 @@ class ChatHeader extends StatelessWidget {
3838
],
3939
),
4040
),
41-
IconButton(
42-
onPressed: () {},
43-
icon: Icon(Icons.settings, color: scheme.onSurfaceVariant),
44-
),
4541
],
4642
),
4743
);
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
CREATE OR REPLACE FUNCTION create_task_full(
2+
p_title TEXT,
3+
p_priority INT4,
4+
p_profile_id UUID,
5+
p_tag_names TEXT[]
6+
)
7+
RETURNS JSON
8+
LANGUAGE plpgsql
9+
AS $$
10+
DECLARE
11+
v_task_id INT8;
12+
v_tag_name TEXT;
13+
v_tag_id INT8;
14+
BEGIN
15+
16+
INSERT INTO task (title, priority, profile_id, status)
17+
VALUES (p_title, p_priority, p_profile_id, 0)
18+
RETURNING id INTO v_task_id;
19+
20+
IF p_tag_names IS NOT NULL THEN
21+
FOREACH v_tag_name IN ARRAY p_tag_names
22+
LOOP
23+
v_tag_name := trim(v_tag_name);
24+
25+
IF v_tag_name != '' THEN
26+
INSERT INTO tag (name, profile_id, color_code)
27+
VALUES (v_tag_name, p_profile_id, '#6200EE')
28+
ON CONFLICT (name, profile_id)
29+
DO UPDATE SET name = EXCLUDED.name
30+
RETURNING id INTO v_tag_id;
31+
32+
33+
INSERT INTO task_tags (task_id, tag_id)
34+
VALUES (v_task_id, v_tag_id)
35+
ON CONFLICT DO NOTHING;
36+
END IF;
37+
END LOOP;
38+
END IF;
39+
40+
RETURN json_build_object(
41+
'success', true,
42+
'task_id', v_task_id,
43+
'message', 'Đã tạo task với priority ' || p_priority
44+
);
45+
46+
EXCEPTION WHEN OTHERS THEN
47+
RETURN json_build_object(
48+
'success', false,
49+
'error', SQLERRM
50+
);
51+
END;
52+
$$;

0 commit comments

Comments
 (0)