Skip to content

Commit 2f9b9b7

Browse files
Merge pull request #1 from Lumen-Labs/dev
implemented tasks, observations, structured_data logic
2 parents 10e910a + a9f8e1f commit 2f9b9b7

File tree

10 files changed

+770
-207
lines changed

10 files changed

+770
-207
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ __pycache__
66
dist
77
common.egg-info
88
.venv
9-
.env.development
9+
.env.development
10+
venv/

src/adapters/data.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Created Date: Sunday October 19th 2025
44
Author: Christian Nonis <[email protected]>
55
-----
6-
Last Modified: Sunday October 19th 2025 9:00:42 am
6+
Last Modified: Saturday December 13th 2025
77
Modified By: the developer formerly known as Christian Nonis at <[email protected]>
88
-----
99
"""
@@ -90,3 +90,53 @@ def save_kg_changes(
9090
Save a KG changes to the data client.
9191
"""
9292
return self.data.save_kg_changes(kg_changes, brain_id)
93+
94+
def get_structured_data_by_id(
95+
self, id: str, brain_id: str = "default"
96+
) -> StructuredData:
97+
"""
98+
Get structured data by ID.
99+
"""
100+
return self.data.get_structured_data_by_id(id, brain_id)
101+
102+
def get_structured_data_list(
103+
self, brain_id: str = "default", limit: int = 10, skip: int = 0, types: list[str] = None, query_text: str = None
104+
) -> list[StructuredData]:
105+
"""
106+
Get a list of structured data.
107+
"""
108+
return self.data.get_structured_data_list(brain_id, limit, skip, types, query_text)
109+
110+
def get_structured_data_types(self, brain_id: str = "default") -> list[str]:
111+
"""
112+
Get all unique types from structured data.
113+
"""
114+
return self.data.get_structured_data_types(brain_id)
115+
116+
def get_observation_by_id(
117+
self, id: str, brain_id: str = "default"
118+
) -> Observation:
119+
"""
120+
Get observation by ID.
121+
"""
122+
return self.data.get_observation_by_id(id, brain_id)
123+
124+
def get_observations_list(
125+
self,
126+
brain_id: str = "default",
127+
limit: int = 10,
128+
skip: int = 0,
129+
resource_id: str = None,
130+
labels: list[str] = None,
131+
query_text: str = None
132+
) -> list[Observation]:
133+
"""
134+
Get a list of observations.
135+
"""
136+
return self.data.get_observations_list(brain_id, limit, skip, resource_id, labels, query_text)
137+
138+
def get_observation_labels(self, brain_id: str = "default") -> list[str]:
139+
"""
140+
Get all unique labels from observations.
141+
"""
142+
return self.data.get_observation_labels(brain_id)

src/adapters/interfaces/data.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Created Date: Sunday October 19th 2025
44
Author: Christian Nonis <[email protected]>
55
-----
6-
Last Modified: Sunday October 19th 2025 9:01:13 am
6+
Last Modified: Saturday December 13th 2025
77
Modified By: the developer formerly known as Christian Nonis at <[email protected]>
88
-----
99
"""
@@ -98,3 +98,55 @@ def save_kg_changes(self, kg_changes: KGChanges, brain_id: str) -> KGChanges:
9898
Save a KG changes to the data client.
9999
"""
100100
raise NotImplementedError("save_kg_changes method not implemented")
101+
102+
@abstractmethod
103+
def get_structured_data_by_id(self, id: str, brain_id: str) -> StructuredData:
104+
"""
105+
Get structured data by ID.
106+
"""
107+
raise NotImplementedError("get_structured_data_by_id method not implemented")
108+
109+
@abstractmethod
110+
def get_structured_data_list(
111+
self, brain_id: str, limit: int = 10, skip: int = 0, types: list[str] = None, query_text: str = None
112+
) -> list[StructuredData]:
113+
"""
114+
Get a list of structured data.
115+
"""
116+
raise NotImplementedError("get_structured_data_list method not implemented")
117+
118+
@abstractmethod
119+
def get_structured_data_types(self, brain_id: str) -> list[str]:
120+
"""
121+
Get all unique types from structured data.
122+
"""
123+
raise NotImplementedError("get_structured_data_types method not implemented")
124+
125+
@abstractmethod
126+
def get_observation_by_id(self, id: str, brain_id: str) -> Observation:
127+
"""
128+
Get observation by ID.
129+
"""
130+
raise NotImplementedError("get_observation_by_id method not implemented")
131+
132+
@abstractmethod
133+
def get_observations_list(
134+
self,
135+
brain_id: str,
136+
limit: int = 10,
137+
skip: int = 0,
138+
resource_id: str = None,
139+
labels: list[str] = None,
140+
query_text: str = None
141+
) -> list[Observation]:
142+
"""
143+
Get a list of observations.
144+
"""
145+
raise NotImplementedError("get_observations_list method not implemented")
146+
147+
@abstractmethod
148+
def get_observation_labels(self, brain_id: str) -> list[str]:
149+
"""
150+
Get all unique labels from observations.
151+
"""
152+
raise NotImplementedError("get_observation_labels method not implemented")

src/lib/mongo/client.py

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Created Date: Saturday October 25th 2025
44
Author: Christian Nonis <[email protected]>
55
-----
6-
Last Modified: Saturday October 25th 2025 11:55:47 am
6+
Last Modified: Saturday December 13th 2025
77
Modified By: the developer formerly known as Christian Nonis at <[email protected]>
88
-----
99
"""
@@ -168,5 +168,117 @@ def save_kg_changes(self, kg_changes: KGChanges, brain_id: str) -> KGChanges:
168168
collection.insert_one(kg_changes.model_dump(mode="json"))
169169
return kg_changes
170170

171+
def get_structured_data_by_id(
172+
self, id: str, brain_id: str
173+
) -> StructuredData:
174+
collection = self.get_collection("structured_data", database=brain_id)
175+
result = collection.find_one({"id": id})
176+
if not result:
177+
return None
178+
return StructuredData(
179+
id=result["id"],
180+
data=result["data"],
181+
types=result["types"],
182+
metadata=result.get("metadata", None),
183+
inserted_at=result.get("inserted_at", None),
184+
)
185+
186+
def get_structured_data_list(
187+
self, brain_id: str, limit: int = 10, skip: int = 0, types: list[str] = None, query_text: str = None
188+
) -> list[StructuredData]:
189+
collection = self.get_collection("structured_data", database=brain_id)
190+
query = {}
191+
if types:
192+
query["types"] = {"$in": types}
193+
194+
if query_text:
195+
query["$or"] = [
196+
{"data": {"$regex": query_text, "$options": "i"}},
197+
{"types": {"$regex": query_text, "$options": "i"}},
198+
{"metadata": {"$regex": query_text, "$options": "i"}}
199+
]
200+
201+
results = collection.find(query).skip(skip).limit(limit)
202+
return [
203+
StructuredData(
204+
id=result["id"],
205+
data=result["data"],
206+
types=result["types"],
207+
metadata=result.get("metadata", None),
208+
inserted_at=result.get("inserted_at", None),
209+
)
210+
for result in results
211+
]
212+
213+
def get_structured_data_types(self, brain_id: str) -> list[str]:
214+
collection = self.get_collection("structured_data", database=brain_id)
215+
pipeline = [
216+
{"$unwind": "$types"},
217+
{"$group": {"_id": "$types"}},
218+
{"$sort": {"_id": 1}}
219+
]
220+
results = collection.aggregate(pipeline)
221+
return [result["_id"] for result in results]
222+
223+
def get_observation_by_id(
224+
self, id: str, brain_id: str
225+
) -> Observation:
226+
collection = self.get_collection("observations", database=brain_id)
227+
result = collection.find_one({"id": id})
228+
if not result:
229+
return None
230+
return Observation(
231+
id=result["id"],
232+
text=result["text"],
233+
metadata=result.get("metadata", None),
234+
resource_id=result["resource_id"],
235+
inserted_at=result.get("inserted_at", None),
236+
)
237+
238+
def get_observations_list(
239+
self,
240+
brain_id: str,
241+
limit: int = 10,
242+
skip: int = 0,
243+
resource_id: str = None,
244+
labels: list[str] = None,
245+
query_text: str = None
246+
) -> list[Observation]:
247+
collection = self.get_collection("observations", database=brain_id)
248+
query = {}
249+
if resource_id:
250+
query["resource_id"] = resource_id
251+
if labels:
252+
query["metadata.labels"] = {"$in": labels}
253+
254+
if query_text:
255+
query["$or"] = [
256+
{"text": {"$regex": query_text, "$options": "i"}},
257+
{"resource_id": {"$regex": query_text, "$options": "i"}},
258+
{"metadata": {"$regex": query_text, "$options": "i"}}
259+
]
260+
261+
results = collection.find(query).skip(skip).limit(limit)
262+
return [
263+
Observation(
264+
id=result["id"],
265+
text=result["text"],
266+
metadata=result.get("metadata", None),
267+
resource_id=result["resource_id"],
268+
inserted_at=result.get("inserted_at", None),
269+
)
270+
for result in results
271+
]
272+
273+
def get_observation_labels(self, brain_id: str) -> list[str]:
274+
collection = self.get_collection("observations", database=brain_id)
275+
pipeline = [
276+
{"$match": {"metadata.labels": {"$exists": True}}},
277+
{"$unwind": "$metadata.labels"},
278+
{"$group": {"_id": "$metadata.labels"}},
279+
{"$sort": {"_id": 1}}
280+
]
281+
results = collection.aggregate(pipeline)
282+
return [result["_id"] for result in results]
171283

172284
_mongo_client = MongoClient()

src/services/api/app.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from src.services.api.routes.retrieve import retrieve_router
2121
from src.services.api.routes.meta import meta_router
2222
from src.services.api.routes.system import system_router
23+
from src.services.api.routes.tasks import tasks_router
2324

2425
app = FastAPI(debug=os.getenv("ENV") == "development")
2526

@@ -38,7 +39,7 @@
3839
app.include_router(retrieve_router)
3940
app.include_router(meta_router)
4041
app.include_router(system_router)
41-
42+
app.include_router(tasks_router)
4243

4344
@app.get("/")
4445
async def root():
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
"""
2+
File: /observations.py
3+
Created Date: Saturday December 6th 2025
4+
Author: Christian Nonis <[email protected]>
5+
-----
6+
Last Modified: Saturday December 13th 2025
7+
Modified By: the developer formerly known as Christian Nonis at <[email protected]>
8+
-----
9+
"""
10+
11+
import asyncio
12+
from typing import Optional
13+
from fastapi import HTTPException
14+
from starlette.responses import JSONResponse
15+
from src.services.data.main import data_adapter
16+
17+
18+
async def get_observation_by_id(
19+
id: str, brain_id: str = "default"
20+
):
21+
"""
22+
Get observation by ID.
23+
"""
24+
def _get_observation():
25+
observation = data_adapter.get_observation_by_id(id, brain_id)
26+
if not observation:
27+
raise HTTPException(status_code=404, detail="Observation not found")
28+
return observation
29+
30+
result = await asyncio.to_thread(_get_observation)
31+
32+
return JSONResponse(
33+
content={
34+
"message": "Observation retrieved successfully",
35+
"observation": result.model_dump(mode="json")
36+
}
37+
)
38+
39+
40+
async def get_observations_list(
41+
limit: int = 10,
42+
skip: int = 0,
43+
resource_id: Optional[str] = None,
44+
labels: Optional[list[str]] = None,
45+
query_text: Optional[str] = None,
46+
brain_id: str = "default",
47+
):
48+
"""
49+
Get a list of observations.
50+
"""
51+
def _get_list():
52+
return data_adapter.get_observations_list(
53+
brain_id, limit, skip, resource_id, labels, query_text
54+
)
55+
56+
results = await asyncio.to_thread(_get_list)
57+
58+
return JSONResponse(
59+
content={
60+
"message": "Observations list retrieved successfully",
61+
"observations": [r.model_dump(mode="json") for r in results],
62+
"count": len(results)
63+
}
64+
)
65+
66+
async def get_observation_labels(
67+
brain_id: str = "default",
68+
):
69+
"""
70+
Get all unique labels from observations.
71+
"""
72+
def _get_labels():
73+
return data_adapter.get_observation_labels(brain_id)
74+
75+
results = await asyncio.to_thread(_get_labels)
76+
77+
return JSONResponse(
78+
content={
79+
"message": "Observation labels retrieved successfully",
80+
"labels": results,
81+
"count": len(results)
82+
}
83+
)

0 commit comments

Comments
 (0)