3030alert_queue = asyncio .Queue ()
3131fim_cache = FimCache ()
3232
33+
3334class AlreadyExistsError (Exception ):
3435 pass
3536
37+
3638class DbCodeGate :
3739 _instance = None
3840
@@ -246,16 +248,15 @@ async def record_context(self, context: Optional[PipelineContext]) -> None:
246248 except Exception as e :
247249 logger .error (f"Failed to record context: { context } ." , error = str (e ))
248250
249- async def add_workspace (self , workspace_name : str ) -> Optional [ Workspace ] :
251+ async def add_workspace (self , workspace_name : str ) -> Workspace :
250252 """Add a new workspace to the DB.
251253
252254 This handles validation and insertion of a new workspace.
253255
254256 It may raise a ValidationError if the workspace name is invalid.
255257 or a AlreadyExistsError if the workspace already exists.
256258 """
257- workspace = Workspace (id = str (uuid .uuid4 ()), name = workspace_name )
258-
259+ workspace = Workspace (id = str (uuid .uuid4 ()), name = workspace_name , system_prompt = None )
259260 sql = text (
260261 """
261262 INSERT INTO workspaces (id, name)
@@ -266,12 +267,28 @@ async def add_workspace(self, workspace_name: str) -> Optional[Workspace]:
266267
267268 try :
268269 added_workspace = await self ._execute_update_pydantic_model (
269- workspace , sql , should_raise = True )
270+ workspace , sql , should_raise = True
271+ )
270272 except IntegrityError as e :
271273 logger .debug (f"Exception type: { type (e )} " )
272274 raise AlreadyExistsError (f"Workspace { workspace_name } already exists." )
273275 return added_workspace
274276
277+ async def update_workspace (self , workspace : Workspace ) -> Workspace :
278+ sql = text (
279+ """
280+ UPDATE workspaces SET
281+ name = :name,
282+ system_prompt = :system_prompt
283+ WHERE id = :id
284+ RETURNING *
285+ """
286+ )
287+ updated_workspace = await self ._execute_update_pydantic_model (
288+ workspace , sql , should_raise = True
289+ )
290+ return updated_workspace
291+
275292 async def update_session (self , session : Session ) -> Optional [Session ]:
276293 sql = text (
277294 """
@@ -284,9 +301,23 @@ async def update_session(self, session: Session) -> Optional[Session]:
284301 """
285302 )
286303 # We only pass an object to respect the signature of the function
287- active_session = await self ._execute_update_pydantic_model (session , sql )
304+ active_session = await self ._execute_update_pydantic_model (session , sql , should_raise = True )
288305 return active_session
289306
307+ async def soft_delete_workspace (self , workspace : Workspace ) -> Optional [Workspace ]:
308+ sql = text (
309+ """
310+ UPDATE workspaces
311+ SET deleted_at = CURRENT_TIMESTAMP
312+ WHERE id = :id
313+ RETURNING *
314+ """
315+ )
316+ deleted_workspace = await self ._execute_update_pydantic_model (
317+ workspace , sql , should_raise = True
318+ )
319+ return deleted_workspace
320+
290321
291322class DbReader (DbCodeGate ):
292323
@@ -317,14 +348,21 @@ async def _execute_select_pydantic_model(
317348 return None
318349
319350 async def _exec_select_conditions_to_pydantic (
320- self , model_type : Type [BaseModel ], sql_command : TextClause , conditions : dict
351+ self ,
352+ model_type : Type [BaseModel ],
353+ sql_command : TextClause ,
354+ conditions : dict ,
355+ should_raise : bool = False ,
321356 ) -> Optional [List [BaseModel ]]:
322357 async with self ._async_db_engine .begin () as conn :
323358 try :
324359 result = await conn .execute (sql_command , conditions )
325360 return await self ._dump_result_to_pydantic_model (model_type , result )
326361 except Exception as e :
327362 logger .error (f"Failed to select model with conditions: { model_type } ." , error = str (e ))
363+ # Exposes errors to the caller
364+ if should_raise :
365+ raise e
328366 return None
329367
330368 async def get_prompts_with_output (self ) -> List [GetPromptWithOutputsRow ]:
@@ -377,22 +415,25 @@ async def get_workspaces(self) -> List[WorkspaceActive]:
377415 w.id, w.name, s.active_workspace_id
378416 FROM workspaces w
379417 LEFT JOIN sessions s ON w.id = s.active_workspace_id
418+ WHERE w.deleted_at IS NULL
380419 """
381420 )
382421 workspaces = await self ._execute_select_pydantic_model (WorkspaceActive , sql )
383422 return workspaces
384423
385- async def get_workspace_by_name (self , name : str ) -> List [Workspace ]:
424+ async def get_workspace_by_name (self , name : str ) -> Optional [Workspace ]:
386425 sql = text (
387426 """
388427 SELECT
389- id, name
428+ id, name, system_prompt
390429 FROM workspaces
391- WHERE name = :name
430+ WHERE name = :name AND deleted_at IS NULL
392431 """
393432 )
394433 conditions = {"name" : name }
395- workspaces = await self ._exec_select_conditions_to_pydantic (Workspace , sql , conditions )
434+ workspaces = await self ._exec_select_conditions_to_pydantic (
435+ Workspace , sql , conditions , should_raise = True
436+ )
396437 return workspaces [0 ] if workspaces else None
397438
398439 async def get_sessions (self ) -> List [Session ]:
@@ -410,7 +451,7 @@ async def get_active_workspace(self) -> Optional[ActiveWorkspace]:
410451 sql = text (
411452 """
412453 SELECT
413- w.id, w.name, s.id as session_id, s.last_update
454+ w.id, w.name, w.system_prompt, s.id as session_id, s.last_update
414455 FROM sessions s
415456 INNER JOIN workspaces w ON w.id = s.active_workspace_id
416457 """
@@ -453,7 +494,11 @@ def init_session_if_not_exists(db_path: Optional[str] = None):
453494 last_update = datetime .datetime .now (datetime .timezone .utc ),
454495 )
455496 db_recorder = DbRecorder (db_path )
456- asyncio .run (db_recorder .update_session (session ))
497+ try :
498+ asyncio .run (db_recorder .update_session (session ))
499+ except Exception as e :
500+ logger .error (f"Failed to initialize session in DB: { e } " )
501+ return
457502 logger .info ("Session in DB initialized successfully." )
458503
459504
0 commit comments