Skip to content

Commit e81e2a1

Browse files
committed
feat: 질문 개선 API 요청 기능 추가 및 UI 개선
1 parent e1dc5c9 commit e81e2a1

3 files changed

Lines changed: 452 additions & 238 deletions

File tree

linkle/api/QuestionApi.js

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,120 @@ export async function requestGeminiSuggestions({ answers, name }) {
7272
clearTimeout(timeoutId);
7373
return { ok: false, reason: error.message || '네트워크 오류' };
7474
}
75+
}
76+
77+
// requestInitialQuestions 및 requestNextQuestion 함수는 이제 사용되지 않으므로 제거하거나 주석 처리합니다.
78+
/*
79+
export async function requestInitialQuestions(name) { ... }
80+
export async function requestNextQuestion(name, previousQuestion, previousAnswer) { ... }
81+
*/
82+
83+
/**
84+
* Gemini API에 이전 대화 내용과 기본 질문, 그리고 개선 지침을 전달하여
85+
* 맥락에 맞게 개선된 다음 질문 하나를 받아옵니다.
86+
* @param {string} name - 친구 이름
87+
* @param {string} baseQuestionToRefine - 개선의 기반이 될 기본 질문 템플릿
88+
* @param {string} immediatePreviousQuestionText - 바로 이전에 물었던 실제 질문 내용
89+
* @param {string} immediatePreviousAnswerText - 이전 질문에 대한 사용자의 답변
90+
* @param {string} refinementPromptVariable - 질문 개선 방식에 대한 사용자 정의 지침
91+
* @param {string | null} firstQuestionText - (옵셔널) 첫 번째 질문의 실제 텍스트 (Q3+ 생성 시 제공)
92+
* @param {string | null} firstAnswerText - (옵셔널) 첫 번째 질문에 대한 답변 (Q3+ 생성 시 제공)
93+
* @returns {Promise<{ok: true, refinedQuestion: string} | {ok: false, reason: string}>}
94+
*/
95+
export async function requestRefinedQuestion(name, baseQuestionToRefine, immediatePreviousQuestionText, immediatePreviousAnswerText, refinementPromptVariable, firstQuestionText, firstAnswerText) {
96+
const controller = new AbortController();
97+
const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
98+
99+
let firstInteractionContext = "";
100+
if (firstQuestionText && firstAnswerText) {
101+
firstInteractionContext = `
102+
[Initial Conversation Context]
103+
First question: ${firstQuestionText}
104+
First answer: ${firstAnswerText}
105+
`;
106+
}
107+
108+
// 시스템 프롬프트를 영어 중심으로 수정, 최종 질문 영어로 명시
109+
const systemPrompt = `You are an AI conversation designer tasked with making a user's chat with their friend (${name}) more natural and meaningful.
110+
111+
User's friend's name: "${name}"
112+
${firstInteractionContext}
113+
[Previous turn in the conversation]
114+
Previous question to ${name}: ${immediatePreviousQuestionText}
115+
User's answer regarding ${name}: ${immediatePreviousAnswerText}
116+
117+
[Base intent for the next question]:
118+
${baseQuestionToRefine}
119+
120+
[Guideline for refining the question]:
121+
${refinementPromptVariable} // This is already in English as per QuestionScreen.js
122+
123+
Considering all the information above (especially "Initial Conversation Context" and "Previous turn"), generate ONE new question based on the "Base intent for the next question".
124+
This new question must follow the "Guideline for refining the question" and should naturally continue the conversation from the previous turn.
125+
It must include the friend's name, "${name}".
126+
**The refined question must be in English.**
127+
128+
Please provide the response ONLY in the following JSON format, with no other text:
129+
{ "refinedQuestion": "The final refined question content (in English)" }
130+
131+
Example (the actual content will vary greatly based on input, but the response must be in English):
132+
{ "refinedQuestion": "So, ${name}, what were some of the specific feelings or thoughts you had during that experience?" }`;
133+
134+
const url = `${GEMINI_API_URL}?key=${GEMINI_API_KEY}`;
135+
136+
try {
137+
const response = await fetch(url, {
138+
method: 'POST',
139+
headers: { 'Content-Type': 'application/json' },
140+
body: JSON.stringify({
141+
contents: [
142+
{
143+
role: 'user',
144+
parts: [{ text: systemPrompt }],
145+
},
146+
],
147+
generationConfig: {
148+
responseMimeType: "application/json",
149+
}
150+
}),
151+
signal: controller.signal,
152+
});
153+
clearTimeout(timeoutId);
154+
155+
if (!response.ok) {
156+
const errorText = await response.text();
157+
console.error(`Gemini API Error (RefinedQuestion): ${response.status} ${response.statusText} - ${errorText}`);
158+
return {
159+
ok: false,
160+
reason: `Gemini API 오류: ${response.status} ${errorText}`,
161+
};
162+
}
163+
164+
const data = await response.json();
165+
let rawJsonText = data.candidates?.[0]?.content?.parts?.[0]?.text;
166+
167+
if (!rawJsonText) {
168+
return { ok: false, reason: 'API로부터 유효한 다음 질문 데이터를 받지 못했습니다 (No text part).' };
169+
}
170+
171+
rawJsonText = rawJsonText.replace(/^```json\s*/, '').replace(/\s*```$/, '');
172+
173+
try {
174+
const parsedResponse = JSON.parse(rawJsonText);
175+
if (parsedResponse && typeof parsedResponse.refinedQuestion === 'string' && parsedResponse.refinedQuestion.trim() !== '') {
176+
return { ok: true, refinedQuestion: parsedResponse.refinedQuestion };
177+
} else {
178+
console.warn("Parsed data is not a valid refined question object:", parsedResponse);
179+
return { ok: false, reason: 'API 응답이 유효한 다음 질문 형식이 아닙니다.' };
180+
}
181+
} catch (parseError) {
182+
console.error("Error parsing refined question JSON:", parseError, "Raw text:", rawJsonText);
183+
return { ok: false, reason: `다음 질문 파싱 오류: ${parseError.message}` };
184+
}
185+
186+
} catch (error) {
187+
clearTimeout(timeoutId);
188+
console.error("Error in requestRefinedQuestion:", error);
189+
return { ok: false, reason: error.message || '네트워크 오류 또는 알 수 없는 다음 질문 생성 오류' };
190+
}
75191
}

0 commit comments

Comments
 (0)