Spaces:
Configuration error
Configuration error
Initial commit
Browse files- .env +3 -0
- README.md +15 -0
- main.py +49 -0
- models.py +15 -0
- requirements.txt +5 -0
- space-config.json +3 -0
- static/app.js +20 -0
- static/index.html +28 -0
- static/style.css +6 -0
.env
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Optional environment variables
|
| 2 |
+
OPENAI_API_KEY=YOUR_KEY
|
| 3 |
+
DEFAULT_EXPIRY=2026-05-10
|
README.md
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
---
|
| 2 |
title: Schulcomply Beta
|
| 3 |
emoji: 📉
|
|
@@ -12,3 +13,17 @@ short_description: AI Compliance Coach for Schools
|
|
| 12 |
---
|
| 13 |
|
| 14 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
HEAD
|
| 2 |
---
|
| 3 |
title: Schulcomply Beta
|
| 4 |
emoji: 📉
|
|
|
|
| 13 |
---
|
| 14 |
|
| 15 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
| 16 |
+
|
| 17 |
+
Dieses Proje
|
| 18 |
+
kt stellt einen einfachen GDPR-Compliance-Coach als Web-App bereit.
|
| 19 |
+
|
| 20 |
+
## Setup
|
| 21 |
+
```bash
|
| 22 |
+
pip install -r requirements.txt
|
| 23 |
+
export OPENAI_API_KEY=YOUR_KEY
|
| 24 |
+
python main.py
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
## Deployment
|
| 28 |
+
In Hugging Face Spaces: keine Docker nötig, der Space liest SDK `static` ein und liefert das Frontend mit FastAPI.
|
| 29 |
+
>>>>>>> c3d1599 (Initial commit)
|
main.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI, HTTPException
|
| 2 |
+
from fastapi.staticfiles import StaticFiles
|
| 3 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 4 |
+
from models import ChatRequest, ChatResponse, ContractRequest, ContractResponse
|
| 5 |
+
import os
|
| 6 |
+
import openai
|
| 7 |
+
|
| 8 |
+
# Load environment
|
| 9 |
+
from dotenv import load_dotenv
|
| 10 |
+
load_dotenv()
|
| 11 |
+
openai.api_key = os.getenv("OPENAI_API_KEY")
|
| 12 |
+
|
| 13 |
+
app = FastAPI(title="SchulComply 2025 Backend")
|
| 14 |
+
app.add_middleware(
|
| 15 |
+
CORSMiddleware,
|
| 16 |
+
allow_origins=["*"],
|
| 17 |
+
allow_methods=["GET", "POST"],
|
| 18 |
+
allow_headers=["*"],
|
| 19 |
+
)
|
| 20 |
+
|
| 21 |
+
# Serve static files
|
| 22 |
+
app.mount("/", StaticFiles(directory="static", html=True), name="static")
|
| 23 |
+
|
| 24 |
+
@app.post("/api/chat", response_model=ChatResponse)
|
| 25 |
+
async def chat(req: ChatRequest):
|
| 26 |
+
# call OpenAI to generate compliance advice
|
| 27 |
+
resp = openai.ChatCompletion.create(
|
| 28 |
+
model="gpt-4",
|
| 29 |
+
messages=[{"role": "system", "content": "You are a GDPR compliance assistant."},
|
| 30 |
+
{"role": "user", "content": req.message}]
|
| 31 |
+
)
|
| 32 |
+
reply = resp.choices[0].message.content
|
| 33 |
+
return ChatResponse(reply=reply)
|
| 34 |
+
|
| 35 |
+
@app.post("/api/contracts", response_model=ContractResponse)
|
| 36 |
+
async def create_contract(req: ContractRequest):
|
| 37 |
+
# Dummy contract generation
|
| 38 |
+
contract_id = f"AV-{os.urandom(4).hex().upper()}"
|
| 39 |
+
expires = (os.getenv("DEFAULT_EXPIRY", "2026-05-10"))
|
| 40 |
+
return ContractResponse(contract_id=contract_id, expires_on=expires)
|
| 41 |
+
|
| 42 |
+
@app.get("/api/risk-analysis/{tool_name}")
|
| 43 |
+
async def risk_analysis(tool_name: str):
|
| 44 |
+
# Placeholder risk scores
|
| 45 |
+
return {"tool": tool_name, "risk_level": "niedrig"}
|
| 46 |
+
|
| 47 |
+
if __name__ == "__main__":
|
| 48 |
+
import uvicorn
|
| 49 |
+
uvicorn.run("main:app", host="0.0.0.0", port=int(os.environ.get("PORT", 7860)))
|
models.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pydantic import BaseModel
|
| 2 |
+
|
| 3 |
+
class ChatRequest(BaseModel):
|
| 4 |
+
message: str
|
| 5 |
+
|
| 6 |
+
class ChatResponse(BaseModel):
|
| 7 |
+
reply: str
|
| 8 |
+
|
| 9 |
+
class ContractRequest(BaseModel):
|
| 10 |
+
vendor: str
|
| 11 |
+
services: str
|
| 12 |
+
|
| 13 |
+
class ContractResponse(BaseModel):
|
| 14 |
+
contract_id: str
|
| 15 |
+
expires_on: str
|
requirements.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi
|
| 2 |
+
uvicorn[standard]
|
| 3 |
+
pydantic
|
| 4 |
+
python-dotenv
|
| 5 |
+
openai
|
space-config.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"sdk": "static"
|
| 3 |
+
}
|
static/app.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
async function postData(url = '', data = {}) {
|
| 2 |
+
const res = await fetch(url, {
|
| 3 |
+
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data)
|
| 4 |
+
});
|
| 5 |
+
return res.json();
|
| 6 |
+
}
|
| 7 |
+
|
| 8 |
+
document.getElementById('chat-send').onclick = async () => {
|
| 9 |
+
const msg = document.getElementById('chat-input').value;
|
| 10 |
+
const res = await postData('/api/chat', { message: msg });
|
| 11 |
+
document.getElementById('chat-output').innerText = res.reply;
|
| 12 |
+
};
|
| 13 |
+
|
| 14 |
+
document.getElementById('contract-send').onclick = async () => {
|
| 15 |
+
const vendor = document.getElementById('vendor').value;
|
| 16 |
+
const services = document.getElementById('services').value;
|
| 17 |
+
const res = await postData('/api/contracts', { vendor, services });
|
| 18 |
+
document.getElementById('contract-output').innerText = `Vertrag-ID: ${res.contract_id}
|
| 19 |
+
Gültig bis: ${res.expires_on}`;
|
| 20 |
+
};
|
static/index.html
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="de">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>SchulComply 2025</title>
|
| 7 |
+
<link rel="stylesheet" href="/style.css">
|
| 8 |
+
</head>
|
| 9 |
+
<body>
|
| 10 |
+
<header><h1>SchulComply 2025</h1></header>
|
| 11 |
+
<main>
|
| 12 |
+
<section id="chat-section">
|
| 13 |
+
<h2>Compliance-Chat</h2>
|
| 14 |
+
<textarea id="chat-input" placeholder="Ihre Frage zur DSGVO..."></textarea>
|
| 15 |
+
<button id="chat-send">Senden</button>
|
| 16 |
+
<div id="chat-output"></div>
|
| 17 |
+
</section>
|
| 18 |
+
<section id="contract-section">
|
| 19 |
+
<h2>AV-Vertrag erstellen</h2>
|
| 20 |
+
<input id="vendor" placeholder="Anbieter...">
|
| 21 |
+
<input id="services" placeholder="Leistungen...">
|
| 22 |
+
<button id="contract-send">Erstellen</button>
|
| 23 |
+
<div id="contract-output"></div>
|
| 24 |
+
</section>
|
| 25 |
+
</main>
|
| 26 |
+
<script src="/app.js"></script>
|
| 27 |
+
</body>
|
| 28 |
+
</html>
|
static/style.css
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
body { font-family: Arial, sans-serif; max-width: 800px; margin: auto; padding: 1rem; }
|
| 2 |
+
header { text-align: center; margin-bottom: 2rem; }
|
| 3 |
+
section { margin-bottom: 2rem; border: 1px solid #ccc; padding: 1rem; border-radius: 8px; }
|
| 4 |
+
textarea, input { width: 100%; padding: .5rem; margin-bottom: .5rem; }
|
| 5 |
+
button { padding: .5rem 1rem; border: none; cursor: pointer; border-radius: 4px; background: #007ACC; color: white; }
|
| 6 |
+
#chat-output, #contract-output { margin-top: 1rem; white-space: pre-wrap; background: #f9f9f9; padding: .5rem; border-radius: 4px; }
|