New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[AI Tutor] CT-253: Teachers can view flagged chats #57669
Conversation
@@ -96,8 +96,9 @@ def main | |||
CHILD_ACCOUNT_COMPLIANCE_STATES | |||
CENSUS_CONSTANTS | |||
DANCE_SONG_MANIFEST_FILENAME | |||
AI_TUTOR_INTERACTION_SAVE_STATUS | |||
AI_TUTOR_INTERACTION_STATUS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See the rationale below for refactoring here. Is it dangerous to refactor shared constants?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems ok to me, especially since it's something pretty clearly owned/consumed by your team?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about Ben's comment at https://github.com/code-dot-org/code-dot-org/pull/57669/files#r1548141051 - are their plans to use STATUS
on the backend for aichat
@sanchitmalhotra126 ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could but it's a bit unclear since our API design is still being finalized. I'm okay with keeping this in the AI Tutor space for now and revisiting/refactoring when the API is more solidified.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I said "dangerous," what I was thinking of was, "Is there some script or something I need to reseed this constant file?" If tests are passing, I'll assume not. I'm happy to refactor this more down the line if it's useful for generative AI.
apps/src/aiTutor/types.ts
Outdated
export type AITutorTypesValue = | ||
(typeof AITutorTypes)[keyof typeof AITutorTypes]; | ||
export type AITutorInteractionStatusValue = | ||
(typeof AITutorInteractionStatus)[keyof typeof AITutorInteractionStatus]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was operating at the edge of my TypeScript knowledge, so I'd appreciate a second set of eyes. I'm also tripped up by naming conventions here and would appreciate any guidance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't seen this syntax before, but let's try to figure it out! AITutorInteractionStatus
is
AI_TUTOR_INTERACTION_STATUS = {
ERROR: 'error',
PII_VIOLATION: 'pii_violation',
PROFANITY_VIOLATION: 'profanity_violation',
OK: 'ok',
UNKNOWN: 'unknown',
}
keyof typeof AITutorInteractionStatus
gives you all the keys in an object (helpful article). In this case it is 'ERROR' | 'PII_VIOLATION' | 'PROFANITY_VIOLATION' | 'OK' | 'UNKNOWN'
typeof AITutorInteractionStatus
is (referencing that same article) {ERROR: string, PII_VIOLATION: string, ...}
.
If you put those together, we are getting the types for each key in typeof AITutorInteractionStatus
, which ends up being string | string | string ...
, which gets combined into just string
(I tried this out locally and that's what I ended up with, assuming my initial assumption on the object was correct). If you hover over AITutorInteractionStatusValue
do you see string
or something else? I'm guessing we actually wanted the values of the object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't find an easy way to turn the values into a type if that's what we want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The as const
assertion can be handy here - if you add that to the original object, TS treat the types as the literal values (instead of string)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example -
const Status = {
key1: 'value1',
key2: 'value2',
};
// type is string
type StatusValues = (typeof Status)[keyof typeof Status];
const StatusConst = {
key1: 'value1',
key2: 'value2',
} as const;
// type is 'value1' | 'value2'
type StatusConstValue = (typeof StatusConst)[keyof typeof StatusConst];
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you so much for the deep dive here, Molly and Sanchit!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrinkle: The imported constant comes from here:
code-dot-org/apps/script/generateSharedConstants.rb
Lines 40 to 41 in 15c5893
raw = source_module.const_get(shared_const_name) | |
begin |
I tried
import { AiTutorTypes } from '@cdo/apps/util/sharedConstants';
const AITutorTypes = AiTutorTypes as const;
And that doesn't work. One idea is to modify generateSharedConstants to re-export with const assertion, but that doesn't seem worth the trouble assuming it's possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like this doesn't seem to work either:
export type AITutorInteractionStatusValue =
| AITutorInteractionStatus.ERROR
| AITutorInteractionStatus.PII_VIOLATION
| AITutorInteractionStatus.PROFANITY_VIOLATION
| AITutorInteractionStatus.OK
| AITutorInteractionStatus.UNKNOWN;
I'm wondering if it's just easiest to have...
export type AITutorInteractionStatusValue =
| 'error'
| 'pii_violation'
| 'profanity_violation'
| 'ok'
| 'unknown';
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah I think just copying the constants over seems fine here
studentName: string; | ||
type: AITutorTypesValue; | ||
updatedAt?: string; | ||
userId: number; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These fields seemed missing but were on the chatMessage
on the front end.
|
||
export type Level = { | ||
export interface Level { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per recommendation from @molly-moen, prefer interface
to type
where possible.
import { | ||
AiTutorInteractionStatus as AITutorInteractionStatus, | ||
PiiTypes as PII, | ||
} from '@cdo/apps/util/sharedConstants'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this file is used much anymore, but it shares types with AI tutor code. Tagging @sanchitmalhotra126 @bencodeorg @fisher-alice for confirmation this is okay.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new "generative AI" lab is using being built on the aichat lab, so it's back in use!
@@ -26,6 +26,12 @@ import { | |||
} from '@cdo/apps/componentLibrary/common/contexts/DropdownContext'; | |||
import {dropdownColors} from '@cdo/apps/componentLibrary/common/constants'; | |||
|
|||
export interface CheckboxOption { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This wasn't exported for use, so I added it because I have a lot of functions creating filters in TypeScript that benefitted from it. @levadadenys Is it okay that I added it?
PROFANITY: 'profanity', | ||
INAPPROPRIATE: 'inappropriate', | ||
PII_VIOLATION: 'pii_violation', | ||
PROFANITY_VIOLATION: 'profanity_violation', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes in these two constants + rationale:
- Rename
AI_TUTOR_INTERACTION_SAVE_STATUS
toAI_TUTOR_INTERACTION_STATUS
because, in this case, "saved" and "ok" are the same, so the original name included the success case described within it. - Rename "personal" to "PII_VIOLATION" and "profanity" to "PROFANITY_VIOLATION" to match the AI TA code:
code-dot-org/lib/cdo/shared_constants.rb
Line 62 in d779649
} - Move "email", "address", and "phone" into their own constant. These are not interaction statuses, they're types of PII that might be flagged, so I think they should live somewhere else.
Doing this refactor also allowed me to build my initial status filter to map directly to the states of this "AI_TUTOR_INTERACTION_STATUS" constant without unused fields.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems reasonable to me! did you find any usages of the email/phone/address values in Ruby? It looks like they might have been moved there in an earlier refactor, might make sense for them just to live in a JS file:
https://github.com/code-dot-org/code-dot-org/pull/56361/files
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I expected to, but I didn't, Ben! Some of my tasks include sending AI Tutor chats through the PII/profanity filters and ensuring they're flagged appropriately. Do you mind leaving this here until I decide whether to use them on the backend? I filed a follow-up task to remove if I don't end up using them: https://codedotorg.atlassian.net/browse/CT-470
const generalChat = tutorType === TutorType.GENERAL_CHAT; | ||
const compilation = tutorType === TutorType.COMPILATION; | ||
const validation = tutorType === TutorType.VALIDATION; | ||
const generalChat = tutorType === TutorTypes.GENERAL_CHAT; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got a little refactor happy and renamed TutorType to TutorTypes to indicate that this references an object that stores all of the types/multiple fields 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just looked at the stuff in the aichat directory / shared code, but that all looks reasonable to me!
@@ -1,11 +1,19 @@ | |||
import {LevelProperties} from '@cdo/apps/lab2/types'; | |||
import {AiTutorInteractionSaveStatus} from '@cdo/apps/util/sharedConstants'; | |||
import { | |||
AiTutorInteractionStatus as AITutorInteractionStatus, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Slightly out of scope, but should this be called AiInteractionStatus
if it's used across aichat and aitutor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bencodeorg I think my ultimate goal is in the spirit of this comment, which is to condense/standardize things. I don't know how long the AI Chat use case will continue to be relevant.
Per @sanchitmalhotra126's comment above, the best way to achieve clarity/reusability might be to 1) wait until we have a better understanding of the API for the gen AI unit, 2) use it to make these values more reusable across gen AI and AI tutor (and then rename them then if applicable), and 3) ultimately look to deprecate any aichat code that doesn't move to AI Tutor or Gen AI code? As far as I know, the AI Chat allthethings level is no longer used...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you all creating a new directory or using the aichat directory?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're actively doing development of the gen AI unit using the AI Chat allthethings level! AI Chat is being repurposed as gen AI. The API will probably be replaced though, to Sanchit's point. Fine to leave as-is for now, just a thought :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry just saw comment re: directory, using the aichat directory!
import { | ||
AiTutorInteractionStatus as AITutorInteractionStatus, | ||
PiiTypes as PII, | ||
} from '@cdo/apps/util/sharedConstants'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new "generative AI" lab is using being built on the aichat lab, so it's back in use!
@@ -96,8 +96,9 @@ def main | |||
CHILD_ACCOUNT_COMPLIANCE_STATES | |||
CENSUS_CONSTANTS | |||
DANCE_SONG_MANIFEST_FILENAME | |||
AI_TUTOR_INTERACTION_SAVE_STATUS | |||
AI_TUTOR_INTERACTION_STATUS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems ok to me, especially since it's something pretty clearly owned/consumed by your team?
PROFANITY: 'profanity', | ||
INAPPROPRIATE: 'inappropriate', | ||
PII_VIOLATION: 'pii_violation', | ||
PROFANITY_VIOLATION: 'profanity_violation', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems reasonable to me! did you find any usages of the email/phone/address values in Ruby? It looks like they might have been moved there in an earlier refactor, might make sense for them just to live in a JS file:
https://github.com/code-dot-org/code-dot-org/pull/56361/files
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The aichat
changes look good to me as well!
apps/src/aichat/chatApi.ts
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW I expect that for Gen AI we may just create a new API file since we're no longer using this chat API. If it makes more sense to just move this to the aiTutor directory, feel free!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you're right that refactoring this code makes sense. It looks like the only way this file is used is to import {getChatCompletionMessage} from '../chatApi';
in two files: aichatRedux.ts
and aiTutorRedux.ts
. There will be a fairly significant diff with that refactor, so I'd like to address it as a follow-up task!
const messageDate = moment(message.createdAt); | ||
let timeMatch = false; | ||
switch (selectedTimeFilter) { | ||
case 'lastHour': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there a way for us to have these values as constants instead of hard-coding them in multiple places? Could we have an enum that we use to generate the formatted dropdown options?
allOptions={statusOptions} | ||
checkedOptions={selectedStatuses} | ||
color="black" | ||
labelText="Filter by Status" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it worth turning these into translated strings now or are things still too in flux for that? Should a follow-up task be followed for translation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call, @molly-moen. Given the quick timeline for the closed beta and uncertainty about whether we will be offering AI Tutor in languages other than English, I'd like permission to handle this as a follow-up task: https://codedotorg.atlassian.net/browse/CT-469
I've added it to the closed beta release because I think the easiest time to add the translated strings is when you write the code, but noted it as non-blocking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep a follow up seems reasonable!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉
(typeof AITutorInteractionStatus)[keyof typeof AITutorInteractionStatus]; | ||
// TODO: Update this once https://codedotorg.atlassian.net/browse/CT-471 is resolved | ||
export type AITutorTypesValue = string; | ||
export type AITutorInteractionStatusValue = string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Functionally, this was only checking for a string before. Narrowing the type-checking here was difficult, and throwing many errors because these are generated constants. I filed https://codedotorg.atlassian.net/browse/CT-471 to address it.
This PR:
Follow-ups:
Video of filters in action:
initial_dropdown_work.mov
Links
Jira ticket: https://codedotorg.atlassian.net/browse/CT-253
Testing story
I tested manually. My manual testing was not the most comprehensive, but there's a lot of follow-up work to flag and persist the PII/profane status in the database, so I expect to continue testing this in the coming days.
TODO:
PR Checklist: