Skip to content

Commit 82f2689

Browse files
committed
Refactor code structure for improved readability and maintainability
1 parent 9768e3b commit 82f2689

File tree

10 files changed

+995
-39
lines changed

10 files changed

+995
-39
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"dependencies": {
33
"react-icons": "^5.5.0"
4-
}
4+
},
5+
"packageManager": "[email protected]+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72"
56
}

src/avatar/app/avatar.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
DEFAULT_CREDENTIAL = DefaultAzureCredential()
1919
GPT4_KEY = os.getenv("GPT4O_KEY", "")
2020
GPT4_URL = os.getenv("GPT4O_URL", "")
21-
MODEL_URL: str = os.environ.get("GPT4_URL", "")
22-
MODEL_KEY: str = os.environ.get("GPT4_KEY", "")
21+
MODEL_URL: str = os.environ.get("AZURE_MODEL_URL", "")
22+
MODEL_KEY: str = os.environ.get("AZURE_MODEL_KEY", "")
2323
COSMOS_ENDPOINT = os.getenv("COSMOS_ENDPOINT", "")
2424
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
2525

src/essays/app/demo/evaluate_essays.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class TransliterationChatManager(GroupChatManager):
3737
This custom manager coordinates the interaction between translation and review agents,
3838
determines when to terminate the chat, and selects the next agent to act based on the chat history.
3939
"""
40+
4041
async def filter_results(self, chat_history: ChatHistory) -> MessageResult:
4142
"""
4243
Extracts the latest transliteration and review results from the chat history and composes a combined response.
@@ -123,18 +124,15 @@ async def select_next_agent(self, chat_history: ChatHistory, participant_descrip
123124
StringResult: The name of the next agent to act.
124125
"""
125126
agents = list(participant_descriptions.keys())
126-
# Se não há mensagens de agentes ainda (apenas user), começa com transliteração
127127
if not chat_history.messages or (
128128
len(chat_history.messages) == 1 and chat_history.messages[0].role == AuthorRole.USER
129129
):
130130
return StringResult(result=agents[0], reason="Primeira rodada: transliteração.")
131-
# Determina se o último respondente foi o reviewer pelo conteúdo
132131
last_msg = chat_history.messages[-1]
133132
last_agent = getattr(last_msg, 'name', '')
134133
is_reviewer = False
135134
if "TranslationReviewerAgent" in last_agent:
136135
is_reviewer = True
137-
# Busca pelo score na última avaliação
138136
score = None
139137
aprovacao = "reexecutar"
140138
if is_reviewer:

src/essays/app/main.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ async def list_essays_endpoint() -> JSONResponse:
165165
message="Successfully retrieved question data.",
166166
content=items,
167167
)
168+
print(response_body)
168169
return JSONResponse(status_code=status.HTTP_200_OK, content=jsonable_encoder(response_body))
169170

170171

src/frontend/components/Avatar/Avatar.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -279,14 +279,12 @@ const AvatarChat: React.FC = () => {
279279
setError(null);
280280
setAvatarVideoStream(null); // Reset video stream
281281
try {
282-
// Store language, gender, and voice in a ref for global access
283-
284282
configRef.current.gender = selectedCase?.profile?.gender || "male";
285-
configRef.current.language = selectedCase?.profile?.language || "pt-BR";
283+
configRef.current.language = selectedCase?.profile?.language || "en-US";
286284
configRef.current.voice = selectedCase?.profile?.voice || (
287285
(selectedCase?.profile?.gender || "male") === "feminino"
288-
? "pt-BR-FranciscaNeural"
289-
: "pt-BR-AntonioNeural"
286+
? "en-US-AvaMultilingualNeural"
287+
: "en-US-AndrewMultilingualNeural"
290288
);
291289

292290
const { language, voice } = configRef.current;
@@ -428,7 +426,6 @@ const AvatarChat: React.FC = () => {
428426
</div>
429427
)}
430428

431-
{/* Chat History Panel */}
432429
{chatHistory.length > 0 && (
433430
<div className="mt-6 w-full max-w-4xl mx-auto bg-white dark:bg-boxdark rounded-xl shadow-md p-4 max-h-[30vh] overflow-y-auto">
434431
<h3 className="text-lg font-medium text-gray-700 dark:text-gray-300 mb-3 border-b pb-2">Conversation History</h3>

src/frontend/components/Essays/EssaySubmission.tsx

Lines changed: 125 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,20 @@
22
import React, { useState, useRef } from "react";
33
import { FaUpload, FaFileAlt, FaHistory, FaSpinner } from "react-icons/fa";
44
import { essaysEngine } from "@/utils/api";
5+
import { v4 as uuidv4 } from 'uuid';
6+
import ReactMarkdown from 'react-markdown';
7+
import remarkGfm from 'remark-gfm';
58

9+
// Backend /assemblies returns Swarm objects: { id, topic_name, agents }
10+
// We adapt to what the UI needs by adding friendly name/description fields.
611
interface EssayCase {
712
id: string;
8-
name: string;
9-
description: string;
13+
topic_name: string; // original backend field used as the display label
14+
agents: any[];
15+
name?: string; // derived (topic_name)
16+
description?: string; // currently not provided by backend
17+
content?: string; // reference text / prompt body
18+
explanation?: string; // instructions / rubric
1019
}
1120

1221
interface Evaluation {
@@ -36,33 +45,85 @@ const EssaySubmission: React.FC = () => {
3645
const [loadingCases, setLoadingCases] = useState(true);
3746
const [loadingHistory, setLoadingHistory] = useState(true);
3847
const fileInputRef = useRef<HTMLInputElement>(null);
48+
const [essayLookup, setEssayLookup] = useState<Record<string, any>>({});
3949

40-
// Load available essay cases
50+
// Load available essay cases (assemblies)
4151
React.useEffect(() => {
4252
setLoadingCases(true);
4353
essaysEngine.get("/assemblies")
4454
.then(res => {
45-
// Failsafe: ensure data is an array
46-
const content = Array.isArray(res.data?.content) ? res.data.content : [];
47-
setCases(content);
55+
const raw = Array.isArray(res.data?.content) ? res.data.content : [];
56+
const mapped: EssayCase[] = raw.map((r: any) => ({
57+
id: r.id,
58+
topic_name: r.topic_name,
59+
// Provide fallbacks so UI never breaks
60+
agents: r.agents || [],
61+
name: r.topic_name,
62+
description: r.description || ""
63+
}));
64+
if (mapped.length === 0) {
65+
// Fallback: try to derive cases from existing essays if assemblies not seeded yet
66+
return essaysEngine.get('/essays').then(er => {
67+
const essays = Array.isArray(er.data?.content) ? er.data.content : [];
68+
const essayCases: EssayCase[] = essays.map((e: any) => ({
69+
id: e.id,
70+
topic_name: e.topic || e.theme || e.id,
71+
agents: [],
72+
name: e.topic || e.theme || e.id,
73+
description: e.explanation || '',
74+
content: e.content,
75+
explanation: e.explanation
76+
}));
77+
setCases(essayCases);
78+
const lookup: Record<string, any> = {};
79+
essays.forEach((e: any) => { lookup[e.id] = e; });
80+
setEssayLookup(lookup);
81+
}).catch(fallbackErr => {
82+
console.error('Fallback /essays fetch failed:', fallbackErr);
83+
setCases([]);
84+
});
85+
}
86+
setCases(mapped);
87+
})
88+
.catch(err => {
89+
console.error('Failed loading /assemblies:', err);
90+
setCases([]);
4891
})
49-
.catch(() => setCases([]))
5092
.finally(() => setLoadingCases(false));
5193
}, []);
5294

53-
// Load submission history
95+
// Load submission history (using /resources now, since submissions are stored as resources)
5496
React.useEffect(() => {
5597
setLoadingHistory(true);
56-
essaysEngine.get("/essays")
98+
essaysEngine.get("/resources")
5799
.then(res => {
58-
// Failsafe: ensure data is an array
59100
const content = Array.isArray(res.data?.content) ? res.data.content : [];
60-
setHistory(content);
101+
const mapped: Submission[] = content.map((r: any) => ({
102+
id: r.id,
103+
essayText: r.content,
104+
description: Array.isArray(r.objective) ? r.objective.slice(1).join(' ') : '',
105+
submittedAt: r.submittedAt || '', // backend currently does not send this
106+
essayFileName: undefined,
107+
evaluations: []
108+
}));
109+
setHistory(mapped);
61110
})
62111
.catch(() => setHistory([]))
63112
.finally(() => setLoadingHistory(false));
64113
}, []);
65114

115+
// Independently load essays to enrich lookup for markdown reference/instructions
116+
React.useEffect(() => {
117+
essaysEngine.get('/essays')
118+
.then(res => {
119+
const items = Array.isArray(res.data?.content) ? res.data.content : [];
120+
const lookup: Record<string, any> = {};
121+
items.forEach((e: any) => { lookup[e.id] = e; });
122+
setEssayLookup(lookup);
123+
})
124+
.catch(err => console.warn('Could not load essays for reference text:', err));
125+
}, []);
126+
66127
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
67128
if (e.target.files && e.target.files[0]) {
68129
setEssayFile(e.target.files[0]);
@@ -74,16 +135,38 @@ const EssaySubmission: React.FC = () => {
74135
if (!selectedCase || (!essayText && !essayFile)) return;
75136
setSubmitting(true);
76137
setEvaluations([]);
77-
const formData = new FormData();
78-
formData.append("caseId", selectedCase.id);
79-
formData.append("description", description);
80-
if (essayText) formData.append("essayText", essayText);
81-
if (essayFile) formData.append("essayFile", essayFile);
82-
const res = await essaysEngine.post("/essays", formData);
83-
const data = res.data;
84-
setEvaluations(data.evaluations || []);
85-
setHistory(h => [data.content, ...h]);
86-
setSubmitting(false);
138+
139+
// NOTE: Backend /resources expects: id, objective (List[str]), content?, essay_id, url?.
140+
// We treat a submission as a Resource referencing the selectedCase (assembly) id.
141+
const resourcePayload = {
142+
id: uuidv4(),
143+
objective: description ? ["student_submission", description] : ["student_submission"],
144+
content: essayText || (essayFile ? `(uploaded file: ${essayFile.name})` : ''),
145+
essay_id: selectedCase.id,
146+
url: undefined
147+
};
148+
149+
try {
150+
const res = await essaysEngine.post("/resources", resourcePayload);
151+
// We don't receive evaluations from this endpoint; keep empty for now.
152+
const submission: Submission = {
153+
id: resourcePayload.id,
154+
essayText: resourcePayload.content,
155+
essayFileName: essayFile?.name,
156+
description: description,
157+
submittedAt: new Date().toISOString(),
158+
evaluations: []
159+
};
160+
setHistory(h => [submission, ...h]);
161+
// Clear form
162+
setEssayText('');
163+
setEssayFile(null);
164+
setDescription('');
165+
} catch (err) {
166+
console.error('Failed to submit resource', err);
167+
} finally {
168+
setSubmitting(false);
169+
}
87170
};
88171

89172
if (loadingCases) {
@@ -120,12 +203,31 @@ const EssaySubmission: React.FC = () => {
120203
>
121204
<option value="">-- 🚀 Choose your challenge --</option>
122205
{cases.map(ca => (
123-
<option key={ca.id} value={ca.id}>📝 {ca.name || "Untitled"}</option>
206+
<option key={ca.id} value={ca.id}>📝 {ca.name || ca.topic_name || "Untitled"}</option>
124207
))}
125208
</select>
126209
)}
127-
{selectedCase && <div className="mt-2 text-cyan-700 text-base italic">{selectedCase.description || "No description."}</div>}
128210
</div>
211+
{selectedCase && (
212+
<div className="mb-8 grid grid-cols-1 md:grid-cols-2 gap-6">
213+
<div className="rounded-2xl border-2 border-blue-200 dark:border-blue-800 bg-blue-50 dark:bg-blue-900/40 p-4 overflow-y-auto max-h-[360px] prose prose-sm md:prose-base dark:prose-invert">
214+
<h3 className="text-lg font-bold mb-2">📄 Reference Text</h3>
215+
<div className="break-words">
216+
<ReactMarkdown remarkPlugins={[remarkGfm]}>
217+
{ (essayLookup[selectedCase.id]?.content) || selectedCase.content || 'No reference text available.' }
218+
</ReactMarkdown>
219+
</div>
220+
</div>
221+
<div className="rounded-2xl border-2 border-green-200 dark:border-green-800 bg-green-50 dark:bg-green-900/40 p-4 overflow-y-auto max-h-[360px] prose prose-sm md:prose-base dark:prose-invert">
222+
<h3 className="text-lg font-bold mb-2">🧭 Instructions / Rubric</h3>
223+
<div className="break-words">
224+
<ReactMarkdown remarkPlugins={[remarkGfm]}>
225+
{ (essayLookup[selectedCase.id]?.explanation) || selectedCase.explanation || selectedCase.description || 'No instructions available.' }
226+
</ReactMarkdown>
227+
</div>
228+
</div>
229+
</div>
230+
)}
129231
{/* Essay submission form */}
130232
<form onSubmit={handleSubmit} className="bg-white dark:bg-boxdark rounded-2xl shadow-lg p-8 mb-8 flex flex-col gap-6 border-2 border-cyan-100 dark:border-cyan-800">
131233
<label className="font-semibold flex items-center gap-2 text-green-700">

src/frontend/components/Forms/Essays.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useState } from "react";
22
import type { Essay } from "@/types/essays";
33
import { essaysEngine } from "@/utils/api";
4-
import { FaPen, FaFileAlt, FaListAlt } from "react-icons/fa";
4+
import { FaPen, FaFileAlt, FaListAlt, FaBell } from "react-icons/fa";
55
import { v4 as uuidv4 } from 'uuid';
66

77
const EssayForm: React.FC<{ essayData?: Essay; onSuccess?: () => void }> = ({ essayData, onSuccess }) => {
@@ -50,14 +50,24 @@ const EssayForm: React.FC<{ essayData?: Essay; onSuccess?: () => void }> = ({ es
5050
className="w-full rounded-2xl border-2 border-cyan-200 focus:border-green-400 focus:ring-2 focus:ring-green-200 px-4 py-3 text-lg transition-all duration-200 bg-cyan-50 dark:bg-cyan-900 placeholder:text-cyan-400 focus:bg-white dark:focus:bg-boxdark"
5151
placeholder="Essay topic"
5252
/>
53+
<label className="flex items-center gap-2 text-cyan-700 font-bold">
54+
<FaBell /> Theme
55+
</label>
56+
<input
57+
type="text"
58+
value={form.theme}
59+
onChange={e => setForm({ ...form, theme: e.target.value })}
60+
className="w-full rounded-2xl border-2 border-cyan-200 focus:border-green-400 focus:ring-2 focus:ring-green-200 px-4 py-3 text-lg transition-all duration-200 bg-cyan-50 dark:bg-cyan-900 placeholder:text-cyan-400 focus:bg-white dark:focus:bg-boxdark"
61+
placeholder="Theme that should be considered by the LLM when writing the essay correction (optional)"
62+
/>
5363
<label className="flex items-center gap-2 text-blue-700 font-bold">
5464
<FaFileAlt /> Content
5565
</label>
5666
<textarea
5767
value={form.content}
5868
onChange={e => setForm({ ...form, content: e.target.value })}
5969
className="w-full rounded-2xl border-2 border-blue-200 focus:border-yellow-400 focus:ring-2 focus:ring-yellow-200 px-4 py-3 text-lg transition-all duration-200 bg-blue-50 dark:bg-blue-900 placeholder:text-blue-400 focus:bg-white dark:focus:bg-boxdark resize-y min-h-[48px] max-h-[320px]"
60-
placeholder="Essay content"
70+
placeholder="Content to be presented by the student to write the essay, and by the LLM to correct it"
6171
rows={3}
6272
/>
6373
<label className="flex items-center gap-2 text-green-700 font-bold">
@@ -67,7 +77,7 @@ const EssayForm: React.FC<{ essayData?: Essay; onSuccess?: () => void }> = ({ es
6777
value={form.explanation}
6878
onChange={e => setForm({ ...form, explanation: e.target.value })}
6979
className="w-full rounded-2xl border-2 border-green-200 focus:border-cyan-400 focus:ring-2 focus:ring-cyan-200 px-4 py-3 text-lg transition-all duration-200 bg-green-50 dark:bg-green-900 placeholder:text-green-400 focus:bg-white dark:focus:bg-boxdark resize-y min-h-[48px] max-h-[320px]"
70-
placeholder="Essay explanation (optional)"
80+
placeholder="Explanation of the essay correction criteria to be used by the student and the LLM (optional)"
7181
rows={2}
7282
/>
7383
{status && <p className="mt-2 text-center text-sm text-gray-700 dark:text-gray-300">{status}</p>}

src/frontend/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323
"react-apexcharts": "^1.4.1",
2424
"react-dom": "^18",
2525
"react-icons": "^5.5.0",
26-
"react-modal": "^3.16.1"
26+
"react-markdown": "^10.1.0",
27+
"react-modal": "^3.16.1",
28+
"remark-gfm": "^4.0.1"
2729
},
2830
"devDependencies": {
2931
"@types/node": "22.5.0",

src/frontend/types/essays.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export type Essay = {
22
id?: string;
33
topic: string;
4+
theme?: string;
45
content: string;
56
explanation?: string;
67
};

0 commit comments

Comments
 (0)