1- import { generateObject } from 'ai' ;
1+ import { generateText } from 'ai' ;
22import { openai } from '@ai-sdk/openai' ;
3- import { z } from 'zod' ;
43import { logger } from '../logger' ;
54
6- // JSON Resume schema for structured output
7- const resumeSchema = z . object ( {
8- basics : z
9- . object ( {
10- name : z . string ( ) . optional ( ) ,
11- label : z . string ( ) . optional ( ) ,
12- image : z . string ( ) . optional ( ) ,
13- email : z . string ( ) . optional ( ) ,
14- phone : z . string ( ) . optional ( ) ,
15- url : z . string ( ) . optional ( ) ,
16- summary : z . string ( ) . optional ( ) ,
17- location : z
18- . object ( {
19- address : z . string ( ) . optional ( ) ,
20- postalCode : z . string ( ) . optional ( ) ,
21- city : z . string ( ) . optional ( ) ,
22- countryCode : z . string ( ) . optional ( ) ,
23- region : z . string ( ) . optional ( ) ,
24- } )
25- . optional ( ) ,
26- profiles : z
27- . array (
28- z . object ( {
29- network : z . string ( ) . optional ( ) ,
30- username : z . string ( ) . optional ( ) ,
31- url : z . string ( ) . optional ( ) ,
32- } )
33- )
34- . optional ( ) ,
35- } )
36- . optional ( ) ,
37- work : z
38- . array (
39- z . object ( {
40- name : z . string ( ) . optional ( ) ,
41- position : z . string ( ) . optional ( ) ,
42- url : z . string ( ) . optional ( ) ,
43- startDate : z . string ( ) . optional ( ) ,
44- endDate : z . string ( ) . optional ( ) ,
45- summary : z . string ( ) . optional ( ) ,
46- highlights : z . array ( z . string ( ) ) . optional ( ) ,
47- } )
48- )
49- . optional ( ) ,
50- volunteer : z
51- . array (
52- z . object ( {
53- organization : z . string ( ) . optional ( ) ,
54- position : z . string ( ) . optional ( ) ,
55- url : z . string ( ) . optional ( ) ,
56- startDate : z . string ( ) . optional ( ) ,
57- endDate : z . string ( ) . optional ( ) ,
58- summary : z . string ( ) . optional ( ) ,
59- highlights : z . array ( z . string ( ) ) . optional ( ) ,
60- } )
61- )
62- . optional ( ) ,
63- education : z
64- . array (
65- z . object ( {
66- institution : z . string ( ) . optional ( ) ,
67- url : z . string ( ) . optional ( ) ,
68- area : z . string ( ) . optional ( ) ,
69- studyType : z . string ( ) . optional ( ) ,
70- startDate : z . string ( ) . optional ( ) ,
71- endDate : z . string ( ) . optional ( ) ,
72- score : z . string ( ) . optional ( ) ,
73- courses : z . array ( z . string ( ) ) . optional ( ) ,
74- } )
75- )
76- . optional ( ) ,
77- awards : z
78- . array (
79- z . object ( {
80- title : z . string ( ) . optional ( ) ,
81- date : z . string ( ) . optional ( ) ,
82- awarder : z . string ( ) . optional ( ) ,
83- summary : z . string ( ) . optional ( ) ,
84- } )
85- )
86- . optional ( ) ,
87- certificates : z
88- . array (
89- z . object ( {
90- name : z . string ( ) . optional ( ) ,
91- date : z . string ( ) . optional ( ) ,
92- issuer : z . string ( ) . optional ( ) ,
93- url : z . string ( ) . optional ( ) ,
94- } )
95- )
96- . optional ( ) ,
97- publications : z
98- . array (
99- z . object ( {
100- name : z . string ( ) . optional ( ) ,
101- publisher : z . string ( ) . optional ( ) ,
102- releaseDate : z . string ( ) . optional ( ) ,
103- url : z . string ( ) . optional ( ) ,
104- summary : z . string ( ) . optional ( ) ,
105- } )
106- )
107- . optional ( ) ,
108- skills : z
109- . array (
110- z . object ( {
111- name : z . string ( ) . optional ( ) ,
112- level : z . string ( ) . optional ( ) ,
113- keywords : z . array ( z . string ( ) ) . optional ( ) ,
114- } )
115- )
116- . optional ( ) ,
117- languages : z
118- . array (
119- z . object ( {
120- language : z . string ( ) . optional ( ) ,
121- fluency : z . string ( ) . optional ( ) ,
122- } )
123- )
124- . optional ( ) ,
125- interests : z
126- . array (
127- z . object ( {
128- name : z . string ( ) . optional ( ) ,
129- keywords : z . array ( z . string ( ) ) . optional ( ) ,
130- } )
131- )
132- . optional ( ) ,
133- references : z
134- . array (
135- z . object ( {
136- name : z . string ( ) . optional ( ) ,
137- reference : z . string ( ) . optional ( ) ,
138- } )
139- )
140- . optional ( ) ,
141- projects : z
142- . array (
143- z . object ( {
144- name : z . string ( ) . optional ( ) ,
145- startDate : z . string ( ) . optional ( ) ,
146- endDate : z . string ( ) . optional ( ) ,
147- description : z . string ( ) . optional ( ) ,
148- highlights : z . array ( z . string ( ) ) . optional ( ) ,
149- url : z . string ( ) . optional ( ) ,
150- } )
151- )
152- . optional ( ) ,
153- meta : z
154- . object ( {
155- canonical : z . string ( ) . optional ( ) ,
156- version : z . string ( ) . optional ( ) ,
157- lastModified : z . string ( ) . optional ( ) ,
158- theme : z . string ( ) . optional ( ) ,
159- } )
160- . optional ( ) ,
161- } ) ;
162-
1635const SYSTEM_PROMPT = `You are a professional resume editor. Your task is to modify a JSON Resume based on user instructions.
1646
1657IMPORTANT RULES:
@@ -198,15 +40,14 @@ export async function transformResumeWithLLM(resume, prompt) {
19840 'Starting LLM resume transformation'
19941 ) ;
20042
201- const result = await generateObject ( {
43+ const result = await generateText ( {
20244 model : openai ( 'gpt-4.1-mini' ) ,
203- schema : resumeSchema ,
20445 system : SYSTEM_PROMPT ,
20546 prompt : `Here is the original resume:\n\n${ JSON . stringify (
20647 resume ,
20748 null ,
20849 2
209- ) } \n\nUser request: ${ prompt } \n\nPlease modify the resume according to the user's request and return the complete updated resume.`,
50+ ) } \n\nUser request: ${ prompt } \n\nPlease modify the resume according to the user's request and return the complete updated resume as valid JSON .`,
21051 } ) ;
21152
21253 const duration = Date . now ( ) - startTime ;
@@ -215,7 +56,16 @@ export async function transformResumeWithLLM(resume, prompt) {
21556 'LLM transformation completed'
21657 ) ;
21758
218- return result . object ;
59+ // Parse the JSON from the response
60+ const text = result . text . trim ( ) ;
61+ // Extract JSON from markdown code blocks if present
62+ const jsonMatch = text . match ( / ` ` ` (?: j s o n ) ? \s * ( [ \s \S ] * ?) ` ` ` / ) || [
63+ null ,
64+ text ,
65+ ] ;
66+ const jsonStr = jsonMatch [ 1 ] . trim ( ) ;
67+
68+ return JSON . parse ( jsonStr ) ;
21969 } catch ( error ) {
22070 logger . error (
22171 { error : error . message , prompt : prompt . substring ( 0 , 100 ) } ,
0 commit comments