Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

https://doi.org/10.5281/zenodo.15281299
25 April 2025, 10:06:35 UTC
  • Code
  • Branches (0)
  • Releases (1)
  • Visits
    • Branches
    • Releases
      • 1
      • 1
    • c999079
    • /
    • conradhuebler-ALIMA-9149c20
    • /
    • chatbot.py
    Raw File Download

    To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
    Select below a type of object currently browsed in order to display its associated SWHID and permalink.

    • content
    • directory
    • snapshot
    • release
    origin badgecontent badge
    swh:1:cnt:b90580e6131e1d82c8eb286d4bf9024056dc8dde
    origin badgedirectory badge
    swh:1:dir:9c72c77c294e128e1d4610d12e313488bc084d66
    origin badgesnapshot badge
    swh:1:snp:4ca73675f00499f7788b560b527d2d3608e24808
    origin badgerelease badge
    swh:1:rel:536b651fe52569a0b29d9afb23a4c016482765cb

    This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
    Select below a type of object currently browsed in order to generate citations for them.

    • content
    • directory
    • snapshot
    • release
    (requires biblatex-software package)
    Generating citation ...
    (requires biblatex-software package)
    Generating citation ...
    (requires biblatex-software package)
    Generating citation ...
    (requires biblatex-software package)
    Generating citation ...
    chatbot.py
    from pathlib import Path
    from datetime import datetime
    import os
    import sys
    from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,  
                                QPushButton, QTextEdit, QFileDialog, QComboBox, QLabel,
                                QProgressBar, QMessageBox, QHBoxLayout)
    from PyQt6.QtCore import Qt, QThread, pyqtSignal
    from haystack.pipelines.standard_pipelines import TextIndexingPipeline
    from haystack.document_stores import InMemoryDocumentStore
    from haystack.nodes import BM25Retriever
    import logging
    import requests
    import json
    from typing import List, Dict
    import torch
    
    from src.core.llm_interface import LLMInterface  # Ihre LLM Interface Klasse
    
    
    class ChatHistory:
        def __init__(self):
            self.messages = []
            self.max_history = 10  # Maximale Anzahl der gespeicherten Nachrichten
    
        def add_message(self, role: str, content: str, documents: List = None):
            """Fügt eine neue Nachricht zur Historie hinzu"""
            message = {
                "role": role,
                "content": content,
                "timestamp": datetime.now().isoformat(),
                "documents": documents
            }
            self.messages.append(message)
            if len(self.messages) > self.max_history:
                self.messages.pop(0)
    
        def get_context(self, max_messages: int = None) -> str:
            """Erstellt einen Kontext-String aus der Historie"""
            messages_to_use = self.messages[-max_messages:] if max_messages else self.messages
            context = ""
            for msg in messages_to_use:
                context += f"{msg['role']}: {msg['content']}\n"
            return context
    
        def clear(self):
            """Löscht die Historie"""
            self.messages.clear()
    
        def save_to_file(self, filepath: Path):
            """Speichert die Historie in einer JSON-Datei"""
            with open(filepath, 'w', encoding='utf-8') as f:
                json.dump(self.messages, f, ensure_ascii=False, indent=2)
    
        def load_from_file(self, filepath: Path):
            """Lädt die Historie aus einer JSON-Datei"""
            if filepath.exists():
                with open(filepath, 'r', encoding='utf-8') as f:
                    self.messages = json.load(f)
    
    class IndexingWorker(QThread):
        finished = pyqtSignal()
        error = pyqtSignal(str)
        progress = pyqtSignal(int)
    
        def __init__(self, document_store, file_paths):
            super().__init__()
            self.document_store = document_store
            self.file_paths = file_paths
    
        def run(self):
            try:
                indexing_pipeline = TextIndexingPipeline(self.document_store)
                total_files = len(self.file_paths)
    
                for i, file_path in enumerate(self.file_paths):
                    indexing_pipeline.run_batch(file_paths=[file_path])
                    self.progress.emit(int((i + 1) * 100 / total_files))
    
                self.finished.emit()
            except Exception as e:
                self.error.emit(f"Indexierungsfehler: {str(e)}")
    
    class RAGWindow(QMainWindow):
        def __init__(self):
            super().__init__()
            self.setWindowTitle("RAG Chat System")
            self.setGeometry(100, 100, 800, 600)
    
            self.document_store = InMemoryDocumentStore(use_bm25=True)
            self.retriever = None
            self.current_worker = None
            self.chat_history = ChatHistory()
            
            # LLM Interface initialisieren
            self.llm = LLMInterface()
            
            # Lade Modell-Empfehlungen
            recommendations_file = Path(__file__).parent / "chat_recommendations.json"
            self.setup_ui()
            self.check_gpu_status()
    
        def check_gpu_status(self):
            if torch.cuda.is_available():
                gpu_info = f"GPU verfügbar: {torch.cuda.get_device_name(0)}"
            else:
                gpu_info = "Keine GPU verfügbar - CPU-Modus aktiv"
            self.chat_display.append(gpu_info)
    
        def setup_ui(self):
            central_widget = QWidget()
            self.setCentralWidget(central_widget)
            layout = QVBoxLayout(central_widget)
    
            # Provider und Modell-Auswahl
            model_layout = QHBoxLayout()
            
            # Provider Auswahl
            self.provider_combo = QComboBox()
            self.provider_combo.addItems(self.llm.get_available_providers())
            print(self.llm.get_available_providers())
            self.provider_combo.currentTextChanged.connect(self.update_models)
            model_layout.addWidget(QLabel("Provider:"))
            model_layout.addWidget(self.provider_combo)
            
            # Modell Auswahl
            self.model_combo = QComboBox()
            model_layout.addWidget(QLabel("Modell:"))
            model_layout.addWidget(self.model_combo)
            
            layout.addLayout(model_layout)
    
            # Verzeichnis-Auswahl-Button
            self.select_dir_btn = QPushButton("Verzeichnis wählen")
            self.select_dir_btn.clicked.connect(self.select_directory)
            layout.addWidget(self.select_dir_btn)
    
            # Progress Bar
            self.progress_bar = QProgressBar()
            self.progress_bar.setVisible(False)
            layout.addWidget(self.progress_bar)
    
            # Detail-Level Dropdown
            self.detail_level = QComboBox()
            self.detail_level.addItems(["minimum", "medium", "all"])
            layout.addWidget(self.detail_level)
    
            # Chat-Historie
            self.chat_display = QTextEdit()
            self.chat_display.setReadOnly(True)
            layout.addWidget(self.chat_display)
    
            # Chat-Eingabefeld
            self.query_input = QTextEdit()
            self.query_input.setPlaceholderText("Stelle deine Frage hier...")
            self.query_input.setMaximumHeight(100)
            layout.addWidget(self.query_input)
    
            # Button Layout
            button_layout = QHBoxLayout()
            
            # Sende-Button
            self.send_btn = QPushButton("Frage senden")
            self.send_btn.clicked.connect(self.process_query)
            self.send_btn.setEnabled(False)
            button_layout.addWidget(self.send_btn)
    
            # Abbrechen-Button
            self.cancel_btn = QPushButton("Abbrechen")
            self.cancel_btn.clicked.connect(self.cancel_processing)
            self.cancel_btn.setVisible(False)
            button_layout.addWidget(self.cancel_btn)
    
            # Chat-Verwaltungs-Buttons
            self.clear_chat_btn = QPushButton("Chat löschen")
            self.clear_chat_btn.clicked.connect(self.clear_chat)
            button_layout.addWidget(self.clear_chat_btn)
            
            self.save_chat_btn = QPushButton("Chat speichern")
            self.save_chat_btn.clicked.connect(self.save_chat)
            button_layout.addWidget(self.save_chat_btn)
            
            self.load_chat_btn = QPushButton("Chat laden")
            self.load_chat_btn.clicked.connect(self.load_chat)
            button_layout.addWidget(self.load_chat_btn)
            
            layout.addLayout(button_layout)
    
            # Initial models update
      #      self.update_models(self.provider_combo.currentText())
    
        def update_models(self, provider: str):
            """Update available models when provider changes"""
            self.model_combo.clear()
            models = self.llm.get_available_models(provider)
            print(models)
            # Hole empfohlene Modelle für diesen Provider
      #      recommended = self.llm.get_recommended_models(provider)
            
            self.model_combo.addItems(models)
    
        def select_directory(self):
            dir_path = QFileDialog.getExistingDirectory(self, "Wähle Dokumentenverzeichnis")
            if dir_path:
                self.index_documents(dir_path)
    
        def index_documents(self, dir_path):
            try:
                files_to_index = [os.path.join(dir_path, f) for f in os.listdir(dir_path)
                                if f.endswith(('.txt', '.pdf', '.doc', '.docx'))]
    
                if not files_to_index:
                    self.show_error("Keine unterstützten Dokumente gefunden!")
                    return
    
                self.progress_bar.setVisible(True)
                self.select_dir_btn.setEnabled(False)
                self.send_btn.setEnabled(False)
    
                self.indexing_worker = IndexingWorker(self.document_store, files_to_index)
                self.indexing_worker.progress.connect(self.progress_bar.setValue)
                self.indexing_worker.finished.connect(self.indexing_finished)
                self.indexing_worker.error.connect(self.show_error)
                self.indexing_worker.start()
    
            except Exception as e:
                self.show_error(f"Fehler beim Indexieren: {str(e)}")
    
        def indexing_finished(self):
            self.retriever = BM25Retriever(document_store=self.document_store)
            self.progress_bar.setVisible(False)
            self.select_dir_btn.setEnabled(True)
            self.send_btn.setEnabled(True)
            self.chat_display.append("Dokumente erfolgreich indexiert!")
    
        def format_documents(self, documents: List) -> str:
            """Formatiert die Dokumente für den Prompt"""
            formatted = ""
            for i, doc in enumerate(documents, 1):
                formatted += f"\nDokument {i}:\n{doc.content[:1000]}\n"
            return formatted
    
        def process_query(self):
            if not self.retriever:
                self.show_error("Bitte zuerst Dokumente indexieren!")
                return
    
            query = self.query_input.toPlainText().strip()
            if not query:
                return
    
            # Füge Benutzeranfrage zur Historie hinzu
            self.chat_history.add_message("user", query)
            self.update_chat_display()
    
            retrieved_docs = self.retriever.retrieve(query, top_k=5)
    
            # Erstelle Prompt mit Chat-Historie
            context = self.chat_history.get_context(max_messages=5)
            prompt = f"""Kontext der Konversation:
    {context}
    
    Basierend auf den folgenden Dokumenten und dem Kontext, beantworte bitte die letzte Frage.
    Wenn du die Antwort nicht in den Dokumenten findest, sage das ehrlich.
    
    Verfügbare Dokumente:
    {self.format_documents(retrieved_docs)}
    
    Antworte bitte ausführlich und präzise.
    """
    
            self.progress_bar.setVisible(True)
            self.send_btn.setEnabled(False)
            self.cancel_btn.setVisible(True)
    
            try:
                provider = self.provider_combo.currentText()
                model = self.model_combo.currentText().strip()
                
                response = self.llm.generate_response(
                    provider=provider,
                    model=model,
                    prompt=prompt
                )
                
                # Füge Antwort zur Historie hinzu
                self.chat_history.add_message("assistant", response, documents=retrieved_docs)
                self.update_chat_display()
                
            except Exception as e:
                self.show_error(f"Fehler bei der Verarbeitung: {str(e)}")
            finally:
                self.progress_bar.setVisible(False)
                self.send_btn.setEnabled(True)
                self.cancel_btn.setVisible(False)
                self.query_input.clear()
    
        def cancel_processing(self):
            if self.current_worker and self.current_worker.isRunning():
                self.current_worker.stop()
                self.current_worker.wait()
                self.progress_bar.setVisible(False)
                self.send_btn.setEnabled(True)
                self.cancel_btn.setVisible(False)
                self.chat_display.append("\n[Abgebrochen]")
    
        def update_chat_display(self):
            """Aktualisiert die Chat-Anzeige"""
            self.chat_display.clear()
            for message in self.chat_history.messages:
                role = message["role"]
                content = message["content"]
                
                # Formatierung je nach Rolle
                if role == "user":
                    self.chat_display.append(f"Sie: {content}\n")
                else:
                    self.chat_display.append(f"Assistant: {content}\n")
                    
                # Zeige Dokumente wenn gewünscht
                if self.detail_level.currentText() != "minimum" and message.get("documents"):
                    self.chat_display.append("\nVerwendete Dokumente:")
                    for doc in message["documents"][:2]:
                        self.chat_display.append(f"- {doc.content[:200]}...\n")
                
                self.chat_display.append("-" * 50 + "\n")
    
        def clear_chat(self):
            """Löscht den Chat-Verlauf"""
            reply = QMessageBox.question(
                self, 
                "Chat löschen",
                "Möchten Sie wirklich den gesamten Chat-Verlauf löschen?",
                QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
            )
            
            if reply == QMessageBox.StandardButton.Yes:
                self.chat_history.clear()
                self.update_chat_display()
    
        def save_chat(self):
            """Speichert den Chat-Verlauf"""
            filepath, _ = QFileDialog.getSaveFileName(
                self,
                "Chat speichern",
                "",
                "JSON Dateien (*.json)"
            )
            if filepath:
                self.chat_history.save_to_file(Path(filepath))
    
        def load_chat(self):
            """Lädt einen gespeicherten Chat-Verlauf"""
            filepath, _ = QFileDialog.getOpenFileName(
                self,
                "Chat laden",
                "",
                "JSON Dateien (*.json)"
            )
            if filepath:
                self.chat_history.load_from_file(Path(filepath))
                self.update_chat_display()
    
        def show_error(self, error_message):
            """Zeigt eine Fehlermeldung an"""
            self.progress_bar.setVisible(False)
            self.send_btn.setEnabled(True)
            self.cancel_btn.setVisible(False)
            QMessageBox.critical(self, "Fehler", error_message)
    
    def main():
        logging.basicConfig(format="%(levelname)s - %(name)s -  %(message)s", level=logging.WARNING)
        logging.getLogger("haystack").setLevel(logging.INFO)
    
        app = QApplication(sys.argv)
        window = RAGWindow()
        window.show()
        sys.exit(app.exec())
    
    if __name__ == '__main__':
        main()
    

    back to top

    Software Heritage — Copyright (C) 2015–2026, The Software Heritage developers. License: GNU AGPLv3+.
    The source code of Software Heritage itself is available on our development forge.
    The source code files archived by Software Heritage are available under their own copyright and licenses.
    Terms of use: Archive access, API— Content policy— Contact— JavaScript license information— Web API