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)}