Spaces:
Paused
Paused
File size: 6,435 Bytes
7606f4c 1b5d2ca eeb5b5c 45dc038 6e26e1d eeb5b5c 3d18df1 45dc038 0cfb1eb 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 93ddfa4 0cfb1eb 701465f 93ddfa4 701465f 93ddfa4 701465f 93ddfa4 0cfb1eb 701465f 3d18df1 701465f 93ddfa4 701465f a3b124a 701465f 93ddfa4 701465f 93ddfa4 701465f 0cfb1eb 93ddfa4 0cfb1eb 1b5d2ca a3b124a 93ddfa4 bb1daa0 3d18df1 93ddfa4 3d18df1 93ddfa4 212e616 3d18df1 212e616 3d18df1 | 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 188 189 190 191 192 193 | 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"
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 ---
# Holt sicher die ID und die Argumente aus dem Vapi-Salat
def parse_vapi_request(data):
tool_call_id = "unknown"
args = {}
try:
msg = data.get("message", {})
# Variante A: Dein Log-Format ("toolCallList")
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"]
# Variante B: Standard Vapi ("toolCalls")
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"]
# Argumente sind oft ein String, müssen zu JSON werden
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 (Strict Vapi Format)
# ==========================================
@app.post("/check_availability")
async def check_availability(request: Request):
data = await request.json()
print(f"🚦 TOOL REQUEST: {json.dumps(data)[:100]}...") # Debug Log
# 1. ID extrahieren (WICHTIG!)
tool_call_id, _ = parse_vapi_request(data)
today = datetime.now().strftime("%Y-%m-%d")
status = "available"
instruction = "Normal arbeiten"
# 2. Datenbank Check
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')}")
status = "unavailable"
instruction = rd.get('instruction_text')
break
except Exception as e:
print(f"❌ ERROR CHECK: {e}")
print(f"👉 RESULTAT: {status} | {instruction}")
# 3. Antwort im strikten Vapi-Format
return {
"results": [
{
"toolCallId": tool_call_id,
"result": {
"status": status,
"instruction": instruction
}
}
]
}
# ==========================================
# TOOL 2: SUCHE (Jetzt auch mit ID Rückgabe!)
# ==========================================
@app.post("/search")
async def search(request: Request):
data = await request.json()
# 1. ID und Query extrahieren
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 keine Informationen."
if query:
# --- SUCHE LOGIK (Mit Udo/Capaneo Boost) ---
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]
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
if best_doc and best_score >= 20:
print(f"🏆 TREFFER ({best_score}): {best_doc.get('question')}")
answer_text = best_doc.get("answer")
else:
print(f"⚠️ Zu wenig Relevanz (Max: {best_score})")
# 3. Antwort im strikten Vapi-Format
# Bei 'search' erwartet das LLM meist einfach den Text im result
return {
"results": [
{
"toolCallId": tool_call_id,
"result": answer_text
}
]
}
# ==========================================
# 3. MÜLLSCHLUCKER
# ==========================================
@app.post("/vapi-incoming")
async def dummy_incoming(request: Request):
return {"status": "ok"}
@app.get("/")
def home():
return {"status": "Online", "docs_loaded": len(KNOWLEDGE_CACHE)} |