Code fixes, style, analysis separation

This commit is contained in:
Dawid Pietrykowski 2024-08-08 18:18:01 +02:00
parent ff9cdc02d8
commit ebededacc1
8 changed files with 169 additions and 103 deletions

1
lib/api_key.dart Normal file
View File

@ -0,0 +1 @@
const String geminiApiKey = '';

View File

@ -0,0 +1 @@

View File

@ -2,8 +2,8 @@ import 'dart:convert';
import 'package:bloc/bloc.dart';
import 'package:flutter/services.dart';
import 'package:gemini_app/api_key.dart';
import 'package:gemini_app/config.dart';
import 'package:gemini_app/bloc/eeg_state.dart';
import 'package:gemini_app/eeg/eeg_service.dart';
import 'package:get_it/get_it.dart';
import 'package:google_generative_ai/google_generative_ai.dart';
@ -23,6 +23,10 @@ here describe what is the state of the student and how to best approach them
here continue with the lesson, respond to answers, etc
""";
const String LESSON_START_TOKEN = "<LESSON_START_TOKEN>";
const String ANALYSIS_START_TOKEN = "<ANALYSIS_START_TOKEN>";
const String QUIZ_START_TOKEN = "<QUIZ_START_TOKEN>";
enum GeminiStatus { initial, loading, success, error }
// enum MessageType { text, image, audio, video }
@ -147,9 +151,10 @@ class GeminiCubit extends Cubit<GeminiState> {
void startLesson() async {
final quizQuestions = await loadQuizQuestions();
final String lessonScript = await rootBundle.loadString('assets/lessons/cells.md');
final String lessonScript =
await rootBundle.loadString('assets/lessons/cells.md');
// final String prompt =
// "Jesteś nauczycielem/chatbotem prowadzącym zajęcia z jednym uczniem. Uczeń ma możliwość zadawania pytań w trakcie, natomiast jesteś odpowiedzialny za prowadzenie lekcji i przedstawienie tematu. Zacznij prowadzić lekcje dla jednego ucznia na podstawie poniszego skryptu:\n$rjp";
// "Jesteś nauczycielem/chatbotem prowadzącym zajęcia z jednym uczniem. Uczeń ma możliwość zadawania pytań w trakcie, natomiast jesteś odpowiedzialny za prowadzenie lekcji i przedstawienie tematu. Zacznij prowadzić lekcje dla jednego ucznia na podstawie poniszego skryptu:\n$rjp";
final String prompt =
"You are a teacher/chatbot conducting a class with one student. The student has the ability to ask questions during the lesson, while you are responsible for leading the class and presenting the topic. Start conducting the lesson for one student based on the script below:\n$lessonScript";
@ -181,42 +186,51 @@ class GeminiCubit extends Cubit<GeminiState> {
model: model);
emit(initialState);
try {
final chat = state.model!.startChat(history: [Content.text(prompt)]);
final stream = chat.sendMessageStream(Content.text(
"EEG DATA:\n${GetIt.instance<EegService>().state.getJsonString()}\nMessage:\n$prompt"));
sendMessage("");
String responseText = '';
// try {
// final chat = state.model!.startChat(history: [Content.text(prompt)]);
// final stream = chat.sendMessageStream(Content.text(
// "EEG DATA:\n${GetIt.instance<EegService>().state.getJsonString()}\nMessage:\n$prompt"));
await for (final chunk in stream) {
responseText += chunk.text ?? '';
emit(initialState.copyWith(
status: GeminiStatus.success,
messages: [
lessonScriptMessage,
Message(
source: MessageSource.agent,
text: responseText,
type: MessageType.text)
],
model: model));
}
} catch (e) {
emit(GeminiState(
status: GeminiStatus.error,
messages: state.messages,
error: e.toString(),
));
}
// String responseText = '';
// await for (final chunk in stream) {
// responseText += chunk.text ?? '';
// emit(initialState.copyWith(
// status: GeminiStatus.success,
// messages: [
// lessonScriptMessage,
// Message(
// source: MessageSource.agent,
// text: responseText,
// type: MessageType.text)
// ],
// model: model));
// }
// } catch (e) {
// emit(GeminiState(
// status: GeminiStatus.error,
// messages: state.messages,
// error: e.toString(),
// ));
// }
}
void sendMessage(String prompt) async {
List<Message> messagesWithoutPrompt = state.messages;
var messagesWithPrompt = state.messages +
[
Message(
text: prompt, type: MessageType.text, source: MessageSource.user)
];
List<Message> messagesWithPrompt;
if (prompt == "") {
messagesWithPrompt = state.messages;
} else {
messagesWithPrompt = state.messages +
[
Message(
text: prompt,
type: MessageType.text,
source: MessageSource.user)
];
}
emit(state.copyWith(
status: GeminiStatus.loading,
@ -233,17 +247,30 @@ class GeminiCubit extends Cubit<GeminiState> {
String responseText = '';
bool isAnalysisDone = false;
await for (final chunk in stream) {
responseText += chunk.text ?? '';
emit(state.copyWith(
status: GeminiStatus.success,
messages: messagesWithPrompt +
[
Message(
source: MessageSource.agent,
text: responseText,
type: MessageType.text)
]));
if (responseText.contains(LESSON_START_TOKEN)) {
isAnalysisDone = true;
var startIndex = responseText.indexOf(LESSON_START_TOKEN) +
LESSON_START_TOKEN.length;
var analysisData = responseText.substring(0, startIndex);
print("ANALYSIS DATA: $analysisData");
responseText =
responseText.substring(startIndex, responseText.length);
}
if (isAnalysisDone) {
emit(state.copyWith(
status: GeminiStatus.success,
messages: messagesWithPrompt +
[
Message(
source: MessageSource.agent,
text: responseText,
type: MessageType.text)
]));
}
}
if (responseText.contains("<QUIZ_START_TOKEN>")) {

View File

@ -1,2 +1,2 @@
const String geminiApiKey = '';
const String geminiApiKey = 'AIzaSyBU-vIDA4IUfRReXcu7Vdw53gnrnroJjzI';
const bool isDebug = true;

View File

@ -2,26 +2,24 @@ import 'dart:async';
import 'package:gemini_app/config.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_bloc/flutter_bloc.dart';
class EegState {
final double mind_wandering;
final double mindWandering;
final double focus;
EegState({
required this.mind_wandering,
required this.mindWandering,
required this.focus,
});
String getJsonString() {
return '{"mind_wandering": $mind_wandering, "focus": $focus}';
return '{"mind_wandering": $mindWandering, "focus": $focus}';
}
}
class EegService {
EegState state;
EegService() : state = EegState(mind_wandering: 0.9, focus: 0.1) {
EegService() : state = EegState(mindWandering: 0.9, focus: 0.1) {
// Start the timer when the cubit is created
if (!isDebug) {
startPolling();
@ -32,7 +30,7 @@ class EegService {
void startPolling() {
// Poll every 1 second (adjust the duration as needed)
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
// Simulate getting new EEG data
// In a real application, you would fetch this data from your EEG device or API
// double newMindWandering = (DateTime.now().millisecondsSinceEpoch % 100) / 100;
@ -84,16 +82,16 @@ class EegService {
}
void updateEegData(double mindWandering, double focus) {
state = EegState(mind_wandering: mindWandering, focus: focus);
state = EegState(mindWandering: mindWandering, focus: focus);
print('Mind Wandering: $mindWandering, Focus: $focus');
}
void toggleState() {
// Toggle the state between mind_wandering and focus
if (state.mind_wandering > state.focus) {
updateEegData(state.focus, state.mind_wandering);
if (state.mindWandering > state.focus) {
updateEegData(state.focus, state.mindWandering);
} else {
updateEegData(state.mind_wandering, state.focus);
updateEegData(state.mindWandering, state.focus);
}
}
}

View File

@ -58,14 +58,14 @@ class GeminiChatState extends State<GeminiChat> {
}
void _sendMessage() async {
context
.read<GeminiCubit>()
.sendMessage(_textController.text);
context.read<GeminiCubit>().sendMessage(_textController.text);
_textController.clear();
}
void _toggleEegState() {
_eegService.toggleState();
setState(() {
_eegService.toggleState();
});
}
void _resetConversation() {
@ -91,20 +91,23 @@ class GeminiChatState extends State<GeminiChat> {
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Mind Wandering: ${_eegService.state.mind_wandering.toStringAsFixed(2)}'),
Text('Focus: ${_eegService.state.focus.toStringAsFixed(2)}'),
],
),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Mind Wandering: ${_eegService.state.mindWandering.toStringAsFixed(2)}'),
Text(
'Focus: ${_eegService.state.focus.toStringAsFixed(2)}'),
],
),
),
),
const SizedBox(height: 16),
Expanded(
child: BlocBuilder<GeminiCubit, GeminiState>(
builder: (context, state) {
@ -118,54 +121,87 @@ class GeminiChatState extends State<GeminiChat> {
},
),
),
const SizedBox(height: 16),
BlocBuilder<GeminiCubit, GeminiState>(
builder: (context, state) {
return state.isQuizMode
? Container() // Hide text input in quiz mode
? Container()
: TextField(
controller: _textController,
decoration: const InputDecoration(
decoration: InputDecoration(
hintText: 'Enter your message',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
filled: true,
fillColor: Colors.grey[200],
contentPadding: const EdgeInsets.symmetric(
horizontal: 16.0, vertical: 12.0),
),
onSubmitted: (_) => _sendMessage(),
);
},
),
const SizedBox(height: 16),
Row(
children: [
BlocBuilder<GeminiCubit, GeminiState>(
builder: (context, state) {
return state.isQuizMode
? Container()
: Expanded(
child: ElevatedButton(
onPressed: _sendMessage,
child: const Text('Send'),
),
);
},
// BlocBuilder<GeminiCubit, GeminiState>(
// builder: (context, state) {
// return state.isQuizMode
// ? Container()
// : Expanded(
// child: ElevatedButton(
// onPressed: _sendMessage,
// child: const Text('Send'),
// ),
// );
// },
// ),
ElevatedButton(
onPressed: _sendMessage,
child: const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.send),
SizedBox(width: 3),
Text('Send'),
],
),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: _resetConversation,
child: const Text('Reset'),
child: const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.refresh),
SizedBox(width: 3),
Text('Reset'),
],
),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: _toggleEegState,
child: const Text('Toggle State'),
),
const SizedBox(width: 8),
BlocBuilder<GeminiCubit, GeminiState>(
builder: (context, state) {
return state.isQuizMode
? Container()
: ElevatedButton(
onPressed: _enterQuizMode,
child: const Text('Start Quiz'),
);
},
child: const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.toggle_on),
SizedBox(width: 3),
Text('Toggle State'),
],
),
),
// ElevatedButton(
// onPressed: _enterQuizMode,
// child: const Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Icon(Icons.quiz),
// SizedBox(width: 8),
// Text('Start Quiz'),
// ],
// ),
// ),
],
),
],
@ -302,12 +338,13 @@ class BouncingDotsState extends State<BouncingDots>
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(3, (index) {
return AnimatedBuilder(
animation: _controllers[index],
builder: (context, child) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 4.0),
padding: const EdgeInsets.all(2.5),
child: Transform.translate(
offset: Offset(0, _animations[index].value),

View File

@ -18,7 +18,7 @@ packages:
source: hosted
version: "2.11.0"
bloc:
dependency: transitive
dependency: "direct main"
description:
name: bloc
sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e"
@ -124,13 +124,13 @@ packages:
source: hosted
version: "0.4.3"
http:
dependency: transitive
dependency: "direct main"
description:
name: http
sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
url: "https://pub.dev"
source: hosted
version: "1.2.1"
version: "1.2.2"
http_parser:
dependency: transitive
description:

View File

@ -42,6 +42,8 @@ dependencies:
flutter_markdown: ^0.7.3
flutter_bloc: ^8.0.1
get_it: ^7.7.0
http: ^1.2.2
bloc: ^8.1.4
dev_dependencies:
flutter_test: