Skip to content

Commit

Permalink
implement resume parsing handler and AI prompy
Browse files Browse the repository at this point in the history
  • Loading branch information
santiagomed committed Sep 2, 2023
1 parent 65c4399 commit 043e2df
Show file tree
Hide file tree
Showing 8 changed files with 52 additions and 26 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# Project
target/
*.env
.env.sh

# VSCode
.vscode
Expand Down
4 changes: 2 additions & 2 deletions Makefile.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
env_files = [
{ path = "secrets.env", profile = "development" },
{ path = "secrets.env", profile = "production" }
{ path = ".secrets.env", profile = "development" },
{ path = ".secrets.env", profile = "production" }
]

[env]
Expand Down
27 changes: 14 additions & 13 deletions src/generate/ai.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,38 +98,39 @@ impl AIClient {
let request = CreateChatCompletionRequestArgs::default()
.max_tokens(1024u16)
.model(self.model)
.temperature(0.2)
.messages([
ChatCompletionRequestMessageArgs::default()
.role(Role::System)
.content(
"You are an extremely accurate resume parser. When you get a resume, you need to clean the text and extract the following information in the format specified below:
"You are an extremely accurate resume parser. When you get a resume, you need to clean the text and extract the following information in the following format:
{
\"education\": [
{
\"school\": <string>,
\"degree\": <string>,
\"field_of_study\": <string>,
\"current\": <bool>,
\"description\": <string>,
\"school\": <string>, // name of the school (e.g. University of California, Berkeley)
\"degree\": <string>, // degree type (e.g. Bachelor of Science)
\"field_of_study\": <string>, // field of study (e.g. Computer Science)
\"current\": <bool>, // whether the candidate is currently enrolled
\"description\": <string>, // description of the degree (e.g. GPA, honors)
}
],
\"experience\": [
{
\"name\": <string>,
\"type\": 'work' | 'volunteer' | 'personal' | 'other',
\"at\": <string>,
\"current\": <bool>,
\"description\": <string>,
\"name\": <string>, // name of the position (e.g. HR Manager)
\"type\": 'work' | 'volunteer' | 'personal' | 'other', // type of experience
\"at\": <string>, // name of the company (e.g. Google)
\"current\": <bool>, // whether the candidate currently works here
\"description\": <string>, // description of the position (e.g. responsibilities)
}
],
\"skills\": [
{
\"skill\": <string> [THIS IS A SINGLE WORD],
\"skill\": <string>, // name of the skill (e.g. Python, Javascript, Leadership, MacOS)
}
],
}
").build()?,
ChatCompletionRequestMessageArgs::default().role(Role::System).content("You can consider sections named 'Projects' as experience.").build()?,
ChatCompletionRequestMessageArgs::default().role(Role::System).content("None of the values can be null.").build()?,
ChatCompletionRequestMessageArgs::default().role(Role::System).content(resume_text).build()?,
ChatCompletionRequestMessageArgs::default().role(Role::System).content("Extracted information:").build()?,
])
Expand Down
5 changes: 4 additions & 1 deletion src/handlers/account_handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,10 @@ pub async fn authenticate_external_account(db: Data<DatabaseRepository>, query:
let token = query.token_id.to_owned();
let google_claims: GoogleAuthClaims = match decode_google_token_id(&token).await {
Ok(c) => c,
Err(e) => return HttpResponse::BadRequest().json(e.to_string()),
Err(e) => {
log::error!("Failed to decode google token. Error: {}", e);
return HttpResponse::BadRequest().json(e.to_string());
}
};
let email = google_claims.email;

Expand Down
27 changes: 21 additions & 6 deletions src/handlers/generate_handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ use actix_web::{
use futures::StreamExt;
use serde::{Deserialize, Serialize};

use crate::auth::user_auth::AuthorizationService;
use crate::generate::ai;
use crate::models::profile::profile::Profile;
use crate::repository::database::DatabaseRepository;
use crate::{auth::user_auth::AuthorizationService, handlers::types::ErrorResponse};

#[derive(Debug, Serialize, Deserialize)]
pub struct Highlights {
Expand All @@ -24,7 +24,7 @@ pub struct GenerateResponse {
pub response: String,
}

#[post("/response/")]
#[post("/response")]
pub async fn generate_openai(data: Json<Highlights>, _auth: AuthorizationService) -> HttpResponse {
let client = ai::AIClient::new();
let response = client.generate_request(data.prompt.clone(), data.profile.clone(), data.additional.clone()).await;
Expand All @@ -47,7 +47,7 @@ pub async fn generate_openai(data: Json<Highlights>, _auth: AuthorizationService
}
}

#[post("/resume/")]
#[post("/profile")]
pub async fn profile_from_resume(db: Data<DatabaseRepository>, mut payload: Payload, auth: AuthorizationService) -> HttpResponse {
let id = auth.id;
let mut bytes = BytesMut::new();
Expand All @@ -61,13 +61,28 @@ pub async fn profile_from_resume(db: Data<DatabaseRepository>, mut payload: Payl
match response {
Ok(response) => {
let content = &response.choices[0].message.content;
// pretty print content
log::debug!("Response: {:#?}", content);
let profile = Profile::from_json(content).unwrap();
db.update_profile(&id, profile.clone()).await.unwrap();
HttpResponse::Ok().json(profile)
match db.update_profile(&id, profile).await {
Ok(_) => match db.get_account(&id).await {
Ok(user) => HttpResponse::Ok().json(user),
Err(e) => {
log::error!("Error: {:#?}", e);
HttpResponse::InternalServerError().json(ErrorResponse::new(e.to_string()))
}
},
Err(e) => {
log::error!("Error: {:#?}", e);
HttpResponse::InternalServerError().json(ErrorResponse::new(
"Error parsing resume. Please make sure your resume is formatted correctly and try again.".to_string(),
))
}
}
}
Err(e) => {
log::error!("Error: {:#?}", e);
HttpResponse::BadRequest().body("Error")
HttpResponse::BadRequest().json(ErrorResponse::new(e.to_string()))
}
}
}
6 changes: 6 additions & 0 deletions src/handlers/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ pub struct ErrorResponse {
pub message: String,
}

impl ErrorResponse {
pub fn new(message: String) -> Self {
Self { message }
}
}

// Start of account handler types

#[derive(Debug, Serialize, Deserialize)]
Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ async fn main() -> std::io::Result<()> {
)
.route("/health", web::get().to(|| async { "OK" }))
.service(web::scope("/profile").service(profile_handlers::change_profile))
.service(web::scope("/generate").service(generate_handlers::generate_openai))
.service(web::scope("/generate").service(generate_handlers::generate_openai).service(generate_handlers::profile_from_resume))
.service(web::scope("/document").service(document_handlers::create_update_document).service(document_handlers::delete_document))
})
.bind("0.0.0.0:8080")?
Expand Down
6 changes: 3 additions & 3 deletions src/repository/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,21 +159,21 @@ impl DatabaseRepository {
}

/// Update profile embedded document
pub async fn update_profile(&self, id: &str, profile: Profile) -> Result<UpdateResult, Error> {
pub async fn update_profile(&self, id: &str, mut profile: Profile) -> Result<UpdateResult, Error> {
let obj_id = ObjectId::parse_str(id).expect("Failed to parse object id");
let filter = doc! {"_id": obj_id};
profile.date_updated = Some(chrono::Utc::now().timestamp());
let update = doc! {
"$set": {
"profile": to_bson(&profile).unwrap(),
"profile.date_updated": chrono::Utc::now().timestamp(),
}
};
let result = self.user_collection.update_one(filter, update, None).await;
match result {
Ok(result) => match result.modified_count {
1 => Ok(result),
_ => Err(Error::DeserializationError {
message: "Failed to update document".to_string(),
message: "Failed to update profile for account".to_string(),
}),
},
Err(e) => {
Expand Down

0 comments on commit 043e2df

Please sign in to comment.