Skip to content

Commit

Permalink
workign through some of the desired workflow. need to continue work o…
Browse files Browse the repository at this point in the history
…n UX and also prompt engineering to get actual 'scoring' as well as REDCap integration still
  • Loading branch information
irvins committed Sep 30, 2024
1 parent 749cbbd commit f2bddfa
Show file tree
Hide file tree
Showing 7 changed files with 248 additions and 60 deletions.
101 changes: 75 additions & 26 deletions ClinicalCoach.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,39 +92,88 @@ public function redcap_module_ajax($action, $payload, $project_id, $record, $ins
case "callAI":
$messages = $payload;

// Retrieve the system context reflection from project settings
$reflection_context = $this->getProjectSetting("system_context_reflection_1");
// Retrieve the main system context reflection from project settings
$main_system_context = $this->getProjectSetting("system_context_summarize");

// Reflection contexts (could be null, so we check them before appending)
$reflection_contexts = [
$this->getProjectSetting("system_context_reflection_1"),
$this->getProjectSetting("system_context_reflection_2"),
$this->getProjectSetting("system_context_reflection_3"),
$this->getProjectSetting("system_context_reflection_4"),
$this->getProjectSetting("system_context_reflection_5"),
$this->getProjectSetting("system_context_reflection_6")
];

// Array to hold results from all API calls
$allResults = [];

// TODO NEED TO ENGIENEER PROMPT TO GIVE RELATIVE SCORE?
function assignScore($responseContent) {
// Example logic: scoring based on the length of the content
$length = strlen($responseContent);
if ($length < 100) {
return 1; // Bad response
} elseif ($length < 300) {
return 2; // Average response
} else {
return 3; // Good response
}
}

// Use the appendSystemContext function to handle system context
$messages = $this->appendSystemContext($messages, $reflection_context);
// Loop through each reflection context
foreach ($reflection_contexts as $index => $reflection_context) {
if (!empty($reflection_context)) {
// Append main system context and current reflection context
$currentMessages = $this->appendSystemContext($messages, $main_system_context);
$currentMessages = $this->appendSystemContext($currentMessages, $reflection_context);

$this->emDebug("chatml Messages array to API", $messages);
$this->emDebug("chatml Messages array to API for reflection context " . ($index + 1), $currentMessages);

// CALL API ENDPOINT WITH AUGMENTED CHATML
$model = "gpt-4o";
$params = array("messages" => $messages);
// Prepare parameters for the API call
$model = "gpt-4o";
$params = array("messages" => $currentMessages);

if ($this->getProjectSetting("gpt-temperature")) {
$params["temperature"] = floatval($this->getProjectSetting("gpt-temperature"));
}
if ($this->getProjectSetting("gpt-top-p")) {
$params["top_p"] = floatval($this->getProjectSetting("gpt-top-p"));
}
if ($this->getProjectSetting("gpt-frequency-penalty")) {
$params["frequency_penalty"] = floatval($this->getProjectSetting("gpt-frequency-penalty"));
}
if ($this->getProjectSetting("presence_penalty")) {
$params["presence_penalty"] = floatval($this->getProjectSetting("presence_penalty"));
}
if ($this->getProjectSetting("gpt-max-tokens")) {
$params["max_tokens"] = intval($this->getProjectSetting("gpt-max-tokens"));
if ($this->getProjectSetting("gpt-temperature")) {
$params["temperature"] = floatval($this->getProjectSetting("gpt-temperature"));
}
if ($this->getProjectSetting("gpt-top-p")) {
$params["top_p"] = floatval($this->getProjectSetting("gpt-top-p"));
}
if ($this->getProjectSetting("gpt-frequency-penalty")) {
$params["frequency_penalty"] = floatval($this->getProjectSetting("gpt-frequency-penalty"));
}
if ($this->getProjectSetting("presence_penalty")) {
$params["presence_penalty"] = floatval($this->getProjectSetting("presence_penalty"));
}
if ($this->getProjectSetting("gpt-max-tokens")) {
$params["max_tokens"] = intval($this->getProjectSetting("gpt-max-tokens"));
}

// Make the API call for the current context
$response = $this->getSecureChatInstance()->callAI($model, $params, PROJECT_ID);
$result = $this->formatResponse($response);

// Extract the response content for scoring
$responseContent = $result['response']['content'] ?? '';
$score = assignScore($responseContent);

// Add the result to the allResults array, including the score
$allResults[] = [
"reflection_context" => "Reflection " . ($index + 1),
"response" => $result,
"score" => $score
];

$this->emDebug("API result for reflection context " . ($index + 1), $result);
}
}

$response = $this->getSecureChatInstance()->callAI($model, $params, PROJECT_ID);
$result = $this->formatResponse($response);
// Return all results as a JSON array
$this->emDebug("All API results", $allResults);
return json_encode($allResults);


$this->emDebug("calling SecureChatAI.callAI()", $result);
return json_encode($result);

case "transcribeAudio":
$messages = $payload;
Expand Down
21 changes: 21 additions & 0 deletions MVP/src/assets/mvp.css
Original file line number Diff line number Diff line change
Expand Up @@ -272,3 +272,24 @@ button:hover {
font-style: italic;
}

.status-indicator {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0;
}

.status-bar {
flex:1;
min-width: 60px;
height: 10px;
margin-bottom: 5px;
margin-right: 1px;
}

.status-label {
width: 100%;
font-size: 65%;
color: black;
text-align: center;
}
63 changes: 46 additions & 17 deletions MVP/src/components/Home.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import StatusIndicator from './StatusIndicator';
import { useStudents } from '../contexts/Students';
import { FaUserCircle } from 'react-icons/fa';

Expand All @@ -16,31 +17,58 @@ function Home() {
{ role: "user", content: selectedStudent.transcription }
];

console.log("chatmlPayload", chatmlPayload);

// Call AI function and handle the callback
callAI(chatmlPayload, (aiContent) => {
console.log("aiContent callback", aiContent);
if (aiContent) {
updateAIResponse(aiContent); // Store AI response in context
if (aiContent && Array.isArray(aiContent)) {
const updatedStudent = {
...selectedStudent,
aiResponse: aiContent // Store the AI response in the student data
};

console.log("HOME handleSubmitToAI Updating student with AI response:", updatedStudent);

// Instead of using updateStudentData, use updateAIResponse
updateAIResponse(selectedStudent.id, aiContent);
} else {
console.error("Failed to get a response from the AI.");
console.error("HOME handleSubmitToAI No content received from AI or invalid format");
}
});
};


const callAI = (chatmlPayload, callback) => {
window.clicnical_coach_jsmo_module.callAI(chatmlPayload, (res) => {
if (res) {
if (callback) callback(res);
} else {
console.log("Unexpected AI response format:", res);
window.clicnical_coach_jsmo_module.callAI(
chatmlPayload,
(res) => {
if (!res) {
console.error("HOME callAI Response is null or undefined");
callback(undefined); // Pass undefined to indicate failure
return;
}

try {
const parsedRes = Array.isArray(res) ? res : typeof res === 'object' ? res : JSON.parse(res);

if (Array.isArray(parsedRes)) {
console.log("HOME callAI Parsed response before callback:", parsedRes);
callback(parsedRes);
} else {
console.error("HOME callAI Unexpected response format:", parsedRes);
callback(undefined); // Call with undefined for failure
}
} catch (error) {
console.error("HOME callAI Error parsing response:", error);
callback(undefined); // Call with undefined for failure
}
},
(err) => {
console.error("HOME callAI AI call failed:", err);
callback(undefined); // Ensure callback is called on error
}
}, (err) => {
console.log("callAI error:", err);
if (callback) callback();
});
);
};


return (
<div className="home-content">
<div className="session-date">
Expand All @@ -59,13 +87,14 @@ function Home() {
<h4>Session Completed</h4>
</div>

<StatusIndicator />

{selectedStudent.transcription && (
<div className="session-summary">
<blockquote>{selectedStudent.transcription}</blockquote>
<button onClick={handleSubmitToAI}>Submit to AI</button>
</div>
)}

<button onClick={handleSubmitToAI}>Submit to AI</button>
</div>
</div>
) : (
Expand Down
36 changes: 36 additions & 0 deletions MVP/src/components/StatusIndicator.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import { useStudents } from '../contexts/Students'; // Adjust the import path as necessary

const scoreColors = {
3: 'green',
2: '#FFD700',
1: 'red'
};

const StatusIndicator = () => {
const { selectedStudent } = useStudents();

if (!selectedStudent || !selectedStudent.reflections) {
return <div>No data available</div>;
}

const { reflections } = selectedStudent;

return (
<div className="status-indicator">
{Object.entries(reflections).map(([key, { score }]) => (
<div key={key} style={{ textAlign: 'center' }}>
<div
className="status-bar"
style={{ backgroundColor: scoreColors[score] }}
/>
<div className="status-label">
{key.charAt(0).toUpperCase() + key.slice(1)}
</div>
</div>
))}
</div>
);
};

export default StatusIndicator;
6 changes: 3 additions & 3 deletions MVP/src/components/VoiceRecorder.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,19 +97,19 @@ const VoiceRecorder = ({ setIsRecording, handleStopRecording }) => {
if (silenceStartRef.current === null) {
silenceStartRef.current = Date.now();
silenceDurationRef.current = 0;
console.log(`Silence started at: ${silenceStartRef.current}`);
// console.log(`Silence started at: ${silenceStartRef.current}`);
} else {
const silenceDurationElapsed = Date.now() - silenceStartRef.current;
silenceDurationRef.current += 100;
// console.log(`Silence duration elapsed: ${silenceDurationRef.current}ms`);
if (silenceDurationRef.current >= silenceDurationMs) {
console.log('Silence threshold reached, stopping recording');
// console.log('Silence threshold reached, stopping recording');
stopRecording(); // Stop the recording when silence is detected
}
}
} else {
if (silenceStartRef.current !== null) {
console.log('Silence ended, resetting silenceStartRef');
// console.log('Silence ended, resetting silenceStartRef');
silenceStartRef.current = null;
silenceDurationRef.current = 0;
}
Expand Down
58 changes: 49 additions & 9 deletions MVP/src/contexts/Students.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@ export const useStudents = () => useContext(StudentsContext);
// Provider component to wrap the app
export const StudentsProvider = ({ children }) => {
const [students, setStudents] = useState([
{ id: 1, name: "John Wick" },
{ id: 2, name: "Selina Kyle" },
{ id: 3, name: "Brock Purdy" }
{ id: 1, name: "John Wick", reflections: {} },
{ id: 2, name: "Selina Kyle", reflections: {} },
{ id: 3, name: "Brock Purdy", reflections: {} }
]);

const [selectedStudent, setSelectedStudent] = useState(null);
const [aiResponse, setAiResponse] = useState('');

// Function to select a student
const selectStudent = (studentId) => {
Expand All @@ -24,19 +23,61 @@ export const StudentsProvider = ({ children }) => {
};

const updateTranscription = (studentId, transcription) => {
console.log("updateTranscription", studentId, transcription);
setStudents((prevStudents) =>
prevStudents.map((student) =>
student.id === studentId ? { ...student, transcription } : student
)
);
};

const updateAIResponse = (response) => {
setAiResponse(response);
// Function to update AI reflections for the student
const updateAIResponse = (studentId, reflectionResults) => {
console.log("updateAIResponse - studentId:", studentId);
console.log("updateAIResponse - reflectionResults:", reflectionResults);

setStudents((prevStudents) => {
const updatedStudents = prevStudents.map((student) =>
student.id === studentId
? {
...student,
reflections: {
strategy: {
content: reflectionResults.find((r) => r.reflection_context === "Reflection 1")?.response.response.content || "No strategy content",
score: reflectionResults.find((r) => r.reflection_context === "Reflection 1")?.score || "No strategy score"
},
solution: {
content: reflectionResults.find((r) => r.reflection_context === "Reflection 2")?.response.response.content || "No solution content",
score: reflectionResults.find((r) => r.reflection_context === "Reflection 2")?.score || "No solution score"
},
knowledge: {
content: reflectionResults.find((r) => r.reflection_context === "Reflection 3")?.response.response.content || "No knowledge content",
score: reflectionResults.find((r) => r.reflection_context === "Reflection 3")?.score || "No knowledge score"
},
problem: {
content: reflectionResults.find((r) => r.reflection_context === "Reflection 4")?.response.response.content || "No problem content",
score: reflectionResults.find((r) => r.reflection_context === "Reflection 4")?.score || "No problem score"
},
data: {
content: reflectionResults.find((r) => r.reflection_context === "Reflection 5")?.response.response.content || "No data content",
score: reflectionResults.find((r) => r.reflection_context === "Reflection 5")?.score || "No data score"
},
mind: {
content: reflectionResults.find((r) => r.reflection_context === "Reflection 6")?.response.response.content || "No mind content",
score: reflectionResults.find((r) => r.reflection_context === "Reflection 6")?.score || "No mind score"
}
}
}
: student
);

console.log("updateAIResponse - updatedStudents:", updatedStudents);

return updatedStudents;
});
};

// Sync selectedStudent with updated transcription

// Sync selectedStudent with updated reflection data
useEffect(() => {
if (selectedStudent) {
const updatedStudent = students.find((s) => s.id === selectedStudent.id);
Expand All @@ -50,7 +91,6 @@ export const StudentsProvider = ({ children }) => {
selectedStudent,
selectStudent,
updateTranscription,
aiResponse,
updateAIResponse
}}>
{children}
Expand Down
Loading

0 comments on commit f2bddfa

Please # to comment.