17
17
</svg>
18
18
'''
19
19
20
+
20
21
def get_svg_base64 (svg_content ):
21
- b64 = base64 .b64encode (svg_content .encode ('utf-8' )).decode ('utf-8' )
22
- return f"data:image/svg+xml;base64,{ b64 } "
22
+ b64 = base64 .b64encode (svg_content .encode ('utf-8' )).decode ('utf-8' )
23
+ return f"data:image/svg+xml;base64,{ b64 } "
24
+
23
25
24
26
# Configure the page with your brand colors
25
27
st .set_page_config (
26
- page_title = "VetsAI: Vets Who Code Assistant" ,
27
- page_icon = get_svg_base64 (svg_content ),
28
- layout = "wide"
28
+ page_title = "VetsAI: Vets Who Code Assistant" ,
29
+ page_icon = get_svg_base64 (svg_content ),
30
+ layout = "wide"
29
31
)
30
32
31
33
# Define VWC brand colors and add custom CSS
@@ -114,12 +116,13 @@ def get_svg_base64(svg_content):
114
116
if not client .api_key : # Changed from openai.api_key to client.api_key
115
117
raise ValueError ("OpenAI API key not found in Streamlit secrets." )
116
118
119
+
117
120
def parse_mos_file (file_content : str ) -> dict :
118
121
"""Parse military job code text file content into a structured dictionary."""
119
122
lines = file_content .strip ().split ('\n ' )
120
123
job_code , title , description = "" , "" , []
121
124
parsing_description = False
122
-
125
+
123
126
for line in lines :
124
127
line = line .strip ()
125
128
if not line :
@@ -130,33 +133,35 @@ def parse_mos_file(file_content: str) -> dict:
130
133
parsing_description = True
131
134
elif parsing_description :
132
135
description .append (line )
133
-
136
+
134
137
for line in description :
135
138
if line :
136
139
title = line
137
140
break
138
-
141
+
139
142
full_text = ' ' .join (description ).lower ()
140
143
category = "general"
141
144
category_keywords = {
142
- "information_technology" : ["technology" , "computer" , "network" , "data" , "software" , "hardware" , "system" , "database" ],
145
+ "information_technology" : ["technology" , "computer" , "network" , "data" , "software" , "hardware" , "system" ,
146
+ "database" ],
143
147
"communications" : ["communications" , "signal" , "radio" , "transmission" , "telecom" ],
144
148
"intelligence" : ["intelligence" , "analysis" , "surveillance" , "reconnaissance" ],
145
149
"maintenance" : ["maintenance" , "repair" , "technical" , "equipment" ],
146
150
"cyber" : ["cyber" , "security" , "information assurance" , "cryptographic" ]
147
151
}
148
-
152
+
149
153
for cat , keywords in category_keywords .items ():
150
154
if any (keyword in full_text for keyword in keywords ):
151
155
category = cat
152
156
break
153
-
157
+
154
158
return {
155
159
"title" : title or "Military Professional" ,
156
160
"category" : category ,
157
161
"skills" : [line for line in description if line and len (line ) > 10 ]
158
162
}
159
163
164
+
160
165
def load_military_job_codes () -> dict :
161
166
base_path = "data/employment_transitions/job_codes"
162
167
job_codes = {}
@@ -167,7 +172,7 @@ def load_military_job_codes() -> dict:
167
172
"navy" : {"path" : "navy" , "prefix" : "RATE" },
168
173
"marine_corps" : {"path" : "marine_corps" , "prefix" : "MOS" }
169
174
}
170
-
175
+
171
176
for branch , info in branches .items ():
172
177
branch_path = os .path .join (base_path , info ["path" ])
173
178
if os .path .exists (branch_path ):
@@ -191,6 +196,7 @@ def load_military_job_codes() -> dict:
191
196
continue
192
197
return job_codes
193
198
199
+
194
200
def map_to_vwc_path (category : str , skills : List [str ]) -> dict :
195
201
"""Map military job categories and skills to VWC tech stack paths."""
196
202
default_path = {
@@ -201,7 +207,7 @@ def map_to_vwc_path(category: str, skills: List[str]) -> dict:
201
207
"Python with FastAPI/Django for backend"
202
208
]
203
209
}
204
-
210
+
205
211
tech_paths = {
206
212
"information_technology" : {
207
213
"path" : "Full Stack Development" ,
@@ -244,36 +250,37 @@ def map_to_vwc_path(category: str, skills: List[str]) -> dict:
244
250
]
245
251
}
246
252
}
247
-
253
+
248
254
skill_keywords = {
249
255
"programming" : "software" ,
250
256
"database" : "data" ,
251
257
"network" : "communications" ,
252
258
"security" : "cyber" ,
253
259
"analysis" : "intelligence"
254
260
}
255
-
261
+
256
262
if category .lower () in tech_paths :
257
263
return tech_paths [category .lower ()]
258
-
264
+
259
265
for skill in skills :
260
266
skill_lower = skill .lower ()
261
267
for keyword , category in skill_keywords .items ():
262
268
if keyword in skill_lower and category in tech_paths :
263
269
return tech_paths [category ]
264
-
270
+
265
271
return default_path
266
272
273
+
267
274
def translate_military_code (code : str , job_codes : dict ) -> dict :
268
275
"""Translate military code to VWC development path."""
269
276
code = code .upper ().strip ()
270
277
prefixes = ["MOS" , "AFSC" , "RATE" ]
271
278
for prefix in prefixes :
272
279
if code .startswith (prefix ):
273
280
code = code .replace (prefix , "" ).strip ()
274
-
281
+
275
282
possible_codes = [f"MOS_{ code } " , f"AFSC_{ code } " , f"RATE_{ code } " ]
276
-
283
+
277
284
for possible_code in possible_codes :
278
285
if possible_code in job_codes :
279
286
job_data = job_codes [possible_code ]
@@ -287,7 +294,7 @@ def translate_military_code(code: str, job_codes: dict) -> dict:
287
294
"skills" : job_data .get ('skills' , [])
288
295
}
289
296
}
290
-
297
+
291
298
return {
292
299
"found" : False ,
293
300
"data" : {
@@ -307,6 +314,7 @@ def translate_military_code(code: str, job_codes: dict) -> dict:
307
314
}
308
315
}
309
316
317
+
310
318
def get_chat_response (messages : List [Dict ]) -> str :
311
319
"""Get response from OpenAI chat completion."""
312
320
try :
@@ -320,6 +328,7 @@ def get_chat_response(messages: List[Dict]) -> str:
320
328
logger .error (f"OpenAI API error: { e } " )
321
329
raise
322
330
331
+
323
332
def export_chat_history (chat_history : List [Dict ]) -> str :
324
333
"""Export chat history to JSON."""
325
334
export_data = {
@@ -328,6 +337,7 @@ def export_chat_history(chat_history: List[Dict]) -> str:
328
337
}
329
338
return json .dumps (export_data , indent = 2 )
330
339
340
+
331
341
def save_feedback (feedback : Dict ):
332
342
"""Save user feedback to file."""
333
343
feedback_dir = "feedback"
@@ -339,37 +349,39 @@ def save_feedback(feedback: Dict):
339
349
with open (feedback_file , 'w' ) as f :
340
350
json .dump (feedback , f , indent = 2 )
341
351
352
+
342
353
def handle_command (command : str ) -> str :
343
354
"""Handle special commands including MOS translation."""
344
355
parts = command .lower ().split ()
345
356
if not parts :
346
357
return None
347
-
358
+
348
359
cmd = parts [0 ]
349
360
if cmd in ['/mos' , '/afsc' , '/rate' ]:
350
361
if len (parts ) < 2 :
351
362
return "Please provide a military job code. Example: `/mos 25B`"
352
-
363
+
353
364
code = parts [1 ]
354
365
translation = translate_military_code (code , st .session_state .job_codes )
355
366
if translation ['found' ]:
356
367
return (
357
- f"🎖️ **{ translation ['data' ]['title' ]} ** ({ translation ['data' ]['branch' ]} )\n \n "
358
- f"💻 **VWC Development Path**: { translation ['data' ]['dev_path' ]} \n \n "
359
- "🔧 **Military Skills**:\n " +
360
- "\n " .join (f"- { skill } " for skill in translation ['data' ]['skills' ]) +
361
- "\n \n 📚 **VWC Tech Focus**:\n " +
362
- "\n " .join (f"{ i + 1 } . { focus } " for i , focus in enumerate (translation ['data' ]['tech_focus' ]))
368
+ f"🎖️ **{ translation ['data' ]['title' ]} ** ({ translation ['data' ]['branch' ]} )\n \n "
369
+ f"💻 **VWC Development Path**: { translation ['data' ]['dev_path' ]} \n \n "
370
+ "🔧 **Military Skills**:\n " +
371
+ "\n " .join (f"- { skill } " for skill in translation ['data' ]['skills' ]) +
372
+ "\n \n 📚 **VWC Tech Focus**:\n " +
373
+ "\n " .join (f"{ i + 1 } . { focus } " for i , focus in enumerate (translation ['data' ]['tech_focus' ]))
363
374
)
364
375
else :
365
376
return (
366
- "I don't have that specific code in my database, but here's a recommended "
367
- "VWC learning path based on general military experience:\n \n " +
368
- "\n " .join (f"{ i + 1 } . { focus } " for i , focus in enumerate (translation ['data' ]['tech_focus' ]))
377
+ "I don't have that specific code in my database, but here's a recommended "
378
+ "VWC learning path based on general military experience:\n \n " +
379
+ "\n " .join (f"{ i + 1 } . { focus } " for i , focus in enumerate (translation ['data' ]['tech_focus' ]))
369
380
)
370
-
381
+
371
382
return None
372
383
384
+
373
385
def initialize_chat ():
374
386
"""Initialize the chat with a VWC-focused welcome message."""
375
387
welcome_message = {
@@ -396,24 +408,25 @@ def initialize_chat():
396
408
}
397
409
return [welcome_message ]
398
410
411
+
399
412
def main ():
400
413
"""Main application function."""
401
414
st .title ("🇺🇸 VetsAI: Vets Who Code Assistant" )
402
-
415
+
403
416
# Initialize session
404
417
if 'session_id' not in st .session_state :
405
418
st .session_state .session_id = hashlib .md5 (str (time .time ()).encode ()).hexdigest ()
406
-
419
+
407
420
if 'job_codes' not in st .session_state :
408
421
try :
409
422
st .session_state .job_codes = load_military_job_codes ()
410
423
except Exception as e :
411
424
logger .error (f"Error loading job codes: { e } " )
412
425
st .session_state .job_codes = {}
413
-
426
+
414
427
if 'messages' not in st .session_state :
415
428
st .session_state .messages = initialize_chat ()
416
-
429
+
417
430
# Sidebar with VWC tech stack resources
418
431
with st .sidebar :
419
432
st .markdown ("""
@@ -481,7 +494,7 @@ def main():
481
494
"content" : (
482
495
"You are a specialized AI assistant for Vets Who Code members, designed to provide clear, practical technical guidance "
483
496
"to veterans transitioning into software development careers.\n \n "
484
-
497
+
485
498
"CORE TECH STACK:\n "
486
499
"- Frontend: JavaScript, TypeScript, React, Next.js\n "
487
500
"- Styling: CSS, Tailwind CSS\n "
@@ -490,40 +503,40 @@ def main():
490
503
"- Advanced: AI/ML fundamentals\n "
491
504
"- Development Tools: Git, GitHub, VS Code\n "
492
505
"- Testing: Jest, Pytest\n \n "
493
-
506
+
494
507
"CAREER TRANSITION GUIDANCE:\n "
495
508
"1. Resume Development:\n "
496
509
" - Technical Skills: Programming Languages, Frameworks, Tools, Cloud, Testing\n "
497
510
" - Military Experience Translation: Leadership, Problem-solving, Team Collaboration\n \n "
498
-
511
+
499
512
"2. Portfolio Development:\n "
500
513
" - Clean code and documentation\n "
501
514
" - Version control and API integration\n "
502
515
" - Responsive design and performance\n "
503
516
" - Testing and TypeScript implementation\n "
504
517
" - Security and accessibility standards\n \n "
505
-
518
+
506
519
"LEARNING PATHS:\n "
507
520
"1. Fundamentals: HTML, CSS, JavaScript, Git\n "
508
521
"2. Intermediate: TypeScript, React, Python\n "
509
522
"3. Advanced: Next.js, FastAPI, Streamlit, AI/ML\n \n "
510
-
523
+
511
524
"PROJECT FOCUS:\n "
512
525
"1. Portfolio Projects: Personal website, APIs, Data visualization\n "
513
526
"2. Technical Skills: Code quality, Testing, Security, Performance\n "
514
527
"3. Career Materials: GitHub profile, Technical blog, Documentation\n \n "
515
-
528
+
516
529
"Remember: Provide practical guidance for building technical skills and transitioning to software development careers. "
517
530
"Focus on concrete examples and best practices."
518
531
)
519
532
})
520
-
521
- response = get_chat_response (messages )
522
- st .markdown (response )
523
- st .session_state .messages .append ({
524
- "role" : "assistant" ,
525
- "content" : response
526
- })
533
+ with st . spinner ( "Building a response..." ):
534
+ response = get_chat_response (messages )
535
+ st .markdown (response )
536
+ st .session_state .messages .append ({
537
+ "role" : "assistant" ,
538
+ "content" : response
539
+ })
527
540
except Exception as e :
528
541
st .error (f"Error generating response: { str (e )} " )
529
542
@@ -547,5 +560,6 @@ def main():
547
560
save_feedback (feedback )
548
561
st .success ("Thank you for your feedback!" )
549
562
563
+
550
564
if __name__ == "__main__" :
551
- main ()
565
+ main ()
0 commit comments