-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c0618c7
commit 48db52c
Showing
24 changed files
with
123,556 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
class Agent{ | ||
constructor(apikey, iBlock, userContext, isTranscript){ | ||
this.apiKey = apikey; | ||
this.iBlock = iBlock; | ||
this.working_context = composePrompt(userContext, isTranscript) | ||
this.promptFromWorkingContext() | ||
} | ||
|
||
//////////// triggers the agent (input) //////////// | ||
promptFromWorkingContext(recursion = 0){ | ||
|
||
console.log('prompting model') | ||
|
||
if (recursion > 3){ | ||
this.pushMessage('AutoTA got messed up, probably from failing to parse correctly. Try restarting the conversation or finding a new topic.', false) | ||
return | ||
} | ||
|
||
console.log('---Working Context Slice---') | ||
console.log(this.working_context.slice(-200)) | ||
console.log('---------------------------') | ||
|
||
let modelResponsePromise = geminiQuery(this.working_context, this.apiKey) | ||
|
||
modelResponsePromise.then((modelResponse) => { | ||
let parsedComponents = this.parseResponse(modelResponse); | ||
if (typeof parsedComponents === 'string') { | ||
console.log('some issue on previous iteration') | ||
let new_context = [parsedComponents].join('\n') + '\n'; | ||
this.working_context += new_context | ||
this.promptFromWorkingContext(recursion+1) | ||
return | ||
} | ||
let errorObservation = this.handleParsedComponents(parsedComponents) | ||
if (!typeof errorObservation === "undefined"){ | ||
console.log('some issue on previous iteration') | ||
let new_context = [model_response, result].join('\n') + '\n'; | ||
this.working_context += new_context | ||
this.promptFromWorkingContext(recursion+1) | ||
return | ||
} | ||
}) | ||
|
||
} | ||
|
||
|
||
addObservation(userStr){ | ||
let new_context | ||
if (!userStr.trim().length) { | ||
new_context = `\nObservation: The student responded with an empty response.`; | ||
}else{ | ||
new_context = `\nObservation: The student responded with "${userStr}"`; | ||
} | ||
this.working_context += new_context | ||
} | ||
|
||
//////////// request feedback (output) //////////// | ||
pushMessage(message, isThought){ | ||
this.iBlock.addMessage(message, isThought) | ||
} | ||
|
||
requestPrompt(args){ | ||
try{ | ||
this.iBlock.addPrompt(args['prompt']) | ||
}catch{ | ||
return 'Observation: The previous action resulted in an unknown error. Please try again, beginning with a Thought.' | ||
} | ||
} | ||
|
||
requestChoice(args){ | ||
try{ | ||
this.iBlock.addChoice(args['prompt'], args['options']) | ||
}catch{ | ||
return 'Observation: The previous action resulted in an unknown error. Please try again, beginning with a Thought.' | ||
} | ||
} | ||
|
||
//////////// Parsing /////////////// | ||
parseResponse(inputText) { | ||
this.working_context += inputText | ||
// const pattern = /(Thought|Action|Action Input): (.*?)(?=\n(?:Thought|Action|Action Input): |$)/gs; | ||
const pattern = /(Thought|Action|Action Input): (.*?)(?=\n*(?:Thought|Action|Action Input): |$)/gs; | ||
const matches = [...inputText.matchAll(pattern)]; | ||
|
||
const parsedComponents = []; | ||
|
||
for (const match of matches) { | ||
const component_name = match[1]; | ||
const component_content = match[2].trim(); | ||
|
||
if (component_name === 'Action Input') { | ||
try { | ||
const parsed_component = JSON.parse(component_content); | ||
parsedComponents.push([component_name, parsed_component]); | ||
} catch (e) { | ||
console.log(`failed to parse: "${component_content}"`); | ||
return 'Observation: The previous action input was not able to be parsed correctly due to the following error:\n\n{e}\n\n. Be sure to use correct and parsable JSON formatting. Please try again. Restate the Action you wish to run, and the Action Input'; | ||
} | ||
} else { | ||
parsedComponents.push([component_name, component_content]); | ||
} | ||
} | ||
|
||
return parsedComponents; | ||
} | ||
|
||
//////////// Triggering Actions /////////////// | ||
handleParsedComponents(parsedComponents) { | ||
for (let i = 0; i < parsedComponents.length; i++) { | ||
const component = parsedComponents[i]; | ||
|
||
if (component[0] === 'Thought') { | ||
let message = `AutoTA is thinking...\n${component[1]}` | ||
console.log(message); | ||
this.pushMessage(message, true) | ||
} | ||
|
||
if (component[0] === 'Action') { | ||
// Handling errors | ||
if (i === parsedComponents.length - 1) { | ||
return 'Observation: The previous action was malformed. No inputs were provided. Please try again, beginning with a Thought.'; | ||
} else if (parsedComponents[i + 1][0] !== 'Action Input') { | ||
return 'Observation: The previous action was malformed. No inputs were provided. Please try again, beginning with a Thought.'; | ||
} | ||
|
||
if (component[1].includes('requestInput')){ | ||
return this.requestPrompt(parsedComponents[i + 1][1]) | ||
}else if (component[1].includes('multipleChoice')){ | ||
return this.requestChoice(parsedComponents[i + 1][1]) | ||
}else{ | ||
return `Observation: The specified tool "${component[1]}" does not exist. The tool was not executed.`; | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
// Constructing Tool Descriptions | ||
let TOOL_DESC = ''; | ||
let toolNames = '['; | ||
TOOLS.forEach((tool, i) => { | ||
const tooltip = `**Tool ${i}**\ntool name: ${tool.name}\ndescription: ${tool.description}\nexample: \`\`\`\n${tool.example}\n\`\`\`\nArguments: ${JSON.stringify(tool.arguments)}`; | ||
TOOL_DESC += tooltip + '\n'; | ||
toolNames += `${tool.name}, `; | ||
}); | ||
toolNames = toolNames.slice(0, -2) + ']'; | ||
|
||
// Constructing header | ||
const SYSTEM_HEADER = `# Instructions | ||
Your name is AutoTA. You are a teachers assistant designed to help students learn by providing summaries, answering questions, and performing quizzes. The student will provide context which will inform you what they would like to learn. At the beginning of the conversation you will ask the student what they need help with, then you will maintain a continual dialogue with the student in order to assist them. | ||
## Communication Style | ||
When conversing with a student: | ||
- Attempt to recover and resume the conversation when there is an error. If there is an error, let the student know there was an error and try to pick up where you left off. | ||
- If the user requests to be tested or quizzed, you can assume they want to be quizzed based on the student provided context to test their knowledge. | ||
- If the user requests something comprehensive, it's advised to plan through a comprehensive list of steps within Thoughts. | ||
- Assume the student wants to continue unless told otherwise. | ||
- Don't assume the lesson is over unless the student asks for it to be over. | ||
- If appropriate, try to use examples when providing students information. Especially with STEM topics. | ||
- Do not ask the student to wait. | ||
- When beginning a conversation, let the student know what you understand about the provided context | ||
- If the student asked to be quizzed, don't ask if they want to continue. Assume they do unless you're told otherwise. | ||
- Try to make quizzes as diverse as possible. Mix it up, don't just ask the same type of question. | ||
- When asking a multiple choice question, one of the options should be correct. | ||
- Thoughts should be long. | ||
- If the student asks about open ended or broad topics, Provide them a list of more specific sub-topics that they might like to learn about. | ||
- Only quiz the student if they want to be quizzed. | ||
- Try to answer questions immediately and directly when possible. Be sure to include interesting and relvent additional information. | ||
## Tools | ||
You have access to a wide variety of tools. You are responsible for using the tools in any sequence you deem appropriate to complete the task at hand. | ||
This may require breaking the task into subtasks and using different tools to complete each subtask. | ||
You have access to the following tools: | ||
--- | ||
${TOOL_DESC} | ||
--- | ||
## Output Format | ||
Please answer in the same language as the question and use the following format: | ||
\`\`\` | ||
Thought: The current language of the user is: (user's language). I need to use a tool to help me answer the question. | ||
Action: tool name (one of [${toolNames}]) if using a tool. | ||
Action Input: the input to the tool, in a JSON format representing the kwargs (e.g. {"input": "hello world", "num_beams": 5}) | ||
\`\`\` | ||
Please ALWAYS start with a Thought. | ||
Please NEVER create output that is not a Thought, Action, or Action Input. | ||
Please use a valid JSON format for the Action Input. Do NOT do this {{"input": "hello world", "num_beams": 5}}. | ||
Please always use escape quotations when outputting a value as a string. For example do this {"prompt": "the word is \\"Hello\\""}, NOT this {"prompt": "the word is "Hello""} | ||
If this format is used, the user will respond in the following format: | ||
\`\`\` | ||
Observation: tool response | ||
\`\`\` | ||
You should keep repeating the above format indefinitely.`; | ||
|
||
// Function for constructing prompt | ||
function composePrompt(userInput, isTranscript = true, systemHeader = SYSTEM_HEADER) { | ||
let tprompt; | ||
if (isTranscript) { | ||
tprompt = 'They provided a transcript of a lecture they attended, and want you to help them with the transcripts contents. This is the transcript:'; | ||
} else { | ||
tprompt = 'This was their response:'; | ||
} | ||
|
||
const prompt = `${systemHeader} | ||
## Student Provided Context | ||
The student was asked what they wanted to learn about. ${tprompt} | ||
--- | ||
${userInput} | ||
--- | ||
# Current Conversation | ||
Below is the current conversation consisting of interleaving human and assistant messages. | ||
Thought: I should introduce myself as AutoTA, and ask the student what they want to learn about based on the context they provided. I'll tell them the topic of the context, then I'll ask them an open ended question about what they would like to learn.`; | ||
return prompt; | ||
} | ||
|
||
// const systemPrompt = composePrompt('I want help learning about python', false); | ||
// console.log(systemPrompt); |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
class ChoiceElement extends PromptElement{ | ||
constructor(xmargin, prompt, choices, hook) { | ||
for(let i=0; i<choices.length; i++) { | ||
prompt += `\n\n${i+1}: ${choices[i]}` | ||
} | ||
prompt += '\n\nChoose One:\n\nOr provide an open ended answer:' | ||
super(xmargin, prompt, hook) | ||
this.choices = choices | ||
this.choiceButtons = [] | ||
} | ||
|
||
mount(){ | ||
for(let i=0; i<this.choices.length; i++) { | ||
this.choiceButtons[i] = createButton(`${i+1}`) | ||
this.choiceButtons[i].mouseClicked(() => this.handleChoice(i)) | ||
} | ||
|
||
super.mount() | ||
} | ||
|
||
unmount(){ | ||
this.choiceButtons.map( b => b.remove()); | ||
super.unmount() | ||
} | ||
|
||
draw(offset){ | ||
let v = super.draw(offset) | ||
|
||
if (this.is_completed){ | ||
offset += 40 | ||
} | ||
|
||
if(this.onScreen){ | ||
let xDelta = 40 | ||
for(let i=0; i<this.choiceButtons.length; i++) { | ||
this.choiceButtons[i].position(xDelta*(i+1)+this.xmargin+90, v+offset-170) | ||
} | ||
return v | ||
}else{ | ||
return v | ||
} | ||
} | ||
|
||
handleChoice(i){ | ||
if (!this.is_completed){ | ||
this.user_string = `The student chose option ${i+1}: "${this.choices[i]}"`; | ||
this.is_completed = true | ||
this.submit.remove() | ||
this.rectHeight = this.calculateRectHeight() | ||
this.hook(this.user_string) | ||
} | ||
} | ||
} |
Oops, something went wrong.