Spaces:
Paused
Paused
File size: 6,077 Bytes
7606f4c 1b5d2ca eeb5b5c 45dc038 6e26e1d eeb5b5c 3d18df1 45dc038 0cfb1eb bbd2971 db10d88 45dc038 0459685 45dc038 c106ebb bb1daa0 eeb5b5c bb1daa0 eeb5b5c bb1daa0 eeb5b5c bb1daa0 6e26e1d a3b124a db10d88 3d18df1 db10d88 a3b124a bb1daa0 db10d88 a3b124a db10d88 a3b124a db10d88 3e87852 a3b124a 3e87852 93ddfa4 0cfb1eb bbd2971 0cfb1eb 701465f 93ddfa4 701465f 93ddfa4 701465f 0cfb1eb 701465f 3d18df1 bbd2971 93ddfa4 701465f a3b124a 701465f bbd2971 701465f 0cfb1eb bbd2971 0cfb1eb 1b5d2ca a3b124a 93ddfa4 bbd2971 93ddfa4 bb1daa0 3d18df1 bbd2971 93ddfa4 3d18df1 93ddfa4 bbd2971 93ddfa4 bbd2971 93ddfa4 bbd2971 93ddfa4 bbd2971 93ddfa4 212e616 3d18df1 bbd2971 | 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 | from fastapi import FastAPI, Request
import json
import os
import firebase_admin
from firebase_admin import credentials, firestore
from datetime import datetime
app = FastAPI()
# --- SETUP ---
COLLECTION_KNOWLEDGE = "knowledge_base"
COLLECTION_RULES = "availability_rules"
COLLECTION_INBOX = "inbox" # NEU: Hier landen ungelöste Fragen
KNOWLEDGE_CACHE = []
# --- FIREBASE VERBINDUNG ---
db = None
try:
key = os.environ.get("FIREBASE_KEY")
if key:
cred = credentials.Certificate(json.loads(key))
if not firebase_admin._apps:
firebase_admin.initialize_app(cred)
db = firestore.client()
print("✅ DB VERBUNDEN")
else:
print("❌ FEHLER: FIREBASE_KEY fehlt!")
except Exception as e:
print(f"❌ DB CRASH: {e}")
# --- CACHE LADEN ---
def reload_knowledge():
global KNOWLEDGE_CACHE
if not db: return
try:
docs = db.collection(COLLECTION_KNOWLEDGE).stream()
KNOWLEDGE_CACHE = [d.to_dict() for d in docs]
print(f"📚 {len(KNOWLEDGE_CACHE)} Einträge geladen.")
except Exception as e:
print(f"❌ Cache Fehler: {e}")
@app.on_event("startup")
async def startup():
reload_knowledge()
def get_stem(word):
w = word.lower().strip()
for end in ["ern", "en", "er", "es", "st", "te", "e", "s", "t"]:
if w.endswith(end) and len(w) > len(end)+2: return w[:-len(end)]
return w
# --- HELPER: VAPI REQUEST PARSER ---
def parse_vapi_request(data):
tool_call_id = "unknown"
args = {}
try:
msg = data.get("message", {})
if "toolCallList" in msg:
call = msg["toolCallList"][0]
tool_call_id = call["id"]
if "function" in call and "arguments" in call["function"]:
args = call["function"]["arguments"]
elif "toolCalls" in msg:
call = msg["toolCalls"][0]
tool_call_id = call["id"]
if "function" in call and "arguments" in call["function"]:
args = call["function"]["arguments"]
if isinstance(args, str):
args = json.loads(args)
except Exception as e:
print(f"⚠️ Parsing Info: {e}")
return tool_call_id, args
# ==========================================
# TOOL 1: VERFÜGBARKEIT
# ==========================================
@app.post("/check_availability")
async def check_availability(request: Request):
data = await request.json()
tool_call_id, _ = parse_vapi_request(data)
today = datetime.now().strftime("%Y-%m-%d")
status = "available"
instruction = "Normal arbeiten"
try:
if db:
rules = db.collection(COLLECTION_RULES).where("active", "==", True).stream()
for r in rules:
rd = r.to_dict()
if rd.get('start_date') <= today <= rd.get('end_date'):
print(f"🛑 REGEL AKTIV: {rd.get('name')}")
# Einfache Logik: Wenn "Ferien" im Namen -> Limited, sonst Unavailable
if "ferien" in rd.get('name', '').lower():
status = "limited"
else:
status = "unavailable"
instruction = rd.get('instruction_text')
break
except Exception as e:
print(f"❌ ERROR CHECK: {e}")
return {
"results": [{"toolCallId": tool_call_id, "result": {"status": status, "instruction": instruction}}]
}
# ==========================================
# TOOL 2: SUCHE (Mit Inbox-Speicherung!)
# ==========================================
@app.post("/search")
async def search(request: Request):
data = await request.json()
tool_call_id, args = parse_vapi_request(data)
query = args.get("search_query") or args.get("query") or data.get("search_query")
print(f"🔎 FRAGE (ID: {tool_call_id}): '{query}'")
answer_text = "Dazu habe ich leider keine Informationen in meiner Datenbank."
if query:
STOP_WORDS = ["hallo", "guten", "tag", "moin", "bitte", "danke", "frage"]
q_words = [get_stem(w) for w in query.lower().split() if len(w)>2]
relevant_words = [w for w in q_words if w not in STOP_WORDS]
found = False
if relevant_words:
best_doc = None
best_score = 0
for doc in KNOWLEDGE_CACHE:
score = 0
title = doc.get("question", "").lower()
content = doc.get("answer", "").lower()
keywords = [k.lower() for k in doc.get("keywords", [])]
for word in relevant_words:
if word in title: score += 50
for k in keywords:
if get_stem(k) == get_stem(word): score += 30
if word in content: score += 5
if score > best_score:
best_score = score
best_doc = doc
# SCHWELLE: 20 PUNKTE
if best_doc and best_score >= 20:
print(f"🏆 TREFFER ({best_score}): {best_doc.get('question')}")
answer_text = best_doc.get("answer")
found = True
else:
print(f"⚠️ Zu wenig Relevanz (Max: {best_score})")
# --- NEU: SPEICHERN WENN NICHT GEFUNDEN ---
if not found and db:
print("📥 Speichere in Inbox...")
db.collection(COLLECTION_INBOX).add({
"query": query,
"timestamp": datetime.now(),
"status": "open"
})
return {
"results": [{"toolCallId": tool_call_id, "result": answer_text}]
}
@app.post("/vapi-incoming")
async def dummy_incoming(request: Request):
return {"status": "ok"}
@app.get("/")
def home():
return {"status": "Online", "docs": len(KNOWLEDGE_CACHE)} |