File size: 7,676 Bytes
a77b059
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# =============================================================================
# DON'T STEAL THE FREE CODE OF DEVS! Use it for free an do not touch credits!
# If you steal this code, in the future you will pay for apps like this!
# A bit of respect goes a long way – all rights reserved under German law.
# Copyright Volkan Kücükbudak https://github.com/volkansah
# Repo URL: https://github.com/AiCodeCraft
# =============================================================================
import streamlit as st
import os
import json
import datetime
import openai
from datetime import timedelta
import logging
from datasets import load_dataset, Dataset, concatenate_datasets
import sys
import schedule
import threading
import time

print("Python Version:", sys.version)
print("Importiere Module...")

# ------------------ Logging konfigurieren ------------------
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

# ------------------ Initialisierung ------------------
# Als globale Variable definieren
HF_TOKEN_MEMORY = ""

def main():
    global HF_TOKEN_MEMORY  # Als global markieren
    logger.info("App-Initialisierung gestartet...")
    
    # ------------------ Hugging Face Token ------------------
    HF_TOKEN_MEMORY = os.getenv("HF_TOKEN_MEMORY", "")
    if not HF_TOKEN_MEMORY:
        try:
            HF_TOKEN_MEMORY = st.secrets["HF_TOKEN_MEMORY"]
            logger.info("Token aus Streamlit Secrets geladen")
        except Exception as e:
            logger.warning(f"Token nicht in Streamlit Secrets gefunden: {str(e)}")
    if HF_TOKEN_MEMORY:
        token_preview = HF_TOKEN_MEMORY[:4] + "..." if len(HF_TOKEN_MEMORY) > 4 else "Ungültig"
        logger.info(f"HF Token gefunden. Startet mit: {token_preview}")
    else:
        logger.warning("Kein HF Token gefunden! Dataset-Funktionen werden nicht verfügbar sein.")
    
    # ------------------ Streamlit UI ------------------
    st.title("AI Customer Support Agent with Memory 🛒")
    st.caption("Chat with an assistant who remembers past interactions")

    # OpenAI Key Eingabe
    openai_api_key = st.text_input("Enter OpenAI API Key", type="password", key="openai_key")
    if not openai_api_key:
        logger.warning("API-Key benötigt")
        st.stop()

    openai.api_key = openai_api_key

    # ------------------ Dataset Funktionen ------------------
    DATASET_REPO = "AiCodeCraft/customer_memory"  # Repository existiert bereits

    @st.cache_resource
    def load_memory_dataset():
        """
        Versucht, das Memory-Dataset vom HF Hub zu laden.
        Falls nicht vorhanden, wird ein leeres Dataset erstellt und gepusht.
        """
        if not HF_TOKEN_MEMORY:
            logger.warning("Kein HF Token vorhanden, verwende lokales Dataset")
            return Dataset.from_dict({"user_id": [], "query": [], "response": [], "timestamp": []})
        
        try:
            logger.info(f"Versuche Dataset {DATASET_REPO} zu laden...")
            ds = load_dataset(DATASET_REPO, split="train", token=HF_TOKEN_MEMORY)
            logger.info(f"Dataset erfolgreich geladen mit {len(ds)} Einträgen.")
            return ds
        except Exception as e:
            logger.warning(f"Fehler beim Laden des Datasets: {str(e)}")
            logger.info("Erstelle neues Dataset...")
            data = {"user_id": [], "query": [], "response": [], "timestamp": []}
            ds = Dataset.from_dict(data)
            try:
                ds.push_to_hub(DATASET_REPO, token=HF_TOKEN_MEMORY)
                logger.info("Neues Dataset erfolgreich erstellt und gepusht.")
            except Exception as push_error:
                logger.error(f"Fehler beim Pushen des Datasets: {str(push_error)}")
            return ds

    # ------------------ AI Agent Klasse ------------------
    class CustomerSupportAIAgent:
        def __init__(self):
            self.memory = load_memory_dataset()
            self.schedule_push()

        def schedule_push(self):
            # Diese Funktion pusht das Dataset periodisch im Hintergrund
            def push_memory():
                if HF_TOKEN_MEMORY:
                    try:
                        self.memory.push_to_hub(DATASET_REPO, token=HF_TOKEN_MEMORY)
                        logger.info("Memory erfolgreich aktualisiert (periodischer Push)")
                    except Exception as e:
                        logger.error(f"Fehler beim periodischen Aktualisieren des Datasets: {str(e)}")

            schedule.every(10).minutes.do(push_memory)
            threading.Thread(target=self.run_schedule, daemon=True).start()

        def run_schedule(self):
            while True:
                schedule.run_pending()
                time.sleep(1)

        def handle_query(self, query, user_id):
            # Memory abrufen
            user_history = self.memory.filter(lambda x: x["user_id"] == user_id)
            context = "Previous interactions:\n" + "\n".join(
                [f"Q: {h['query']}\nA: {h['response']}" for h in user_history]
            ) if len(user_history) > 0 else "No previous interactions"
            
            # API-Anfrage
            try:
                response = openai.ChatCompletion.create(
                    model="gpt-3.5-turbo",
                    messages=[
                        {"role": "system", "content": f"You are a support agent. Context:\n{context}"},
                        {"role": "user", "content": query}
                    ]
                )
                answer = response.choices[0].message.content
            except Exception as e:
                logger.error(f"Fehler bei OpenAI-API-Anfrage: {str(e)}")
                answer = "Entschuldigung, ein Fehler ist aufgetreten."
            
            # Memory aktualisieren mit Timestamp
            current_time = datetime.datetime.now().isoformat()
            new_entry = Dataset.from_dict({
                "user_id": [user_id],
                "query": [query],
                "response": [answer],
                "timestamp": [current_time]
            })
            self.memory = concatenate_datasets([self.memory, new_entry])
            logger.info(f"Memory lokal aktualisiert für User {user_id}")
            # Der Push erfolgt periodisch im Hintergrund
            return answer

    # ------------------ App-Logik ------------------
    support_agent = CustomerSupportAIAgent()

    # Customer ID Handling
    customer_id = st.sidebar.text_input("Customer ID", key="cust_id")
    if not customer_id:
        logger.warning("Keine Customer ID eingegeben")
        st.stop()

    # Chat-History initialisieren
    if "messages" not in st.session_state:
        st.session_state.messages = []

    # Zeige nur die letzten 20 Nachrichten an
    max_messages = 20
    displayed_messages = st.session_state.messages[-max_messages:]
    for msg in displayed_messages:
        st.chat_message(msg["role"]).write(msg["content"])

    # Eingabe verarbeiten
    if prompt := st.chat_input("Your question"):
        st.session_state.messages.append({"role": "user", "content": prompt})
        st.chat_message("user").write(prompt)
        with st.spinner("Denke nach..."):
            response = support_agent.handle_query(prompt, customer_id)
        st.session_state.messages.append({"role": "assistant", "content": response})
        st.chat_message("assistant").write(response)

# ------------------ Hauptausführung ------------------
if __name__ == "__main__":
    try:
        main()
    except Exception as e:
        logger.error(f"Startup-Fehler: {str(e)}", exc_info=True)