play7284 commited on
Commit
b776a3d
·
verified ·
1 Parent(s): cc5a160

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +266 -0
app.py ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import json
4
+ from dataclasses import dataclass, field, asdict
5
+ from typing import List, Dict, Any
6
+
7
+ # --- 1. 数据结构定义 (与之前相同) ---
8
+
9
+ @dataclass
10
+ class Entity:
11
+ name: str
12
+ entityType: str
13
+ observations: List[str] = field(default_factory=list)
14
+
15
+ @dataclass
16
+ class Relation:
17
+ from_entity: str
18
+ to_entity: str
19
+ relationType: str
20
+
21
+ @dataclass
22
+ class KnowledgeGraph:
23
+ entities: Dict[str, Entity] = field(default_factory=dict)
24
+ relations: List[Relation] = field(default_factory=list)
25
+
26
+
27
+ # --- 2. 核心逻辑: KnowledgeGraphManager (修改为纯内存) ---
28
+
29
+ class KnowledgeGraphManager:
30
+ """
31
+ 这个版本的 Manager 将知识图谱完全保存在内存中。
32
+ 服务器重启后,所有数据都会丢失。
33
+ """
34
+ def __init__(self):
35
+ # 服务器启动时,在内存中初始化一个空的知识图谱
36
+ self.graph = KnowledgeGraph()
37
+ print("Initialized a new, empty in-memory knowledge graph.")
38
+
39
+ def _reset_graph(self):
40
+ """提供一个方法来手动重置图谱,主要用于UI。"""
41
+ self.graph = KnowledgeGraph()
42
+ return "In-memory knowledge graph has been reset to an empty state."
43
+
44
+ def create_entities(self, entities_data: List[Dict]) -> List[Entity]:
45
+ new_entities = []
46
+ for e_data in entities_data:
47
+ if e_data['name'] not in self.graph.entities:
48
+ new_entity = Entity(**e_data)
49
+ self.graph.entities[e_data['name']] = new_entity
50
+ new_entities.append(new_entity)
51
+ return new_entities
52
+
53
+ def create_relations(self, relations_data: List[Dict]) -> List[Relation]:
54
+ new_relations = []
55
+ existing_relations_set = {
56
+ (r.from_entity, r.to_entity, r.relationType) for r in self.graph.relations
57
+ }
58
+ for r_data in relations_data:
59
+ r_data['from_entity'] = r_data.pop('from')
60
+ r_data['to_entity'] = r_data.pop('to')
61
+ if (r_data['from_entity'], r_data['to_entity'], r_data['relationType']) not in existing_relations_set:
62
+ new_relation = Relation(**r_data)
63
+ self.graph.relations.append(new_relation)
64
+ new_relations.append(new_relation)
65
+ return new_relations
66
+
67
+ def add_observations(self, observations_data: List[Dict]) -> List[Dict]:
68
+ results = []
69
+ for o_data in observations_data:
70
+ entity = self.graph.entities.get(o_data['entityName'])
71
+ if not entity:
72
+ raise ValueError(f"Entity with name {o_data['entityName']} not found")
73
+
74
+ new_observations = [
75
+ obs for obs in o_data['contents'] if obs not in entity.observations
76
+ ]
77
+ entity.observations.extend(new_observations)
78
+ results.append({"entityName": entity.name, "addedObservations": new_observations})
79
+ return results
80
+
81
+ def delete_entities(self, entity_names: List[str]) -> None:
82
+ names_to_delete = set(entity_names)
83
+ self.graph.entities = {name: entity for name, entity in self.graph.entities.items() if name not in names_to_delete}
84
+ self.graph.relations = [
85
+ r for r in self.graph.relations
86
+ if r.from_entity not in names_to_delete and r.to_entity not in names_to_delete
87
+ ]
88
+
89
+ def delete_observations(self, deletions_data: List[Dict]) -> None:
90
+ for d_data in deletions_data:
91
+ entity = self.graph.entities.get(d_data['entityName'])
92
+ if entity:
93
+ obs_to_delete = set(d_data['observations'])
94
+ entity.observations = [obs for obs in entity.observations if obs not in obs_to_delete]
95
+
96
+ def delete_relations(self, relations_data: List[Dict]) -> None:
97
+ relations_to_delete = {
98
+ (r.get('from'), r.get('to'), r.get('relationType')) for r in relations_data
99
+ }
100
+ self.graph.relations = [
101
+ r for r in self.graph.relations
102
+ if (r.from_entity, r.to_entity, r.relationType) not in relations_to_delete
103
+ ]
104
+
105
+ def read_graph(self) -> Dict:
106
+ return {
107
+ "entities": [asdict(e) for e in self.graph.entities.values()],
108
+ "relations": [
109
+ {"from": r.from_entity, "to": r.to_entity, "relationType": r.relationType}
110
+ for r in self.graph.relations
111
+ ]
112
+ }
113
+
114
+ def search_nodes(self, query: str) -> Dict:
115
+ query_lower = query.lower()
116
+
117
+ filtered_entities = [
118
+ e for e in self.graph.entities.values() if
119
+ query_lower in e.name.lower() or
120
+ query_lower in e.entityType.lower() or
121
+ any(query_lower in obs.lower() for obs in e.observations)
122
+ ]
123
+
124
+ filtered_entity_names = {e.name for e in filtered_entities}
125
+
126
+ filtered_relations = [
127
+ r for r in self.graph.relations if
128
+ r.from_entity in filtered_entity_names and r.to_entity in filtered_entity_names
129
+ ]
130
+
131
+ return {
132
+ "entities": [asdict(e) for e in filtered_entities],
133
+ "relations": [
134
+ {"from": r.from_entity, "to": r.to_entity, "relationType": r.relationType}
135
+ for r in filtered_relations
136
+ ]
137
+ }
138
+
139
+ def open_nodes(self, names: List[str]) -> Dict:
140
+ names_set = set(names)
141
+
142
+ filtered_entities = [e for name, e in self.graph.entities.items() if name in names_set]
143
+ filtered_entity_names = {e.name for e in filtered_entities}
144
+ filtered_relations = [
145
+ r for r in self.graph.relations if
146
+ r.from_entity in filtered_entity_names and r.to_entity in filtered_entity_names
147
+ ]
148
+
149
+ return {
150
+ "entities": [asdict(e) for e in filtered_entities],
151
+ "relations": [
152
+ {"from": r.from_entity, "to": r.to_entity, "relationType": r.relationType}
153
+ for r in filtered_relations
154
+ ]
155
+ }
156
+
157
+ # 初始化一个全局的、单例的 Manager 实例
158
+ # 所有API调用都将共享这一个实例
159
+ kg_manager = KnowledgeGraphManager()
160
+
161
+
162
+ # --- 3. Gradio MCP 工具函数定义 (与之前相同, 只调用 manager) ---
163
+ # ... (所有工具函数如 create_entities, read_graph 等保持不变) ...
164
+ def create_entities(entities: List[Dict]) -> str:
165
+ try:
166
+ new_entities = kg_manager.create_entities(entities)
167
+ return json.dumps([asdict(e) for e in new_entities], indent=2)
168
+ except Exception as e: return f"Error: {e}"
169
+ def create_relations(relations: List[Dict]) -> str:
170
+ try:
171
+ new_relations = kg_manager.create_relations(relations)
172
+ return json.dumps([{"from": r.from_entity, "to": r.to_entity, "relationType": r.relationType} for r in new_relations], indent=2)
173
+ except Exception as e: return f"Error: {e}"
174
+ def add_observations(observations: List[Dict]) -> str:
175
+ try:
176
+ results = kg_manager.add_observations(observations)
177
+ return json.dumps(results, indent=2)
178
+ except Exception as e: return f"Error: {e}"
179
+ def delete_entities(entityNames: List[str]) -> str:
180
+ try:
181
+ kg_manager.delete_entities(entityNames)
182
+ return "Entities and their relations deleted successfully."
183
+ except Exception as e: return f"Error: {e}"
184
+ def delete_observations(deletions: List[Dict]) -> str:
185
+ try:
186
+ kg_manager.delete_observations(deletions)
187
+ return "Observations deleted successfully."
188
+ except Exception as e: return f"Error: {e}"
189
+ def delete_relations(relations: List[Dict]) -> str:
190
+ try:
191
+ kg_manager.delete_relations(relations)
192
+ return "Relations deleted successfully."
193
+ except Exception as e: return f"Error: {e}"
194
+ def read_graph() -> Dict: return kg_manager.read_graph()
195
+ def search_nodes(query: str) -> Dict: return kg_manager.search_nodes(query)
196
+ def open_nodes(names: List[str]) -> Dict: return kg_manager.open_nodes(names)
197
+ def reset_graph_ui() -> Tuple[str, Dict]:
198
+ message = kg_manager._reset_graph()
199
+ empty_graph = kg_manager.read_graph()
200
+ return message, empty_graph
201
+
202
+
203
+ # --- 4. Gradio 界面构建 (略作调整) ---
204
+
205
+ with gr.Blocks(theme=gr.themes.Soft()) as app:
206
+ gr.Markdown("# 知识图谱记忆工具 (纯内存模式)")
207
+ gr.Markdown("所有数据都保存在服务器内存中。**关闭或重启服务器将清空所有数据。**")
208
+
209
+ with gr.Tabs():
210
+ # --- 查看/搜索 Tab ---
211
+ with gr.TabItem("查看与搜索"):
212
+ graph_output = gr.JSON(label="知识图谱内容")
213
+ with gr.Row():
214
+ read_btn = gr.Button("读取/刷新图谱")
215
+ reset_btn = gr.Button("清空并重置图谱", variant="stop")
216
+ with gr.Row():
217
+ search_query = gr.Textbox(label="搜索查询", placeholder="输入实体名称、类型或观察内容中的关键词...")
218
+ search_btn = gr.Button("搜索节点")
219
+ with gr.Row():
220
+ open_names = gr.Textbox(label="打开节点", placeholder="输入实体名称,用逗号分隔...")
221
+ open_btn = gr.Button("打开指定节点")
222
+
223
+ # ... 其他Tab (创建/添加, 删除) 保持不变 ...
224
+ with gr.TabItem("创建与添加"):
225
+ status_output_create = gr.Textbox(label="操作结果", interactive=False)
226
+ with gr.Accordion("创建实体", open=True):
227
+ create_entities_input = gr.Code(label="实体 (JSON格式)", language="json", value='[\n {\n "name": "Claude 3",\n "entityType": "AI Model",\n "observations": ["Developed by Anthropic", "Is a large language model"]\n }\n]')
228
+ create_entities_btn = gr.Button("创建实体")
229
+ with gr.Accordion("创建关系", open=False):
230
+ create_relations_input = gr.Code(label="关系 (JSON格式)", language="json", value='[\n {\n "from": "Claude 3",\n "to": "Anthropic",\n "relationType": "developed by"\n }\n]')
231
+ create_relations_btn = gr.Button("创建关系")
232
+ with gr.Accordion("添加观察记录", open=False):
233
+ add_obs_input = gr.Code(label="观察记录 (JSON格式)", language="json", value='[\n {\n "entityName": "Claude 3",\n "contents": ["Released in 2024"]\n }\n]')
234
+ add_obs_btn = gr.Button("添加观察记录")
235
+ with gr.TabItem("删除"):
236
+ status_output_delete = gr.Textbox(label="操作结果", interactive=False)
237
+ with gr.Accordion("删除实体", open=True):
238
+ delete_entities_input = gr.Textbox(label="实体名称 (逗号分隔)", placeholder="Claude 3,Anthropic")
239
+ delete_entities_btn = gr.Button("删除实体")
240
+ with gr.Accordion("删除关系", open=False):
241
+ delete_relations_input = gr.Code(label="关系 (JSON格式)", language="json", value='[\n {\n "from": "Claude 3",\n "to": "Anthropic",\n "relationType": "developed by"\n }\n]')
242
+ delete_relations_btn = gr.Button("删除关系")
243
+ with gr.Accordion("删除观察记录", open=False):
244
+ delete_obs_input = gr.Code(label="要删除的观察记录 (JSON格式)", language="json", value='[\n {\n "entityName": "Claude 3",\n "observations": ["Released in 2024"]\n }\n]')
245
+ delete_obs_btn = gr.Button("删除观察记录")
246
+
247
+
248
+ # --- 5. 事件与API绑定 (与之前相同) ---
249
+ # ... (所有 .click 事件绑定保持不变, 添加 reset_btn 的绑定) ...
250
+ read_btn.click(fn=read_graph, outputs=graph_output, api_name="read_graph")
251
+ search_btn.click(fn=search_nodes, inputs=search_query, outputs=graph_output, api_name="search_nodes")
252
+ open_btn.click(fn=lambda names_str: open_nodes(names=[name.strip() for name in names_str.split(',')]), inputs=open_names, outputs=graph_output, api_name="open_nodes")
253
+ create_entities_btn.click(fn=lambda entities_json: create_entities(json.loads(entities_json)), inputs=create_entities_input, outputs=status_output_create, api_name="create_entities")
254
+ create_relations_btn.click(fn=lambda relations_json: create_relations(json.loads(relations_json)), inputs=create_relations_input, outputs=status_output_create, api_name="create_relations")
255
+ add_obs_btn.click(fn=lambda obs_json: add_observations(json.loads(obs_json)), inputs=add_obs_input, outputs=status_output_create, api_name="add_observations")
256
+ delete_entities_btn.click(fn=lambda names_str: delete_entities(entityNames=[name.strip() for name in names_str.split(',')]), inputs=delete_entities_input, outputs=status_output_delete, api_name="delete_entities")
257
+ delete_relations_btn.click(fn=lambda relations_json: delete_relations(json.loads(relations_json)), inputs=delete_relations_input, outputs=status_output_delete, api_name="delete_relations")
258
+ delete_obs_btn.click(fn=lambda obs_json: delete_observations(json.loads(obs_json)), inputs=delete_obs_input, outputs=status_output_delete, api_name="delete_observations")
259
+
260
+ # UI-only reset button
261
+ # We need a status output for the reset button, let's reuse one.
262
+ reset_btn.click(fn=reset_graph_ui, outputs=[status_output_create, graph_output])
263
+
264
+
265
+ if __name__ == "__main__":
266
+ app.launch(mcp_server=True)