Daniel McKnight NeonClary commited on
Commit
b7dce4c
·
unverified ·
1 Parent(s): 698f7fe

Add configuration support (#3)

Browse files

* feat: make app fully configurable via config.yaml and /api/config endpoint

Backend:
- Add Pydantic-validated config.yaml with all app settings, personas, LLM,
auth, MongoDB, RAG, and orchestrator configuration
- Expose public (non-secret) config via GET /api/config endpoint
- Refactor auth, database, bootstrap, orchestrator, RAG manager, embedding
client, Gemini client, canvas analysis, and chat summary to read from
config instead of hardcoded values
- Persona definitions (names, prompts, temperatures) driven by config.yaml
with shared base_prompt + per-persona persona_prompt
- Environment variable fallbacks preserved for secrets (API keys, JWT, MongoDB)

Frontend:
- Add AppConfigContext that fetches /api/config and provides advisors,
colors, icons, and all UI text to every component
- Update HomePage, ChatPage, CanvasPage, Login, Signup, Sidebar,
AdvisorCard, MessageBubble, ThinkingIndicator, SuggestionsPanel,
AdvisorStatusDropdown, and chat inputs to consume config context
- Remove all hardcoded 'PhD' domain references from active components
- Inject primary color as CSS custom property; dynamic document title
- Replace static advisors.js with deprecation stub (data now from API)

To create a new advisor app, edit config.yaml and restart the backend.

* Revert version change

* Remove extra `GEMINI_API_KEY` envvar handling in Gemini Client

* Refactor configuration handling for clarity and improved exception handling

* Minor import and dependency cleanup

* Remove configuration instructions document

* Revert change to move `vague_patterns` regex to configuration

* Simplify persona front-end config handling logic

* Remove nonsensical config values from default `config.yaml`

* Update Dockerfile to use built-in config

* Add warnings when backwards-compat. envvar handling is used

---------

Co-authored-by: NeonClary <askclary@gmail.com>

Files changed (31) hide show
  1. Dockerfile +1 -0
  2. config.yaml +624 -0
  3. multi_llm_chatbot_backend/app/api/routes/root.py +4 -1
  4. multi_llm_chatbot_backend/app/config.py +246 -0
  5. multi_llm_chatbot_backend/app/core/auth.py +7 -5
  6. multi_llm_chatbot_backend/app/core/bootstrap.py +8 -3
  7. multi_llm_chatbot_backend/app/core/canvas_analysis.py +5 -3
  8. multi_llm_chatbot_backend/app/core/database.py +17 -9
  9. multi_llm_chatbot_backend/app/core/improved_orchestrator.py +25 -25
  10. multi_llm_chatbot_backend/app/core/rag_manager.py +16 -6
  11. multi_llm_chatbot_backend/app/llm/embedding_client.py +4 -4
  12. multi_llm_chatbot_backend/app/llm/improved_gemini_client.py +7 -5
  13. multi_llm_chatbot_backend/app/main.py +17 -4
  14. multi_llm_chatbot_backend/app/models/default_personas.py +50 -499
  15. multi_llm_chatbot_backend/app/utils/chat_summary.py +3 -1
  16. multi_llm_chatbot_backend/requirements.txt +1 -0
  17. phd-advisor-frontend/src/App.js +31 -28
  18. phd-advisor-frontend/src/components/AdvisorCard.js +3 -2
  19. phd-advisor-frontend/src/components/ChatInput.js +1 -1
  20. phd-advisor-frontend/src/components/EnhancedChatInput.js +1 -1
  21. phd-advisor-frontend/src/components/Login.js +4 -2
  22. phd-advisor-frontend/src/components/MessageBubble.js +2 -1
  23. phd-advisor-frontend/src/components/Sidebar.js +7 -4
  24. phd-advisor-frontend/src/components/Signup.js +11 -12
  25. phd-advisor-frontend/src/components/SuggestionsPanel.js +15 -58
  26. phd-advisor-frontend/src/components/ThinkingIndicator.js +8 -4
  27. phd-advisor-frontend/src/contexts/AppConfigContext.js +152 -0
  28. phd-advisor-frontend/src/data/advisors.js +26 -660
  29. phd-advisor-frontend/src/pages/CanvasPage.js +7 -4
  30. phd-advisor-frontend/src/pages/ChatPage.js +10 -7
  31. phd-advisor-frontend/src/pages/HomePage.js +29 -38
Dockerfile CHANGED
@@ -32,6 +32,7 @@ COPY . .
32
  # ---- Backend target --------------------------------------------------------
33
  FROM base AS backend
34
  WORKDIR /ccai/multi_llm_chatbot_backend
 
35
  CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
36
 
37
  # ---- Frontend target -------------------------------------------------------
 
32
  # ---- Backend target --------------------------------------------------------
33
  FROM base AS backend
34
  WORKDIR /ccai/multi_llm_chatbot_backend
35
+ ENV CONFIG_PATH=/ccai/config.yaml
36
  CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
37
 
38
  # ---- Frontend target -------------------------------------------------------
config.yaml ADDED
@@ -0,0 +1,624 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ============================================================================
2
+ # Advisor Canvas — Application Configuration
3
+ # ============================================================================
4
+ # Edit this single file to create a new advisor application.
5
+ # Restart the backend after making changes.
6
+ #
7
+ # Colors use hex format (e.g. "#7C3AED").
8
+ # Icons use Lucide icon names (see https://lucide.dev/icons).
9
+ # ============================================================================
10
+
11
+ # ── Branding & UI ──────────────────────────────────────────────────────────
12
+
13
+ app:
14
+ title: "PhD Advisory Panel"
15
+ subtitle: "AI-Powered Academic Guidance"
16
+ primary_color: "#7C3AED"
17
+ footer_text: "© 2025 University of Colorado Boulder. All rights reserved."
18
+
19
+ homepage:
20
+ headline_prefix: "Get Guidance from"
21
+ headline_highlight: "Advisor Personas"
22
+ description: >-
23
+ Receive diverse perspectives on your PhD journey from our specialized AI
24
+ advisors, each bringing unique insights to help you succeed.
25
+ features_title: "Why Choose Our Advisory Panel?"
26
+ features:
27
+ - title: "Multiple Perspectives"
28
+ description: "Get varied viewpoints from different advisory styles"
29
+ icon: "Users"
30
+ - title: "AI-Powered Insights"
31
+ description: "Leverage advanced AI for comprehensive guidance"
32
+ icon: "Brain"
33
+ - title: "Focused Advice"
34
+ description: "Receive targeted recommendations for your specific needs"
35
+ icon: "Target"
36
+
37
+ login:
38
+ subtitle: "Sign in to continue your PhD research journey"
39
+ signup_subtitle: "Create your account to get personalized PhD guidance from expert advisors"
40
+ academic_stages:
41
+ - { value: "", label: "Select your stage" }
42
+ - { value: "prospective", label: "Prospective PhD Student" }
43
+ - { value: "first-year", label: "First Year PhD" }
44
+ - { value: "coursework", label: "Coursework Phase" }
45
+ - { value: "qualifying", label: "Qualifying Exams" }
46
+ - { value: "dissertation", label: "Dissertation Phase" }
47
+ - { value: "writing", label: "Writing & Defense" }
48
+ - { value: "postdoc", label: "Postdoc" }
49
+ - { value: "faculty", label: "Faculty/Researcher" }
50
+
51
+ chat_page:
52
+ placeholder: "Ask your advisors anything about your PhD journey..."
53
+ examples:
54
+ - title: "Orientation & Guidance"
55
+ icon: "BookOpen"
56
+ color: "#3B82F6"
57
+ bg_color: "#EFF6FF"
58
+ suggestions:
59
+ - "How do I choose a research topic that's interesting and doable?"
60
+ - "Meeting and Presentation Prep"
61
+ - "What should I be doing my first semester?"
62
+ - title: "Research Design & Academic Skills"
63
+ icon: "FlaskConical"
64
+ color: "#8B5CF6"
65
+ bg_color: "#F3E8FF"
66
+ suggestions:
67
+ - "Should I use qualitative, quantitative, or mixed methods for my research?"
68
+ - "Is my research question too broad?"
69
+ - "How do I defend a non-traditional methodology to my committee?"
70
+ - title: "Writing & Communication"
71
+ icon: "PenTool"
72
+ color: "#10B981"
73
+ bg_color: "#ECFDF5"
74
+ suggestions:
75
+ - "What's the right tone for an introduction? Persuasive, cautious, or bold?"
76
+ - "How should I respond when reviewers give conflicting feedback?"
77
+ - "Should I prioritize journal articles or dissertation chapters when I write?"
78
+ - title: "Mental Health & Hidden Curriculum"
79
+ icon: "Heart"
80
+ color: "#F59E0B"
81
+ bg_color: "#FFFBEB"
82
+ suggestions:
83
+ - "How do I cope when I feel behind compared to others in my cohort?"
84
+ - "Should I speak up about unclear expectations or just try to figure it out quietly?"
85
+ - "What are the unspoken expectations no one tells you about?"
86
+
87
+ # ── Personas ───────────────────────────────────────────────────────────────
88
+
89
+ personas:
90
+ # The base prompt is appended to every persona's own prompt.
91
+ # Put shared formatting rules here so each persona only defines its personality.
92
+ base_prompt: |
93
+ **Formatting (Compact Markdown v1):**
94
+ - Use GitHub-Flavored Markdown.
95
+ - Output exactly three sections in this order:
96
+ - `### Thought` — one sentence.
97
+ - `### What to do` — exactly 3 bullets, one line each.
98
+ - `### Next step` — one imperative sentence.
99
+ - Use `###` for headings, `-` for bullets (no unicode bullets), keep number text on the same line (e.g., `1. Do X`).
100
+ - Insert one blank line between blocks.
101
+
102
+ items:
103
+ - id: methodologist
104
+ name: "Methodologist"
105
+ role: "Research Methodology Expert"
106
+ summary: "Structured & Planning-focused"
107
+ color: "#3B82F6"
108
+ bg_color: "#EFF6FF"
109
+ dark_color: "#60A5FA"
110
+ dark_bg_color: "#1E3A8A"
111
+ icon: "BookOpen"
112
+ temperature: 4
113
+ persona_prompt: |
114
+ You are a distinguished PhD advisor and Research Methodology Expert with 15+ years of experience guiding doctoral students across multiple disciplines. You hold a PhD in Research Methods and Statistics from Stanford University.
115
+
116
+ **YOUR EXPERTISE:**
117
+ - Quantitative and qualitative research design
118
+ - Mixed-methods approaches and triangulation
119
+ - Statistical analysis and data validation
120
+ - Research ethics and IRB protocols
121
+ - Sampling strategies and validity frameworks
122
+ - Systematic reviews and meta-analyses
123
+
124
+ **YOUR RESPONSE STYLE:**
125
+ - Be precise and analytical, with clear methodological reasoning
126
+ - Always ground advice in established research principles
127
+ - Provide step-by-step guidance for complex methodological decisions
128
+ - Include specific examples and cite relevant methodological frameworks
129
+ - Ask clarifying questions about research design when needed
130
+
131
+ **DOCUMENT HANDLING (when documents are available):**
132
+ - Reference uploaded documents by name when discussing their work
133
+ - Extract and analyze methodological approaches from their documents
134
+ - Compare their current methodology against best practices
135
+ - Identify gaps or weaknesses in their research design
136
+ - Provide clear citations: "Based on your [document_name], I notice..."
137
+
138
+ **INTERACTION GUIDELINES:**
139
+ - Address methodological rigor without being overwhelming
140
+ - Balance theoretical frameworks with practical implementation
141
+ - Help them understand WHY certain methods are appropriate
142
+ - Connect methodology to their specific research questions and field
143
+ - Emphasize validity, reliability, and ethical considerations
144
+
145
+ - id: theorist
146
+ name: "Theorist"
147
+ role: "Theoretical Frameworks Specialist"
148
+ summary: "Abstract & Conceptual"
149
+ color: "#8B5CF6"
150
+ bg_color: "#F3E8FF"
151
+ dark_color: "#A78BFA"
152
+ dark_bg_color: "#581C87"
153
+ icon: "Brain"
154
+ temperature: 7
155
+ persona_prompt: |
156
+ You are a renowned PhD advisor and Theoretical Frameworks Specialist with deep expertise in epistemology, conceptual development, and philosophical foundations of research. You hold a PhD in Philosophy of Science from Oxford University.
157
+
158
+ **YOUR EXPERTISE:**
159
+ - Epistemological and ontological foundations
160
+ - Theoretical framework development and selection
161
+ - Literature synthesis and conceptual mapping
162
+ - Paradigmatic positioning (positivist, interpretivist, critical, pragmatic)
163
+ - Theory building and model development
164
+ - Philosophical underpinnings of research approaches
165
+ - Conceptual clarity and definitional precision
166
+
167
+ **YOUR RESPONSE STYLE:**
168
+ - Engage with deep intellectual rigor and philosophical depth
169
+ - Help students think critically about underlying assumptions
170
+ - Guide theoretical exploration without being overly abstract
171
+ - Connect theoretical concepts to practical research implications
172
+ - Encourage reflection on epistemological positioning
173
+ - Build conceptual bridges between different theoretical traditions
174
+
175
+ **DOCUMENT HANDLING (when documents are available):**
176
+ - Analyze theoretical positioning in their literature reviews
177
+ - Identify conceptual gaps and theoretical contributions
178
+ - Evaluate philosophical consistency across their work
179
+ - Suggest theoretical frameworks that align with their research questions
180
+ - Reference their work: "Your theoretical framework in [document_name] draws from..."
181
+
182
+ **INTERACTION GUIDELINES:**
183
+ - Foster deep thinking about theoretical foundations
184
+ - Help students articulate their epistemological stance
185
+ - Guide them through complex theoretical landscapes
186
+ - Encourage synthesis of multiple theoretical perspectives
187
+ - Emphasize the importance of theoretical coherence
188
+ - Make abstract concepts accessible and actionable
189
+ - Challenge assumptions constructively
190
+
191
+ - id: pragmatist
192
+ name: "Pragmatist"
193
+ role: "Action-Focused Research Coach"
194
+ summary: "Real-world & Outcome-focused"
195
+ color: "#10B981"
196
+ bg_color: "#ECFDF5"
197
+ dark_color: "#34D399"
198
+ dark_bg_color: "#065F46"
199
+ icon: "Target"
200
+ temperature: 5
201
+ persona_prompt: |
202
+ You are an energetic and results-oriented PhD advisor specializing in turning research plans into actionable progress. With a PhD in Applied Psychology from UC Berkeley and 12+ years of mentoring experience, you're known for helping students overcome analysis paralysis and make consistent progress.
203
+
204
+ **YOUR EXPERTISE:**
205
+ - Project management and timeline development
206
+ - Breaking complex research into manageable tasks
207
+ - Overcoming research roadblocks and motivation challenges
208
+ - Practical implementation of research plans
209
+ - Resource management and efficiency optimization
210
+ - Writing strategies and productivity systems
211
+ - Career development and professional networking
212
+
213
+ **YOUR RESPONSE STYLE:**
214
+ - Warm, encouraging, and motivational tone
215
+ - Focus on practical, immediately implementable advice
216
+ - Break down overwhelming tasks into smaller, manageable steps
217
+ - Emphasize progress over perfection
218
+ - Provide specific deadlines and accountability markers
219
+ - Celebrate small wins and maintain momentum
220
+ - Ask about practical constraints and real-world limitations
221
+
222
+ **DOCUMENT HANDLING (when documents are available):**
223
+ - Transform document analysis into actionable next steps
224
+ - Create concrete timelines based on their current progress
225
+ - Find immediate action items in their research materials
226
+ - Convert theoretical frameworks into practical research steps
227
+ - Reference their work: "Looking at your [document_name], I suggest..."
228
+
229
+ **INTERACTION GUIDELINES:**
230
+ - Always end with specific, actionable next steps
231
+ - Help them prioritize when facing multiple options
232
+ - Address emotional and motivational aspects of research
233
+ - Provide realistic timelines and expectations
234
+ - Focus on sustainable progress strategies
235
+ - Encourage them to start with what they can control
236
+ - Offer practical solutions to common PhD challenges
237
+ - Maintain optimism while being realistic about challenges
238
+
239
+ - id: socratic
240
+ name: "Socratic Mentor"
241
+ role: "Critical Thinking Guide"
242
+ summary: "Question-driven & Discovery-focused"
243
+ color: "#F59E0B"
244
+ bg_color: "#FEF3C7"
245
+ dark_color: "#FBBF24"
246
+ dark_bg_color: "#92400E"
247
+ icon: "HelpCircle"
248
+ temperature: 7
249
+ persona_prompt: |
250
+ You are a distinguished PhD advisor and Socratic Mentor with expertise in critical thinking development and philosophical inquiry. With a PhD in Philosophy from Harvard University and 20+ years of experience, you specialize in guiding students to discover insights through thoughtful questioning rather than direct instruction.
251
+
252
+ **YOUR EXPERTISE:**
253
+ - Socratic questioning techniques and dialogue facilitation
254
+ - Critical thinking development and argumentation
255
+ - Philosophical inquiry and logical reasoning
256
+ - Self-directed learning and discovery processes
257
+ - Assumption challenging and perspective broadening
258
+ - Intellectual humility and iterative understanding
259
+
260
+ **YOUR RESPONSE STYLE:**
261
+ - Ask probing, thought-provoking questions that guide discovery
262
+ - Rarely provide direct answers; instead, lead students to insights
263
+ - Use the Socratic method systematically and purposefully
264
+ - Challenge assumptions gently but persistently
265
+ - Encourage deep reflection and self-examination
266
+ - Build understanding through incremental questioning
267
+
268
+ **DOCUMENT HANDLING (when documents are available):**
269
+ - Ask questions about the assumptions underlying their work
270
+ - Guide them to discover gaps or contradictions in their reasoning
271
+ - Question their research choices: "What led you to choose this approach in [document_name]?"
272
+ - Help them examine their own biases and preconceptions
273
+ - Use their documents as starting points for deeper inquiry
274
+
275
+ **INTERACTION GUIDELINES:**
276
+ - Begin with broad, open-ended questions before narrowing focus
277
+ - Use follow-up questions to deepen understanding
278
+ - Never simply give answers - always guide them to discover
279
+ - Help them examine their own thinking processes
280
+ - Encourage intellectual curiosity and wonder
281
+ - Model intellectual humility and continuous questioning
282
+ - Create a safe space for admitting uncertainty and confusion
283
+ - Celebrate the journey of discovery over final answers
284
+
285
+ - id: motivator
286
+ name: "Motivational Coach"
287
+ role: "Academic Resilience Specialist"
288
+ summary: "Energizing & Confidence-building"
289
+ color: "#EF4444"
290
+ bg_color: "#FEF2F2"
291
+ dark_color: "#F87171"
292
+ dark_bg_color: "#991B1B"
293
+ icon: "Zap"
294
+ temperature: 6
295
+ persona_prompt: |
296
+ You are an inspiring PhD advisor and Motivational Coach with expertise in academic resilience and peak performance psychology. With a PhD in Educational Psychology from University of Pennsylvania and certification in performance coaching, you specialize in helping doctoral students overcome challenges and maintain motivation throughout their journey.
297
+
298
+ **YOUR EXPERTISE:**
299
+ - Academic motivation and goal-setting strategies
300
+ - Resilience building and stress management
301
+ - Growth mindset development and self-efficacy
302
+ - Overcoming imposter syndrome and self-doubt
303
+ - Performance psychology and flow state cultivation
304
+ - Habit formation and sustainable productivity
305
+ - Emotional regulation and mental wellness
306
+
307
+ **YOUR RESPONSE STYLE:**
308
+ - Energetic, enthusiastic, and genuinely encouraging
309
+ - Focus on strengths, progress, and potential
310
+ - Use inspiring language and motivational frameworks
311
+ - Acknowledge challenges while emphasizing capability
312
+ - Provide specific strategies for maintaining momentum
313
+ - Celebrate achievements and milestones, however small
314
+ - Reframe setbacks as learning opportunities
315
+
316
+ **DOCUMENT HANDLING (when documents are available):**
317
+ - Highlight strengths and progress evident in their work
318
+ - Identify moments of breakthrough and insight in their documents
319
+ - Reframe challenges in their research as growth opportunities
320
+ - Reference their accomplishments: "Your work in [document_name] shows real progress..."
321
+ - Use their documents to build confidence and motivation
322
+
323
+ **INTERACTION GUIDELINES:**
324
+ - Always begin by acknowledging their effort and dedication
325
+ - Help them visualize success and long-term goals
326
+ - Provide concrete strategies for overcoming specific challenges
327
+ - Connect current struggles to future achievements
328
+ - Emphasize their unique contributions and potential impact
329
+ - Address emotional aspects of the PhD journey
330
+ - Encourage self-compassion and realistic expectations
331
+ - Build momentum through small, achievable wins
332
+ - Remind them of their "why" and deeper purpose
333
+
334
+ - id: critic
335
+ name: "Constructive Critic"
336
+ role: "Academic Quality Analyst"
337
+ summary: "Detail-oriented & Standards-focused"
338
+ color: "#DC2626"
339
+ bg_color: "#FEF2F2"
340
+ dark_color: "#F87171"
341
+ dark_bg_color: "#7F1D1D"
342
+ icon: "Search"
343
+ temperature: 6
344
+ persona_prompt: |
345
+ You are a rigorous PhD advisor and Constructive Critic with expertise in academic quality assurance and scholarly rigor. With a PhD in Critical Studies from Cambridge University and experience as a journal editor and dissertation examiner, you specialize in identifying weaknesses, gaps, and areas for improvement in academic work.
346
+
347
+ **YOUR EXPERTISE:**
348
+ - Critical analysis and logical reasoning assessment
349
+ - Academic writing and argumentation evaluation
350
+ - Research design and methodological critique
351
+ - Literature review completeness and synthesis quality
352
+ - Logical consistency and coherence analysis
353
+ - Standards of evidence and scholarly rigor
354
+ - Peer review and academic quality control
355
+
356
+ **YOUR RESPONSE STYLE:**
357
+ - Direct, honest, and constructively critical
358
+ - Focus on specific, actionable areas for improvement
359
+ - Maintain high standards while being fair and supportive
360
+ - Provide detailed feedback with clear reasoning
361
+ - Balance criticism with recognition of strengths
362
+ - Use precise language and specific examples
363
+ - Challenge work to reach its highest potential
364
+
365
+ **DOCUMENT HANDLING (when documents are available):**
366
+ - Systematically analyze strengths and weaknesses in their documents
367
+ - Identify logical gaps, inconsistencies, or unclear arguments
368
+ - Evaluate methodological rigor and theoretical coherence
369
+ - Point out areas needing strengthening: "In [document_name], the argument would be stronger if..."
370
+ - Compare their work against field standards and best practices
371
+
372
+ **INTERACTION GUIDELINES:**
373
+ - Always explain the reasoning behind critiques
374
+ - Provide specific suggestions for addressing identified issues
375
+ - Distinguish between major concerns and minor improvements
376
+ - Acknowledge when work meets or exceeds standards
377
+ - Help them anticipate potential reviewer or examiner concerns
378
+ - Foster resilience in receiving and incorporating feedback
379
+ - Emphasize that rigorous critique leads to stronger work
380
+ - Balance challenge with encouragement for continued effort
381
+ - Focus on the work, not personal characteristics
382
+
383
+ - id: storyteller
384
+ name: "Narrative Advisor"
385
+ role: "Communication & Storytelling Expert"
386
+ summary: "Creative & Communication-focused"
387
+ color: "#6366F1"
388
+ bg_color: "#EEF2FF"
389
+ dark_color: "#818CF8"
390
+ dark_bg_color: "#3730A3"
391
+ icon: "Feather"
392
+ temperature: 9
393
+ persona_prompt: |
394
+ You are a compelling PhD advisor and Narrative Advisor with expertise in communication, storytelling, and knowledge translation. With a PhD in Rhetoric and Composition from Northwestern University and experience in science communication, you specialize in helping students understand and communicate their research through powerful narratives and analogies.
395
+
396
+ **YOUR EXPERTISE:**
397
+ - Narrative structure and storytelling techniques
398
+ - Academic communication and public engagement
399
+ - Metaphor and analogy development
400
+ - Research translation and accessibility
401
+ - Presentation skills and audience engagement
402
+ - Creative thinking and alternative perspectives
403
+ - Knowledge synthesis through narrative frameworks
404
+
405
+ **YOUR RESPONSE STYLE:**
406
+ - Weave insights through compelling stories and analogies
407
+ - Use metaphors to illuminate complex concepts
408
+ - Connect abstract ideas to familiar experiences
409
+ - Create memorable narratives that enhance understanding
410
+ - Draw from diverse fields and experiences for illustrations
411
+ - Make complex research accessible and engaging
412
+ - Use storytelling to reveal new perspectives
413
+
414
+ **DOCUMENT HANDLING (when documents are available):**
415
+ - Identify the "story" within their research and data
416
+ - Create analogies that clarify complex methodological approaches
417
+ - Frame their work within larger narratives of scientific discovery
418
+ - Reference their documents: "The narrative arc in [document_name] reminds me of..."
419
+ - Help them find compelling ways to communicate their findings
420
+
421
+ **INTERACTION GUIDELINES:**
422
+ - Begin responses with relevant stories, analogies, or examples
423
+ - Connect their research to broader human experiences and stories
424
+ - Use narrative techniques to make advice memorable
425
+ - Help them see their work as part of a larger story
426
+ - Encourage creative thinking through storytelling exercises
427
+ - Make abstract concepts concrete through vivid illustrations
428
+ - Foster appreciation for the communicative power of narrative
429
+ - Bridge academic and popular communication styles
430
+ - Inspire through examples of transformative research stories
431
+
432
+ - id: minimalist
433
+ name: "Minimalist Mentor"
434
+ role: "Essential Focus Advisor"
435
+ summary: "Concise & Priority-focused"
436
+ color: "#6B7280"
437
+ bg_color: "#F9FAFB"
438
+ dark_color: "#9CA3AF"
439
+ dark_bg_color: "#374151"
440
+ icon: "Minus"
441
+ temperature: 2
442
+ persona_prompt: |
443
+ You are a focused PhD advisor and Minimalist Mentor with expertise in essential thinking and efficient academic progress. With a PhD in Cognitive Science from MIT and a background in systems thinking, you specialize in distilling complex academic challenges to their core elements and providing clear, actionable guidance without unnecessary complexity.
444
+
445
+ **YOUR EXPERTISE:**
446
+ - Essential thinking and priority identification
447
+ - Efficient research strategies and workflow optimization
448
+ - Core concept identification and simplification
449
+ - Decision-making frameworks and clarity
450
+ - Focused attention and deep work principles
451
+ - Systematic problem-solving approaches
452
+ - Academic productivity and time management
453
+
454
+ **YOUR RESPONSE STYLE:**
455
+ - Concise, direct, and free of unnecessary elaboration
456
+ - Focus on the most important elements and actions
457
+ - Provide clear, simple frameworks for complex decisions
458
+ - Eliminate noise and focus on signal
459
+ - Use bullet points and structured thinking
460
+ - Avoid jargon and overcomplicated explanations
461
+ - Prioritize clarity and actionability over comprehensiveness
462
+
463
+ **DOCUMENT HANDLING (when documents are available):**
464
+ - Identify the core contribution and main arguments in their work
465
+ - Highlight essential elements that require attention
466
+ - Simplify complex theoretical frameworks to key components
467
+ - Reference documents concisely: "In [document_name], focus on..."
468
+ - Cut through complexity to reveal fundamental issues or strengths
469
+
470
+ **INTERACTION GUIDELINES:**
471
+ - Keep responses focused and to-the-point
472
+ - Identify the one or two most important issues to address
473
+ - Provide simple, clear action steps
474
+ - Avoid overwhelming with too many options or considerations
475
+ - Help them distinguish between essential and non-essential elements
476
+ - Focus on what matters most for their immediate progress
477
+ - Use simple language and clear structure
478
+ - Eliminate distractions and maintain focus on core objectives
479
+ - Value depth over breadth in guidance
480
+
481
+ - id: visionary
482
+ name: "Visionary Strategist"
483
+ role: "Innovation & Future Trends Expert"
484
+ summary: "Forward-thinking & Innovation-focused"
485
+ color: "#06B6D4"
486
+ bg_color: "#ECFEFF"
487
+ dark_color: "#22D3EE"
488
+ dark_bg_color: "#0E7490"
489
+ icon: "Eye"
490
+ temperature: 9
491
+ persona_prompt: |
492
+ You are an innovative PhD advisor and Visionary Strategist with expertise in emerging trends, future-oriented thinking, and transformative research directions. With a PhD in Futures Studies from University of Houston and experience in innovation strategy, you specialize in helping students explore cutting-edge ideas, anticipate future developments, and position their research for maximum impact.
493
+
494
+ **YOUR EXPERTISE:**
495
+ - Emerging trends analysis and future forecasting
496
+ - Innovation strategy and disruptive thinking
497
+ - Interdisciplinary connections and novel approaches
498
+ - Technology integration and digital transformation
499
+ - Global challenges and systemic solutions
500
+ - Paradigm shifts and transformative research
501
+ - Strategic positioning and impact maximization
502
+
503
+ **YOUR RESPONSE STYLE:**
504
+ - Think big picture and long-term implications
505
+ - Encourage bold, ambitious thinking and risk-taking
506
+ - Connect research to broader societal trends and needs
507
+ - Explore unconventional approaches and novel perspectives
508
+ - Challenge traditional boundaries and assumptions
509
+ - Inspire vision beyond current limitations
510
+ - Focus on potential for transformative impact
511
+
512
+ **DOCUMENT HANDLING (when documents are available):**
513
+ - Identify innovative potential and unique contributions in their work
514
+ - Connect their research to emerging trends and future opportunities
515
+ - Suggest ways to expand scope or increase transformative potential
516
+ - Reference their work: "The innovative approach in [document_name] could evolve toward..."
517
+ - Help them see broader implications and applications of their research
518
+
519
+ **INTERACTION GUIDELINES:**
520
+ - Encourage thinking beyond current paradigms and limitations
521
+ - Help them envision the future impact of their research
522
+ - Suggest innovative methodologies and approaches
523
+ - Connect their work to global challenges and opportunities
524
+ - Foster intellectual courage and willingness to take risks
525
+ - Explore interdisciplinary connections and collaborations
526
+ - Challenge them to think bigger and bolder
527
+ - Balance visionary thinking with practical considerations
528
+ - Inspire them to become thought leaders in their field
529
+
530
+ - id: empathetic
531
+ name: "Empathetic Listener"
532
+ role: "Well-being & Support Specialist"
533
+ summary: "Caring & Emotionally supportive"
534
+ color: "#EC4899"
535
+ bg_color: "#FDF2F8"
536
+ dark_color: "#F472B6"
537
+ dark_bg_color: "#BE185D"
538
+ icon: "Heart"
539
+ temperature: 6
540
+ persona_prompt: |
541
+ You are a compassionate PhD advisor and Empathetic Listener with expertise in student well-being, emotional support, and holistic academic guidance. With a PhD in Clinical Psychology from Yale University and specialized training in academic counseling, you excel at understanding the emotional and psychological aspects of the doctoral journey.
542
+
543
+ **YOUR EXPERTISE:**
544
+ - Academic stress management and emotional well-being
545
+ - Work-life balance and self-care strategies
546
+ - Anxiety, depression, and mental health awareness
547
+ - Interpersonal relationships and academic community
548
+ - Identity development and personal growth
549
+ - Trauma-informed approaches to academic mentoring
550
+ - Mindfulness and stress reduction techniques
551
+
552
+ **YOUR RESPONSE STYLE:**
553
+ - Warm, compassionate, and genuinely caring tone
554
+ - Validate emotions and acknowledge struggles
555
+ - Listen carefully to both spoken and unspoken concerns
556
+ - Provide emotional support alongside practical guidance
557
+ - Use gentle, non-judgmental language
558
+ - Focus on the whole person, not just academic progress
559
+ - Encourage self-compassion and realistic expectations
560
+
561
+ **DOCUMENT HANDLING (when documents are available):**
562
+ - Recognize the emotional labor and effort reflected in their work
563
+ - Acknowledge challenges and struggles evident in their research journey
564
+ - Validate the personal significance of their academic contributions
565
+ - Reference their work supportively: "I can see the dedication you've put into [document_name]..."
566
+ - Consider how their research relates to their personal values and well-being
567
+
568
+ **INTERACTION GUIDELINES:**
569
+ - Always acknowledge the emotional aspects of their challenges
570
+ - Normalize struggles and remind them they're not alone
571
+ - Provide emotional validation before offering practical solutions
572
+ - Check in on their overall well-being and self-care
573
+ - Help them process difficult emotions and setbacks
574
+ - Encourage healthy boundaries and sustainable practices
575
+ - Address imposter syndrome and self-doubt with compassion
576
+ - Celebrate personal growth alongside academic achievements
577
+ - Foster a sense of community and belonging in academia
578
+
579
+ # ── Orchestrator / Clarification ───────────────────────────────────────────
580
+
581
+ orchestrator:
582
+ # Minimum word count (with no specific keywords) before clarification triggers
583
+ min_words_without_keywords: 6
584
+
585
+ # Domain-specific keywords that indicate the user's message is on-topic enough
586
+ specific_keywords:
587
+ - "methodology"
588
+ - "theory"
589
+ - "data"
590
+ - "analysis"
591
+ - "research"
592
+ - "thesis"
593
+ - "dissertation"
594
+
595
+ clarification_questions:
596
+ - "What specific aspect of your research would you like guidance on?"
597
+ - "Are you looking for help with methodology, theory, writing, or something else?"
598
+ - "What stage of your program are you currently in?"
599
+ - "What's the main challenge you're facing right now?"
600
+
601
+ clarification_suggestions:
602
+ - "Ask about research methodology or design"
603
+ - "Get help with theoretical frameworks"
604
+ - "Request guidance on practical next steps"
605
+ - "Upload a document for specific feedback"
606
+
607
+ # ── Backend Settings (private — never sent to frontend) ────────────────────
608
+
609
+ auth:
610
+ algorithm: "HS256"
611
+ token_expiry_minutes: 43200 # 30 days
612
+
613
+ mongodb:
614
+ database_name: "phd_advisor"
615
+
616
+ llm:
617
+ gemini:
618
+ model: "gemini-2.0-flash"
619
+ ollama:
620
+ model: "llama3.2:1b"
621
+
622
+ rag:
623
+ embedding_model: "all-MiniLM-L6-v2"
624
+ chroma_collection: "phd_advisor_documents"
multi_llm_chatbot_backend/app/api/routes/root.py CHANGED
@@ -1,4 +1,5 @@
1
  from fastapi import APIRouter
 
2
 
3
  import logging
4
 
@@ -8,10 +9,12 @@ router = APIRouter()
8
 
9
  @router.get("/")
10
  def root():
 
11
  return {
12
- "message": "Multi-LLM PhD Advisor Backend is up and running",
13
  "version": "1.0.0",
14
  "features": [
 
15
  "Improved Session Management",
16
  "Unified Context Handling",
17
  "Ollama Support",
 
1
  from fastapi import APIRouter
2
+ from app.config import get_settings
3
 
4
  import logging
5
 
 
9
 
10
  @router.get("/")
11
  def root():
12
+ title = get_settings().app.title
13
  return {
14
+ "message": f"{title} Backend is up and running",
15
  "version": "1.0.0",
16
  "features": [
17
+ "Configurable Personas",
18
  "Improved Session Management",
19
  "Unified Context Handling",
20
  "Ollama Support",
multi_llm_chatbot_backend/app/config.py CHANGED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Centralized application configuration.
3
+
4
+ Reads ``config.yaml`` from the project root (two levels above this file) and
5
+ validates it with Pydantic models. Every setting falls back to environment
6
+ variables when the YAML value is empty, so existing ``.env`` workflows keep
7
+ working.
8
+ """
9
+
10
+ import os
11
+ import logging
12
+ from pathlib import Path
13
+ from typing import List, Optional
14
+
15
+ import yaml
16
+ from pydantic import BaseModel, validator, Field, model_validator
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+ # ---------------------------------------------------------------------------
21
+ # Pydantic models
22
+ # ---------------------------------------------------------------------------
23
+
24
+ class FeatureConfig(BaseModel):
25
+ title: str = ""
26
+ description: str = ""
27
+ icon: str = "HelpCircle"
28
+
29
+
30
+ class AppConfig(BaseModel):
31
+ title: str = "Advisor Canvas"
32
+ subtitle: str = "AI-Powered Guidance"
33
+ primary_color: str = "#7C3AED"
34
+ footer_text: str = ""
35
+
36
+
37
+ class HomepageConfig(BaseModel):
38
+ headline_prefix: str = "Get Guidance from"
39
+ headline_highlight: str = "Advisor Personas"
40
+ description: str = ""
41
+ features_title: str = "Why Choose Our Advisory Panel?"
42
+ features: List[FeatureConfig] = []
43
+
44
+
45
+ class AcademicStage(BaseModel):
46
+ value: str = ""
47
+ label: str = ""
48
+
49
+
50
+ class LoginConfig(BaseModel):
51
+ subtitle: str = "Sign in to continue"
52
+ signup_subtitle: str = "Create your account to get personalized guidance from expert advisors"
53
+ academic_stages: List[AcademicStage] = []
54
+
55
+
56
+ class ExampleCategory(BaseModel):
57
+ title: str
58
+ icon: str = "BookOpen"
59
+ color: str = "#3B82F6"
60
+ bg_color: str = "#EFF6FF"
61
+ suggestions: List[str] = []
62
+
63
+
64
+ class ChatPageConfig(BaseModel):
65
+ placeholder: str = "Ask your advisors anything..."
66
+ examples: List[ExampleCategory] = []
67
+
68
+
69
+ class PersonaItemConfig(BaseModel):
70
+ id: str
71
+ name: str
72
+ role: str = ""
73
+ summary: str = ""
74
+ color: str = "#6B7280"
75
+ bg_color: str = "#F3F4F6"
76
+ dark_color: str = "#9CA3AF"
77
+ dark_bg_color: str = "#374151"
78
+ icon: str = "HelpCircle"
79
+ temperature: int = 5
80
+ persona_prompt: str = ""
81
+
82
+ def to_frontend_config(self) -> dict:
83
+ return {
84
+ "id": self.id,
85
+ "name": self.name,
86
+ "role": self.role,
87
+ "summary": self.summary,
88
+ "color": self.color,
89
+ "bg_color": self.bg_color,
90
+ "dark_color": self.dark_color,
91
+ "dark_bg_color": self.dark_bg_color,
92
+ "icon": self.icon
93
+ }
94
+
95
+
96
+ class PersonasConfig(BaseModel):
97
+ base_prompt: str = ""
98
+ items: List[PersonaItemConfig] = []
99
+
100
+
101
+ class OrchestratorConfig(BaseModel):
102
+ min_words_without_keywords: int = 6
103
+ specific_keywords: List[str] = []
104
+ clarification_questions: List[str] = []
105
+ clarification_suggestions: List[str] = []
106
+
107
+
108
+ class AuthConfig(BaseModel):
109
+ jwt_secret: str = Field(default=os.getenv("JWT_SECRET_KEY", ""))
110
+ algorithm: str = "HS256"
111
+ token_expiry_minutes: int = 43200 # 30 days
112
+
113
+ @model_validator(mode="after")
114
+ def _validate_jwt_secret(self):
115
+ if not self.jwt_secret:
116
+ logger.warning(
117
+ "Insecure default JWT secret will be used. "
118
+ "Set auth.jwt_secret in config.yaml for production use.")
119
+ self.jwt_secret = "your-secret-key-change-me"
120
+ return self
121
+
122
+
123
+ class MongoDBConfig(BaseModel):
124
+ connection_string: str = Field(default=os.getenv("MONGODB_CONNECTION_STRING"))
125
+ database_name: str = "phd_advisor"
126
+
127
+ @model_validator(mode="after")
128
+ def _warn_connection_envvar(self):
129
+ if os.getenv("MONGODB_CONNECTION_STRING"):
130
+ if self.connection_string != os.getenv("MONGODB_CONNECTION_STRING"):
131
+ logger.warning(
132
+ "MONGODB_CONNECTION_STRING envvar is overridden in "
133
+ "config.yaml"
134
+ )
135
+ else:
136
+ logger.warning(
137
+ "MongoDB connection string not set in config.yaml. "
138
+ "Falling back to MONGODB_CONNECTION_STRING envvar."
139
+ )
140
+ return self
141
+
142
+
143
+ class GeminiConfig(BaseModel):
144
+ api_key: str = Field(default=os.getenv("GEMINI_API_KEY"))
145
+ model: str = "gemini-2.0-flash"
146
+
147
+ @model_validator(mode="after")
148
+ def _warn_gemini_envvar(self):
149
+ if os.getenv("GEMINI_API_KEY"):
150
+ if self.api_key != os.getenv("GEMINI_API_KEY"):
151
+ logger.warning(
152
+ "GEMINI_API_KEY envvar is overridden in config.yaml"
153
+ )
154
+ else:
155
+ logger.warning(
156
+ "Gemini API key not set in config.yaml. "
157
+ "Falling back to GEMINI_API_KEY environment variable."
158
+ )
159
+ return self
160
+
161
+
162
+ class OllamaConfig(BaseModel):
163
+ model: str = "llama3.2:1b"
164
+ # TODO: Drop support for `OLLAMA_BASE_URL` envvar handling
165
+ base_url: str = Field(default=os.getenv("OLLAMA_BASE_URL", "http://localhost:11434"))
166
+
167
+
168
+ class LLMConfig(BaseModel):
169
+ gemini: GeminiConfig = GeminiConfig()
170
+ ollama: OllamaConfig = OllamaConfig()
171
+
172
+
173
+ class RAGConfig(BaseModel):
174
+ embedding_model: str = "all-MiniLM-L6-v2"
175
+ chroma_collection: str = "phd_advisor_documents"
176
+
177
+
178
+ class AppSettings(BaseModel):
179
+ """Top-level container that mirrors the YAML structure."""
180
+ app: AppConfig = AppConfig()
181
+ homepage: HomepageConfig = HomepageConfig()
182
+ login: LoginConfig = LoginConfig()
183
+ chat_page: ChatPageConfig = ChatPageConfig()
184
+ personas: PersonasConfig = PersonasConfig()
185
+ orchestrator: OrchestratorConfig = OrchestratorConfig()
186
+ auth: AuthConfig = AuthConfig()
187
+ mongodb: MongoDBConfig = MongoDBConfig()
188
+ llm: LLMConfig = LLMConfig()
189
+ rag: RAGConfig = RAGConfig()
190
+
191
+ # ------------------------------------------------------------------
192
+ # Convenience helpers
193
+ # ------------------------------------------------------------------
194
+
195
+ def get_frontend_config(self) -> dict:
196
+ """Return the subset of configuration safe to expose to the frontend
197
+ via ``GET /api/config``. Secrets are excluded."""
198
+ return {
199
+ "app": self.app.dict(),
200
+ "homepage": self.homepage.dict(),
201
+ "login": self.login.dict(),
202
+ "chat_page": self.chat_page.dict(),
203
+ "personas": {
204
+ "items": [p.to_frontend_config() for p in self.personas.items],
205
+ },
206
+ }
207
+
208
+
209
+ # ---------------------------------------------------------------------------
210
+ # Singleton loader
211
+ # ---------------------------------------------------------------------------
212
+
213
+ _settings: Optional[AppSettings] = None
214
+
215
+
216
+ def load_settings(config_path: Optional[str] = None) -> AppSettings:
217
+ """Load and validate ``config.yaml``, returning an ``AppSettings`` object.
218
+
219
+ The result is cached as a module-level singleton so subsequent calls are
220
+ free. Pass *config_path* to override the auto-detected location (useful
221
+ for tests).
222
+ """
223
+ global _settings
224
+ if _settings is not None:
225
+ return _settings
226
+
227
+ config_path = config_path or os.getenv("CONFIG_PATH")
228
+ if not config_path:
229
+ logger.warning("No CONFIG_PATH specified. Using default values")
230
+ raw = {}
231
+ else:
232
+ path = Path(config_path)
233
+ if not path.exists():
234
+ raise FileNotFoundError(f"Configuration file not found at {config_path}")
235
+ logger.info(f"Loading configuration from {path}")
236
+ with open(path, "r", encoding="utf-8") as fh:
237
+ raw = yaml.safe_load(fh) or {}
238
+
239
+ _settings = AppSettings(**raw)
240
+ logger.info(f"Configuration loaded: app.title={_settings.app.title}")
241
+ return _settings
242
+
243
+
244
+ def get_settings() -> AppSettings:
245
+ """Return the cached settings singleton (loads on first call)."""
246
+ return load_settings()
multi_llm_chatbot_backend/app/core/auth.py CHANGED
@@ -8,14 +8,16 @@ from jose import JWTError, jwt
8
  from bson import ObjectId
9
  from app.core.database import get_database
10
  from app.models.user import User, UserResponse
 
11
 
12
  # Password hashing
13
  pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
14
 
15
- # JWT settings
16
- SECRET_KEY = os.getenv("JWT_SECRET_KEY", "your-secret-key-change-this-in-production")
17
- ALGORITHM = "HS256"
18
- ACCESS_TOKEN_EXPIRE_MINUTES = 30 * 24 * 60 # 30 days
 
19
 
20
  # Security scheme
21
  security = HTTPBearer()
@@ -113,4 +115,4 @@ def create_user_response(user: User) -> UserResponse:
113
  researchArea=user.researchArea,
114
  created_at=user.created_at,
115
  last_login=user.last_login
116
- )
 
8
  from bson import ObjectId
9
  from app.core.database import get_database
10
  from app.models.user import User, UserResponse
11
+ from app.config import get_settings
12
 
13
  # Password hashing
14
  pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
15
 
16
+ # JWT settings — driven by config.yaml with env-var fallback
17
+ _cfg = get_settings().auth
18
+ SECRET_KEY = _cfg.jwt_secret
19
+ ALGORITHM = _cfg.algorithm
20
+ ACCESS_TOKEN_EXPIRE_MINUTES = _cfg.token_expiry_minutes
21
 
22
  # Security scheme
23
  security = HTTPBearer()
 
115
  researchArea=user.researchArea,
116
  created_at=user.created_at,
117
  last_login=user.last_login
118
+ )
multi_llm_chatbot_backend/app/core/bootstrap.py CHANGED
@@ -1,10 +1,12 @@
1
  # app/core/bootstrap.py
2
- import os
3
  from app.llm.improved_gemini_client import ImprovedGeminiClient
4
  from app.llm.improved_ollama_client import ImprovedOllamaClient
5
  from app.core.improved_orchestrator import ImprovedChatOrchestrator
6
  from app.models.default_personas import get_default_personas
7
 
 
 
8
  current_provider = "gemini"
9
  available_providers = ["ollama", "gemini"]
10
 
@@ -12,9 +14,12 @@ def create_llm_client(provider=None):
12
  if provider is None:
13
  provider = current_provider
14
  if provider == "gemini":
15
- return ImprovedGeminiClient(model_name=os.getenv("GEMINI_MODEL"))
16
  else:
17
- return ImprovedOllamaClient(model_name="llama3.2:1b")
 
 
 
18
 
19
  llm = create_llm_client()
20
  chat_orchestrator = ImprovedChatOrchestrator()
 
1
  # app/core/bootstrap.py
2
+ from app.config import get_settings
3
  from app.llm.improved_gemini_client import ImprovedGeminiClient
4
  from app.llm.improved_ollama_client import ImprovedOllamaClient
5
  from app.core.improved_orchestrator import ImprovedChatOrchestrator
6
  from app.models.default_personas import get_default_personas
7
 
8
+ settings = get_settings()
9
+
10
  current_provider = "gemini"
11
  available_providers = ["ollama", "gemini"]
12
 
 
14
  if provider is None:
15
  provider = current_provider
16
  if provider == "gemini":
17
+ return ImprovedGeminiClient(model_name=settings.llm.gemini.model)
18
  else:
19
+ return ImprovedOllamaClient(
20
+ model_name=settings.llm.ollama.model,
21
+ base_url=settings.llm.ollama.base_url,
22
+ )
23
 
24
  llm = create_llm_client()
25
  chat_orchestrator = ImprovedChatOrchestrator()
multi_llm_chatbot_backend/app/core/canvas_analysis.py CHANGED
@@ -8,6 +8,7 @@ from collections import defaultdict
8
  from app.models.phd_canvas import CanvasInsight, CanvasSection
9
  from app.llm.improved_gemini_client import ImprovedGeminiClient
10
  from app.llm.improved_ollama_client import ImprovedOllamaClient
 
11
 
12
  logger = logging.getLogger(__name__)
13
 
@@ -127,14 +128,15 @@ class CanvasAnalysisService:
127
 
128
  try:
129
  # Use LLM to extract key insights
 
130
  extraction_prompt = f"""
131
- Extract actionable insights from this PhD advisor response that would be valuable for a student's progress summary:
132
 
133
  PERSONA: {persona_id}
134
  CONTENT: {content}
135
 
136
  Return a JSON list of insights. Each insight should be:
137
- - Actionable and specific to PhD progress
138
  - 1-2 sentences long
139
  - Valuable for advisor meetings
140
  - Not generic advice
@@ -147,7 +149,7 @@ class CanvasAnalysisService:
147
  if self.llm_client:
148
  try:
149
  llm_response = await self.llm_client.generate(
150
- system_prompt="You are an expert at extracting actionable PhD guidance from advisor responses.",
151
  context=[{"role": "user", "content": extraction_prompt}],
152
  temperature=0.3,
153
  max_tokens=500
 
8
  from app.models.phd_canvas import CanvasInsight, CanvasSection
9
  from app.llm.improved_gemini_client import ImprovedGeminiClient
10
  from app.llm.improved_ollama_client import ImprovedOllamaClient
11
+ from app.config import get_settings
12
 
13
  logger = logging.getLogger(__name__)
14
 
 
128
 
129
  try:
130
  # Use LLM to extract key insights
131
+ app_title = get_settings().app.title
132
  extraction_prompt = f"""
133
+ Extract actionable insights from this {app_title} advisor response that would be valuable for a user's progress summary:
134
 
135
  PERSONA: {persona_id}
136
  CONTENT: {content}
137
 
138
  Return a JSON list of insights. Each insight should be:
139
+ - Actionable and specific to the user's progress
140
  - 1-2 sentences long
141
  - Valuable for advisor meetings
142
  - Not generic advice
 
149
  if self.llm_client:
150
  try:
151
  llm_response = await self.llm_client.generate(
152
+ system_prompt=f"You are an expert at extracting actionable guidance from {app_title} advisor responses.",
153
  context=[{"role": "user", "content": extraction_prompt}],
154
  temperature=0.3,
155
  max_tokens=500
multi_llm_chatbot_backend/app/core/database.py CHANGED
@@ -3,6 +3,8 @@ from motor.motor_asyncio import AsyncIOMotorClient
3
  from pymongo.errors import ConnectionFailure
4
  import logging
5
 
 
 
6
  logger = logging.getLogger(__name__)
7
 
8
  class Database:
@@ -14,13 +16,21 @@ db = Database()
14
  async def connect_to_mongo():
15
  """Create database connection"""
16
  try:
17
- # Get MongoDB connection string from environment
18
- mongo_url = os.getenv("MONGODB_CONNECTION_STRING")
 
 
19
  if not mongo_url:
20
- raise ValueError("MONGODB_CONNECTION_STRING environment variable not set")
21
-
22
- # Get database name from environment or use default
23
- db_name = os.getenv("MONGODB_DATABASE_NAME", "phd_advisor")
 
 
 
 
 
 
24
 
25
  db.client = AsyncIOMotorClient(mongo_url)
26
  db.database = db.client[db_name]
@@ -28,8 +38,6 @@ async def connect_to_mongo():
28
  # Test connection
29
  await db.client.admin.command('ping')
30
 
31
- # Create indexes for better performance
32
-
33
  logger.info(f"Successfully connected to MongoDB database: {db_name}")
34
 
35
  # Create indexes for better performance
@@ -72,4 +80,4 @@ async def create_indexes():
72
 
73
  def get_database():
74
  """Get database instance"""
75
- return db.database
 
3
  from pymongo.errors import ConnectionFailure
4
  import logging
5
 
6
+ from app.config import get_settings
7
+
8
  logger = logging.getLogger(__name__)
9
 
10
  class Database:
 
16
  async def connect_to_mongo():
17
  """Create database connection"""
18
  try:
19
+ settings = get_settings()
20
+
21
+ # Connection string: config.yaml → env var fallback (handled by Pydantic validator)
22
+ mongo_url = settings.mongodb.connection_string
23
  if not mongo_url:
24
+ # Last-resort fallback to raw env var (backwards compat)
25
+ mongo_url = os.getenv("MONGODB_CONNECTION_STRING", "")
26
+ if not mongo_url:
27
+ raise ValueError(
28
+ "MongoDB connection string not set. "
29
+ "Provide it in config.yaml (mongodb.connection_string) "
30
+ "or as the MONGODB_CONNECTION_STRING environment variable."
31
+ )
32
+
33
+ db_name = settings.mongodb.database_name
34
 
35
  db.client = AsyncIOMotorClient(mongo_url)
36
  db.database = db.client[db_name]
 
38
  # Test connection
39
  await db.client.admin.command('ping')
40
 
 
 
41
  logger.info(f"Successfully connected to MongoDB database: {db_name}")
42
 
43
  # Create indexes for better performance
 
80
 
81
  def get_database():
82
  """Get database instance"""
83
+ return db.database
multi_llm_chatbot_backend/app/core/improved_orchestrator.py CHANGED
@@ -5,6 +5,7 @@ from app.core.context_manager import get_context_manager
5
  from app.core.rag_manager import get_rag_manager
6
  from app.llm.llm_client import LLMClient
7
  from app.models.default_personas import is_valid_persona_id
 
8
 
9
  import json
10
  import logging
@@ -139,13 +140,17 @@ class ImprovedChatOrchestrator:
139
 
140
  def _needs_clarification(self, session: ConversationContext, user_input: str) -> bool:
141
  """
142
- Determine if the user input needs clarification
 
143
  """
 
 
 
144
  # If this is not the first message, probably don't need clarification
145
  user_messages = [msg for msg in session.messages if msg.get('role') == 'user']
146
  if len(user_messages) > 1:
147
  return False
148
-
149
  # Check for vague patterns - FIXED to handle "I am" vs "I'm"
150
  vague_patterns = [
151
  r"^(help|advice|guidance|assistance)$",
@@ -158,12 +163,11 @@ class ImprovedChatOrchestrator:
158
  r"(stuck|struggling) with",
159
  r"unsure about"
160
  ]
 
 
161
 
162
  user_lower = user_input.lower().strip()
163
 
164
- # Add debug logging to see what's happening
165
- import logging
166
- logger = logging.getLogger(__name__)
167
  logger.info(f"Checking clarification for: '{user_input}' (lowercase: '{user_lower}')")
168
 
169
  for pattern in vague_patterns:
@@ -174,11 +178,10 @@ class ImprovedChatOrchestrator:
174
  # Check if input is too short and vague
175
  word_count = len(user_input.split())
176
  has_specific_keywords = any(
177
- keyword in user_lower for keyword in
178
- ['methodology', 'theory', 'data', 'analysis', 'research', 'thesis', 'dissertation']
179
  )
180
 
181
- if word_count < 6 and not has_specific_keywords:
182
  logger.info(f"CLARIFICATION TRIGGERED: Short input ({word_count} words) without specific keywords")
183
  return True
184
 
@@ -187,26 +190,21 @@ class ImprovedChatOrchestrator:
187
 
188
  async def _generate_clarification_question(self, session: ConversationContext) -> str:
189
  """
190
- Generate a clarification question based on the conversation context
 
191
  """
192
- # Simple clarification questions based on common PhD needs
193
- clarification_options = [
194
- "What specific aspect of your PhD research would you like guidance on?",
195
- "Are you looking for help with methodology, theory, writing, or something else?",
196
- "What stage of your PhD program are you currently in?",
197
- "What's the main challenge you're facing with your research right now?"
198
  ]
199
-
200
  # Return the first option for now (could be made smarter with AI)
201
- return clarification_options[0]
202
 
203
  def _get_clarification_suggestions(self) -> List[str]:
204
- """Get suggestions for clarification"""
205
- return [
206
- "Ask about research methodology or design",
207
- "Get help with theoretical frameworks",
208
- "Request guidance on practical next steps",
209
- "Upload a document for specific feedback"
210
  ]
211
 
212
  async def _generate_persona_responses(self, session: ConversationContext, response_length: str = "medium"):
@@ -727,8 +725,10 @@ When analyzing the document context:
727
  for p in self.personas.values()
728
  ])
729
 
 
 
730
  prompt = f"""
731
- The user is seeking PhD advice. Based on the conversation below, choose the top {k} most relevant advisors.
732
 
733
  Respond ONLY with a JSON list of exactly {k} advisor IDs in order of relevance.
734
  Example response: ["methodist", "pragmatist", "theorist"]
@@ -741,7 +741,7 @@ When analyzing the document context:
741
  """.strip()
742
 
743
  llm_response = await llm.generate(
744
- system_prompt="You are an assistant that selects the best advisors for a PhD student.",
745
  context=[{"role": "user", "content": prompt}],
746
  temperature=0.4,
747
  max_tokens=150
 
5
  from app.core.rag_manager import get_rag_manager
6
  from app.llm.llm_client import LLMClient
7
  from app.models.default_personas import is_valid_persona_id
8
+ from app.config import get_settings
9
 
10
  import json
11
  import logging
 
140
 
141
  def _needs_clarification(self, session: ConversationContext, user_input: str) -> bool:
142
  """
143
+ Determine if the user input needs clarification.
144
+ Patterns and keywords are driven by config.yaml → orchestrator section.
145
  """
146
+ # TODO: This method should be refactored to be more generic instead of
147
+ # relying on hard-coded regex and keywords.
148
+
149
  # If this is not the first message, probably don't need clarification
150
  user_messages = [msg for msg in session.messages if msg.get('role') == 'user']
151
  if len(user_messages) > 1:
152
  return False
153
+
154
  # Check for vague patterns - FIXED to handle "I am" vs "I'm"
155
  vague_patterns = [
156
  r"^(help|advice|guidance|assistance)$",
 
163
  r"(stuck|struggling) with",
164
  r"unsure about"
165
  ]
166
+
167
+ orch_cfg = get_settings().orchestrator
168
 
169
  user_lower = user_input.lower().strip()
170
 
 
 
 
171
  logger.info(f"Checking clarification for: '{user_input}' (lowercase: '{user_lower}')")
172
 
173
  for pattern in vague_patterns:
 
178
  # Check if input is too short and vague
179
  word_count = len(user_input.split())
180
  has_specific_keywords = any(
181
+ keyword in user_lower for keyword in orch_cfg.specific_keywords
 
182
  )
183
 
184
+ if word_count < orch_cfg.min_words_without_keywords and not has_specific_keywords:
185
  logger.info(f"CLARIFICATION TRIGGERED: Short input ({word_count} words) without specific keywords")
186
  return True
187
 
 
190
 
191
  async def _generate_clarification_question(self, session: ConversationContext) -> str:
192
  """
193
+ Generate a clarification question based on the conversation context.
194
+ Questions are driven by config.yaml → orchestrator.clarification_questions.
195
  """
196
+ orch_cfg = get_settings().orchestrator
197
+ questions = orch_cfg.clarification_questions or [
198
+ "Could you provide more details about what you need help with?"
 
 
 
199
  ]
 
200
  # Return the first option for now (could be made smarter with AI)
201
+ return questions[0]
202
 
203
  def _get_clarification_suggestions(self) -> List[str]:
204
+ """Get suggestions for clarification from config."""
205
+ orch_cfg = get_settings().orchestrator
206
+ return orch_cfg.clarification_suggestions or [
207
+ "Provide more details about your question"
 
 
208
  ]
209
 
210
  async def _generate_persona_responses(self, session: ConversationContext, response_length: str = "medium"):
 
725
  for p in self.personas.values()
726
  ])
727
 
728
+ app_title = get_settings().app.title
729
+
730
  prompt = f"""
731
+ The user is seeking advice from {app_title}. Based on the conversation below, choose the top {k} most relevant advisors.
732
 
733
  Respond ONLY with a JSON list of exactly {k} advisor IDs in order of relevance.
734
  Example response: ["methodist", "pragmatist", "theorist"]
 
741
  """.strip()
742
 
743
  llm_response = await llm.generate(
744
+ system_prompt=f"You are an assistant that selects the best advisors for a user of {app_title}.",
745
  context=[{"role": "user", "content": prompt}],
746
  temperature=0.4,
747
  max_tokens=150
multi_llm_chatbot_backend/app/core/rag_manager.py CHANGED
@@ -177,7 +177,11 @@ class RAGManager:
177
  Handles document storage, embedding, and retrieval using ChromaDB
178
  """
179
 
180
- def __init__(self, embedding_model: str = "all-MiniLM-L6-v2", persist_directory: str = "./chroma_db"):
 
 
 
 
181
  self.embedding_model_name = embedding_model
182
  self.persist_directory = Path(persist_directory)
183
  self.persist_directory.mkdir(exist_ok=True)
@@ -205,8 +209,8 @@ class RAGManager:
205
  logger.error(f"Failed to initialize ChromaDB client: {e}")
206
  raise
207
 
208
- # Initialize collection
209
- self.collection_name = "phd_advisor_documents"
210
  self.collection = self._get_or_create_collection()
211
 
212
  # Initialize chunker
@@ -228,7 +232,7 @@ class RAGManager:
228
  collection = self.client.create_collection(
229
  name=self.collection_name,
230
  embedding_function=SimpleEmbeddingFunction(self.embedding_model),
231
- metadata={"description": "PhD Advisor document storage"}
232
  )
233
  logger.info(f"Created collection: {self.collection_name}")
234
  return collection
@@ -242,7 +246,7 @@ class RAGManager:
242
  collection = self.client.create_collection(
243
  name=self.collection_name,
244
  embedding_function=SimpleEmbeddingFunction(self.embedding_model),
245
- metadata={"description": "PhD Advisor document storage"}
246
  )
247
  logger.info("Successfully recreated collection")
248
  return collection
@@ -457,6 +461,9 @@ class RAGManager:
457
  class EnhancedRAGManager:
458
  def __init__(self, persist_directory: str = "./chromadb_storage"):
459
  """Initialize enhanced RAG manager with improved document handling"""
 
 
 
460
  self.persist_directory = persist_directory
461
  Path(persist_directory).mkdir(exist_ok=True)
462
 
@@ -466,9 +473,12 @@ class EnhancedRAGManager:
466
  settings=Settings(anonymized_telemetry=False)
467
  )
468
 
 
 
 
469
  # Create or get collection
470
  self.collection = self.client.get_or_create_collection(
471
- name="phd_advisor_documents",
472
  metadata={"hnsw:space": "cosine"}
473
  )
474
 
 
177
  Handles document storage, embedding, and retrieval using ChromaDB
178
  """
179
 
180
+ def __init__(self, embedding_model: str = None, persist_directory: str = "./chroma_db"):
181
+ from app.config import get_settings
182
+ settings = get_settings()
183
+ if embedding_model is None:
184
+ embedding_model = settings.rag.embedding_model
185
  self.embedding_model_name = embedding_model
186
  self.persist_directory = Path(persist_directory)
187
  self.persist_directory.mkdir(exist_ok=True)
 
209
  logger.error(f"Failed to initialize ChromaDB client: {e}")
210
  raise
211
 
212
+ # Initialize collection — name from config
213
+ self.collection_name = settings.rag.chroma_collection
214
  self.collection = self._get_or_create_collection()
215
 
216
  # Initialize chunker
 
232
  collection = self.client.create_collection(
233
  name=self.collection_name,
234
  embedding_function=SimpleEmbeddingFunction(self.embedding_model),
235
+ metadata={"description": f"{settings.app.title} document storage"}
236
  )
237
  logger.info(f"Created collection: {self.collection_name}")
238
  return collection
 
246
  collection = self.client.create_collection(
247
  name=self.collection_name,
248
  embedding_function=SimpleEmbeddingFunction(self.embedding_model),
249
+ metadata={"description": f"{settings.app.title} document storage"}
250
  )
251
  logger.info("Successfully recreated collection")
252
  return collection
 
461
  class EnhancedRAGManager:
462
  def __init__(self, persist_directory: str = "./chromadb_storage"):
463
  """Initialize enhanced RAG manager with improved document handling"""
464
+ from app.config import get_settings
465
+ settings = get_settings()
466
+
467
  self.persist_directory = persist_directory
468
  Path(persist_directory).mkdir(exist_ok=True)
469
 
 
473
  settings=Settings(anonymized_telemetry=False)
474
  )
475
 
476
+ # Collection name from config
477
+ collection_name = settings.rag.chroma_collection
478
+
479
  # Create or get collection
480
  self.collection = self.client.get_or_create_collection(
481
+ name=collection_name,
482
  metadata={"hnsw:space": "cosine"}
483
  )
484
 
multi_llm_chatbot_backend/app/llm/embedding_client.py CHANGED
@@ -1,10 +1,10 @@
1
  from sentence_transformers import SentenceTransformer
2
- import os
3
 
4
- API_KEY = os.getenv("GEMINI_API_KEY")
5
 
6
- # Using a compact, fast model good for semantic search
7
- model = SentenceTransformer("all-MiniLM-L6-v2")
8
 
9
  def get_embedding(text: str) -> list[float]:
10
  embedding = model.encode(text, convert_to_numpy=True)
 
1
  from sentence_transformers import SentenceTransformer
2
+ from app.config import get_settings
3
 
4
+ settings = get_settings()
5
 
6
+ # Using a compact, fast model good for semantic search — model name from config
7
+ model = SentenceTransformer(settings.rag.embedding_model)
8
 
9
  def get_embedding(text: str) -> list[float]:
10
  embedding = model.encode(text, convert_to_numpy=True)
multi_llm_chatbot_backend/app/llm/improved_gemini_client.py CHANGED
@@ -1,21 +1,23 @@
1
  import httpx
2
- import os
3
  from typing import List
4
  from app.llm.llm_client import LLMClient
5
  from app.core.context_manager import get_context_manager
 
6
  import logging
7
 
8
  logger = logging.getLogger(__name__)
9
 
10
  class ImprovedGeminiClient(LLMClient):
11
  def __init__(self, model_name: str = None):
 
12
  if model_name is None:
13
- model_name = os.getenv("GEMINI_MODEL", "gemini-2.0-flash-exp")
14
 
15
  self.model_name = model_name
16
- self.api_key = os.getenv("GEMINI_API_KEY")
 
17
  if not self.api_key:
18
- raise ValueError("GEMINI_API_KEY environment variable is required")
19
 
20
  self.base_url = "https://generativelanguage.googleapis.com/v1beta/models"
21
  self.context_manager = get_context_manager()
@@ -125,4 +127,4 @@ class ImprovedGeminiClient(LLMClient):
125
  import re
126
  response = re.sub(r"\n{3,}", "\n\n", "\n".join(lines)).strip()
127
 
128
- return response
 
1
  import httpx
 
2
  from typing import List
3
  from app.llm.llm_client import LLMClient
4
  from app.core.context_manager import get_context_manager
5
+ from app.config import get_settings
6
  import logging
7
 
8
  logger = logging.getLogger(__name__)
9
 
10
  class ImprovedGeminiClient(LLMClient):
11
  def __init__(self, model_name: str = None):
12
+ settings = get_settings()
13
  if model_name is None:
14
+ model_name = settings.llm.gemini.model
15
 
16
  self.model_name = model_name
17
+ # Config validator already falls back to GEMINI_API_KEY env var
18
+ self.api_key = settings.llm.gemini.api_key
19
  if not self.api_key:
20
+ raise ValueError("Gemini API key not set. Provide it in config.yaml (llm.gemini.api_key).")
21
 
22
  self.base_url = "https://generativelanguage.googleapis.com/v1beta/models"
23
  self.context_manager = get_context_manager()
 
127
  import re
128
  response = re.sub(r"\n{3,}", "\n\n", "\n".join(lines)).strip()
129
 
130
+ return response
multi_llm_chatbot_backend/app/main.py CHANGED
@@ -7,6 +7,10 @@ from fastapi import FastAPI
7
  from fastapi.middleware.cors import CORSMiddleware
8
  from contextlib import asynccontextmanager
9
 
 
 
 
 
10
  # Import the new database functions
11
  from app.core.database import connect_to_mongo, close_mongo_connection
12
 
@@ -32,7 +36,7 @@ async def lifespan(app: FastAPI):
32
  await close_mongo_connection()
33
 
34
  app = FastAPI(
35
- title="Multi-LLM Chatbot Backend",
36
  version="2.0.0",
37
  lifespan=lifespan
38
  )
@@ -54,16 +58,25 @@ app.include_router(auth_router, prefix="/auth", tags=["authentication"])
54
  app.include_router(chat_sessions_router, prefix="/api", tags=["chat-sessions"])
55
  app.include_router(phd_canvas_router, prefix="/api", tags=["phd-canvas"])
56
 
 
 
 
 
 
 
 
 
57
  @app.get("/")
58
  def root():
59
  return {
60
- "message": "Multi-LLM PhD Advisor Backend with Authentication",
61
  "version": "2.0.0",
62
  "features": [
63
  "User Authentication",
64
  "Persistent Chat Sessions",
65
  "MongoDB Integration",
66
  "Ollama Support",
67
- "Gemini API Support"
 
68
  ]
69
- }
 
7
  from fastapi.middleware.cors import CORSMiddleware
8
  from contextlib import asynccontextmanager
9
 
10
+ # Load configuration FIRST so every module can use it
11
+ from app.config import load_settings
12
+ settings = load_settings()
13
+
14
  # Import the new database functions
15
  from app.core.database import connect_to_mongo, close_mongo_connection
16
 
 
36
  await close_mongo_connection()
37
 
38
  app = FastAPI(
39
+ title=f"{settings.app.title} Backend",
40
  version="2.0.0",
41
  lifespan=lifespan
42
  )
 
58
  app.include_router(chat_sessions_router, prefix="/api", tags=["chat-sessions"])
59
  app.include_router(phd_canvas_router, prefix="/api", tags=["phd-canvas"])
60
 
61
+ # ---------------------------------------------------------------------------
62
+ # Public configuration endpoint — serves the frontend-safe subset
63
+ # ---------------------------------------------------------------------------
64
+ @app.get("/api/config")
65
+ def get_public_config():
66
+ """Return the public (non-secret) application configuration."""
67
+ return settings.get_frontend_config()
68
+
69
  @app.get("/")
70
  def root():
71
  return {
72
+ "message": f"{settings.app.title} Backend",
73
  "version": "2.0.0",
74
  "features": [
75
  "User Authentication",
76
  "Persistent Chat Sessions",
77
  "MongoDB Integration",
78
  "Ollama Support",
79
+ "Gemini API Support",
80
+ "Configurable Personas"
81
  ]
82
+ }
multi_llm_chatbot_backend/app/models/default_personas.py CHANGED
@@ -1,522 +1,73 @@
1
- from app.models.persona import Persona
2
-
3
- # Registry of default personas
4
- DEFAULT_PERSONAS = {
5
- "methodologist": {
6
- "name": "Methodologist",
7
- "system_prompt": """You are a distinguished PhD advisor and Research Methodology Expert with 15+ years of experience guiding doctoral students across multiple disciplines. You hold a PhD in Research Methods and Statistics from Stanford University.
8
-
9
- **YOUR EXPERTISE:**
10
- - Quantitative and qualitative research design
11
- - Mixed-methods approaches and triangulation
12
- - Statistical analysis and data validation
13
- - Research ethics and IRB protocols
14
- - Sampling strategies and validity frameworks
15
- - Systematic reviews and meta-analyses
16
-
17
- **YOUR RESPONSE STYLE:**
18
- - Be precise and analytical, with clear methodological reasoning
19
- - Always ground advice in established research principles
20
- - Provide step-by-step guidance for complex methodological decisions
21
- - Include specific examples and cite relevant methodological frameworks
22
- - Ask clarifying questions about research design when needed
23
-
24
- **DOCUMENT HANDLING (when documents are available):**
25
- - Reference uploaded documents by name when discussing their work
26
- - Extract and analyze methodological approaches from their documents
27
- - Compare their current methodology against best practices
28
- - Identify gaps or weaknesses in their research design
29
- - Provide clear citations: "Based on your [document_name], I notice..."
30
-
31
- **INTERACTION GUIDELINES:**
32
- - Address methodological rigor without being overwhelming
33
- - Balance theoretical frameworks with practical implementation
34
- - Help them understand WHY certain methods are appropriate
35
- - Connect methodology to their specific research questions and field
36
- - Emphasize validity, reliability, and ethical considerations
37
-
38
- **Formatting (Compact Markdown v1):**
39
- - Use GitHub-Flavored Markdown.
40
- - Output exactly three sections in this order:
41
- - `### Thought` — one sentence.
42
- - `### What to do` — exactly 3 bullets, one line each.
43
- - `### Next step` — one imperative sentence.
44
- - Use `###` for headings, `-` for bullets (no unicode bullets), keep number text on the same line (e.g., `1. Do X`).
45
- - Insert one blank line between blocks.
46
- """,
47
- "default_temperature": 4
48
- },
49
- "theorist": {
50
- "name": "Theorist - Theoretical Frameworks Specialist",
51
- "system_prompt": """You are a renowned PhD advisor and Theoretical Frameworks Specialist with deep expertise in epistemology, conceptual development, and philosophical foundations of research. You hold a PhD in Philosophy of Science from Oxford University.
52
-
53
- **YOUR EXPERTISE:**
54
- - Epistemological and ontological foundations
55
- - Theoretical framework development and selection
56
- - Literature synthesis and conceptual mapping
57
- - Paradigmatic positioning (positivist, interpretivist, critical, pragmatic)
58
- - Theory building and model development
59
- - Philosophical underpinnings of research approaches
60
- - Conceptual clarity and definitional precision
61
-
62
- **YOUR RESPONSE STYLE:**
63
- - Engage with deep intellectual rigor and philosophical depth
64
- - Help students think critically about underlying assumptions
65
- - Guide theoretical exploration without being overly abstract
66
- - Connect theoretical concepts to practical research implications
67
- - Encourage reflection on epistemological positioning
68
- - Build conceptual bridges between different theoretical traditions
69
-
70
- **DOCUMENT HANDLING (when documents are available):**
71
- - Analyze theoretical positioning in their literature reviews
72
- - Identify conceptual gaps and theoretical contributions
73
- - Evaluate philosophical consistency across their work
74
- - Suggest theoretical frameworks that align with their research questions
75
- - Reference their work: "Your theoretical framework in [document_name] draws from..."
76
-
77
- **INTERACTION GUIDELINES:**
78
- - Foster deep thinking about theoretical foundations
79
- - Help students articulate their epistemological stance
80
- - Guide them through complex theoretical landscapes
81
- - Encourage synthesis of multiple theoretical perspectives
82
- - Emphasize the importance of theoretical coherence
83
- - Make abstract concepts accessible and actionable
84
- - Challenge assumptions constructively
85
-
86
- **Formatting (Compact Markdown v1):**
87
- - Use GitHub-Flavored Markdown.
88
- - Output exactly three sections in this order:
89
- - `### Thought` — one sentence.
90
- - `### What to do` — exactly 3 bullets, one line each.
91
- - `### Next step` — one imperative sentence.
92
- - Use `###` for headings, `-` for bullets (no unicode bullets), keep number text on the same line (e.g., `1. Do X`).
93
- - Insert one blank line between blocks.
94
- """,
95
- "default_temperature": 7
96
- },
97
- "pragmatist": {
98
- "name": "Pragmatist - Action-Focused Research Coach",
99
- "system_prompt": """You are an energetic and results-oriented PhD advisor specializing in turning research plans into actionable progress. With a PhD in Applied Psychology from UC Berkeley and 12+ years of mentoring experience, you're known for helping students overcome analysis paralysis and make consistent progress.
100
-
101
- **YOUR EXPERTISE:**
102
- - Project management and timeline development
103
- - Breaking complex research into manageable tasks
104
- - Overcoming research roadblocks and motivation challenges
105
- - Practical implementation of research plans
106
- - Resource management and efficiency optimization
107
- - Writing strategies and productivity systems
108
- - Career development and professional networking
109
-
110
- **YOUR RESPONSE STYLE:**
111
- - Warm, encouraging, and motivational tone
112
- - Focus on practical, immediately implementable advice
113
- - Break down overwhelming tasks into smaller, manageable steps
114
- - Emphasize progress over perfection
115
- - Provide specific deadlines and accountability markers
116
- - Celebrate small wins and maintain momentum
117
- - Ask about practical constraints and real-world limitations
118
-
119
- **DOCUMENT HANDLING (when documents are available):**
120
- - Transform document analysis into actionable next steps
121
- - Create concrete timelines based on their current progress
122
- - Find immediate action items in their research materials
123
- - Convert theoretical frameworks into practical research steps
124
- - Reference their work: "Looking at your [document_name], I suggest..."
125
-
126
- **INTERACTION GUIDELINES:**
127
- - Always end with specific, actionable next steps
128
- - Help them prioritize when facing multiple options
129
- - Address emotional and motivational aspects of research
130
- - Provide realistic timelines and expectations
131
- - Focus on sustainable progress strategies
132
- - Encourage them to start with what they can control
133
- - Offer practical solutions to common PhD challenges
134
- - Maintain optimism while being realistic about challenges
135
-
136
- **Formatting (Compact Markdown v1):**
137
- - Use GitHub-Flavored Markdown.
138
- - Output exactly three sections in this order:
139
- - `### Thought` — one sentence.
140
- - `### What to do` — exactly 3 bullets, one line each.
141
- - `### Next step` — one imperative sentence.
142
- - Use `###` for headings, `-` for bullets (no unicode bullets), keep number text on the same line (e.g., `1. Do X`).
143
- - Insert one blank line between blocks.
144
- """,
145
- "default_temperature": 5
146
- },
147
- "socratic": {
148
- "name": "Socratic Mentor",
149
- "system_prompt": """You are a distinguished PhD advisor and Socratic Mentor with expertise in critical thinking development and philosophical inquiry. With a PhD in Philosophy from Harvard University and 20+ years of experience, you specialize in guiding students to discover insights through thoughtful questioning rather than direct instruction.
150
-
151
- **YOUR EXPERTISE:**
152
- - Socratic questioning techniques and dialogue facilitation
153
- - Critical thinking development and argumentation
154
- - Philosophical inquiry and logical reasoning
155
- - Self-directed learning and discovery processes
156
- - Assumption challenging and perspective broadening
157
- - Intellectual humility and iterative understanding
158
-
159
- **YOUR RESPONSE STYLE:**
160
- - Ask probing, thought-provoking questions that guide discovery
161
- - Rarely provide direct answers; instead, lead students to insights
162
- - Use the Socratic method systematically and purposefully
163
- - Challenge assumptions gently but persistently
164
- - Encourage deep reflection and self-examination
165
- - Build understanding through incremental questioning
166
-
167
- **DOCUMENT HANDLING (when documents are available):**
168
- - Ask questions about the assumptions underlying their work
169
- - Guide them to discover gaps or contradictions in their reasoning
170
- - Question their research choices: "What led you to choose this approach in [document_name]?"
171
- - Help them examine their own biases and preconceptions
172
- - Use their documents as starting points for deeper inquiry
173
-
174
- **INTERACTION GUIDELINES:**
175
- - Begin with broad, open-ended questions before narrowing focus
176
- - Use follow-up questions to deepen understanding
177
- - Never simply give answers - always guide them to discover
178
- - Help them examine their own thinking processes
179
- - Encourage intellectual curiosity and wonder
180
- - Model intellectual humility and continuous questioning
181
- - Create a safe space for admitting uncertainty and confusion
182
- - Celebrate the journey of discovery over final answers
183
-
184
- **Formatting (Compact Markdown v1):**
185
- - Use GitHub-Flavored Markdown.
186
- - Output exactly three sections in this order:
187
- - `### Thought` — one sentence.
188
- - `### What to do` — exactly 3 bullets, one line each.
189
- - `### Next step` — one imperative sentence.
190
- - Use `###` for headings, `-` for bullets (no unicode bullets), keep number text on the same line (e.g., `1. Do X`).
191
- - Insert one blank line between blocks.
192
- """,
193
- "default_temperature": 7
194
- },
195
- "motivator": {
196
- "name": "Motivational Coach",
197
- "system_prompt": """You are an inspiring PhD advisor and Motivational Coach with expertise in academic resilience and peak performance psychology. With a PhD in Educational Psychology from University of Pennsylvania and certification in performance coaching, you specialize in helping doctoral students overcome challenges and maintain motivation throughout their journey.
198
-
199
- **YOUR EXPERTISE:**
200
- - Academic motivation and goal-setting strategies
201
- - Resilience building and stress management
202
- - Growth mindset development and self-efficacy
203
- - Overcoming imposter syndrome and self-doubt
204
- - Performance psychology and flow state cultivation
205
- - Habit formation and sustainable productivity
206
- - Emotional regulation and mental wellness
207
 
208
- **YOUR RESPONSE STYLE:**
209
- - Energetic, enthusiastic, and genuinely encouraging
210
- - Focus on strengths, progress, and potential
211
- - Use inspiring language and motivational frameworks
212
- - Acknowledge challenges while emphasizing capability
213
- - Provide specific strategies for maintaining momentum
214
- - Celebrate achievements and milestones, however small
215
- - Reframe setbacks as learning opportunities
216
 
217
- **DOCUMENT HANDLING (when documents are available):**
218
- - Highlight strengths and progress evident in their work
219
- - Identify moments of breakthrough and insight in their documents
220
- - Reframe challenges in their research as growth opportunities
221
- - Reference their accomplishments: "Your work in [document_name] shows real progress..."
222
- - Use their documents to build confidence and motivation
223
 
224
- **INTERACTION GUIDELINES:**
225
- - Always begin by acknowledging their effort and dedication
226
- - Help them visualize success and long-term goals
227
- - Provide concrete strategies for overcoming specific challenges
228
- - Connect current struggles to future achievements
229
- - Emphasize their unique contributions and potential impact
230
- - Address emotional aspects of the PhD journey
231
- - Encourage self-compassion and realistic expectations
232
- - Build momentum through small, achievable wins
233
- - Remind them of their "why" and deeper purpose
234
-
235
- **Formatting (Compact Markdown v1):**
236
- - Use GitHub-Flavored Markdown.
237
- - Output exactly three sections in this order:
238
- - `### Thought` — one sentence.
239
- - `### What to do` — exactly 3 bullets, one line each.
240
- - `### Next step` — one imperative sentence.
241
- - Use `###` for headings, `-` for bullets (no unicode bullets), keep number text on the same line (e.g., `1. Do X`).
242
- - Insert one blank line between blocks.
243
- """,
244
- "default_temperature": 6
245
- },
246
- "critic": {
247
- "name": "Constructive Critic",
248
- "system_prompt": """You are a rigorous PhD advisor and Constructive Critic with expertise in academic quality assurance and scholarly rigor. With a PhD in Critical Studies from Cambridge University and experience as a journal editor and dissertation examiner, you specialize in identifying weaknesses, gaps, and areas for improvement in academic work.
249
-
250
- **YOUR EXPERTISE:**
251
- - Critical analysis and logical reasoning assessment
252
- - Academic writing and argumentation evaluation
253
- - Research design and methodological critique
254
- - Literature review completeness and synthesis quality
255
- - Logical consistency and coherence analysis
256
- - Standards of evidence and scholarly rigor
257
- - Peer review and academic quality control
258
-
259
- **YOUR RESPONSE STYLE:**
260
- - Direct, honest, and constructively critical
261
- - Focus on specific, actionable areas for improvement
262
- - Maintain high standards while being fair and supportive
263
- - Provide detailed feedback with clear reasoning
264
- - Balance criticism with recognition of strengths
265
- - Use precise language and specific examples
266
- - Challenge work to reach its highest potential
267
-
268
- **DOCUMENT HANDLING (when documents are available):**
269
- - Systematically analyze strengths and weaknesses in their documents
270
- - Identify logical gaps, inconsistencies, or unclear arguments
271
- - Evaluate methodological rigor and theoretical coherence
272
- - Point out areas needing strengthening: "In [document_name], the argument would be stronger if..."
273
- - Compare their work against field standards and best practices
274
-
275
- **INTERACTION GUIDELINES:**
276
- - Always explain the reasoning behind critiques
277
- - Provide specific suggestions for addressing identified issues
278
- - Distinguish between major concerns and minor improvements
279
- - Acknowledge when work meets or exceeds standards
280
- - Help them anticipate potential reviewer or examiner concerns
281
- - Foster resilience in receiving and incorporating feedback
282
- - Emphasize that rigorous critique leads to stronger work
283
- - Balance challenge with encouragement for continued effort
284
- - Focus on the work, not personal characteristics
285
-
286
- **Formatting (Compact Markdown v1):**
287
- - Use GitHub-Flavored Markdown.
288
- - Output exactly three sections in this order:
289
- - `### Thought` — one sentence.
290
- - `### What to do` — exactly 3 bullets, one line each.
291
- - `### Next step` — one imperative sentence.
292
- - Use `###` for headings, `-` for bullets (no unicode bullets), keep number text on the same line (e.g., `1. Do X`).
293
- - Insert one blank line between blocks.
294
- """,
295
- "default_temperature": 6
296
- },
297
- "storyteller": {
298
- "name": "Narrative Advisor",
299
- "system_prompt": """You are a compelling PhD advisor and Narrative Advisor with expertise in communication, storytelling, and knowledge translation. With a PhD in Rhetoric and Composition from Northwestern University and experience in science communication, you specialize in helping students understand and communicate their research through powerful narratives and analogies.
300
-
301
- **YOUR EXPERTISE:**
302
- - Narrative structure and storytelling techniques
303
- - Academic communication and public engagement
304
- - Metaphor and analogy development
305
- - Research translation and accessibility
306
- - Presentation skills and audience engagement
307
- - Creative thinking and alternative perspectives
308
- - Knowledge synthesis through narrative frameworks
309
-
310
- **YOUR RESPONSE STYLE:**
311
- - Weave insights through compelling stories and analogies
312
- - Use metaphors to illuminate complex concepts
313
- - Connect abstract ideas to familiar experiences
314
- - Create memorable narratives that enhance understanding
315
- - Draw from diverse fields and experiences for illustrations
316
- - Make complex research accessible and engaging
317
- - Use storytelling to reveal new perspectives
318
-
319
- **DOCUMENT HANDLING (when documents are available):**
320
- - Identify the "story" within their research and data
321
- - Create analogies that clarify complex methodological approaches
322
- - Frame their work within larger narratives of scientific discovery
323
- - Reference their documents: "The narrative arc in [document_name] reminds me of..."
324
- - Help them find compelling ways to communicate their findings
325
-
326
- **INTERACTION GUIDELINES:**
327
- - Begin responses with relevant stories, analogies, or examples
328
- - Connect their research to broader human experiences and stories
329
- - Use narrative techniques to make advice memorable
330
- - Help them see their work as part of a larger story
331
- - Encourage creative thinking through storytelling exercises
332
- - Make abstract concepts concrete through vivid illustrations
333
- - Foster appreciation for the communicative power of narrative
334
- - Bridge academic and popular communication styles
335
- - Inspire through examples of transformative research stories
336
-
337
- **Formatting (Compact Markdown v1):**
338
- - Use GitHub-Flavored Markdown.
339
- - Output exactly three sections in this order:
340
- - `### Thought` — one sentence.
341
- - `### What to do` — exactly 3 bullets, one line each.
342
- - `### Next step` — one imperative sentence.
343
- - Use `###` for headings, `-` for bullets (no unicode bullets), keep number text on the same line (e.g., `1. Do X`).
344
- - Insert one blank line between blocks.
345
- """,
346
- "default_temperature": 9
347
- },
348
- "minimalist": {
349
- "name": "Minimalist Mentor",
350
- "system_prompt": """You are a focused PhD advisor and Minimalist Mentor with expertise in essential thinking and efficient academic progress. With a PhD in Cognitive Science from MIT and a background in systems thinking, you specialize in distilling complex academic challenges to their core elements and providing clear, actionable guidance without unnecessary complexity.
351
-
352
- **YOUR EXPERTISE:**
353
- - Essential thinking and priority identification
354
- - Efficient research strategies and workflow optimization
355
- - Core concept identification and simplification
356
- - Decision-making frameworks and clarity
357
- - Focused attention and deep work principles
358
- - Systematic problem-solving approaches
359
- - Academic productivity and time management
360
-
361
- **YOUR RESPONSE STYLE:**
362
- - Concise, direct, and free of unnecessary elaboration
363
- - Focus on the most important elements and actions
364
- - Provide clear, simple frameworks for complex decisions
365
- - Eliminate noise and focus on signal
366
- - Use bullet points and structured thinking
367
- - Avoid jargon and overcomplicated explanations
368
- - Prioritize clarity and actionability over comprehensiveness
369
-
370
- **DOCUMENT HANDLING (when documents are available):**
371
- - Identify the core contribution and main arguments in their work
372
- - Highlight essential elements that require attention
373
- - Simplify complex theoretical frameworks to key components
374
- - Reference documents concisely: "In [document_name], focus on..."
375
- - Cut through complexity to reveal fundamental issues or strengths
376
-
377
- **INTERACTION GUIDELINES:**
378
- - Keep responses focused and to-the-point
379
- - Identify the one or two most important issues to address
380
- - Provide simple, clear action steps
381
- - Avoid overwhelming with too many options or considerations
382
- - Help them distinguish between essential and non-essential elements
383
- - Focus on what matters most for their immediate progress
384
- - Use simple language and clear structure
385
- - Eliminate distractions and maintain focus on core objectives
386
- - Value depth over breadth in guidance
387
-
388
- **Formatting (Compact Markdown v1):**
389
- - Use GitHub-Flavored Markdown.
390
- - Output exactly three sections in this order:
391
- - `### Thought` — one sentence.
392
- - `### What to do` — exactly 3 bullets, one line each.
393
- - `### Next step` — one imperative sentence.
394
- - Use `###` for headings, `-` for bullets (no unicode bullets), keep number text on the same line (e.g., `1. Do X`).
395
- - Insert one blank line between blocks.
396
- """,
397
- "default_temperature": 2
398
- },
399
- "visionary": {
400
- "name": "Visionary Strategist",
401
- "system_prompt": """You are an innovative PhD advisor and Visionary Strategist with expertise in emerging trends, future-oriented thinking, and transformative research directions. With a PhD in Futures Studies from University of Houston and experience in innovation strategy, you specialize in helping students explore cutting-edge ideas, anticipate future developments, and position their research for maximum impact.
402
-
403
- **YOUR EXPERTISE:**
404
- - Emerging trends analysis and future forecasting
405
- - Innovation strategy and disruptive thinking
406
- - Interdisciplinary connections and novel approaches
407
- - Technology integration and digital transformation
408
- - Global challenges and systemic solutions
409
- - Paradigm shifts and transformative research
410
- - Strategic positioning and impact maximization
411
-
412
- **YOUR RESPONSE STYLE:**
413
- - Think big picture and long-term implications
414
- - Encourage bold, ambitious thinking and risk-taking
415
- - Connect research to broader societal trends and needs
416
- - Explore unconventional approaches and novel perspectives
417
- - Challenge traditional boundaries and assumptions
418
- - Inspire vision beyond current limitations
419
- - Focus on potential for transformative impact
420
 
421
- **DOCUMENT HANDLING (when documents are available):**
422
- - Identify innovative potential and unique contributions in their work
423
- - Connect their research to emerging trends and future opportunities
424
- - Suggest ways to expand scope or increase transformative potential
425
- - Reference their work: "The innovative approach in [document_name] could evolve toward..."
426
- - Help them see broader implications and applications of their research
427
 
428
- **INTERACTION GUIDELINES:**
429
- - Encourage thinking beyond current paradigms and limitations
430
- - Help them envision the future impact of their research
431
- - Suggest innovative methodologies and approaches
432
- - Connect their work to global challenges and opportunities
433
- - Foster intellectual courage and willingness to take risks
434
- - Explore interdisciplinary connections and collaborations
435
- - Challenge them to think bigger and bolder
436
- - Balance visionary thinking with practical considerations
437
- - Inspire them to become thought leaders in their field
 
 
 
 
 
 
 
438
 
439
- **Formatting (Compact Markdown v1):**
440
- - Use GitHub-Flavored Markdown.
441
- - Output exactly three sections in this order:
442
- - `### Thought` — one sentence.
443
- - `### What to do` — exactly 3 bullets, one line each.
444
- - `### Next step` — one imperative sentence.
445
- - Use `###` for headings, `-` for bullets (no unicode bullets), keep number text on the same line (e.g., `1. Do X`).
446
- - Insert one blank line between blocks.
447
- """,
448
- "default_temperature": 9
449
- },
450
- "empathetic": {
451
- "name": "Empathetic Listener",
452
- "system_prompt": """You are a compassionate PhD advisor and Empathetic Listener with expertise in student well-being, emotional support, and holistic academic guidance. With a PhD in Clinical Psychology from Yale University and specialized training in academic counseling, you excel at understanding the emotional and psychological aspects of the doctoral journey.
453
 
454
- **YOUR EXPERTISE:**
455
- - Academic stress management and emotional well-being
456
- - Work-life balance and self-care strategies
457
- - Anxiety, depression, and mental health awareness
458
- - Interpersonal relationships and academic community
459
- - Identity development and personal growth
460
- - Trauma-informed approaches to academic mentoring
461
- - Mindfulness and stress reduction techniques
462
 
463
- **YOUR RESPONSE STYLE:**
464
- - Warm, compassionate, and genuinely caring tone
465
- - Validate emotions and acknowledge struggles
466
- - Listen carefully to both spoken and unspoken concerns
467
- - Provide emotional support alongside practical guidance
468
- - Use gentle, non-judgmental language
469
- - Focus on the whole person, not just academic progress
470
- - Encourage self-compassion and realistic expectations
471
 
472
- **DOCUMENT HANDLING (when documents are available):**
473
- - Recognize the emotional labor and effort reflected in their work
474
- - Acknowledge challenges and struggles evident in their research journey
475
- - Validate the personal significance of their academic contributions
476
- - Reference their work supportively: "I can see the dedication you've put into [document_name]..."
477
- - Consider how their research relates to their personal values and well-being
478
 
479
- **INTERACTION GUIDELINES:**
480
- - Always acknowledge the emotional aspects of their challenges
481
- - Normalize struggles and remind them they're not alone
482
- - Provide emotional validation before offering practical solutions
483
- - Check in on their overall well-being and self-care
484
- - Help them process difficult emotions and setbacks
485
- - Encourage healthy boundaries and sustainable practices
486
- - Address imposter syndrome and self-doubt with compassion
487
- - Celebrate personal growth alongside academic achievements
488
- - Foster a sense of community and belonging in academia
489
 
490
- **Formatting (Compact Markdown v1):**
491
- - Use GitHub-Flavored Markdown.
492
- - Output exactly three sections in this order:
493
- - `### Thought` — one sentence.
494
- - `### What to do` — exactly 3 bullets, one line each.
495
- - `### Next step` — one imperative sentence.
496
- - Use `###` for headings, `-` for bullets (no unicode bullets), keep number text on the same line (e.g., `1. Do X`).
497
- - Insert one blank line between blocks.
498
- """,
499
- "default_temperature": 6
500
- }
501
- }
502
 
503
- def get_default_personas(llm):
 
504
  return [
505
  Persona(
506
  id=pid,
507
  name=data["name"],
508
  system_prompt=data["system_prompt"],
509
  llm=llm,
510
- temperature=data.get("default_temperature", 5)
511
- ) for pid, data in DEFAULT_PERSONAS.items()
 
512
  ]
513
 
514
- def get_default_persona_prompt(persona_id):
515
- data = DEFAULT_PERSONAS.get(persona_id)
 
516
  return data["system_prompt"] if data else None
517
 
518
- def is_valid_persona_id(pid):
519
- return pid in DEFAULT_PERSONAS
520
 
521
- def list_available_personas():
522
- return list(DEFAULT_PERSONAS.keys())
 
 
 
 
 
1
+ """
2
+ Persona registry — now driven by ``config.yaml``.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
4
+ The heavy persona definitions have moved into ``config.yaml`` (under the
5
+ ``personas`` key). This module reads them via :func:`app.config.get_settings`
6
+ and exposes the same public API the rest of the codebase already relies on.
7
+ """
 
 
 
 
8
 
9
+ from typing import List, Optional
 
 
 
 
 
10
 
11
+ from app.config import get_settings
12
+ from app.models.persona import Persona
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
 
 
 
 
 
 
14
 
15
+ def _build_personas_dict() -> dict:
16
+ """Build the ``{id: {name, system_prompt, temperature}}`` registry from
17
+ the YAML configuration."""
18
+ cfg = get_settings()
19
+ base_prompt = cfg.personas.base_prompt.strip()
20
+ registry: dict = {}
21
+ for p in cfg.personas.items:
22
+ # Combine the persona-specific prompt with the shared base prompt
23
+ full_prompt = p.persona_prompt.strip()
24
+ if base_prompt:
25
+ full_prompt = f"{full_prompt}\n\n{base_prompt}"
26
+ registry[p.id] = {
27
+ "name": p.name,
28
+ "system_prompt": full_prompt,
29
+ "default_temperature": p.temperature,
30
+ }
31
+ return registry
32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
+ # Lazy singleton — built once on first access
35
+ _DEFAULT_PERSONAS: Optional[dict] = None
 
 
 
 
 
 
36
 
 
 
 
 
 
 
 
 
37
 
38
+ def _get_registry() -> dict:
39
+ global _DEFAULT_PERSONAS
40
+ if _DEFAULT_PERSONAS is None:
41
+ _DEFAULT_PERSONAS = _build_personas_dict()
42
+ return _DEFAULT_PERSONAS
 
43
 
 
 
 
 
 
 
 
 
 
 
44
 
45
+ # ------------------------------------------------------------------
46
+ # Public API — unchanged signatures so existing callers keep working
47
+ # ------------------------------------------------------------------
 
 
 
 
 
 
 
 
 
48
 
49
+ def get_default_personas(llm) -> List[Persona]:
50
+ """Return a list of :class:`Persona` objects wired to *llm*."""
51
  return [
52
  Persona(
53
  id=pid,
54
  name=data["name"],
55
  system_prompt=data["system_prompt"],
56
  llm=llm,
57
+ temperature=data.get("default_temperature", 5),
58
+ )
59
+ for pid, data in _get_registry().items()
60
  ]
61
 
62
+
63
+ def get_default_persona_prompt(persona_id: str) -> Optional[str]:
64
+ data = _get_registry().get(persona_id)
65
  return data["system_prompt"] if data else None
66
 
 
 
67
 
68
+ def is_valid_persona_id(pid: str) -> bool:
69
+ return pid in _get_registry()
70
+
71
+
72
+ def list_available_personas() -> List[str]:
73
+ return list(_get_registry().keys())
multi_llm_chatbot_backend/app/utils/chat_summary.py CHANGED
@@ -1,5 +1,6 @@
1
  from typing import List
2
  from app.llm.llm_client import LLMClient
 
3
  import logging
4
  import re
5
  from typing import List, Dict
@@ -11,10 +12,11 @@ async def generate_summary_from_messages(messages: List[dict], llm: LLMClient, m
11
  Summarize the conversation using the given LLM client.
12
  """
13
  try:
 
14
  full_text = "\n\n".join([f"{m['role']}:\n{m['content']}" for m in messages])
15
 
16
  system_prompt = (
17
- "You are an academic assistant. Summarize the following PhD chat conversation "
18
  "into a well-formatted summary with clear bullet points. "
19
  "Please format your response as follows:\n"
20
  "- Use bullet points (starting with *) for key insights\n"
 
1
  from typing import List
2
  from app.llm.llm_client import LLMClient
3
+ from app.config import get_settings
4
  import logging
5
  import re
6
  from typing import List, Dict
 
12
  Summarize the conversation using the given LLM client.
13
  """
14
  try:
15
+ app_title = get_settings().app.title
16
  full_text = "\n\n".join([f"{m['role']}:\n{m['content']}" for m in messages])
17
 
18
  system_prompt = (
19
+ f"You are an assistant for {app_title}. Summarize the following chat conversation "
20
  "into a well-formatted summary with clear bullet points. "
21
  "Please format your response as follows:\n"
22
  "- Use bullet points (starting with *) for key insights\n"
multi_llm_chatbot_backend/requirements.txt CHANGED
@@ -13,6 +13,7 @@ python-docx
13
 
14
  # Environment configuration
15
  python-dotenv
 
16
 
17
  # Vector database and embeddings
18
  chromadb
 
13
 
14
  # Environment configuration
15
  python-dotenv
16
+ pyyaml~=6.0
17
 
18
  # Vector database and embeddings
19
  chromadb
phd-advisor-frontend/src/App.js CHANGED
@@ -1,5 +1,6 @@
1
  import React, { useState, useEffect } from 'react';
2
  import { ThemeProvider } from './contexts/ThemeContext';
 
3
  import HomePage from './pages/HomePage';
4
  import ChatPage from './pages/ChatPage';
5
  import AuthPage from './pages/AuthPage';
@@ -72,34 +73,36 @@ function App() {
72
  };
73
 
74
  return (
75
- <ThemeProvider>
76
- <div className="App">
77
- {currentView === 'home' && (
78
- <HomePage onNavigateToChat={navigateToAuth} />
79
- )}
80
- {currentView === 'auth' && (
81
- <AuthPage onAuthSuccess={handleAuthSuccess} />
82
- )}
83
- {currentView === 'canvas' && isAuthenticated && (
84
- <CanvasPage
85
- user={user}
86
- authToken={authToken}
87
- onNavigateToChat={navigateToChat}
88
- onSignOut={handleSignOut}
89
- />
90
- )}
91
- {currentView === 'chat' && isAuthenticated && (
92
- <ChatPage
93
- user={user}
94
- authToken={authToken}
95
- onNavigateToHome={navigateToHome}
96
- onNavigateToCanvas={navigateToCanvas}
97
- onSignOut={handleSignOut}
98
- />
99
- )}
100
- </div>
101
- </ThemeProvider>
 
 
102
  );
103
  }
104
 
105
- export default App;
 
1
  import React, { useState, useEffect } from 'react';
2
  import { ThemeProvider } from './contexts/ThemeContext';
3
+ import { AppConfigProvider } from './contexts/AppConfigContext';
4
  import HomePage from './pages/HomePage';
5
  import ChatPage from './pages/ChatPage';
6
  import AuthPage from './pages/AuthPage';
 
73
  };
74
 
75
  return (
76
+ <AppConfigProvider>
77
+ <ThemeProvider>
78
+ <div className="App">
79
+ {currentView === 'home' && (
80
+ <HomePage onNavigateToChat={navigateToAuth} />
81
+ )}
82
+ {currentView === 'auth' && (
83
+ <AuthPage onAuthSuccess={handleAuthSuccess} />
84
+ )}
85
+ {currentView === 'canvas' && isAuthenticated && (
86
+ <CanvasPage
87
+ user={user}
88
+ authToken={authToken}
89
+ onNavigateToChat={navigateToChat}
90
+ onSignOut={handleSignOut}
91
+ />
92
+ )}
93
+ {currentView === 'chat' && isAuthenticated && (
94
+ <ChatPage
95
+ user={user}
96
+ authToken={authToken}
97
+ onNavigateToHome={navigateToHome}
98
+ onNavigateToCanvas={navigateToCanvas}
99
+ onSignOut={handleSignOut}
100
+ />
101
+ )}
102
+ </div>
103
+ </ThemeProvider>
104
+ </AppConfigProvider>
105
  );
106
  }
107
 
108
+ export default App;
phd-advisor-frontend/src/components/AdvisorCard.js CHANGED
@@ -1,10 +1,11 @@
1
  import React from 'react';
2
- import { getAdvisorColors } from '../data/advisors';
3
  import { useTheme } from '../contexts/ThemeContext';
4
 
5
  const AdvisorCard = ({ advisor, advisorId }) => {
6
  const Icon = advisor.icon;
7
  const { isDark } = useTheme();
 
8
  const colors = getAdvisorColors(advisorId, isDark);
9
 
10
  return (
@@ -27,4 +28,4 @@ const AdvisorCard = ({ advisor, advisorId }) => {
27
  );
28
  };
29
 
30
- export default AdvisorCard;
 
1
  import React from 'react';
2
+ import { useAppConfig } from '../contexts/AppConfigContext';
3
  import { useTheme } from '../contexts/ThemeContext';
4
 
5
  const AdvisorCard = ({ advisor, advisorId }) => {
6
  const Icon = advisor.icon;
7
  const { isDark } = useTheme();
8
+ const { getAdvisorColors } = useAppConfig();
9
  const colors = getAdvisorColors(advisorId, isDark);
10
 
11
  return (
 
28
  );
29
  };
30
 
31
+ export default AdvisorCard;
phd-advisor-frontend/src/components/ChatInput.js CHANGED
@@ -1,7 +1,7 @@
1
  import React, { useState } from 'react';
2
  import { Send } from 'lucide-react';
3
 
4
- const ChatInput = ({ onSendMessage, isLoading, placeholder = "Ask your advisors anything about your PhD journey..." }) => {
5
  const [inputMessage, setInputMessage] = useState('');
6
 
7
  const handleSend = () => {
 
1
  import React, { useState } from 'react';
2
  import { Send } from 'lucide-react';
3
 
4
+ const ChatInput = ({ onSendMessage, isLoading, placeholder = "Ask your advisors anything..." }) => {
5
  const [inputMessage, setInputMessage] = useState('');
6
 
7
  const handleSend = () => {
phd-advisor-frontend/src/components/EnhancedChatInput.js CHANGED
@@ -9,7 +9,7 @@ const EnhancedChatInput = ({
9
  isLoading,
10
  currentChatSessionId,
11
  authToken,
12
- placeholder = "Ask your advisors anything about your PhD journey..."
13
  }) => {
14
  const [inputMessage, setInputMessage] = useState('');
15
  const [showUpload, setShowUpload] = useState(false);
 
9
  isLoading,
10
  currentChatSessionId,
11
  authToken,
12
+ placeholder = "Ask your advisors anything..."
13
  }) => {
14
  const [inputMessage, setInputMessage] = useState('');
15
  const [showUpload, setShowUpload] = useState(false);
phd-advisor-frontend/src/components/Login.js CHANGED
@@ -1,8 +1,10 @@
1
  import React, { useState } from 'react';
2
  import { Eye, EyeOff, Mail, Lock, ArrowRight, BookOpen, Phone } from 'lucide-react';
 
3
  import '../styles/Login.css';
4
 
5
  const Login = ({ onNavigateToSignup, onNavigateToHome }) => {
 
6
  const [showPassword, setShowPassword] = useState(false);
7
  const [formData, setFormData] = useState({
8
  email: '',
@@ -103,7 +105,7 @@ const Login = ({ onNavigateToSignup, onNavigateToHome }) => {
103
  </div>
104
  <h1 className="login-title">Welcome Back</h1>
105
  <p className="login-subtitle">
106
- Sign in to continue your PhD research journey
107
  </p>
108
  </div>
109
 
@@ -251,4 +253,4 @@ const Login = ({ onNavigateToSignup, onNavigateToHome }) => {
251
  );
252
  };
253
 
254
- export default Login;
 
1
  import React, { useState } from 'react';
2
  import { Eye, EyeOff, Mail, Lock, ArrowRight, BookOpen, Phone } from 'lucide-react';
3
+ import { useAppConfig } from '../contexts/AppConfigContext';
4
  import '../styles/Login.css';
5
 
6
  const Login = ({ onNavigateToSignup, onNavigateToHome }) => {
7
+ const { config } = useAppConfig();
8
  const [showPassword, setShowPassword] = useState(false);
9
  const [formData, setFormData] = useState({
10
  email: '',
 
105
  </div>
106
  <h1 className="login-title">Welcome Back</h1>
107
  <p className="login-subtitle">
108
+ {config?.login?.subtitle || 'Sign in to continue'}
109
  </p>
110
  </div>
111
 
 
253
  );
254
  };
255
 
256
+ export default Login;
phd-advisor-frontend/src/components/MessageBubble.js CHANGED
@@ -2,7 +2,7 @@ import React, { useState, useRef, useEffect } from 'react';
2
  import ReactMarkdown from 'react-markdown';
3
  import remarkGfm from 'remark-gfm';
4
  import { Reply, Copy, Check, Maximize2, Info, FileText, Hash, Target } from 'lucide-react';
5
- import { advisors, getAdvisorColors } from '../data/advisors';
6
  import { useTheme } from '../contexts/ThemeContext';
7
 
8
  const MessageBubble = ({
@@ -13,6 +13,7 @@ const MessageBubble = ({
13
  showReplyButton = false
14
  }) => {
15
  const { isDark } = useTheme();
 
16
  const [showTooltip, setShowTooltip] = useState(null);
17
  const [copiedStates, setCopiedStates] = useState({});
18
  const [showInfoOverlay, setShowInfoOverlay] = useState(false);
 
2
  import ReactMarkdown from 'react-markdown';
3
  import remarkGfm from 'remark-gfm';
4
  import { Reply, Copy, Check, Maximize2, Info, FileText, Hash, Target } from 'lucide-react';
5
+ import { useAppConfig } from '../contexts/AppConfigContext';
6
  import { useTheme } from '../contexts/ThemeContext';
7
 
8
  const MessageBubble = ({
 
13
  showReplyButton = false
14
  }) => {
15
  const { isDark } = useTheme();
16
+ const { advisors, getAdvisorColors } = useAppConfig();
17
  const [showTooltip, setShowTooltip] = useState(null);
18
  const [copiedStates, setCopiedStates] = useState({});
19
  const [showInfoOverlay, setShowInfoOverlay] = useState(false);
phd-advisor-frontend/src/components/Sidebar.js CHANGED
@@ -13,6 +13,7 @@ import {
13
  ChevronRight,
14
  FileText
15
  } from 'lucide-react';
 
16
  import '../styles/Sidebar.css';
17
 
18
  const Sidebar = ({
@@ -27,6 +28,8 @@ const Sidebar = ({
27
  onMobileToggle,
28
  onNavigateToCanvas
29
  }) => {
 
 
30
  const [chatSessions, setChatSessions] = useState([]);
31
  const [searchTerm, setSearchTerm] = useState('');
32
  const [isLoading, setIsLoading] = useState(true);
@@ -227,10 +230,10 @@ const Sidebar = ({
227
  <button
228
  className="sidebar-canvas-btn"
229
  onClick={onNavigateToCanvas}
230
- title="PhD Canvas"
231
  >
232
  <FileText size={20} />
233
- {!isCollapsed && <span>PhD Canvas</span>}
234
  </button>
235
  </>
236
  )}
@@ -256,10 +259,10 @@ const Sidebar = ({
256
  <button
257
  className="sidebar-canvas-btn"
258
  onClick={onNavigateToCanvas}
259
- title="PhD Canvas"
260
  >
261
  <FileText size={20} />
262
- {!isCollapsed && <span>PhD Canvas</span>}
263
  </button>
264
  </div>
265
  )}
 
13
  ChevronRight,
14
  FileText
15
  } from 'lucide-react';
16
+ import { useAppConfig } from '../contexts/AppConfigContext';
17
  import '../styles/Sidebar.css';
18
 
19
  const Sidebar = ({
 
28
  onMobileToggle,
29
  onNavigateToCanvas
30
  }) => {
31
+ const { config } = useAppConfig();
32
+ const canvasLabel = config?.app?.title ? `${config.app.title} Canvas` : 'Canvas';
33
  const [chatSessions, setChatSessions] = useState([]);
34
  const [searchTerm, setSearchTerm] = useState('');
35
  const [isLoading, setIsLoading] = useState(true);
 
230
  <button
231
  className="sidebar-canvas-btn"
232
  onClick={onNavigateToCanvas}
233
+ title={canvasLabel}
234
  >
235
  <FileText size={20} />
236
+ {!isCollapsed && <span>{canvasLabel}</span>}
237
  </button>
238
  </>
239
  )}
 
259
  <button
260
  className="sidebar-canvas-btn"
261
  onClick={onNavigateToCanvas}
262
+ title={canvasLabel}
263
  >
264
  <FileText size={20} />
265
+ {!isCollapsed && <span>{canvasLabel}</span>}
266
  </button>
267
  </div>
268
  )}
phd-advisor-frontend/src/components/Signup.js CHANGED
@@ -1,8 +1,10 @@
1
  import React, { useState } from 'react';
2
  import { Eye, EyeOff, Mail, Lock, User, ArrowRight, BookOpen, Phone, GraduationCap } from 'lucide-react';
 
3
  import '../styles/Signup.css';
4
 
5
  const Signup = ({ onNavigateToLogin, onNavigateToHome }) => {
 
6
  const [showPassword, setShowPassword] = useState(false);
7
  const [showConfirmPassword, setShowConfirmPassword] = useState(false);
8
  const [formData, setFormData] = useState({
@@ -17,17 +19,14 @@ const Signup = ({ onNavigateToLogin, onNavigateToHome }) => {
17
  const [isLoading, setIsLoading] = useState(false);
18
  const [errors, setErrors] = useState({});
19
 
20
- const academicStages = [
21
- { value: '', label: 'Select your stage' },
22
- { value: 'prospective', label: 'Prospective PhD Student' },
23
- { value: 'first-year', label: 'First Year PhD' },
24
- { value: 'coursework', label: 'Coursework Phase' },
25
- { value: 'qualifying', label: 'Qualifying Exams' },
26
- { value: 'dissertation', label: 'Dissertation Phase' },
27
- { value: 'writing', label: 'Writing & Defense' },
28
- { value: 'postdoc', label: 'Postdoc' },
29
- { value: 'faculty', label: 'Faculty/Researcher' }
30
- ];
31
 
32
  const handleInputChange = (e) => {
33
  const { name, value } = e.target;
@@ -145,7 +144,7 @@ const Signup = ({ onNavigateToLogin, onNavigateToHome }) => {
145
  </div>
146
  <h1 className="signup-title">Join Our Community</h1>
147
  <p className="signup-subtitle">
148
- Create your account to get personalized PhD guidance from expert advisors
149
  </p>
150
  </div>
151
 
 
1
  import React, { useState } from 'react';
2
  import { Eye, EyeOff, Mail, Lock, User, ArrowRight, BookOpen, Phone, GraduationCap } from 'lucide-react';
3
+ import { useAppConfig } from '../contexts/AppConfigContext';
4
  import '../styles/Signup.css';
5
 
6
  const Signup = ({ onNavigateToLogin, onNavigateToHome }) => {
7
+ const { config } = useAppConfig();
8
  const [showPassword, setShowPassword] = useState(false);
9
  const [showConfirmPassword, setShowConfirmPassword] = useState(false);
10
  const [formData, setFormData] = useState({
 
19
  const [isLoading, setIsLoading] = useState(false);
20
  const [errors, setErrors] = useState({});
21
 
22
+ const academicStages = config?.login?.academic_stages?.length
23
+ ? config.login.academic_stages
24
+ : [
25
+ { value: '', label: 'Select your stage' },
26
+ { value: 'beginner', label: 'Beginner' },
27
+ { value: 'intermediate', label: 'Intermediate' },
28
+ { value: 'advanced', label: 'Advanced' },
29
+ ];
 
 
 
30
 
31
  const handleInputChange = (e) => {
32
  const { name, value } = e.target;
 
144
  </div>
145
  <h1 className="signup-title">Join Our Community</h1>
146
  <p className="signup-subtitle">
147
+ {config?.login?.signup_subtitle || 'Create your account to get personalized guidance from expert advisors'}
148
  </p>
149
  </div>
150
 
phd-advisor-frontend/src/components/SuggestionsPanel.js CHANGED
@@ -1,54 +1,11 @@
1
  // src/components/SuggestionsPanel.js
2
  import React from 'react';
3
- import { BookOpen, FlaskConical, PenTool, Heart } from 'lucide-react';
4
 
5
  const SuggestionsPanel = ({ onSuggestionClick }) => {
6
- const suggestionCategories = [
7
- {
8
- title: "Orientation & Guidance",
9
- icon: BookOpen,
10
- color: "#3B82F6",
11
- bgColor: "#EFF6FF",
12
- suggestions: [
13
- "How do I choose a research topic that's interesting and doable?",
14
- "Meeting and Presentation Prep",
15
- "What should I be doing my first semester?"
16
- ]
17
- },
18
- {
19
- title: "Research Design & Academic Skills",
20
- icon: FlaskConical,
21
- color: "#8B5CF6",
22
- bgColor: "#F3E8FF",
23
- suggestions: [
24
- "Should I use qualitative, quantitative, or mixed methods for my research?",
25
- "Is my research question too broad?",
26
- "How do I defend a non-traditional methodology to my committee?"
27
- ]
28
- },
29
- {
30
- title: "Writing & Communication",
31
- icon: PenTool,
32
- color: "#10B981",
33
- bgColor: "#ECFDF5",
34
- suggestions: [
35
- "What's the right tone for an introduction? Persuasive, cautious, or bold?",
36
- "How should I respond when reviewers give conflicting feedback?",
37
- "Should I prioritize journal articles or dissertation chapters when I write?"
38
- ]
39
- },
40
- {
41
- title: "Mental Health & Hidden Curriculum",
42
- icon: Heart,
43
- color: "#F59E0B",
44
- bgColor: "#FFFBEB",
45
- suggestions: [
46
- "How do I cope when I feel behind compared to others in my cohort?",
47
- "Should I speak up about unclear expectations or just try to figure it out quietly?",
48
- "What are the unspoken expectations no one tells you about?"
49
- ]
50
- }
51
- ];
52
 
53
  return (
54
  <div className="suggestions-panel">
@@ -60,39 +17,39 @@ const SuggestionsPanel = ({ onSuggestionClick }) => {
60
  </div>
61
 
62
  <div className="suggestions-grid">
63
- {suggestionCategories.map((category, categoryIndex) => {
64
- const Icon = category.icon;
65
  return (
66
  <div key={categoryIndex} className="suggestion-category">
67
  <div className="category-header">
68
  <div
69
  className="category-icon"
70
  style={{
71
- backgroundColor: category.bgColor,
72
- color: category.color
73
  }}
74
  >
75
  <Icon size={20} />
76
  </div>
77
  <h3
78
  className="category-title"
79
- style={{ color: category.color }}
80
  >
81
  {category.title}
82
  </h3>
83
  </div>
84
 
85
  <div className="suggestion-buttons">
86
- {category.suggestions.map((suggestion, suggestionIndex) => (
87
  <button
88
  key={suggestionIndex}
89
  onClick={() => onSuggestionClick(suggestion)}
90
  className="suggestion-button"
91
  style={{
92
- borderColor: category.color + '20',
93
- '--hover-bg': category.bgColor,
94
- '--hover-border': category.color,
95
- '--hover-text': category.color
96
  }}
97
  >
98
  {suggestion}
@@ -107,4 +64,4 @@ const SuggestionsPanel = ({ onSuggestionClick }) => {
107
  );
108
  };
109
 
110
- export default SuggestionsPanel;
 
1
  // src/components/SuggestionsPanel.js
2
  import React from 'react';
3
+ import { useAppConfig } from '../contexts/AppConfigContext';
4
 
5
  const SuggestionsPanel = ({ onSuggestionClick }) => {
6
+ const { config, resolveIcon } = useAppConfig();
7
+
8
+ const examples = config?.chat_page?.examples || [];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
  return (
11
  <div className="suggestions-panel">
 
17
  </div>
18
 
19
  <div className="suggestions-grid">
20
+ {examples.map((category, categoryIndex) => {
21
+ const Icon = resolveIcon(category.icon);
22
  return (
23
  <div key={categoryIndex} className="suggestion-category">
24
  <div className="category-header">
25
  <div
26
  className="category-icon"
27
  style={{
28
+ backgroundColor: category.bg_color || '#F3F4F6',
29
+ color: category.color || '#6B7280'
30
  }}
31
  >
32
  <Icon size={20} />
33
  </div>
34
  <h3
35
  className="category-title"
36
+ style={{ color: category.color || '#6B7280' }}
37
  >
38
  {category.title}
39
  </h3>
40
  </div>
41
 
42
  <div className="suggestion-buttons">
43
+ {(category.suggestions || []).map((suggestion, suggestionIndex) => (
44
  <button
45
  key={suggestionIndex}
46
  onClick={() => onSuggestionClick(suggestion)}
47
  className="suggestion-button"
48
  style={{
49
+ borderColor: (category.color || '#6B7280') + '20',
50
+ '--hover-bg': category.bg_color || '#F3F4F6',
51
+ '--hover-border': category.color || '#6B7280',
52
+ '--hover-text': category.color || '#6B7280'
53
  }}
54
  >
55
  {suggestion}
 
64
  );
65
  };
66
 
67
+ export default SuggestionsPanel;
phd-advisor-frontend/src/components/ThinkingIndicator.js CHANGED
@@ -1,20 +1,24 @@
1
  import React from 'react';
2
- import { advisors, getAdvisorColors } from '../data/advisors';
3
  import { useTheme } from '../contexts/ThemeContext';
4
 
5
  const ThinkingIndicator = ({ advisorId }) => {
 
6
  const advisor = advisors[advisorId];
7
- const Icon = advisor.icon;
8
  const { isDark } = useTheme();
9
  const colors = getAdvisorColors(advisorId, isDark);
10
 
 
 
 
 
11
  return (
12
  <div className="thinking-container">
13
  <div
14
  className="advisor-avatar"
15
  style={{ backgroundColor: colors.bgColor }}
16
  >
17
- <Icon style={{ color: colors.color }} />
18
  </div>
19
  <div
20
  className="thinking-bubble"
@@ -68,4 +72,4 @@ const ThinkingIndicator = ({ advisorId }) => {
68
  );
69
  };
70
 
71
- export default ThinkingIndicator;
 
1
  import React from 'react';
2
+ import { useAppConfig } from '../contexts/AppConfigContext';
3
  import { useTheme } from '../contexts/ThemeContext';
4
 
5
  const ThinkingIndicator = ({ advisorId }) => {
6
+ const { advisors, getAdvisorColors } = useAppConfig();
7
  const advisor = advisors[advisorId];
 
8
  const { isDark } = useTheme();
9
  const colors = getAdvisorColors(advisorId, isDark);
10
 
11
+ if (!advisor) return null;
12
+
13
+ const Icon = advisor.icon;
14
+
15
  return (
16
  <div className="thinking-container">
17
  <div
18
  className="advisor-avatar"
19
  style={{ backgroundColor: colors.bgColor }}
20
  >
21
+ {Icon ? <Icon style={{ color: colors.color }} /> : null}
22
  </div>
23
  <div
24
  className="thinking-bubble"
 
72
  );
73
  };
74
 
75
+ export default ThinkingIndicator;
phd-advisor-frontend/src/contexts/AppConfigContext.js ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { createContext, useContext, useState, useEffect } from 'react';
2
+ import * as LucideIcons from 'lucide-react';
3
+
4
+ const AppConfigContext = createContext(null);
5
+
6
+ /**
7
+ * Resolve a Lucide icon name string (e.g. "BookOpen") to the actual React
8
+ * component. Falls back to HelpCircle if the name isn't found.
9
+ */
10
+ const resolveIcon = (iconName) => {
11
+ if (!iconName) return LucideIcons.HelpCircle;
12
+ return LucideIcons[iconName] || LucideIcons.HelpCircle;
13
+ };
14
+
15
+ /**
16
+ * Build the advisors lookup object (keyed by persona id) from the config
17
+ * personas array, mirroring the shape that components already expect.
18
+ */
19
+ const buildAdvisors = (personaItems) => {
20
+ if (!personaItems || !Array.isArray(personaItems)) return {};
21
+ const advisors = {};
22
+ for (const p of personaItems) {
23
+ advisors[p.id] = {
24
+ name: p.name,
25
+ role: p.role || '',
26
+ description: p.summary || '',
27
+ color: p.color || '#6B7280',
28
+ bgColor: p.bg_color || '#F3F4F6',
29
+ darkColor: p.dark_color || '#9CA3AF',
30
+ darkBgColor: p.dark_bg_color || '#374151',
31
+ icon: resolveIcon(p.icon),
32
+ };
33
+ }
34
+ return advisors;
35
+ };
36
+
37
+ /**
38
+ * Derive theme-appropriate colors for a given advisor, identical to the
39
+ * previous `getAdvisorColors` helper.
40
+ */
41
+ const buildGetAdvisorColors = (advisors) => (advisorId, isDark = false) => {
42
+ const advisor = advisors[advisorId];
43
+ if (!advisor) return { color: '#6B7280', bgColor: '#F3F4F6' };
44
+ return {
45
+ color: isDark ? advisor.darkColor : advisor.color,
46
+ bgColor: isDark ? advisor.darkBgColor : advisor.bgColor,
47
+ textColor: isDark ? '#F9FAFB' : advisor.color,
48
+ };
49
+ };
50
+
51
+ export const useAppConfig = () => {
52
+ const ctx = useContext(AppConfigContext);
53
+ if (!ctx) {
54
+ throw new Error('useAppConfig must be used within an AppConfigProvider');
55
+ }
56
+ return ctx;
57
+ };
58
+
59
+ export const AppConfigProvider = ({ children }) => {
60
+ const [config, setConfig] = useState(null);
61
+ const [advisors, setAdvisors] = useState({});
62
+ const [loading, setLoading] = useState(true);
63
+ const [error, setError] = useState(null);
64
+
65
+ useEffect(() => {
66
+ const fetchConfig = async () => {
67
+ try {
68
+ const response = await fetch(
69
+ `${process.env.REACT_APP_API_URL}/api/config`
70
+ );
71
+ if (!response.ok) throw new Error(`Config fetch failed: ${response.status}`);
72
+ const data = await response.json();
73
+ setConfig(data);
74
+ const builtAdvisors = buildAdvisors(data.personas?.items);
75
+ setAdvisors(builtAdvisors);
76
+ } catch (err) {
77
+ console.error('Failed to load app config:', err);
78
+ setError(err.message);
79
+ } finally {
80
+ setLoading(false);
81
+ }
82
+ };
83
+ fetchConfig();
84
+ }, []);
85
+
86
+ // Inject the primary colour as a CSS custom property on <html> so it is
87
+ // available everywhere without prop-drilling.
88
+ useEffect(() => {
89
+ if (config?.app?.primary_color) {
90
+ document.documentElement.style.setProperty(
91
+ '--accent-primary',
92
+ config.app.primary_color
93
+ );
94
+ }
95
+ // Also update the <title> tag dynamically
96
+ if (config?.app?.title) {
97
+ document.title = config.app.title;
98
+ }
99
+ }, [config]);
100
+
101
+ const getAdvisorColors = buildGetAdvisorColors(advisors);
102
+
103
+ const value = {
104
+ config, // raw config object from /api/config
105
+ advisors, // { methodologist: { name, role, icon, color, ... }, ... }
106
+ getAdvisorColors,
107
+ resolveIcon,
108
+ loading,
109
+ error,
110
+ };
111
+
112
+ if (loading) {
113
+ return (
114
+ <div style={{
115
+ display: 'flex',
116
+ alignItems: 'center',
117
+ justifyContent: 'center',
118
+ height: '100vh',
119
+ fontFamily: 'system-ui, sans-serif',
120
+ color: '#6B7280',
121
+ }}>
122
+ Loading configuration…
123
+ </div>
124
+ );
125
+ }
126
+
127
+ if (error && !config) {
128
+ return (
129
+ <div style={{
130
+ display: 'flex',
131
+ flexDirection: 'column',
132
+ alignItems: 'center',
133
+ justifyContent: 'center',
134
+ height: '100vh',
135
+ fontFamily: 'system-ui, sans-serif',
136
+ color: '#EF4444',
137
+ gap: '8px',
138
+ }}>
139
+ <p>Failed to load application configuration.</p>
140
+ <p style={{ fontSize: '14px', color: '#6B7280' }}>{error}</p>
141
+ </div>
142
+ );
143
+ }
144
+
145
+ return (
146
+ <AppConfigContext.Provider value={value}>
147
+ {children}
148
+ </AppConfigContext.Provider>
149
+ );
150
+ };
151
+
152
+ export default AppConfigContext;
phd-advisor-frontend/src/data/advisors.js CHANGED
@@ -1,660 +1,26 @@
1
- import {
2
- BookOpen, Target, Brain, HelpCircle, Zap, Search,
3
- Feather, Minus, Eye, Heart
4
- } from 'lucide-react';
5
-
6
- export const advisors = {
7
- methodologist: {
8
- name: 'Methodologist',
9
- role: 'Research Methodology Expert',
10
- // Light theme colors
11
- color: '#3B82F6',
12
- bgColor: '#EFF6FF',
13
- // Dark theme colors
14
- darkColor: '#60A5FA',
15
- darkBgColor: '#1E3A8A',
16
- // Icon and description
17
- icon: BookOpen,
18
- description: 'Structured & Planning-focused',
19
-
20
- fullTitle: 'Methodologist - Research Methodology Expert',
21
- credentials: 'PhD in Research Methods and Statistics from Stanford University',
22
- experience: '15+ years of experience guiding doctoral students',
23
-
24
- specialties: [
25
- 'Quantitative & Qualitative Research Design',
26
- 'Statistical Analysis & Data Validation',
27
- 'Research Ethics & IRB Protocols',
28
- 'Mixed-Methods Approaches',
29
- 'Systematic Reviews & Meta-Analyses'
30
- ],
31
-
32
- expertise: {
33
- primary: 'Research Methodology',
34
- secondary: ['Statistical Analysis', 'Research Design', 'Data Collection'],
35
- documentTypes: ['methodology chapters', 'research proposals', 'data analysis plans'],
36
- strengths: [
37
- 'Methodological rigor assessment',
38
- 'Validity and reliability guidance',
39
- 'Statistical approach optimization',
40
- 'Research ethics consultation'
41
- ]
42
- },
43
-
44
- personality: {
45
- tone: 'Precise and analytical',
46
- approach: 'Systematic, evidence-based guidance',
47
- communicationStyle: 'Clear, structured, methodically reasoned',
48
- documentHandling: 'Analyzes methodological approaches and identifies design improvements'
49
- },
50
-
51
- sampleQuestions: [
52
- "What methodology does my research proposal suggest?",
53
- "How can I improve the validity of my study design?",
54
- "What sampling strategy would work best for my research?",
55
- "Are there any methodological gaps in my approach?"
56
- ],
57
-
58
- responseStyle: {
59
- length: 'Detailed with step-by-step guidance',
60
- structure: 'Organized with clear methodological reasoning',
61
- citations: 'References established research principles and frameworks'
62
- }
63
- },
64
-
65
- theorist: {
66
- name: 'Theorist',
67
- role: 'Theoretical Frameworks Specialist',
68
- // Light theme colors
69
- color: '#8B5CF6',
70
- bgColor: '#F3E8FF',
71
- // Dark theme colors
72
- darkColor: '#A78BFA',
73
- darkBgColor: '#581C87',
74
- // Icon and description
75
- icon: Brain,
76
- description: 'Abstract & Conceptual',
77
-
78
- fullTitle: 'Theorist - Theoretical Frameworks Specialist',
79
- credentials: 'PhD in Philosophy of Science from Oxford University',
80
- experience: 'Deep expertise in epistemology and conceptual development',
81
-
82
- specialties: [
83
- 'Epistemological & Ontological Foundations',
84
- 'Theoretical Framework Development',
85
- 'Literature Synthesis & Conceptual Mapping',
86
- 'Philosophy of Science',
87
- 'Theory Building & Model Development'
88
- ],
89
-
90
- expertise: {
91
- primary: 'Theoretical Frameworks',
92
- secondary: ['Conceptual Development', 'Literature Synthesis', 'Philosophical Foundations'],
93
- documentTypes: ['literature reviews', 'theoretical chapters', 'conceptual frameworks'],
94
- strengths: [
95
- 'Theoretical positioning guidance',
96
- 'Conceptual clarity development',
97
- 'Epistemological stance articulation',
98
- 'Literature synthesis strategy'
99
- ]
100
- },
101
-
102
- personality: {
103
- tone: 'Intellectually rigorous and philosophically deep',
104
- approach: 'Explores theoretical foundations and conceptual relationships',
105
- communicationStyle: 'Thoughtful, reflective, theoretically grounded',
106
- documentHandling: 'Analyzes theoretical positioning and identifies conceptual gaps'
107
- },
108
-
109
- sampleQuestions: [
110
- "What theoretical framework best fits my research?",
111
- "How do I position my work within existing literature?",
112
- "What are the philosophical assumptions in my approach?",
113
- "How can I strengthen my conceptual foundation?"
114
- ],
115
-
116
- responseStyle: {
117
- length: 'Comprehensive with deep theoretical exploration',
118
- structure: 'Builds from foundational concepts to specific applications',
119
- citations: 'References major theoretical traditions and philosophers'
120
- }
121
- },
122
-
123
- pragmatist: {
124
- name: 'Pragmatist',
125
- role: 'Action-Focused Research Coach',
126
- // Light theme colors
127
- color: '#10B981',
128
- bgColor: '#ECFDF5',
129
- // Dark theme colors
130
- darkColor: '#34D399',
131
- darkBgColor: '#065F46',
132
- // Icon and description
133
- icon: Target,
134
- description: 'Real-world & Outcome-focused',
135
-
136
- fullTitle: 'Pragmatist - Action-Focused Research Coach',
137
- credentials: 'PhD in Applied Psychology from UC Berkeley',
138
- experience: '12+ years of mentoring experience specializing in practical progress',
139
-
140
- specialties: [
141
- 'Project Management & Timeline Development',
142
- 'Research Implementation Strategies',
143
- 'Productivity & Workflow Optimization',
144
- 'Academic Career Development',
145
- 'Overcoming Research Roadblocks'
146
- ],
147
-
148
- expertise: {
149
- primary: 'Practical Implementation',
150
- secondary: ['Project Management', 'Productivity Systems', 'Career Development'],
151
- documentTypes: ['research timelines', 'progress reports', 'implementation plans'],
152
- strengths: [
153
- 'Task prioritization and planning',
154
- 'Productivity system design',
155
- 'Motivation and momentum building',
156
- 'Real-world constraint navigation'
157
- ]
158
- },
159
-
160
- personality: {
161
- tone: 'Warm, encouraging, and motivational',
162
- approach: 'Focuses on practical, immediately implementable advice',
163
- communicationStyle: 'Energetic, supportive, action-oriented',
164
- documentHandling: 'Transforms analysis into concrete next steps and timelines'
165
- },
166
-
167
- sampleQuestions: [
168
- "What should be my next immediate steps?",
169
- "How can I prioritize my research tasks?",
170
- "What's a realistic timeline for my project?",
171
- "How do I overcome analysis paralysis?"
172
- ],
173
-
174
- responseStyle: {
175
- length: 'Concise with clear action items',
176
- structure: 'Always ends with specific, actionable next steps',
177
- citations: 'References practical examples and success strategies'
178
- }
179
- },
180
-
181
- socratic: {
182
- name: 'Socratic Mentor',
183
- role: 'Critical Thinking Guide',
184
- // Light theme colors
185
- color: '#F59E0B',
186
- bgColor: '#FEF3C7',
187
- // Dark theme colors
188
- darkColor: '#FBBF24',
189
- darkBgColor: '#92400E',
190
- // Icon and description
191
- icon: HelpCircle,
192
- description: 'Question-driven & Discovery-focused',
193
-
194
- fullTitle: 'Socratic Mentor - Critical Thinking Guide',
195
- credentials: 'PhD in Philosophy from Harvard University',
196
- experience: '20+ years of experience in philosophical inquiry and critical thinking development',
197
-
198
- specialties: [
199
- 'Socratic Questioning Techniques',
200
- 'Critical Thinking Development',
201
- 'Philosophical Inquiry & Logic',
202
- 'Self-directed Learning Facilitation',
203
- 'Assumption Challenging',
204
- 'Perspective Broadening'
205
- ],
206
-
207
- expertise: {
208
- primary: 'Critical Thinking & Inquiry',
209
- secondary: ['Philosophical Methods', 'Logical Reasoning', 'Self-Discovery'],
210
- documentTypes: ['argument analyses', 'research justifications', 'theoretical discussions'],
211
- strengths: [
212
- 'Guiding self-discovery through questions',
213
- 'Challenging assumptions constructively',
214
- 'Developing critical thinking skills',
215
- 'Facilitating intellectual breakthroughs'
216
- ]
217
- },
218
-
219
- personality: {
220
- tone: 'Thoughtful, probing, and intellectually curious',
221
- approach: 'Guides discovery through systematic questioning',
222
- communicationStyle: 'Question-heavy, reflective, discovery-oriented',
223
- documentHandling: 'Questions assumptions and guides deeper inquiry into their reasoning'
224
- },
225
-
226
- sampleQuestions: [
227
- "What assumptions are underlying my research approach?",
228
- "How did I arrive at this particular research question?",
229
- "What would happen if I questioned this fundamental assumption?",
230
- "What evidence would challenge my current thinking?"
231
- ],
232
-
233
- responseStyle: {
234
- length: 'Focused on strategic questioning with minimal direct answers',
235
- structure: 'Series of probing questions building toward insight',
236
- citations: 'References philosophical traditions and questioning methodologies'
237
- }
238
- },
239
-
240
- motivator: {
241
- name: 'Motivational Coach',
242
- role: 'Academic Resilience Specialist',
243
- // Light theme colors
244
- color: '#EF4444',
245
- bgColor: '#FEF2F2',
246
- // Dark theme colors
247
- darkColor: '#F87171',
248
- darkBgColor: '#991B1B',
249
- // Icon and description
250
- icon: Zap,
251
- description: 'Energizing & Confidence-building',
252
-
253
- fullTitle: 'Motivational Coach - Academic Resilience Specialist',
254
- credentials: 'PhD in Educational Psychology from University of Pennsylvania',
255
- experience: 'Performance coaching certification and expertise in academic motivation',
256
-
257
- specialties: [
258
- 'Academic Motivation & Goal Setting',
259
- 'Resilience Building & Stress Management',
260
- 'Growth Mindset Development',
261
- 'Overcoming Imposter Syndrome',
262
- 'Performance Psychology',
263
- 'Sustainable Productivity Habits'
264
- ],
265
-
266
- expertise: {
267
- primary: 'Motivation & Resilience',
268
- secondary: ['Goal Setting', 'Stress Management', 'Performance Psychology'],
269
- documentTypes: ['progress reports', 'goal statements', 'reflection journals'],
270
- strengths: [
271
- 'Building academic confidence',
272
- 'Maintaining motivation through challenges',
273
- 'Developing resilience strategies',
274
- 'Creating sustainable work habits'
275
- ]
276
- },
277
-
278
- personality: {
279
- tone: 'Energetic, enthusiastic, and genuinely encouraging',
280
- approach: 'Focuses on strengths, progress, and potential',
281
- communicationStyle: 'Inspiring, supportive, achievement-oriented',
282
- documentHandling: 'Highlights progress and reframes challenges as growth opportunities'
283
- },
284
-
285
- sampleQuestions: [
286
- "How can I stay motivated during difficult research phases?",
287
- "What strategies help overcome academic imposter syndrome?",
288
- "How do I build resilience for the long PhD journey?",
289
- "What are my core strengths I can leverage more?"
290
- ],
291
-
292
- responseStyle: {
293
- length: 'Energetic and uplifting with concrete motivation strategies',
294
- structure: 'Acknowledges challenges then focuses on solutions and strengths',
295
- citations: 'References motivational psychology and success stories'
296
- }
297
- },
298
-
299
- critic: {
300
- name: 'Constructive Critic',
301
- role: 'Academic Quality Analyst',
302
- // Light theme colors
303
- color: '#DC2626',
304
- bgColor: '#FEF2F2',
305
- // Dark theme colors
306
- darkColor: '#F87171',
307
- darkBgColor: '#7F1D1D',
308
- // Icon and description
309
- icon: Search,
310
- description: 'Detail-oriented & Standards-focused',
311
-
312
- fullTitle: 'Constructive Critic - Academic Quality Analyst',
313
- credentials: 'PhD in Critical Studies from Cambridge University',
314
- experience: 'Journal editor and dissertation examiner with expertise in scholarly rigor',
315
-
316
- specialties: [
317
- 'Critical Analysis & Logic Assessment',
318
- 'Academic Writing Evaluation',
319
- 'Research Design Critique',
320
- 'Literature Review Quality Control',
321
- 'Scholarly Rigor Standards',
322
- 'Peer Review Excellence'
323
- ],
324
-
325
- expertise: {
326
- primary: 'Critical Analysis & Quality Assurance',
327
- secondary: ['Academic Standards', 'Logical Consistency', 'Evidence Evaluation'],
328
- documentTypes: ['draft chapters', 'research proposals', 'manuscript submissions'],
329
- strengths: [
330
- 'Identifying logical gaps and weaknesses',
331
- 'Ensuring methodological rigor',
332
- 'Improving argument coherence',
333
- 'Preparing work for peer review'
334
- ]
335
- },
336
-
337
- personality: {
338
- tone: 'Direct, honest, and constructively critical',
339
- approach: 'Systematic analysis with specific improvement recommendations',
340
- communicationStyle: 'Precise, detailed, standards-focused',
341
- documentHandling: 'Thoroughly analyzes work for gaps, inconsistencies, and improvement areas'
342
- },
343
-
344
- sampleQuestions: [
345
- "What are the weakest aspects of my argument?",
346
- "Where might reviewers find fault with my methodology?",
347
- "How can I strengthen the logic of my research design?",
348
- "What gaps exist in my literature review?"
349
- ],
350
-
351
- responseStyle: {
352
- length: 'Detailed with specific critiques and improvement suggestions',
353
- structure: 'Systematic analysis with balanced critique and constructive guidance',
354
- citations: 'References academic standards and best practices'
355
- }
356
- },
357
-
358
- storyteller: {
359
- name: 'Narrative Advisor',
360
- role: 'Communication & Storytelling Expert',
361
- // Light theme colors
362
- color: '#6366F1',
363
- bgColor: '#EEF2FF',
364
- // Dark theme colors
365
- darkColor: '#818CF8',
366
- darkBgColor: '#3730A3',
367
- // Icon and description
368
- icon: Feather,
369
- description: 'Creative & Communication-focused',
370
-
371
- fullTitle: 'Narrative Advisor - Communication & Storytelling Expert',
372
- credentials: 'PhD in Rhetoric and Composition from Northwestern University',
373
- experience: 'Science communication expertise and narrative-based knowledge translation',
374
-
375
- specialties: [
376
- 'Narrative Structure & Storytelling',
377
- 'Academic Communication & Translation',
378
- 'Metaphor & Analogy Development',
379
- 'Public Engagement & Accessibility',
380
- 'Creative Thinking & Perspective',
381
- 'Knowledge Synthesis Through Stories'
382
- ],
383
-
384
- expertise: {
385
- primary: 'Communication & Storytelling',
386
- secondary: ['Knowledge Translation', 'Creative Expression', 'Audience Engagement'],
387
- documentTypes: ['presentations', 'public-facing writing', 'research narratives'],
388
- strengths: [
389
- 'Making complex ideas accessible',
390
- 'Creating compelling research narratives',
391
- 'Developing effective analogies',
392
- 'Enhancing communication skills'
393
- ]
394
- },
395
-
396
- personality: {
397
- tone: 'Creative, engaging, and imaginatively insightful',
398
- approach: 'Uses stories, analogies, and narratives to illuminate concepts',
399
- communicationStyle: 'Vivid, memorable, narrative-driven',
400
- documentHandling: 'Identifies narrative threads and helps communicate research stories'
401
- },
402
-
403
- sampleQuestions: [
404
- "How can I tell the story of my research more compellingly?",
405
- "What analogies would help explain my complex methodology?",
406
- "How do I make my findings accessible to broader audiences?",
407
- "What's the narrative arc of my dissertation?"
408
- ],
409
-
410
- responseStyle: {
411
- length: 'Rich with stories, examples, and creative illustrations',
412
- structure: 'Narrative-driven with memorable analogies and examples',
413
- citations: 'References storytelling traditions and communication research'
414
- }
415
- },
416
-
417
- minimalist: {
418
- name: 'Minimalist Mentor',
419
- role: 'Essential Focus Advisor',
420
- // Light theme colors
421
- color: '#6B7280',
422
- bgColor: '#F9FAFB',
423
- // Dark theme colors
424
- darkColor: '#9CA3AF',
425
- darkBgColor: '#374151',
426
- // Icon and description
427
- icon: Minus,
428
- description: 'Concise & Priority-focused',
429
-
430
- fullTitle: 'Minimalist Mentor - Essential Focus Advisor',
431
- credentials: 'PhD in Cognitive Science from MIT',
432
- experience: 'Systems thinking background with expertise in efficient academic progress',
433
-
434
- specialties: [
435
- 'Essential Thinking & Priority Identification',
436
- 'Efficient Research Strategies',
437
- 'Core Concept Simplification',
438
- 'Decision-making Frameworks',
439
- 'Focused Attention & Deep Work',
440
- 'Academic Productivity Optimization'
441
- ],
442
-
443
- expertise: {
444
- primary: 'Essential Focus & Efficiency',
445
- secondary: ['Priority Setting', 'Simplification', 'Clear Decision-making'],
446
- documentTypes: ['focused plans', 'priority matrices', 'streamlined processes'],
447
- strengths: [
448
- 'Cutting through complexity to essentials',
449
- 'Identifying core priorities',
450
- 'Streamlining decision-making',
451
- 'Eliminating unnecessary elements'
452
- ]
453
- },
454
-
455
- personality: {
456
- tone: 'Concise, direct, and clarity-focused',
457
- approach: 'Strips away complexity to reveal essential elements',
458
- communicationStyle: 'Brief, structured, action-oriented',
459
- documentHandling: 'Identifies core contributions and essential elements requiring focus'
460
- },
461
-
462
- sampleQuestions: [
463
- "What's the one most important thing I should focus on?",
464
- "How can I simplify my research approach?",
465
- "What can I eliminate to improve my progress?",
466
- "What are the essential elements of my dissertation?"
467
- ],
468
-
469
- responseStyle: {
470
- length: 'Concise and to-the-point without unnecessary elaboration',
471
- structure: 'Simple frameworks with clear, actionable guidance',
472
- citations: 'References efficiency principles and focus methodologies'
473
- }
474
- },
475
-
476
- visionary: {
477
- name: 'Visionary Strategist',
478
- role: 'Innovation & Future Trends Expert',
479
- // Light theme colors
480
- color: '#06B6D4',
481
- bgColor: '#ECFEFF',
482
- // Dark theme colors
483
- darkColor: '#22D3EE',
484
- darkBgColor: '#0E7490',
485
- // Icon and description
486
- icon: Eye,
487
- description: 'Forward-thinking & Innovation-focused',
488
-
489
- fullTitle: 'Visionary Strategist - Innovation & Future Trends Expert',
490
- credentials: 'PhD in Futures Studies from University of Houston',
491
- experience: 'Innovation strategy experience with expertise in transformative research directions',
492
-
493
- specialties: [
494
- 'Emerging Trends Analysis & Forecasting',
495
- 'Innovation Strategy & Disruptive Thinking',
496
- 'Interdisciplinary Connections',
497
- 'Technology Integration & Digital Transformation',
498
- 'Global Challenges & Systemic Solutions',
499
- 'Transformative Research Positioning'
500
- ],
501
-
502
- expertise: {
503
- primary: 'Innovation & Future Strategy',
504
- secondary: ['Trend Analysis', 'Interdisciplinary Thinking', 'Impact Maximization'],
505
- documentTypes: ['innovation proposals', 'future scenarios', 'strategic plans'],
506
- strengths: [
507
- 'Identifying emerging opportunities',
508
- 'Connecting disparate fields',
509
- 'Anticipating future developments',
510
- 'Maximizing transformative potential'
511
- ]
512
- },
513
-
514
- personality: {
515
- tone: 'Inspiring, ambitious, and forward-looking',
516
- approach: 'Encourages bold thinking and explores future possibilities',
517
- communicationStyle: 'Visionary, expansive, possibility-oriented',
518
- documentHandling: 'Identifies innovative potential and connects to future trends'
519
- },
520
-
521
- sampleQuestions: [
522
- "How might my research transform the field in 10 years?",
523
- "What emerging trends should influence my research direction?",
524
- "How can I position my work for maximum future impact?",
525
- "What innovative approaches haven't been tried yet?"
526
- ],
527
-
528
- responseStyle: {
529
- length: 'Expansive with big-picture thinking and future scenarios',
530
- structure: 'Explores possibilities and connects to broader trends',
531
- citations: 'References innovation research and future studies'
532
- }
533
- },
534
-
535
- empathetic: {
536
- name: 'Empathetic Listener',
537
- role: 'Well-being & Support Specialist',
538
- // Light theme colors
539
- color: '#EC4899',
540
- bgColor: '#FDF2F8',
541
- // Dark theme colors
542
- darkColor: '#F472B6',
543
- darkBgColor: '#BE185D',
544
- // Icon and description
545
- icon: Heart,
546
- description: 'Caring & Emotionally supportive',
547
-
548
- fullTitle: 'Empathetic Listener - Well-being & Support Specialist',
549
- credentials: 'PhD in Clinical Psychology from Yale University',
550
- experience: 'Academic counseling specialization with expertise in student well-being',
551
-
552
- specialties: [
553
- 'Academic Stress Management & Well-being',
554
- 'Work-life Balance & Self-care',
555
- 'Mental Health Awareness & Support',
556
- 'Interpersonal Relationships in Academia',
557
- 'Identity Development & Personal Growth',
558
- 'Mindfulness & Stress Reduction'
559
- ],
560
-
561
- expertise: {
562
- primary: 'Emotional Support & Well-being',
563
- secondary: ['Stress Management', 'Self-care', 'Personal Development'],
564
- documentTypes: ['reflection journals', 'well-being plans', 'personal statements'],
565
- strengths: [
566
- 'Providing emotional validation',
567
- 'Supporting work-life balance',
568
- 'Addressing mental health concerns',
569
- 'Fostering personal growth'
570
- ]
571
- },
572
-
573
- personality: {
574
- tone: 'Warm, compassionate, and genuinely caring',
575
- approach: 'Validates emotions and provides holistic support',
576
- communicationStyle: 'Gentle, understanding, person-centered',
577
- documentHandling: 'Recognizes emotional labor and provides supportive validation'
578
- },
579
-
580
- sampleQuestions: [
581
- "How can I manage PhD stress and maintain well-being?",
582
- "How do I deal with feelings of isolation in research?",
583
- "What strategies help with academic anxiety?",
584
- "How can I maintain healthy boundaries during my PhD?"
585
- ],
586
-
587
- responseStyle: {
588
- length: 'Supportive and validating with emphasis on emotional well-being',
589
- structure: 'Acknowledges emotions first, then provides gentle guidance',
590
- citations: 'References well-being research and self-care practices'
591
- }
592
- }
593
- };
594
-
595
- // Helper function to get theme-appropriate colors (preserved exactly)
596
- export const getAdvisorColors = (advisorId, isDark = false) => {
597
- const advisor = advisors[advisorId];
598
- if (!advisor) return { color: '#6B7280', bgColor: '#F3F4F6' };
599
-
600
- return {
601
- color: isDark ? advisor.darkColor : advisor.color,
602
- bgColor: isDark ? advisor.darkBgColor : advisor.bgColor,
603
- // For text that needs to be readable on colored backgrounds
604
- textColor: isDark ? '#F9FAFB' :
605
- advisor.color === '#10B981' ? '#047857' :
606
- advisor.color === '#3B82F6' ? '#1D4ED8' :
607
- advisor.color === '#8B5CF6' ? '#7C3AED' :
608
- advisor.color === '#F59E0B' ? '#92400E' :
609
- advisor.color === '#EF4444' ? '#DC2626' :
610
- advisor.color === '#DC2626' ? '#7F1D1D' :
611
- advisor.color === '#6366F1' ? '#3730A3' :
612
- advisor.color === '#6B7280' ? '#374151' :
613
- advisor.color === '#06B6D4' ? '#0E7490' :
614
- advisor.color === '#EC4899' ? '#BE185D' :
615
- '#374151' // fallback
616
- };
617
- };
618
-
619
- // Additional helper functions for enhanced functionality
620
- export const getAdvisorById = (id) => {
621
- return advisors[id];
622
- };
623
-
624
- export const getAdvisorSpecialties = (id) => {
625
- const advisor = getAdvisorById(id);
626
- return advisor ? advisor.specialties : [];
627
- };
628
-
629
- export const getAdvisorExpertise = (id) => {
630
- const advisor = getAdvisorById(id);
631
- return advisor ? advisor.expertise : {};
632
- };
633
-
634
- export const getDocumentHandlingInfo = (id) => {
635
- const advisor = getAdvisorById(id);
636
- return advisor?.personality?.documentHandling || 'Provides general guidance based on uploaded documents';
637
- };
638
-
639
- export const getSampleQuestionsForDocuments = (id) => {
640
- const advisor = getAdvisorById(id);
641
- return advisor?.sampleQuestions || [];
642
- };
643
-
644
- export const getAdvisorDocumentTypes = (id) => {
645
- const advisor = getAdvisorById(id);
646
- return advisor?.expertise?.documentTypes || [];
647
- };
648
-
649
- // Backward compatibility: get advisor list as array (if needed elsewhere)
650
- export const getAdvisorsList = () => {
651
- return Object.entries(advisors).map(([id, advisor]) => ({
652
- id,
653
- ...advisor
654
- }));
655
- };
656
-
657
- // Get advisor IDs for iteration
658
- export const getAdvisorIds = () => {
659
- return Object.keys(advisors);
660
- };
 
1
+ /**
2
+ * DEPRECATED advisor data is now served dynamically by the backend
3
+ * via GET /api/config and consumed through AppConfigContext.
4
+ *
5
+ * Components should use:
6
+ * const { advisors, getAdvisorColors, resolveIcon } = useAppConfig();
7
+ *
8
+ * This file is kept only for reference / backward-compatibility of any
9
+ * third-party code that may still import it. The static data below is
10
+ * a snapshot of the PhD Advisory Panel defaults and will NOT be updated
11
+ * when config.yaml changes.
12
+ */
13
+
14
+ // If you need advisor data in a non-React context, fetch /api/config
15
+ // instead of importing from this file.
16
+
17
+ export const advisors = {};
18
+ export const getAdvisorColors = () => ({ color: '#6B7280', bgColor: '#F3F4F6', textColor: '#374151' });
19
+ export const getAdvisorById = () => undefined;
20
+ export const getAdvisorSpecialties = () => [];
21
+ export const getAdvisorExpertise = () => ({});
22
+ export const getDocumentHandlingInfo = () => 'Provides general guidance based on uploaded documents';
23
+ export const getSampleQuestionsForDocuments = () => [];
24
+ export const getAdvisorDocumentTypes = () => [];
25
+ export const getAdvisorsList = () => [];
26
+ export const getAdvisorIds = () => [];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
phd-advisor-frontend/src/pages/CanvasPage.js CHANGED
@@ -15,6 +15,7 @@ import {
15
  ArrowLeft,
16
  Printer
17
  } from 'lucide-react';
 
18
  import '../styles/CanvasPage.css';
19
 
20
  // Section icons mapping
@@ -86,6 +87,8 @@ const CanvasSection = ({ section, sectionKey, isExpanded, onToggle }) => {
86
  };
87
 
88
  const CanvasPage = ({ user, authToken, onNavigateToChat, onSignOut }) => {
 
 
89
  const [canvasData, setCanvasData] = useState(null);
90
  const [isLoading, setIsLoading] = useState(true);
91
  const [isUpdating, setIsUpdating] = useState(false);
@@ -361,7 +364,7 @@ const CanvasPage = ({ user, authToken, onNavigateToChat, onSignOut }) => {
361
  return (
362
  <div className="canvas-loading">
363
  <div className="loading-spinner"></div>
364
- <p>Loading your PhD Canvas...</p>
365
  </div>
366
  );
367
  }
@@ -392,7 +395,7 @@ const CanvasPage = ({ user, authToken, onNavigateToChat, onSignOut }) => {
392
  <div className="canvas-title-section">
393
  <h1 className="canvas-title">
394
  <FileText className="canvas-title-icon" />
395
- PhD Journey Canvas
396
  </h1>
397
  <p className="canvas-subtitle">Your research progress at a glance</p>
398
  </div>
@@ -463,7 +466,7 @@ const CanvasPage = ({ user, authToken, onNavigateToChat, onSignOut }) => {
463
  </div>
464
  ) : (
465
  <div>
466
- <p>Start chatting with your AI advisors to populate your PhD Canvas with insights!</p>
467
  <div className="empty-canvas-actions">
468
  <button
469
  className="start-chatting-button"
@@ -500,7 +503,7 @@ const CanvasPage = ({ user, authToken, onNavigateToChat, onSignOut }) => {
500
  {/* Print Footer */}
501
  {isPrintView && (
502
  <div className="print-footer">
503
- <p>Generated by PhD Advisory Panel - {new Date().toLocaleDateString()}</p>
504
  <p>Student: {user?.email} | Total Insights: {canvasData?.total_insights || 0}</p>
505
  </div>
506
  )}
 
15
  ArrowLeft,
16
  Printer
17
  } from 'lucide-react';
18
+ import { useAppConfig } from '../contexts/AppConfigContext';
19
  import '../styles/CanvasPage.css';
20
 
21
  // Section icons mapping
 
87
  };
88
 
89
  const CanvasPage = ({ user, authToken, onNavigateToChat, onSignOut }) => {
90
+ const { config } = useAppConfig();
91
+ const appName = config?.app_settings?.app_name || 'Advisory Panel';
92
  const [canvasData, setCanvasData] = useState(null);
93
  const [isLoading, setIsLoading] = useState(true);
94
  const [isUpdating, setIsUpdating] = useState(false);
 
364
  return (
365
  <div className="canvas-loading">
366
  <div className="loading-spinner"></div>
367
+ <p>Loading your {appName} Canvas...</p>
368
  </div>
369
  );
370
  }
 
395
  <div className="canvas-title-section">
396
  <h1 className="canvas-title">
397
  <FileText className="canvas-title-icon" />
398
+ {appName} Canvas
399
  </h1>
400
  <p className="canvas-subtitle">Your research progress at a glance</p>
401
  </div>
 
466
  </div>
467
  ) : (
468
  <div>
469
+ <p>Start chatting with your AI advisors to populate your {appName} Canvas with insights!</p>
470
  <div className="empty-canvas-actions">
471
  <button
472
  className="start-chatting-button"
 
503
  {/* Print Footer */}
504
  {isPrintView && (
505
  <div className="print-footer">
506
+ <p>Generated by {appName} - {new Date().toLocaleDateString()}</p>
507
  <p>Student: {user?.email} | Total Insights: {canvasData?.total_insights || 0}</p>
508
  </div>
509
  )}
phd-advisor-frontend/src/pages/ChatPage.js CHANGED
@@ -8,13 +8,14 @@ import ThemeToggle from '../components/ThemeToggle';
8
  import ProviderDropdown from '../components/ProviderDropdown';
9
  import ExportButton from '../components/ExportButton';
10
  import Sidebar from '../components/Sidebar';
11
- import { advisors, getAdvisorColors } from '../data/advisors';
12
  import { useTheme } from '../contexts/ThemeContext';
13
  import '../styles/ChatPage.css';
14
  import '../styles/EnhancedChatInput.css';
15
  import AdvisorStatusDropdown from '../components/AdvisorStatusDropdown';
16
 
17
  const ChatPage = ({ user, authToken, onNavigateToHome, onNavigateToCanvas, onSignOut }) => {
 
18
  const [messages, setMessages] = useState([]);
19
  const [isLoading, setIsLoading] = useState(false);
20
  const [thinkingAdvisors, setThinkingAdvisors] = useState([]);
@@ -643,7 +644,7 @@ const handleNewChat = async (sessionId = null) => {
643
  setReplyingTo({
644
  advisorId: message.persona_id,
645
  messageId: message.id,
646
- advisorName: advisor.name,
647
  persona_id: message.persona_id
648
  });
649
  };
@@ -654,7 +655,7 @@ const handleNewChat = async (sessionId = null) => {
654
  setReplyingTo({
655
  advisorId: message.persona_id,
656
  messageId: message.id,
657
- advisorName: advisor.name,
658
  persona_id: message.persona_id
659
  });
660
  }
@@ -683,6 +684,8 @@ const handleNewChat = async (sessionId = null) => {
683
  const hasMessages = messages.length > 0;
684
  const hasConversationMessages = messages.filter(m => m.type !== 'system' && m.type !== 'document_upload').length > 0;
685
 
 
 
686
  return (
687
  <div className="chat-page-with-sidebar">
688
  {/* Sidebar Component */}
@@ -718,8 +721,8 @@ const handleNewChat = async (sessionId = null) => {
718
  <Users size={24} />
719
  </div>
720
  <div className="brand-text">
721
- <h1>PhD Advisory</h1>
722
- <p>AI-Powered Academic Guidance</p>
723
  </div>
724
  </div>
725
  </div>
@@ -921,7 +924,7 @@ const handleNewChat = async (sessionId = null) => {
921
  placeholder={
922
  replyingTo
923
  ? `Reply to ${replyingTo.advisorName}...`
924
- : "Ask your advisors anything about your PhD journey..."
925
  }
926
  />
927
  </div>
@@ -931,4 +934,4 @@ const handleNewChat = async (sessionId = null) => {
931
  );
932
  };
933
 
934
- export default ChatPage;
 
8
  import ProviderDropdown from '../components/ProviderDropdown';
9
  import ExportButton from '../components/ExportButton';
10
  import Sidebar from '../components/Sidebar';
11
+ import { useAppConfig } from '../contexts/AppConfigContext';
12
  import { useTheme } from '../contexts/ThemeContext';
13
  import '../styles/ChatPage.css';
14
  import '../styles/EnhancedChatInput.css';
15
  import AdvisorStatusDropdown from '../components/AdvisorStatusDropdown';
16
 
17
  const ChatPage = ({ user, authToken, onNavigateToHome, onNavigateToCanvas, onSignOut }) => {
18
+ const { config, advisors, getAdvisorColors } = useAppConfig();
19
  const [messages, setMessages] = useState([]);
20
  const [isLoading, setIsLoading] = useState(false);
21
  const [thinkingAdvisors, setThinkingAdvisors] = useState([]);
 
644
  setReplyingTo({
645
  advisorId: message.persona_id,
646
  messageId: message.id,
647
+ advisorName: advisor?.name || message.advisorName || 'Advisor',
648
  persona_id: message.persona_id
649
  });
650
  };
 
655
  setReplyingTo({
656
  advisorId: message.persona_id,
657
  messageId: message.id,
658
+ advisorName: advisor?.name || message.advisorName || 'Advisor',
659
  persona_id: message.persona_id
660
  });
661
  }
 
684
  const hasMessages = messages.length > 0;
685
  const hasConversationMessages = messages.filter(m => m.type !== 'system' && m.type !== 'document_upload').length > 0;
686
 
687
+ const chatPlaceholder = config?.chat_page?.placeholder || "Ask your advisors anything...";
688
+
689
  return (
690
  <div className="chat-page-with-sidebar">
691
  {/* Sidebar Component */}
 
721
  <Users size={24} />
722
  </div>
723
  <div className="brand-text">
724
+ <h1>{config?.app?.title || 'Advisory'}</h1>
725
+ <p>{config?.app?.subtitle || 'AI-Powered Guidance'}</p>
726
  </div>
727
  </div>
728
  </div>
 
924
  placeholder={
925
  replyingTo
926
  ? `Reply to ${replyingTo.advisorName}...`
927
+ : chatPlaceholder
928
  }
929
  />
930
  </div>
 
934
  );
935
  };
936
 
937
+ export default ChatPage;
phd-advisor-frontend/src/pages/HomePage.js CHANGED
@@ -1,10 +1,14 @@
1
  import React from 'react';
2
- import { MessageCircle, Users, Target, Brain, ArrowRight } from 'lucide-react';
3
  import AdvisorCard from '../components/AdvisorCard';
4
  import ThemeToggle from '../components/ThemeToggle';
5
- import { advisors } from '../data/advisors';
6
 
7
  const HomePage = ({ onNavigateToChat }) => {
 
 
 
 
8
  return (
9
  <div className="homepage">
10
  {/* Header */}
@@ -12,11 +16,11 @@ const HomePage = ({ onNavigateToChat }) => {
12
  <div className="header-content">
13
  <div className="header-left">
14
  <div className="logo-container">
15
- <Users className="logo-icon" />
16
  </div>
17
  <div>
18
- <h1 className="logo-title">PhD Advisory Panel</h1>
19
- <p className="logo-subtitle">Your AI-powered academic advisors</p>
20
  </div>
21
  </div>
22
  <div className="header-right">
@@ -29,11 +33,11 @@ const HomePage = ({ onNavigateToChat }) => {
29
  <main className="main">
30
  <div className="hero-section">
31
  <h2 className="hero-title">
32
- Get Guidance from <span className="hero-highlight">Advisor Personas</span>
 
33
  </h2>
34
  <p className="hero-subtitle">
35
- Receive diverse perspectives on your PhD journey from our specialized AI advisors,
36
- each bringing unique insights to help you succeed.
37
  </p>
38
  <button
39
  onClick={onNavigateToChat}
@@ -54,35 +58,22 @@ const HomePage = ({ onNavigateToChat }) => {
54
 
55
  {/* Features Section */}
56
  <div className="features-section">
57
- <h3 className="features-title">Why Choose Our Advisory Panel?</h3>
58
  <div className="features-grid">
59
- <div className="feature-card">
60
- <div className="feature-icon">
61
- <Users />
62
- </div>
63
- <h4 className="feature-title">Multiple Perspectives</h4>
64
- <p className="feature-description">
65
- Get varied viewpoints from different advisory styles
66
- </p>
67
- </div>
68
- <div className="feature-card">
69
- <div className="feature-icon">
70
- <Brain />
71
- </div>
72
- <h4 className="feature-title">AI-Powered Insights</h4>
73
- <p className="feature-description">
74
- Leverage advanced AI for comprehensive guidance
75
- </p>
76
- </div>
77
- <div className="feature-card">
78
- <div className="feature-icon">
79
- <Target />
80
- </div>
81
- <h4 className="feature-title">Focused Advice</h4>
82
- <p className="feature-description">
83
- Receive targeted recommendations for your specific needs
84
- </p>
85
- </div>
86
  </div>
87
  </div>
88
  </main>
@@ -90,7 +81,7 @@ const HomePage = ({ onNavigateToChat }) => {
90
  <footer className="footer">
91
  <div className="footer-content">
92
  <p className="footer-text">
93
- © 2025 University of Colorado Boulder. All rights reserved.
94
  </p>
95
  </div>
96
  </footer>
@@ -98,4 +89,4 @@ const HomePage = ({ onNavigateToChat }) => {
98
  );
99
  };
100
 
101
- export default HomePage;
 
1
  import React from 'react';
2
+ import { MessageCircle, ArrowRight } from 'lucide-react';
3
  import AdvisorCard from '../components/AdvisorCard';
4
  import ThemeToggle from '../components/ThemeToggle';
5
+ import { useAppConfig } from '../contexts/AppConfigContext';
6
 
7
  const HomePage = ({ onNavigateToChat }) => {
8
+ const { config, advisors, resolveIcon } = useAppConfig();
9
+
10
+ const UsersIcon = resolveIcon('Users');
11
+
12
  return (
13
  <div className="homepage">
14
  {/* Header */}
 
16
  <div className="header-content">
17
  <div className="header-left">
18
  <div className="logo-container">
19
+ <UsersIcon className="logo-icon" />
20
  </div>
21
  <div>
22
+ <h1 className="logo-title">{config.app.title}</h1>
23
+ <p className="logo-subtitle">{config.app.subtitle}</p>
24
  </div>
25
  </div>
26
  <div className="header-right">
 
33
  <main className="main">
34
  <div className="hero-section">
35
  <h2 className="hero-title">
36
+ {config.homepage.headline_prefix}{' '}
37
+ <span className="hero-highlight">{config.homepage.headline_highlight}</span>
38
  </h2>
39
  <p className="hero-subtitle">
40
+ {config.homepage.description}
 
41
  </p>
42
  <button
43
  onClick={onNavigateToChat}
 
58
 
59
  {/* Features Section */}
60
  <div className="features-section">
61
+ <h3 className="features-title">{config.homepage.features_title}</h3>
62
  <div className="features-grid">
63
+ {(config.homepage.features || []).map((feature, index) => {
64
+ const FeatureIcon = resolveIcon(feature.icon);
65
+ return (
66
+ <div key={index} className="feature-card">
67
+ <div className="feature-icon">
68
+ <FeatureIcon />
69
+ </div>
70
+ <h4 className="feature-title">{feature.title}</h4>
71
+ <p className="feature-description">
72
+ {feature.description}
73
+ </p>
74
+ </div>
75
+ );
76
+ })}
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  </div>
78
  </div>
79
  </main>
 
81
  <footer className="footer">
82
  <div className="footer-content">
83
  <p className="footer-text">
84
+ {config.app.footer_text}
85
  </p>
86
  </div>
87
  </footer>
 
89
  );
90
  };
91
 
92
+ export default HomePage;