saving of tokens, changing of top language calculation, documentation

pull/1384/head
William Jordan-Cooley 1 year ago
parent 2526331706
commit c6d3f36805

@ -16,7 +16,6 @@ import 'package:fluffychat/pages/chat/recording_dialog.dart';
import 'package:fluffychat/pages/chat_details/chat_details.dart'; import 'package:fluffychat/pages/chat_details/chat_details.dart';
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart'; import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/enum/use_type.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/models/choreo_record.dart'; import 'package:fluffychat/pangea/models/choreo_record.dart';
@ -586,7 +585,6 @@ class ChatController extends State<ChatPageWithRoom>
PangeaMessageTokens? tokensSent, PangeaMessageTokens? tokensSent,
PangeaMessageTokens? tokensWritten, PangeaMessageTokens? tokensWritten,
ChoreoRecord? choreo, ChoreoRecord? choreo,
UseType? useType,
}) async { }) async {
// Pangea# // Pangea#
if (sendController.text.trim().isEmpty) return; if (sendController.text.trim().isEmpty) return;
@ -630,7 +628,6 @@ class ChatController extends State<ChatPageWithRoom>
tokensSent: tokensSent, tokensSent: tokensSent,
tokensWritten: tokensWritten, tokensWritten: tokensWritten,
choreo: choreo, choreo: choreo,
useType: useType,
) )
.then( .then(
(String? msgEventId) async { (String? msgEventId) async {
@ -644,7 +641,6 @@ class ChatController extends State<ChatPageWithRoom>
GoogleAnalytics.sendMessage( GoogleAnalytics.sendMessage(
room.id, room.id,
room.classCode, room.classCode,
useType ?? UseType.un,
); );
if (msgEventId == null) { if (msgEventId == null) {

@ -470,7 +470,7 @@ class Message extends StatelessWidget {
?.showUseType ?? ?.showUseType ??
false) ...[ false) ...[
pangeaMessageEvent! pangeaMessageEvent!
.useType .msgUseType
.iconView( .iconView(
context, context,
textColor textColor

@ -24,7 +24,6 @@ import 'package:flutter/material.dart';
import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:sentry_flutter/sentry_flutter.dart';
import '../../../widgets/matrix.dart'; import '../../../widgets/matrix.dart';
import '../../enum/use_type.dart';
import '../../models/choreo_record.dart'; import '../../models/choreo_record.dart';
import '../../models/language_model.dart'; import '../../models/language_model.dart';
import '../../models/pangea_match_model.dart'; import '../../models/pangea_match_model.dart';
@ -108,27 +107,16 @@ class Choreographer {
originalSent: false, originalSent: false,
) )
: null; : null;
//TODO - confirm that IT is indeed making sure the message is in the user's L1
// if the message has not been processed to determine its language
// then run it through the language detection endpoint. If the detection
// confidence is high enough, use that language code as the message's language
// to save that pangea representation
// TODO - move this to somewhere such that the message can be cleared from the input field
// before the language detection is complete. Otherwise, user is going to be waiting
// in cases of slow internet or slow language detection
final String? originalSentLangCode = igc.igcTextData?.detectedLanguage;
// TODO - why does both it and igc need to be enabled for choreo to be applicable? // TODO - why does both it and igc need to be enabled for choreo to be applicable?
final ChoreoRecord? applicableChoreo = // final ChoreoRecord? applicableChoreo =
isITandIGCEnabled && igc.igcTextData != null ? choreoRecord : null; // isITandIGCEnabled && igc.igcTextData != null ? choreoRecord : null;
final UseType useType = useTypeCalculator(applicableChoreo); // if tokens or language detection are not available, we should get them
// notes
// if tokens or language detection are not available, get them // 1) we probably need to move this to after we clear the input field
// note that we probably need to move this to after we clear the input field // or the user could experience some lag here.
// or the user could experience some lag here. note that this call is being // 2) that this call is being made after we've determined if we have an applicable choreo in order to
// made after we've determined if we have an applicable choreo in order to
// say whether correction was run on the message. we may eventually want // say whether correction was run on the message. we may eventually want
// to edit the useType after // to edit the useType after
if (igc.igcTextData?.tokens == null || if (igc.igcTextData?.tokens == null ||
@ -137,26 +125,24 @@ class Choreographer {
} }
final PangeaRepresentation originalSent = PangeaRepresentation( final PangeaRepresentation originalSent = PangeaRepresentation(
langCode: originalSentLangCode ?? LanguageKeys.unknownLanguage, langCode:
igc.igcTextData?.detectedLanguage ?? LanguageKeys.unknownLanguage,
text: currentText, text: currentText,
originalSent: true, originalSent: true,
originalWritten: originalWritten == null, originalWritten: originalWritten == null,
); );
debugger(when: kDebugMode);
final PangeaMessageTokens? tokensSent = igc.igcTextData?.tokens != null
? PangeaMessageTokens(tokens: igc.igcTextData!.tokens)
: null;
chatController.send( chatController.send(
// PTODO - turn this back on in conjunction with saving tokens
// we need to save those tokens as well, in order for exchanges to work
// properly. in an exchange, the other user will want
// originalWritten: originalWritten, // originalWritten: originalWritten,
originalSent: originalSent, originalSent: originalSent,
tokensSent: igc.igcTextData?.tokens != null tokensSent: tokensSent,
? PangeaMessageTokens(tokens: igc.igcTextData!.tokens)
: null,
//TODO - save originalwritten tokens //TODO - save originalwritten tokens
choreo: applicableChoreo, // choreo: applicableChoreo,
useType: useType, choreo: choreoRecord,
); );
clear(); clear();

@ -66,7 +66,6 @@ class ModelKey {
static const String tokensSent = "tokens_sent"; static const String tokensSent = "tokens_sent";
static const String tokensWritten = "tokens_written"; static const String tokensWritten = "tokens_written";
static const String choreoRecord = "choreo_record"; static const String choreoRecord = "choreo_record";
static const String useType = "use_type";
static const String baseDefinition = "base_definition"; static const String baseDefinition = "base_definition";
static const String targetDefinition = "target_definition"; static const String targetDefinition = "target_definition";

@ -76,15 +76,20 @@ class LanguageDetectionResponse {
}; };
} }
LanguageDetection get _bestDetection { /// Return the highest confidence detection.
/// If there are no detections, the unknown language detection is returned.
LanguageDetection get highestConfidenceDetection {
detections.sort((a, b) => b.confidence.compareTo(a.confidence)); detections.sort((a, b) => b.confidence.compareTo(a.confidence));
return detections.firstOrNull ?? unknownLanguageDetection; return detections.firstOrNull ?? unknownLanguageDetection;
} }
LanguageDetection bestDetection({double? threshold}) => /// Returns the highest validated detection based on the confidence threshold.
_bestDetection.confidence >= /// If the highest confidence detection is below the threshold, the unknown language
/// detection is returned.
LanguageDetection highestValidatedDetection({double? threshold}) =>
highestConfidenceDetection.confidence >=
(threshold ?? languageDetectionConfidenceThreshold) (threshold ?? languageDetectionConfidenceThreshold)
? _bestDetection ? highestConfidenceDetection
: unknownLanguageDetection; : unknownLanguageDetection;
} }

@ -216,8 +216,6 @@ class MyAnalyticsController {
.where((room) => !room.isSpace && !room.isAnalyticsRoom) .where((room) => !room.isSpace && !room.isAnalyticsRoom)
.toList(); .toList();
final DateTime now = DateTime.now();
// get the recent message events and activity records for each chat // get the recent message events and activity records for each chat
final List<Future<List<Event>>> recentMsgFutures = []; final List<Future<List<Event>>> recentMsgFutures = [];
final List<Future<List<Event>>> recentActivityFutures = []; final List<Future<List<Event>>> recentActivityFutures = [];
@ -309,10 +307,13 @@ class MyAnalyticsController {
recentConstructUses.addAll(constructLists.expand((e) => e)); recentConstructUses.addAll(constructLists.expand((e) => e));
//TODO - confirm that this is the correct construct content //TODO - confirm that this is the correct construct content
debugger( // debugger(
when: kDebugMode && // when: kDebugMode,
(recentPangeaMessageEvents.isNotEmpty || // );
recentActivityRecords.isNotEmpty)); // ; debugger(
// when: kDebugMode &&
// (allRecentMessages.isNotEmpty || recentActivityRecords.isNotEmpty),
// );
await analyticsRoom.sendConstructsEvent( await analyticsRoom.sendConstructsEvent(
recentConstructUses, recentConstructUses,

@ -1,8 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import '../models/choreo_record.dart';
import '../utils/bot_style.dart'; import '../utils/bot_style.dart';
enum UseType { wa, ta, ga, un } enum UseType { wa, ta, ga, un }
@ -93,17 +91,3 @@ extension UseTypeMethods on UseType {
} }
} }
} }
UseType useTypeCalculator(
ChoreoRecord? choreoRecord,
) {
if (choreoRecord == null) {
return UseType.un;
} else if (choreoRecord.includedIT) {
return UseType.ta;
} else if (choreoRecord.hasAcceptedMatches) {
return UseType.ga;
} else {
return UseType.wa;
}
}

@ -229,7 +229,6 @@ extension EventsRoomExtension on Room {
PangeaMessageTokens? tokensSent, PangeaMessageTokens? tokensSent,
PangeaMessageTokens? tokensWritten, PangeaMessageTokens? tokensWritten,
ChoreoRecord? choreo, ChoreoRecord? choreo,
UseType? useType,
}) { }) {
// if (parseCommands) { // if (parseCommands) {
// return client.parseAndRunCommand(this, message, // return client.parseAndRunCommand(this, message,
@ -247,7 +246,6 @@ extension EventsRoomExtension on Room {
ModelKey.originalWritten: originalWritten?.toJson(), ModelKey.originalWritten: originalWritten?.toJson(),
ModelKey.tokensSent: tokensSent?.toJson(), ModelKey.tokensSent: tokensSent?.toJson(),
ModelKey.tokensWritten: tokensWritten?.toJson(), ModelKey.tokensWritten: tokensWritten?.toJson(),
ModelKey.useType: useType?.string,
}; };
if (parseMarkdown) { if (parseMarkdown) {
final html = markdown( final html = markdown(
@ -347,7 +345,7 @@ extension EventsRoomExtension on Room {
RecentMessageRecord( RecentMessageRecord(
eventId: event.eventId, eventId: event.eventId,
chatId: id, chatId: id,
useType: pMsgEvent.useType, useType: pMsgEvent.msgUseType,
time: event.originServerTs, time: event.originServerTs,
), ),
); );

@ -34,7 +34,6 @@ import 'package:sentry_flutter/sentry_flutter.dart';
import '../../../config/app_config.dart'; import '../../../config/app_config.dart';
import '../../constants/pangea_event_types.dart'; import '../../constants/pangea_event_types.dart';
import '../../enum/use_type.dart';
import '../../models/choreo_record.dart'; import '../../models/choreo_record.dart';
import '../../models/representation_content_model.dart'; import '../../models/representation_content_model.dart';
import '../client_extension/client_extension.dart'; import '../client_extension/client_extension.dart';
@ -181,7 +180,6 @@ extension PangeaRoom on Room {
PangeaMessageTokens? tokensSent, PangeaMessageTokens? tokensSent,
PangeaMessageTokens? tokensWritten, PangeaMessageTokens? tokensWritten,
ChoreoRecord? choreo, ChoreoRecord? choreo,
UseType? useType,
}) => }) =>
_pangeaSendTextEvent( _pangeaSendTextEvent(
message, message,
@ -198,7 +196,6 @@ extension PangeaRoom on Room {
tokensSent: tokensSent, tokensSent: tokensSent,
tokensWritten: tokensWritten, tokensWritten: tokensWritten,
choreo: choreo, choreo: choreo,
useType: useType,
); );
Future<String> updateStateEvent(Event stateEvent) => Future<String> updateStateEvent(Event stateEvent) =>

@ -37,7 +37,6 @@ class PangeaMessageEvent {
late Event _event; late Event _event;
final Timeline timeline; final Timeline timeline;
final bool ownMessage; final bool ownMessage;
bool _isValidPangeaMessageEvent = true;
PangeaMessageEvent({ PangeaMessageEvent({
required Event event, required Event event,
@ -45,7 +44,7 @@ class PangeaMessageEvent {
required this.ownMessage, required this.ownMessage,
}) { }) {
if (event.type != EventTypes.Message) { if (event.type != EventTypes.Message) {
_isValidPangeaMessageEvent = false; debugger(when: kDebugMode);
ErrorHandler.logError( ErrorHandler.logError(
m: "${event.type} should not be used to make a PangeaMessageEvent", m: "${event.type} should not be used to make a PangeaMessageEvent",
); );
@ -548,7 +547,18 @@ class PangeaMessageEvent {
originalWritten: false, originalWritten: false,
); );
UseType get useType => useTypeCalculator(originalSent?.choreo); UseType get msgUseType {
final ChoreoRecord? choreoRecord = originalSent?.choreo;
if (choreoRecord == null) {
return UseType.un;
} else if (choreoRecord.includedIT) {
return UseType.ta;
} else if (choreoRecord.hasAcceptedMatches) {
return UseType.ga;
} else {
return UseType.wa;
}
}
bool get showUseType => bool get showUseType =>
!ownMessage && !ownMessage &&
@ -662,30 +672,7 @@ class PangeaMessageEvent {
/// all construct uses for the message, including vocab and grammar /// all construct uses for the message, including vocab and grammar
List<OneConstructUse> get allConstructUses => List<OneConstructUse> get allConstructUses =>
[..._grammarConstructUses, ..._vocabUses]; [..._grammarConstructUses, ..._vocabUses, ..._itStepsToConstructUses];
/// get construct uses of type vocab for the message
List<OneConstructUse> get _vocabUses {
final List<OneConstructUse> uses = [];
// missing vital info so return. should not happen
if (event.roomId == null) {
debugger(when: kDebugMode);
return uses;
}
// for each token, record whether selected in ga, ta, or wa
if (originalSent?.tokens != null) {
for (final token in originalSent!.tokens!) {
uses.addAll(_getVocabUseForToken(token));
}
}
// add construct uses related to IT use
uses.addAll(_itStepsToConstructUses);
return uses;
}
/// Returns a list of [OneConstructUse] from itSteps for which the continuance /// Returns a list of [OneConstructUse] from itSteps for which the continuance
/// was selected or ignored. Correct selections are considered in the tokens /// was selected or ignored. Correct selections are considered in the tokens
@ -696,6 +683,8 @@ class PangeaMessageEvent {
/// are actually in the final message. /// are actually in the final message.
List<OneConstructUse> get _itStepsToConstructUses { List<OneConstructUse> get _itStepsToConstructUses {
final List<OneConstructUse> uses = []; final List<OneConstructUse> uses = [];
if (originalSent?.choreo == null) return uses;
for (final itStep in originalSent!.choreo!.itSteps) { for (final itStep in originalSent!.choreo!.itSteps) {
for (final continuance in itStep.continuances) { for (final continuance in itStep.continuances) {
// this seems to always be false for continuances right now // this seems to always be false for continuances right now
@ -728,6 +717,24 @@ class PangeaMessageEvent {
return uses; return uses;
} }
/// get construct uses of type vocab for the message
List<OneConstructUse> get _vocabUses {
final List<OneConstructUse> uses = [];
// missing vital info so return
if (event.roomId == null || originalSent?.tokens == null) {
debugger(when: kDebugMode);
return uses;
}
// for each token, record whether selected in ga, ta, or wa
for (final token in originalSent!.tokens!) {
uses.addAll(_getVocabUseForToken(token));
}
return uses;
}
/// Returns a list of [OneConstructUse] objects for the given [token] /// Returns a list of [OneConstructUse] objects for the given [token]
/// If there is no [originalSent] or [originalSent.choreo], the [token] is /// If there is no [originalSent] or [originalSent.choreo], the [token] is
/// considered to be a [ConstructUseTypeEnum.wa] as long as it matches the target language. /// considered to be a [ConstructUseTypeEnum.wa] as long as it matches the target language.

@ -50,7 +50,7 @@ class SummaryAnalyticsModel extends AnalyticsModel {
(msg) => RecentMessageRecord( (msg) => RecentMessageRecord(
eventId: msg.eventId, eventId: msg.eventId,
chatId: msg.room.id, chatId: msg.room.id,
useType: msg.useType, useType: msg.msgUseType,
time: msg.originServerTs, time: msg.originServerTs,
), ),
) )

@ -51,7 +51,8 @@ class IGCTextData {
"full_text": json["original_input"], "full_text": json["original_input"],
}) })
: LanguageDetectionResponse.fromJson( : LanguageDetectionResponse.fromJson(
json[_detectionsKey] as Map<String, dynamic>); json[_detectionsKey] as Map<String, dynamic>,
);
return IGCTextData( return IGCTextData(
tokens: (json[_tokensKey] as Iterable) tokens: (json[_tokensKey] as Iterable)
@ -96,7 +97,17 @@ class IGCTextData {
"enable_igc": enableIGC, "enable_igc": enableIGC,
}; };
String get detectedLanguage => detections.bestDetection().langCode; /// if we haven't run IGC or IT or there are no matches, we use the highest validated detection
/// from [LanguageDetectionResponse.highestValidatedDetection]
/// if we have run igc/it and there are no matches, we can relax the threshold
/// and use the highest confidence detection
String get detectedLanguage {
if (!(enableIGC && enableIT) || matches.isNotEmpty) {
return detections.highestValidatedDetection().langCode;
} else {
return detections.highestConfidenceDetection.langCode;
}
}
// reconstruct fullText based on accepted match // reconstruct fullText based on accepted match
//update offsets in existing matches to reflect the change //update offsets in existing matches to reflect the change

@ -1,6 +1,13 @@
/// Represents a lemma object
class Lemma { class Lemma {
/// [text] ex "ir" - text of the lemma of the word
final String text; final String text;
/// [form] ex "vamos" - conjugated form of the lemma and as it appeared in some original text
final String form; final String form;
/// [saveVocab] true - whether to save the lemma to the user's vocabulary
/// vocab that are not saved: emails, urls, numbers, punctuation, etc.
final bool saveVocab; final bool saveVocab;
Lemma({required this.text, required this.saveVocab, required this.form}); Lemma({required this.text, required this.saveVocab, required this.form});

@ -4,7 +4,6 @@ import 'package:fluffychat/pangea/controllers/subscription_controller.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import '../../config/firebase_options.dart'; import '../../config/firebase_options.dart';
import '../enum/use_type.dart';
// PageRoute import // PageRoute import
@ -90,13 +89,12 @@ class GoogleAnalytics {
logEvent('join_group', parameters: {'group_id': classCode}); logEvent('join_group', parameters: {'group_id': classCode});
} }
static sendMessage(String chatRoomId, String classCode, UseType useType) { static sendMessage(String chatRoomId, String classCode) {
logEvent( logEvent(
'sent_message', 'sent_message',
parameters: { parameters: {
"chat_id": chatRoomId, "chat_id": chatRoomId,
'group_id': classCode, 'group_id': classCode,
"message_type": useType.toString(),
}, },
); );
} }

@ -166,7 +166,7 @@ class OverlayMessage extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
if (pangeaMessageEvent.showUseType) ...[ if (pangeaMessageEvent.showUseType) ...[
pangeaMessageEvent.useType.iconView( pangeaMessageEvent.msgUseType.iconView(
context, context,
textColor.withAlpha(164), textColor.withAlpha(164),
), ),

@ -1,9 +1,8 @@
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../../models/chat_topic_model.dart'; import '../../models/chat_topic_model.dart';
import '../../models/lemma.dart'; import '../../models/lemma.dart';
import '../../repo/topic_data_repo.dart'; import '../../repo/topic_data_repo.dart';
@ -76,7 +75,7 @@ class ChatVocabularyList extends StatelessWidget {
for (final word in topic.vocab) for (final word in topic.vocab)
Chip( Chip(
labelStyle: Theme.of(context).textTheme.bodyMedium, labelStyle: Theme.of(context).textTheme.bodyMedium,
label: Text(word.form), label: Text(word.text),
onDeleted: () { onDeleted: () {
onChanged(topic.vocab..remove(word)); onChanged(topic.vocab..remove(word));
}, },
@ -464,7 +463,7 @@ class PromptsFieldState extends State<PromptsField> {
// button to call API // button to call API
ElevatedButton.icon( ElevatedButton.icon(
icon: BotFace( icon: const BotFace(
width: 50.0, width: 50.0,
expression: BotExpression.idle, expression: BotExpression.idle,
), ),

Loading…
Cancel
Save