File size: 7,068 Bytes
84dd0c3
 
 
 
 
6986165
84dd0c3
 
 
 
 
 
 
6986165
 
84dd0c3
 
1794b72
6bf3623
84dd0c3
 
 
 
 
 
 
 
 
 
6bf3623
6986165
 
84dd0c3
 
 
 
 
 
 
6986165
 
 
84dd0c3
6986165
 
 
 
 
 
84dd0c3
6986165
 
 
84dd0c3
a9822bc
84dd0c3
 
 
 
a9822bc
84dd0c3
 
 
 
 
a9822bc
84dd0c3
 
 
 
 
 
 
a9822bc
84dd0c3
 
 
 
 
 
a9822bc
84dd0c3
a9822bc
 
 
 
 
 
 
 
 
 
84dd0c3
 
a9822bc
84dd0c3
a9822bc
 
 
 
 
84dd0c3
 
 
 
 
 
 
 
 
 
 
 
a9822bc
84dd0c3
 
 
 
a9822bc
84dd0c3
 
 
a9822bc
84dd0c3
 
 
 
a9822bc
84dd0c3
a9822bc
84dd0c3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a9822bc
84dd0c3
 
 
a9822bc
84dd0c3
 
 
a9822bc
84dd0c3
 
 
 
 
 
 
 
 
 
 
 
 
a9822bc
84dd0c3
 
 
 
 
 
 
 
 
a9822bc
84dd0c3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a9822bc
 
6986165
84dd0c3
 
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
# Install required packages first:
# pip install langchain huggingface_hub pandas faiss-cpu numpy transformers torch accelerate bitsandbytes gradio

import pandas as pd
import numpy as np
import gradio as gr
import torch
from langchain.vectorstores import FAISS
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
from langchain.llms import HuggingFacePipeline
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
from huggingface_hub import InferenceClient

# Configuration for both components
EMBEDDING_MODEL_NAME = "sentence-transformers/all-mpnet-base-v2"
LLM_MODEL_NAME = "EleutherAI/gpt-neo-1.3B"
CHAT_MODEL_NAME = "PygmalionAI/pygmalion-1.3b"

# ------ Chat Component ------
chat_client = InferenceClient(CHAT_MODEL_NAME)

def chat_respond(
    message: str,
    history: list[list[str]],
    system_message: str,
    max_tokens: int,
    temperature: float,
    top_p: float,   
):
    messages = [{"role": "system", "content": system_message}]
    
    for human, assistant in history:
        messages.extend([
            {"role": "user", "content": human},
            {"role": "assistant", "content": assistant}
        ])
    
    messages.append({"role": "user", "content": message})

    response = ""
    for chunk in chat_client.chat_completion(
        messages,
        max_tokens=max_tokens,
        stream=True,
        temperature=temperature,
        top_p=top_p,
    ):
        token = chunk.choices[0].delta.content
        response += token
        yield response

# ------ RAG Component ------
def load_embeddings(csv_path):
    df = pd.read_csv(csv_path)
    
    if 'Cleaned_Ideas' not in df.columns or 'Embeddings' not in df.columns:
        raise ValueError("CSV must contain 'Cleaned_Ideas' and 'Embeddings' columns")
        
    df['Embeddings'] = df['Embeddings'].apply(
        lambda x: np.fromstring(
            x.strip("[]").replace("\n", ""),
            sep=", ",
            dtype=np.float32
        )
    )
    
    expected_dim = 768
    valid_embeddings = df['Embeddings'].apply(lambda x: len(x) == expected_dim)
    if not valid_embeddings.all():
        invalid_count = len(df) - valid_embeddings.sum()
        raise ValueError(f"{invalid_count} entries have invalid embedding dimensions")
        
    return df['Cleaned_Ideas'].tolist(), np.array(df['Embeddings'].tolist())
#                                                           Fixed here → ↑
def setup_rag_system():
    # Load data and create vector store
    texts, embeddings = load_embeddings("ideas_with_embeddings.csv")
    
    embedding_model = HuggingFaceEmbeddings(
        model_name=EMBEDDING_MODEL_NAME,
        model_kwargs={'device': 'cpu'},
        encode_kwargs={'normalize_embeddings': True}
    )
    
    vector_store = FAISS.from_embeddings(
        text_embeddings=list(zip(texts, embeddings)),
        embedding=embedding_model,
        normalize_L2=True
    )

    # Load LLM
    tokenizer = AutoTokenizer.from_pretrained(LLM_MODEL_NAME)
    model = AutoModelForCausalLM.from_pretrained(
        LLM_MODEL_NAME,
        device_map="auto",
        torch_dtype=torch.float16,
        trust_remote_code=True
    )
    
    pipe = pipeline(
        "text-generation",
        model=model,
        tokenizer=tokenizer,
        max_new_tokens=250,
        temperature=0.7,
        top_p=0.9,
        repetition_penalty=1.1,
        do_sample=True,
        return_full_text=False,
        pad_token_id=tokenizer.eos_token_id
    )

    # Setup QA chain
    template = """<context>
    {context}
    </context>

    <question>
    {question}
    </question>

    Analyze the philosophical concept using ONLY the provided context. Follow these steps:
    1. Identify key elements from the context
    2. Explain relationships between concepts
    3. Highlight any mentioned philosophical implications

    If context is insufficient, state: "This requires deeper analysis beyond current knowledge." 

    Answer:"""
    
    qa_chain = RetrievalQA.from_chain_type(
        llm=HuggingFacePipeline(pipeline=pipe),
        chain_type="stuff",
        retriever=vector_store.as_retriever(
            search_type="similarity_score_threshold",
            search_kwargs={
                "k": 6,
                "score_threshold": 0.55,
                "include_metadata": True
            }
        ),
        return_source_documents=True,
        chain_type_kwargs={
            "prompt": PromptTemplate(
                template=template,
                input_variables=["context", "question"],
                template_format="f-string"
            )
        },
        verbose=True
    )
    return qa_chain

# ------ Unified Interface ------
def create_interface():
    qa_chain = setup_rag_system()
    
    with gr.Blocks(title="Philosophical AI Assistant") as demo:
        gr.Markdown("# 🤖 AI Philosophy Assistant")
        gr.Markdown("Combining conversational AI with philosophical knowledge base")
        
        # Chat Tab
        with gr.Tab("Chat"):
            gr.ChatInterface(
                fn=chat_respond,
                additional_inputs=[
                    gr.Textbox(value="You are a philosophy expert assistant.", label="System Role"),
                    gr.Slider(1, 2048, value=512, label="Max Tokens"),
                    gr.Slider(0.1, 4.0, value=0.7, label="Temperature"),
                    gr.Slider(0.1, 1.0, value=0.95, label="Top-p")
                ],
                chatbot=gr.Chatbot(height=500, avatar_images=("👤", "🤖")),
                textbox=gr.Textbox(placeholder="Ask me anything about philosophy...", container=False, scale=7),
            )
        
        # Research Assistant Tab
        with gr.Tab("Research Assistant"):
            with gr.Row():
                with gr.Column(scale=3):
                    query_input = gr.Textbox(label="Research Query", lines=3)
                    submit_btn = gr.Button("Analyze", variant="primary")
                with gr.Column(scale=2):
                    sources_output = gr.Markdown(label="Source Context")
            response_output = gr.Markdown(label="Analysis Results")
            
            def process_query(query):
                try:
                    result = qa_chain({"query": query})
                    response = result['result'].split("Answer:")[-1].strip()
                    sources = "\n".join(
                        f"**Source {i+1}:** {doc.page_content[:200]}..." 
                        for i, doc in enumerate(result['source_documents'][:3])
                    )
                    return response, sources
                except Exception as e:
                    return f"Error: {str(e)}", ""
            
            submit_btn.click(
                process_query,
                inputs=query_input,
                outputs=[response_output, sources_output]
            )
    
    return demo

if __name__ == "__main__":
    demo = create_interface()
    demo.launch(share=True)