File size: 11,211 Bytes
8721ec5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
080031c
 
 
8721ec5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
import os
import json
import copy
from datetime import datetime
import pandas as pd
import gradio as gr
from openai import OpenAI
from supabase import create_client
from language_info import *

# 시스템 프롬프트 정의
FIRST_PROMPT = """
너는 유저의 질문을 분석하는 AI야! 유저의 질문을 바탕으로 주어진 조건에 맞도록 질문을 분석해줘!
조건 1. 아래 Task 리스트 중 1개의 작업 선택!
Task : ["ReAsk", "FindProduct", "Recommandation", "Etc"]
조건 2. 만약, Task가 "FindProduct"라면, 원하는 "Product"를 "Option"으로 정의를 해줘!
조건 3. 만약, Task가 "Recommandation"라면, 유저의 조건을 "Condition"이라고 "Option"에 제공해줘!
조건 4. 만약, Task가 "ReAsk"라면, 유저의 질문에서 추가적으로 요구되는 조건을 "Condition"이라고 "Option"에 제공해줘!
** 조건 5. 화장품 관련 이야기를 제외한 다른 이야기는 답할 수 없다는 내용으로 답변을 해줘! **
** 조건 6. 언어가 다르더라도 출력은 전부 "한국어"를 사용해서 출력해! 그리고 유저 입력에 대한 한국어 번역을 "Translation"으로 제공해!**

OUTPUT 포맷
json
{
    "Task":"...",
    "Option":{"...":"...",  ..., key:val},
    "Translation":"..."
}
"""

# Supabase 로깅 함수
def log_to_supabase(supabase, question, answer, history):
    """Supabase에 채팅 로그를 저장하는 함수"""
    try:
        timestamp = datetime.now().isoformat()
        supabase.table("chat_log").insert({
            "question": question,
            "answer": answer,
            "history": json.dumps(history, ensure_ascii=False),
            "timestamp": timestamp
        }).execute()
        return True
    except Exception as e:
        print(f"Error logging to Supabase: {e}")
        return False

def local_RAG(model, message, client, vector_id):
    """로컬 검색 기반 생성 함수"""
    try:
        response = client.responses.create(
            model=model,
            input=message,
            tools=[{
                "type": "file_search",
                "vector_store_ids": [vector_id],
                "max_num_results": 5
            }]
        )
        if len(response.output)==0:
            return ""
        if not response.output[1].content[0].annotations:
            return ""
        return response.output[1].content[0].text
    except Exception as e:
        print(f"Error in RAG: {e}")
        return ""

def routing(messages, client, language):
    """사용자 질문 분석 및 라우팅 함수"""
    try:
        routing_messages = copy.deepcopy(messages)
        routing_messages[0]['content'] = FIRST_PROMPT
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=routing_messages,
            response_format={"type": "json_object"}
        )
        return json.loads(response.choices[0].message.content)
    except Exception as e:
        print(f"Error in routing: {e}")
        return {"Task": "Etc"}

def generate_response(query_parsing, client, vector_id, user_message, chat_history, language):
    """쿼리 분석 결과에 따른 응답 생성 함수"""

    # 메시지 형식으로 변환
    messages = [{"role": "system", "content": get_system_prompt(language)}]
    for user_msg, bot_msg in chat_history:
        if user_msg:
            messages.append({"role": "user", "content": user_msg})
        if bot_msg:
            messages.append({"role": "assistant", "content": bot_msg})
    if language!="한국어":
        messages.append({"role": "user", "content": user_message})
    else:
        messages.append({"role": "user", "content": query_parsing["Translation"]})

    # 쿼리 분석 결과가 없는 경우
    if "Task" not in query_parsing:
        return get_text('error_understanding', language)

    # ReAsk: 추가 정보 요청
    if query_parsing["Task"] == "ReAsk":
        if "Option" in query_parsing and "Condition" in query_parsing["Option"]:
            return f"{get_text('additional_info_needed', language)}{query_parsing['Option']['Condition']}"
        else:
            return get_text('request_more_info', language)

    # FindProduct: 제품 검색
    if query_parsing["Task"] == "FindProduct":
        if "Option" not in query_parsing:
            return get_text('error_product_not_found', language)

        product = query_parsing['Option']["Product"]

        # 수파베이스에서 화장품 데이터 가져오기
        try:
            product_list = supabase.table("cosmetics").select("*").execute().data
        except:
            # 실제 구현 시 데이터베이스 연결 필수
            product_list = []  # 임시 빈 리스트

        find_data = ""
        for item in product_list:
            if product in item["title"]:
                find_data += f"item : {item['title']}\tbrand : {item['brand']}\tmake : {item['maker']}\tsummary : {item['summary']}\n\n"

        if len(find_data) > 2:
            # 데이터를 찾았을 때
            messages[-1]['content'] = messages[-1]['content'] + "\n\n<데이터>\n\n" + find_data + "\n\n</데이터>\n\n" + messages[-1]['content']
            response = client.chat.completions.create(
                model="gpt-4o",
                messages=messages
            )
            return response.choices[0].message.content
        else:
            # 데이터를 찾지 못했을 때 RAG 시도
            response = local_RAG("gpt-4o", user_message, client, vector_id)
            if response == "":
                # RAG도 실패했을 때 검색 모델 사용
                response = client.chat.completions.create(
                    model="gpt-4o-search-preview",
                    messages=messages
                )
                return response.choices[0].message.content
            return response

    # Recommendation: 제품 추천
    if query_parsing["Task"] == "Recommandation":
        response = local_RAG("gpt-4o", user_message, client, vector_id)
        if response == "":
            # RAG 실패 시 검색 모델 사용
            response = client.chat.completions.create(
                model="gpt-4o-search-preview",
                messages=messages
            )
            return response.choices[0].message.content
        return response

    # Etc: 일반 응답
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=messages
    )
    return response.choices[0].message.content

def get_system_prompt(language):
    """언어에 맞는 시스템 프롬프트 생성"""
    return f"""
너는 한국의 화장품을 상담해 주는 AI야!
유저의 한국 화장품 관련 질문에 가장 적절한 답변을 해줘!
조건1 : 화장품과 관련되지 않은 질문은 대답하지 마.
조건2 : 주어진 language에 맞는 언어로 답변해줘!
조건3 : 유저가 별도의 데이터를 제공하면 (<데이터>...</데이터> 형식), 제공된 데이터 내에서만 답변해줘.
language: {language}
"""

# 현재 선택된 언어를 저장할 전역 변수
selected_language = "English"

def chatbot(message, history, language_choice=None):
    """챗봇의 메인 함수"""
    global selected_language

    # language_choice가 제공되었으면 업데이트
    if language_choice is not None:
        selected_language = language_choice

    # OpenAI API 키 및 환경 변수 설정
    openai_api_key = os.getenv("OPENAI_API_KEY")
    vector_id = os.getenv("vector_id")

    # Supabase 설정
    supabase_url = os.getenv("SUPABASE_URL")
    supabase_key = os.getenv("SUPABASE_KEY")
    supabase = None

    client = OpenAI(api_key=openai_api_key)

    # Supabase 클라이언트 생성 (있을 경우)
    if supabase_url and supabase_key:
        supabase = create_client(supabase_url, supabase_key)

    # 쿼리 분석 및 응답 생성
    query_parsing = routing([{"role": "system", "content": ""}, {"role": "user", "content": message}], client, selected_language)
    response = generate_response(query_parsing, client, vector_id, message, history, selected_language)

    # Supabase 로깅 (해당 환경변수가 있을 경우)
    if supabase:
        chat_history = []
        for user_msg, bot_msg in history:
            if user_msg:
                chat_history.append({"role": "user", "content": user_msg})
            if bot_msg:
                chat_history.append({"role": "assistant", "content": bot_msg})
        chat_history.append({"role": "user", "content": message})
        chat_history.append({"role": "assistant", "content": response})

        log_to_supabase(supabase, message, response, chat_history)

    return response

def change_language(language_choice):
    """언어 변경 함수"""
    global selected_language
    selected_language = language_choice
    return language_choice

# 메인 함수
def main():
    """Gradio 인터페이스 설정 함수"""
    # 언어 설정
    languages = ["English", "한국어", "Español", "中文", "日本語", "ภาษาไทย", "Tiếng Việt", "Bahasa Indonesia"]
    global selected_language
    selected_language = "English"
    current_dir = os.path.dirname(os.path.abspath(__file__))
    image_path = os.path.join(current_dir, "image.png")
    # Gradio 인터페이스 구성
    with gr.Blocks(css="footer {visibility: hidden}") as demo:

        gr.Markdown(f"# {get_text('title', selected_language)}")
        gr.Markdown(f"(Conversation logs are saved.)")

        with gr.Row():
            language_dropdown = gr.Dropdown(
                choices=languages,
                value=selected_language,
                label=get_text("language_selector", selected_language)
            )

        # 언어 상태를 저장할 state 객체 생성
        lang_state = gr.State(value=selected_language)

        # 언어가 변경될 때 실행되는 함수
        def update_language(new_lang, history):
            global selected_language
            selected_language = new_lang
            return new_lang, history

        # 챗봇의 메시지 전송 함수를 수정하여 현재 언어 상태를 전달
        def handle_message(message, history, lang):
            return chatbot(message, history, lang)

        chatbot_interface = gr.ChatInterface(
            fn=handle_message,
            additional_inputs=[language_dropdown],
            title="",
            examples=[
                ["이니스프리 그린티 제품 알려줘", "한국어"],
                ["แนะนำผลิตภัณฑ์ดูแลผิวที่ดีสำหรับผู้หญิง", "ภาษาไทย"],
                ["What products are effective for whitening?", "English"]
            ],
        )

        # 언어 변경 시 이벤트
        language_dropdown.change(
            fn=update_language,
            inputs=[language_dropdown, chatbot_interface.chatbot],
            outputs=[lang_state, chatbot_interface.chatbot]
        )
        gr.Markdown(f"# Architecture")

        gr.Image(value=image_path, show_label=False, height=400, width=400)

    # Gradio 앱 실행
    demo.launch()

if __name__ == "__main__":
    main()