daniel vor 7 Monaten
Commit
41e6dd504d

+ 9 - 0
.idea/.gitignore

@@ -0,0 +1,9 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+

+ 8 - 0
.idea/deepseek-test-1.iml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/deepseek-test-1.iml" filepath="$PROJECT_DIR$/.idea/deepseek-test-1.iml" />
+    </modules>
+  </component>
+</project>

+ 22 - 0
.idea/php.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="MessDetectorOptionsConfiguration">
+    <option name="transferred" value="true" />
+  </component>
+  <component name="PHPCSFixerOptionsConfiguration">
+    <option name="transferred" value="true" />
+  </component>
+  <component name="PHPCodeSnifferOptionsConfiguration">
+    <option name="highlightLevel" value="WARNING" />
+    <option name="transferred" value="true" />
+  </component>
+  <component name="PhpStanOptionsConfiguration">
+    <option name="transferred" value="true" />
+  </component>
+  <component name="PhpTerminalSettingsCustomizer">
+    <option name="myIsInterpreterAdded" value="false" />
+  </component>
+  <component name="PsalmOptionsConfiguration">
+    <option name="transferred" value="true" />
+  </component>
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+  </component>
+</project>

+ 24 - 0
api/CsvReader.php

@@ -0,0 +1,24 @@
+<?php
+class CsvReader {
+    private $dir;
+
+    public function __construct($directory) {
+        $this->dir = rtrim($directory, '/') . '/';
+    }
+
+    public function readAll() {
+        $rows = array();
+
+        foreach (glob($this->dir . '*.csv') as $file) {
+            if (($h = fopen($file, 'r')) !== false) {
+                $header = fgetcsv($h, 0, ',', '"', '');
+
+                while (($data = fgetcsv($h, 0, ',', '"', '')) !== false) {
+                    $rows[] = array_combine($header, $data);
+                }
+                fclose($h);
+            }
+        }
+        return $rows;
+    }
+}

+ 67 - 0
api/chatbot.php

@@ -0,0 +1,67 @@
+<?php
+require_once 'CsvReader.php';
+
+header('Content-Type: application/json');
+
+// only accept POST requests
+if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
+    http_response_code(405);
+    echo json_encode(['error' => 'Only POST allowed']);
+    exit;
+}
+
+function extractPdfText($file) {
+    $tmp = tempnam(sys_get_temp_dir(), 'pdftext');
+    exec("pdftotext " . escapeshellarg($file) . " " . escapeshellarg($tmp));
+    $text = file_get_contents($tmp);
+    unlink($tmp);
+    return $text ?: '';
+}
+
+// ### Build prompt for deepseek
+$input = json_decode(file_get_contents('php://input'), true);
+$question = isset($input['question']) ? $input['question'] : '';
+$roleFile = __DIR__ . '/data/role.txt';
+$role = file_exists($roleFile) ? file_get_contents($roleFile) : '';
+
+$reader = new CsvReader(__DIR__ . '/data/csv');
+$rows = $reader->readAll();
+$contextText = "";
+foreach ($rows as $row) {
+    $contextText .= "- " . implode(" | ", $row) . "\n";
+}
+$pdf1 = extractPdfText(__DIR__ . '/data/pdf/onboarding.pdf');
+$pdf2 = extractPdfText(__DIR__ . '/data/pdf/urlaub.pdf');
+
+$pdfContext = "\n\nAus Onboarding-Dokument:\n" . $pdf1 . "\n\nAus Urlaubsdokument:\n" . $pdf2;
+
+$prompt = "Deine Rolle ist: ". $role;
+$prompt .= "\nDeine Frage ist: " . $question;
+$prompt .= "\nCSV-Daten:\n" . $contextText;
+$prompt .= "\nPDF-Kontext:\n" . $pdfContext;
+
+$payload = json_encode([
+    "model" => "deepseek-r1",
+    "prompt" => $prompt,
+    "stream" => false
+]);
+
+// ### Send Request
+$ch = curl_init('http://localhost:11434/api/generate');
+curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+curl_setopt($ch, CURLOPT_POST, true);
+curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
+curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
+$response = curl_exec($ch);
+curl_close($ch);
+$data = json_decode($response, true);
+
+// ### Prepare Response
+$responseText = isset($data['response']) ? $data['response'] : 'No answer from Deepseek!';
+$responseText = preg_replace('/<think>.*?<\/think>/s', ' ', $responseText);
+$responseText = trim($responseText);
+
+echo json_encode([
+    'reply' => $responseText,
+    'misc' => $pdfContext
+]);

+ 6 - 0
api/data/csv/drink.csv

@@ -0,0 +1,6 @@
+Item,Price,Unit
+Wasser,0.00,EUR
+Kaffee,1.20,EUR
+Tee,1.00,EUR
+Saft,2.50,EUR
+Softdrink,2.00,EUR

+ 6 - 0
api/data/csv/food.csv

@@ -0,0 +1,6 @@
+Item,Price,Unit
+Sandwich,3.50,EUR
+Salat,4.20,EUR
+Pasta,5.80,EUR
+Suppe,3.00,EUR
+Brotzeit,2.50,EUR

BIN
api/data/pdf/onboarding.pdf


BIN
api/data/pdf/urlaub.pdf


+ 17 - 0
api/data/role.txt

@@ -0,0 +1,17 @@
+Du bist ein HR-Chatbot mit Fokus auf:
+
+- Arbeitszeitenregelungen (Home-Office, Pausen)
+- Preise & Kosten (Kaffee, Getränke, Leistungen)
+
+Deine Aufgabe:
+- Antworte **freundlich, präzise und direkt** auf Fragen zu diesen Themen.
+- Wenn du die Antwort **aus dem Kontext** ableiten kannst, sag sie klar.
+- Wenn du **unsicher** bist oder die Info **fehlt**, stelle eine gezielte Rückfrage.
+- Vermeide lange Einleitungen oder Gedankengänge, bleib bei der Sache.
+
+Dein Nutzen:
+- Du gibst kurze und prägnante Antworten für gängige Fragen, welche Mitarbeitende häufig
+im Berufsalltag stellen.
+
+Vorstellung:
+- Stelle dich als: "Hallo, ich bin Dodo - ihr Büroassistent bei Cybob ^_^".

+ 26 - 0
client/deepseek-request.php

@@ -0,0 +1,26 @@
+<?php
+
+$frage = isset($_POST['frage']) ? $_POST['frage'] : '';
+$kontext = isset($_POST['kontext']) ? $_POST['kontext'] : '';
+
+$prompt = "Du bist ein HR-Bot. Antworte ausschließlich basierend auf dem folgenden Kontext:\n\n" . $kontext . "\n\nFrage: " . $frage . "\nAntwort:";
+
+$payload = json_encode(array(
+    "model" => "deepseek-r1",
+    "prompt" => $prompt,
+    "stream" => false
+));
+
+$ch = curl_init('http://localhost:11434/api/generate');
+curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+curl_setopt($ch, CURLOPT_POST, true);
+curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
+curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
+
+$response = curl_exec($ch);
+curl_close($ch);
+
+$data = json_decode($response, true);
+$antwort = isset($data['response']) ? $data['response'] : 'Fehler bei DeepSeek';
+
+echo "<h3>Antwort vom HR-Bot:</h3><pre>" . htmlspecialchars($antwort) . "</pre>";

+ 107 - 0
client/index.html

@@ -0,0 +1,107 @@
+<!DOCTYPE html>
+<html lang="de">
+<head>
+  <meta charset="UTF-8">
+  <title>HR-Bot Webinterface</title>
+  <style>
+    body {
+      margin: 0;
+      padding: 0;
+      font-family: 'Segoe UI', sans-serif;
+      background: linear-gradient(135deg, #ce2d6d 0%, #143d83 100%);
+      height: 100vh;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+
+    .glass-container {
+      background: rgba(255, 255, 255, 0.2);
+      border-radius: 20px;
+      box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
+      backdrop-filter: blur(10px);
+      -webkit-backdrop-filter: blur(10px);
+      border: 1px solid rgba(255, 255, 255, 0.18);
+      padding: 2em;
+      width: 90%;
+      max-width: 600px;
+      color: #fff;
+      text-align: center;
+      animation: fadeIn 1s ease-out;
+    }
+
+    textarea {
+      width: 100%;
+      padding: 1em;
+      border-radius: 10px;
+      border: none;
+      resize: none;
+      font-size: 1em;
+    }
+
+    button {
+      padding: 0.8em 2em;
+      margin-top: 1em;
+      border: none;
+      border-radius: 8px;
+      background-color: #ffffff33;
+      color: white;
+      font-size: 1em;
+      cursor: pointer;
+      transition: background 0.3s ease;
+    }
+
+    button:hover {
+      background-color: #ffffff55;
+    }
+
+    #responseBox {
+      margin-top: 1.5em;
+      background: rgba(255, 255, 255, 0.1);
+      border-radius: 10px;
+      padding: 1em;
+      min-height: 4em;
+      white-space: pre-wrap;
+    }
+
+    @keyframes fadeIn {
+      from { opacity: 0; transform: translateY(20px); }
+      to { opacity: 1; transform: translateY(0); }
+    }
+  </style>
+</head>
+<body>
+<div class="glass-container">
+  <h2>🤖 HR-Bot (DeepSeek lokal)</h2>
+
+  <textarea id="question" rows="3" placeholder="Frage eingeben..." >
+    Hallo wieviel Tage hab ich noch frei ich hatte 4 Urlaubstage
+  </textarea><br>
+  <button id="sendBtn">Frage stellen</button>
+
+  <div id="responseBox">Antwort erscheint hier...</div>
+</div>
+
+<script>
+  document.getElementById('sendBtn').addEventListener('click', async () => {
+    const frage = document.getElementById('question').value.trim();
+    if (!frage) return;
+
+    const box = document.getElementById('responseBox');
+    box.textContent = '⏳ Bitte warten...';
+
+    try {
+      const res = await fetch('/api/chatbot.php', {
+        method: 'POST',
+        headers: {'Content-Type': 'application/json'},
+        body: JSON.stringify({ question: frage })
+      });
+      const json = await res.json();
+      box.textContent = json.reply || json.answer || '❗️Keine Antwort';
+    } catch (e) {
+      box.textContent = 'Fehler: ' + e.message;
+    }
+  });
+</script>
+</body>
+</html>

+ 5 - 0
scraper/getEntries.js

@@ -0,0 +1,5 @@
+const puppeteer = require('puppeteer');
+const fs = require('fs');
+const path = require('path');
+
+const BASE_URL = 'https://projects.cybob.com/ajax/&am=Knowledgebase.showEntryList&search=&tags=undefined';