import os import gradio as gr import pandas as pd import numpy as np from typing import Dict, List from openai import OpenAI from dotenv import load_dotenv # Load environment variables load_dotenv() # 1) Вкажіть свій OpenAI ключ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) ############################################################################## # 1. Вихідні дані: JSON із "хінтами" ############################################################################## classes_json = { "Pain": [ "ache", "aches", "hurts", "pain", "painful", "sore" # ... ], "Chest pain": [ "aches in my chest", "chest pain", "chest hurts", "sternum pain" ], "Physical Activity": [ "exercise", "walking", "running", "biking" ], "Office visit": [ "appointment scheduled", "annual checkup", "office visit" ], # ... } ############################################################################## # 2. Глобальні змінні (спрощено) ############################################################################## df = None embeddings = None class_signatures = None ############################################################################## # 3. Функція для завантаження даних ############################################################################## def load_data(csv_path: str = "messages.csv", emb_path: str = "embeddings.npy"): global df, embeddings df_local = pd.read_csv(csv_path) emb_local = np.load(emb_path) assert len(df_local) == len(emb_local), "CSV і embeddings різної довжини!" df_local["Target"] = "Unlabeled" # Нормалізація embeddings emb_local = (emb_local - emb_local.mean(axis=0)) / emb_local.std(axis=0) df = df_local embeddings = emb_local ############################################################################## # 4. Виклик OpenAI для отримання одного embedding ############################################################################## def get_openai_embedding(text: str, model_name: str = "text-embedding-3-small") -> list: response = client.embeddings.create( input=text, model=model_name ) return response.data[0].embedding ############################################################################## # 5. Отримати embeddings для списку фраз (хінтів) і усереднити ############################################################################## def embed_hints(hint_list: List[str], model_name: str) -> np.ndarray: emb_list = [] for hint in hint_list: emb = get_openai_embedding(hint, model_name=model_name) emb_list.append(emb) return np.array(emb_list, dtype=np.float32) ############################################################################## # 6. Будуємо signatures для кожного класу ############################################################################## def build_class_signatures(model_name: str): global class_signatures signatures = {} for cls_name, hints in classes_json.items(): if not hints: continue arr = embed_hints(hints, model_name=model_name) signatures[cls_name] = arr.mean(axis=0) class_signatures = signatures return "Signatures побудовано!" ############################################################################## # 7. Функція класифікації одного рядка (dot product) ############################################################################## def predict_class(text_embedding: np.ndarray, signatures: Dict[str, np.ndarray]) -> str: best_label = "Unknown" best_score = float("-inf") for cls, sign in signatures.items(): score = np.dot(text_embedding, sign) if score > best_score: best_score = score best_label = cls return best_label ############################################################################## # 8. Класифікація відфільтрованих рядків ############################################################################## def classify_rows(filter_substring: str): global df, embeddings, class_signatures if class_signatures is None: return "Спочатку збудуйте signatures!" if df is None or embeddings is None: return "Дані не завантажені! Спочатку викличте load_data." if filter_substring: filtered_idx = df[df["Message"].str.contains(filter_substring, case=False, na=False)].index else: filtered_idx = df.index for i in filtered_idx: emb_vec = embeddings[i] pred = predict_class(emb_vec, class_signatures) df.at[i, "Target"] = pred result_df = df.loc[filtered_idx, ["Message", "Target"]].copy() return result_df.reset_index(drop=True) ############################################################################## # 9. Збереження CSV ############################################################################## def save_data(): global df if df is None: return "Дані відсутні!" df.to_csv("messages_with_labels.csv", index=False) return "Файл 'messages_with_labels.csv' збережено!" ############################################################################## # 10. Gradio UI ############################################################################## def ui_load_data(csv_path, emb_path): load_data(csv_path, emb_path) return f"Data loaded from {csv_path} and {emb_path}. Rows: {len(df)}" def ui_build_signatures(model_name): msg = build_class_signatures(model_name) return msg def ui_classify_data(filter_substring): result = classify_rows(filter_substring) if isinstance(result, str): return result return result def ui_save_data(): return save_data() def main(): import gradio as gr with gr.Blocks() as demo: gr.Markdown("# SDC Classifier з Gradio") gr.Markdown("## 1) Завантаження даних") with gr.Row(): csv_input = gr.Textbox(value="messages.csv", label="CSV-файл") emb_input = gr.Textbox(value="embeddings.npy", label="Numpy Embeddings") load_btn = gr.Button("Load data") load_output = gr.Label(label="Loading result") load_btn.click(fn=ui_load_data, inputs=[csv_input, emb_input], outputs=load_output) gr.Markdown("## 2) Побудова Class Signatures") # openai_key_in = gr.Textbox(label="OpenAI API Key", type="password") model_choice = gr.Dropdown(choices=["text-embedding-3-large","text-embedding-3-small"], value="text-embedding-3-small", label="OpenAI model") build_btn = gr.Button("Build signatures") build_out = gr.Label(label="Signatures") build_btn.click(fn=ui_build_signatures, inputs=[model_choice], outputs=build_out) gr.Markdown("## 3) Класифікація") filter_in = gr.Textbox(label="Filter substring (optional)") classify_btn = gr.Button("Classify") classify_out = gr.Dataframe(label="Result (Message / Target)") classify_btn.click(fn=ui_classify_data, inputs=[filter_in], outputs=[classify_out]) gr.Markdown("## 4) Зберегти CSV") save_btn = gr.Button("Save labeled data") save_out = gr.Label() save_btn.click(fn=ui_save_data, inputs=[], outputs=save_out) gr.Markdown(""" ### Опис: 1. Натисніть 'Load data', щоб завантажити ваші дані (CSV + embeddings). 2. Укажіть OpenAI API модель, натисніть 'Build signatures'. 3. Вкажіть фільтр (необов'язково), натисніть 'Classify'. Отримаєте таблицю з полем Target. 4. 'Save labeled data' збереже 'messages_with_labels.csv'. """) demo.launch(server_name="0.0.0.0", server_port=7860, share=True) # demo.launch() if __name__ == "__main__": main()