erdemyavuz commited on
Commit
0e365b9
·
1 Parent(s): 3dbf4e8

Added app folder and backend services

Browse files
app/__pycache__/main.cpython-310.pyc ADDED
Binary file (368 Bytes). View file
 
app/__pycache__/main.cpython-311.pyc ADDED
Binary file (512 Bytes). View file
 
app/__pycache__/main.cpython-313.pyc ADDED
Binary file (423 Bytes). View file
 
app/main.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+
4
+ # Router importu
5
+ from app.routes import chat # <--- chat.py dosyan buradan geliyor
6
+
7
+ app = FastAPI(
8
+ title="AI Memory Graph",
9
+ version="0.1.0",
10
+ description="Triplet extraction, graph, query & QA"
11
+ )
12
+
13
+ # CORS (UI veya farklı origin’den çağrı için)
14
+ app.add_middleware(
15
+ CORSMiddleware,
16
+ allow_origins=["*"], # ilk aşamada geniş bırak
17
+ allow_credentials=True,
18
+ allow_methods=["*"],
19
+ allow_headers=["*"],
20
+ )
21
+
22
+ # 🔗 Chat router'ını ekle
23
+ app.include_router(chat.router, prefix="", tags=["memory-graph"])
24
+
25
+ # ✅ Root path: GET + HEAD
26
+ @app.api_route("/", methods=["GET", "HEAD"])
27
+ def root():
28
+ return {"status": "ok", "service": "ai-memory-graph"}
29
+
30
+ # Health endpoint (kontrol için)
31
+ @app.get("/healthz")
32
+ def healthz():
33
+ return {"status": "ok"}
app/routes/__pycache__/chat.cpython-310.pyc ADDED
Binary file (6.19 kB). View file
 
app/routes/__pycache__/chat.cpython-311.pyc ADDED
Binary file (11.5 kB). View file
 
app/routes/__pycache__/chat.cpython-313.pyc ADDED
Binary file (1.14 kB). View file
 
app/routes/chat.py ADDED
@@ -0,0 +1,284 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, Query
2
+ from pydantic import BaseModel
3
+ from typing import List
4
+ import os
5
+ import json
6
+
7
+ from app.services.nlp_triplet import extract_triplets_from_text
8
+ from app.services.graph_builder import build_graph_from_triplets, export_graph_as_edges
9
+ from app.services.memory_engine import (
10
+ group_by_author,
11
+ count_predicates,
12
+ most_common_subjects,
13
+ get_triplets_by_author,
14
+ get_triplets_by_subject,
15
+ get_triplets_by_predicate,
16
+ query_memory,
17
+ )
18
+
19
+ router = APIRouter()
20
+
21
+
22
+ class Message(BaseModel):
23
+ sender: str
24
+ text: str
25
+ timestamp: str
26
+
27
+
28
+ # ----------------------------------------------------------
29
+ # Triplet Extraction
30
+ # ----------------------------------------------------------
31
+ @router.post("/extract")
32
+ async def extract_triplets(messages: List[Message]):
33
+ all_triplets = []
34
+
35
+ for msg in messages:
36
+ extracted = extract_triplets_from_text(msg.text)
37
+ for triplet in extracted:
38
+ triplet["timestamp"] = msg.timestamp
39
+ triplet["author"] = msg.sender
40
+ all_triplets.append(triplet)
41
+
42
+ graph = build_graph_from_triplets(all_triplets)
43
+ edges = export_graph_as_edges(graph)
44
+
45
+ return {
46
+ "triplets": all_triplets,
47
+ "graph": edges
48
+ }
49
+
50
+
51
+ # ----------------------------------------------------------
52
+ # Triplet Statistics Summary
53
+ # ----------------------------------------------------------
54
+ @router.post("/memory-summary")
55
+ async def memory_summary(messages: List[Message]):
56
+ all_triplets = []
57
+
58
+ for msg in messages:
59
+ extracted = extract_triplets_from_text(msg.text)
60
+ for triplet in extracted:
61
+ triplet["timestamp"] = msg.timestamp
62
+ triplet["author"] = msg.sender
63
+ all_triplets.append(triplet)
64
+
65
+ summary = {
66
+ "total_triplets": len(all_triplets),
67
+ "by_user": group_by_author(all_triplets),
68
+ "predicate_counts": count_predicates(all_triplets),
69
+ "common_subjects": most_common_subjects(all_triplets)
70
+ }
71
+
72
+ return summary
73
+
74
+
75
+ # ----------------------------------------------------------
76
+ # Triplet Query (Filtered)
77
+ # ----------------------------------------------------------
78
+ @router.post("/query")
79
+ async def query_triplets(
80
+ messages: List[Message],
81
+ author: str = Query(None),
82
+ subject: str = Query(None),
83
+ predicate: str = Query(None)
84
+ ):
85
+ all_triplets = []
86
+
87
+ for msg in messages:
88
+ extracted = extract_triplets_from_text(msg.text)
89
+ for triplet in extracted:
90
+ triplet["timestamp"] = msg.timestamp
91
+ triplet["author"] = msg.sender
92
+ all_triplets.append(triplet)
93
+
94
+ filtered = all_triplets
95
+
96
+ if author:
97
+ filtered = get_triplets_by_author(filtered, author)
98
+ if subject:
99
+ filtered = get_triplets_by_subject(filtered, subject)
100
+ if predicate:
101
+ filtered = get_triplets_by_predicate(filtered, predicate)
102
+
103
+ return {
104
+ "total_triplets": len(filtered),
105
+ "results": filtered
106
+ }
107
+
108
+
109
+ # ----------------------------------------------------------
110
+ # Natural Language QA over Memory
111
+ # ----------------------------------------------------------
112
+ @router.get("/qa")
113
+ async def qa_query(question: str = Query(..., description="Doğal dilde soru girin")):
114
+ filters = extract_query_from_question(question)
115
+ memory = load_memory()
116
+
117
+ print("🚀 SORU:", question)
118
+ print("🔍 FILTERS:", filters)
119
+
120
+ results = query_memory(
121
+ memory,
122
+ author=filters.get("author"),
123
+ predicate=filters.get("predicate"),
124
+ subject=filters.get("subject"),
125
+ object_=filters.get("object"),
126
+ )
127
+
128
+ print("📦 SONUÇ TRIPLETLER:", results)
129
+
130
+ answer = format_answer_smart(results, filters)
131
+
132
+ return {
133
+ "soru": question,
134
+ "filters": filters,
135
+ "cevap": answer,
136
+ "triplet_sayisi": len(results),
137
+ "tripletler": results
138
+ }
139
+
140
+
141
+
142
+ @router.delete("/triplet/delete/{triplet_id}")
143
+ async def delete_triplet(triplet_id: str):
144
+ memory = load_memory()
145
+ changed = False
146
+
147
+ for author, triplets in memory.items():
148
+ original_count = len(triplets)
149
+ memory[author] = [t for t in triplets if t.get("id") != triplet_id]
150
+ if len(memory[author]) < original_count:
151
+ changed = True
152
+
153
+ if changed:
154
+ _save_memory(memory)
155
+ return {"status": "deleted", "id": triplet_id}
156
+ else:
157
+ return {"status": "not found", "id": triplet_id}
158
+
159
+
160
+
161
+ @router.put("/triplet/update/{triplet_id}")
162
+ async def update_triplet(triplet_id: str, updated_fields: dict):
163
+ memory = load_memory()
164
+ updated = False
165
+
166
+ for author, triplets in memory.items():
167
+ for t in triplets:
168
+ if t.get("id") == triplet_id:
169
+ t.update(updated_fields) # ✅ güncellenen alanları uygula
170
+ updated = True
171
+ break
172
+
173
+ if updated:
174
+ _save_memory(memory)
175
+ return {"status": "updated", "id": triplet_id, "new_data": updated_fields}
176
+ else:
177
+ return {"status": "not found", "id": triplet_id}
178
+
179
+
180
+
181
+
182
+ # ----------------------------------------------------------
183
+ # Helpers
184
+ # ----------------------------------------------------------
185
+ def extract_query_from_question(question: str) -> dict:
186
+ filters = {}
187
+ q = question.lower()
188
+
189
+ # --- AUTHOR eşleştirmeleri ---
190
+ if "ayşe" in q:
191
+ filters["author"] = "Ayşe"
192
+ if "erdem" in q:
193
+ filters["author"] = "Erdem"
194
+ if "ali" in q:
195
+ filters["author"] = "Ali"
196
+
197
+ if "ne dedi" in q:
198
+ filters["return"] = "triplet"
199
+ return filters # erken çık
200
+
201
+ # --- SUBJECT eşleştirmeleri ---
202
+ if "redis" in q:
203
+ filters["subject"] = "Redis"
204
+ if "react" in q:
205
+ filters["subject"] = "React"
206
+ if "fastapi" in q:
207
+ filters["subject"] = "FastAPI"
208
+ if "we" in q:
209
+ filters["subject"] = "we"
210
+ if "ben" in q or (" i " in q): # boşluklarla eşleştir, yanlış anlamasın
211
+ filters["subject"] = "I"
212
+
213
+
214
+ # --- OBJECT eşleştirmeleri ---
215
+ if "mongodb" in q:
216
+ filters["object"] = "MongoDB"
217
+ if "ui" in q:
218
+ filters["object"] = "UI"
219
+ if "data" in q:
220
+ filters["object"] = "data"
221
+ if "joins" in q:
222
+ filters["object"] = "joins"
223
+ if "backend" in q:
224
+ filters["object"] = "backend"
225
+
226
+ # --- PREDICATE eşleştirmeleri ---
227
+ if "öner" in q or "tavsiye" in q:
228
+ filters["predicate"] = "suggest"
229
+ if "seviyor" in q or "sever" in q or "beğen" in q:
230
+ filters["predicate"] = "like"
231
+ if "destekliyor" in q or "destek" in q:
232
+ filters["predicate"] = "support"
233
+ if "düşünüyor" in q or "düşün" in q:
234
+ filters["predicate"] = "think"
235
+ if "önbellek" in q or "cache" in q:
236
+ filters["predicate"] = "cache"
237
+
238
+ # --- Geri dönüş tipi ---
239
+ if "ne dedi" in q or "kim ne dedi" in q:
240
+ filters["return"] = "triplet"
241
+ if "kim" in q:
242
+ filters["return"] = "subject"
243
+
244
+ return filters
245
+
246
+
247
+
248
+ def format_answer_smart(triplets: list, filters: dict) -> str:
249
+ if not triplets:
250
+ return "Üzgünüm, bu soruya dair bir bilgi bulamadım."
251
+
252
+ return_type = filters.get("return")
253
+ messages = []
254
+
255
+ for triplet in triplets:
256
+ subj = triplet.get("subject")
257
+ pred = triplet.get("predicate")
258
+ obj = triplet.get("object")
259
+ author = triplet.get("author")
260
+
261
+ if return_type == "subject":
262
+ messages.append(f"{obj} ile ilgili eylemi gerçekleştiren kişi: {subj}")
263
+ elif return_type == "triplet":
264
+ messages.append(f"{author} dedi ki: \"{subj} {pred} {obj}\"")
265
+ else:
266
+ sentence = f"{author} dedi ki: \"{subj} {pred} {obj}\""
267
+ messages.append(sentence)
268
+
269
+ return "\n".join(messages)
270
+
271
+
272
+ def load_memory():
273
+ base_dir = os.path.dirname(os.path.abspath(__file__)) # backend/app/routes/
274
+ full_path = os.path.abspath(os.path.join(base_dir, "..", "..", "memory_export.json"))
275
+ with open(full_path, "r", encoding="utf-8") as f:
276
+ return json.load(f)
277
+
278
+
279
+ def _save_memory(memory: dict):
280
+ base_dir = os.path.dirname(os.path.abspath(__file__))
281
+ full_path = os.path.abspath(os.path.join(base_dir, "..", "..", "memory_export.json"))
282
+ with open(full_path, "w", encoding="utf-8") as f:
283
+ json.dump(memory, f, indent=2, ensure_ascii=False)
284
+
app/services/__pycache__/graph_builder.cpython-310.pyc ADDED
Binary file (879 Bytes). View file
 
app/services/__pycache__/graph_builder.cpython-311.pyc ADDED
Binary file (1.51 kB). View file
 
app/services/__pycache__/graph_builder.cpython-313.pyc ADDED
Binary file (1.31 kB). View file
 
app/services/__pycache__/graph_visualizer.cpython-311.pyc ADDED
Binary file (1.33 kB). View file
 
app/services/__pycache__/graph_visualizer.cpython-313.pyc ADDED
Binary file (1.03 kB). View file
 
app/services/__pycache__/memory_engine.cpython-310.pyc ADDED
Binary file (1.35 kB). View file
 
app/services/__pycache__/memory_engine.cpython-311.pyc ADDED
Binary file (7.74 kB). View file
 
app/services/__pycache__/nlp_triplet.cpython-310.pyc ADDED
Binary file (799 Bytes). View file
 
app/services/__pycache__/nlp_triplet.cpython-311.pyc ADDED
Binary file (1.23 kB). View file
 
app/services/graph_builder.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # backend/app/services/graph_builder.py
2
+
3
+ import networkx as nx
4
+ from typing import List, Dict
5
+
6
+ def build_graph_from_triplets(triplets: List[Dict]) -> nx.DiGraph:
7
+ G = nx.DiGraph()
8
+
9
+ for triplet in triplets:
10
+ subject = triplet["subject"]
11
+ predicate = triplet["predicate"]
12
+ obj = triplet["object"]
13
+
14
+ G.add_node(subject)
15
+ G.add_node(obj)
16
+ G.add_edge(subject, obj, label=predicate)
17
+
18
+ return G
19
+
20
+ def export_graph_as_edges(graph: nx.DiGraph) -> List[Dict]:
21
+ edges = []
22
+ for u, v, data in graph.edges(data=True):
23
+ edges.append({
24
+ "from": u,
25
+ "to": v,
26
+ "relation": data.get("label", "")
27
+ })
28
+ return edges
app/services/graph_visualizer.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # backend/app/services/graph_visualizer.py
2
+
3
+ from pyvis.network import Network
4
+ import networkx as nx
5
+
6
+ def visualize_graph(graph: nx.DiGraph, output_path="graph.html"):
7
+ net = Network(height="600px", width="100%", directed=True)
8
+ net.barnes_hut() # güzel bir düzenleme algoritması
9
+
10
+ for node in graph.nodes():
11
+ net.add_node(node, label=node)
12
+
13
+ for source, target, data in graph.edges(data=True):
14
+ label = data.get("label", "")
15
+ net.add_edge(source, target, label=label)
16
+
17
+ net.write_html(output_path)
18
+ ## import webbrowser
19
+ ## webbrowser.open(output_path)
20
+
app/services/memory_engine.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Dict
2
+ from collections import defaultdict
3
+
4
+ def group_by_author(triplets: List[Dict]) -> Dict[str, List[Dict]]:
5
+ memory = defaultdict(list)
6
+ for t in triplets:
7
+ memory[t["author"]].append(t)
8
+ return dict(memory)
9
+
10
+ def count_predicates(triplets: List[Dict]) -> Dict[str, int]:
11
+ counts = defaultdict(int)
12
+ for t in triplets:
13
+ counts[t["predicate"]] += 1
14
+ return dict(counts)
15
+
16
+ def most_common_subjects(triplets: List[Dict], top_n=3) -> List[str]:
17
+ counts = defaultdict(int)
18
+ for t in triplets:
19
+ counts[t["subject"]] += 1
20
+ sorted_subjects = sorted(counts.items(), key=lambda x: x[1], reverse=True)
21
+ return [s[0] for s in sorted_subjects[:top_n]]
22
+
23
+ def get_triplets_by_author(triplets: List[Dict], author: str) -> List[Dict]:
24
+ return [t for t in triplets if t.get("author", "").lower() == author.lower()]
25
+
26
+ def get_triplets_by_subject(triplets: List[Dict], subject: str) -> List[Dict]:
27
+ return [t for t in triplets if t.get("subject", "").lower() == subject.lower()]
28
+
29
+ def get_triplets_by_predicate(triplets: List[Dict], predicate: str) -> List[Dict]:
30
+ return [t for t in triplets if t.get("predicate", "").lower() == predicate.lower()]
31
+
32
+ import json
33
+
34
+ import uuid
35
+ import json
36
+ from collections import defaultdict
37
+ from typing import List, Dict
38
+
39
+ def export_memory_to_json(triplets: List[Dict], output_path="backend/memory_export.json") -> None:
40
+ memory = defaultdict(list)
41
+
42
+ for t in triplets:
43
+ memory[t["author"]].append({
44
+ "id": str(uuid.uuid4()), # ✅ benzersiz id
45
+ "subject": t["subject"],
46
+ "predicate": t["predicate"],
47
+ "object": t["object"],
48
+ "timestamp": t["timestamp"]
49
+ })
50
+
51
+ with open(output_path, "w", encoding="utf-8") as f:
52
+ json.dump(memory, f, indent=2, ensure_ascii=False)
53
+
54
+
55
+
56
+
57
+ def query_memory(memory: dict, author=None, subject=None, predicate=None, object_=None):
58
+ results = []
59
+
60
+ for user, triplets in memory.items():
61
+ for triplet in triplets:
62
+ triplet_with_author = dict(triplet) # orijinali değiştirmeyelim
63
+ triplet_with_author["author"] = user
64
+
65
+ if author and user != author:
66
+ continue
67
+ if subject and triplet.get("subject") != subject:
68
+ continue
69
+ if predicate and triplet.get("predicate") != predicate:
70
+ continue
71
+ if object_ and triplet.get("object") != object_:
72
+ continue
73
+
74
+ results.append(triplet_with_author)
75
+
76
+ return results
77
+
78
+
79
+ # app/services/memory_engine.py
80
+
81
+
82
+
83
+ import os
84
+
85
+ def load_memory(path=None):
86
+ if path is None:
87
+ base_dir = os.path.dirname(os.path.abspath(__file__))
88
+ path = os.path.join(base_dir, "..", "..", "memory_export.json")
89
+ with open(path, "r", encoding="utf-8") as f:
90
+ return json.load(f)
91
+
92
+ def save_memory(memory: dict, path=None):
93
+ if path is None:
94
+ base_dir = os.path.dirname(os.path.abspath(__file__))
95
+ path = os.path.join(base_dir, "..", "..", "memory_export.json")
96
+ with open(path, "w", encoding="utf-8") as f:
97
+ json.dump(memory, f, indent=2, ensure_ascii=False)
98
+
99
+
app/services/nlp_triplet.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # backend/app/services/nlp_triplet.py
2
+
3
+ import spacy
4
+ from typing import List, Dict
5
+ import os
6
+
7
+ MODEL_NAME = os.getenv("SPACY_MODEL", "en_core_web_trf")
8
+ try:
9
+ nlp = spacy.load(MODEL_NAME)
10
+ except Exception:
11
+ # Transformer yoksa hafif modele düş
12
+ nlp = spacy.load("en_core_web_sm")
13
+
14
+ def extract_triplets_from_text(text: str) -> List[Dict]:
15
+ doc = nlp(text)
16
+ triplets = []
17
+
18
+ for sent in doc.sents:
19
+ subject = ""
20
+ verb = ""
21
+ obj = ""
22
+
23
+ for token in sent:
24
+ if token.dep_ in ("nsubj", "nsubjpass"):
25
+ subject = token.text
26
+ if token.dep_ in ("dobj", "pobj", "attr"):
27
+ obj = token.text
28
+ if token.head.pos_ == "VERB":
29
+ verb = token.head.lemma_
30
+
31
+ if subject and verb and obj:
32
+ triplets.append({
33
+ "subject": subject,
34
+ "predicate": verb,
35
+ "object": obj
36
+ })
37
+
38
+ return triplets