Skip to content

Commit fa31923

Browse files
committed
Allow workspace operations to work without caring about the case
Ultimately all operations will work regardless of the case. Though the final storage format will be lowercase. Signed-off-by: Juan Antonio Osorio <[email protected]>
1 parent 2414395 commit fa31923

File tree

7 files changed

+85
-49
lines changed

7 files changed

+85
-49
lines changed

src/codegate/api/v1.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ async def list_workspaces() -> v1_models.ListWorkspacesResponse:
2727
"""List all workspaces."""
2828
wslist = await wscrud.get_workspaces()
2929

30-
resp = v1_models.ListWorkspacesResponse.from_db_workspaces_active(wslist)
30+
resp = v1_models.ListWorkspacesResponse.from_db_workspaces_with_sessioninfo(wslist)
3131

3232
return resp
3333

src/codegate/api/v1_models.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,18 @@ class ListWorkspacesResponse(pydantic.BaseModel):
2323
workspaces: list[Workspace]
2424

2525
@classmethod
26-
def from_db_workspaces_active(
27-
cls, db_workspaces: List[db_models.WorkspaceActive]
26+
def from_db_workspaces_with_sessioninfo(
27+
cls, db_workspaces: List[db_models.WorkspaceWithSessionInfo]
2828
) -> "ListWorkspacesResponse":
2929
return cls(
3030
workspaces=[
31-
Workspace(name=ws.name, is_active=ws.active_workspace_id is not None)
32-
for ws in db_workspaces
31+
Workspace(name=ws.name, is_active=ws.session_id is not None) for ws in db_workspaces
3332
]
3433
)
3534

3635
@classmethod
3736
def from_db_workspaces(
38-
cls, db_workspaces: List[db_models.Workspace]
37+
cls, db_workspaces: List[db_models.WorkspaceRow]
3938
) -> "ListWorkspacesResponse":
4039
return cls(workspaces=[Workspace(name=ws.name, is_active=False) for ws in db_workspaces])
4140

src/codegate/db/connection.py

+22-19
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@
1919
Alert,
2020
GetAlertsWithPromptAndOutputRow,
2121
GetPromptWithOutputsRow,
22+
GetWorkspaceByNameConditions,
2223
Output,
2324
Prompt,
2425
Session,
25-
Workspace,
26-
WorkspaceActive,
26+
WorkspaceRow,
27+
WorkspaceWithSessionInfo,
2728
)
2829
from codegate.pipeline.base import PipelineContext
2930

@@ -263,15 +264,17 @@ async def record_context(self, context: Optional[PipelineContext]) -> None:
263264
except Exception as e:
264265
logger.error(f"Failed to record context: {context}.", error=str(e))
265266

266-
async def add_workspace(self, workspace_name: str) -> Workspace:
267+
async def add_workspace(self, workspace_name: str) -> WorkspaceRow:
267268
"""Add a new workspace to the DB.
268269
269270
This handles validation and insertion of a new workspace.
270271
271272
It may raise a ValidationError if the workspace name is invalid.
272273
or a AlreadyExistsError if the workspace already exists.
273274
"""
274-
workspace = Workspace(id=str(uuid.uuid4()), name=workspace_name, custom_instructions=None)
275+
workspace = WorkspaceRow(
276+
id=str(uuid.uuid4()), name=workspace_name, custom_instructions=None
277+
)
275278
sql = text(
276279
"""
277280
INSERT INTO workspaces (id, name)
@@ -289,7 +292,7 @@ async def add_workspace(self, workspace_name: str) -> Workspace:
289292
raise AlreadyExistsError(f"Workspace {workspace_name} already exists.")
290293
return added_workspace
291294

292-
async def update_workspace(self, workspace: Workspace) -> Workspace:
295+
async def update_workspace(self, workspace: WorkspaceRow) -> WorkspaceRow:
293296
sql = text(
294297
"""
295298
UPDATE workspaces SET
@@ -319,7 +322,7 @@ async def update_session(self, session: Session) -> Optional[Session]:
319322
active_session = await self._execute_update_pydantic_model(session, sql, should_raise=True)
320323
return active_session
321324

322-
async def soft_delete_workspace(self, workspace: Workspace) -> Optional[Workspace]:
325+
async def soft_delete_workspace(self, workspace: WorkspaceRow) -> Optional[WorkspaceRow]:
323326
sql = text(
324327
"""
325328
UPDATE workspaces
@@ -333,7 +336,7 @@ async def soft_delete_workspace(self, workspace: Workspace) -> Optional[Workspac
333336
)
334337
return deleted_workspace
335338

336-
async def hard_delete_workspace(self, workspace: Workspace) -> Optional[Workspace]:
339+
async def hard_delete_workspace(self, workspace: WorkspaceRow) -> Optional[WorkspaceRow]:
337340
sql = text(
338341
"""
339342
DELETE FROM workspaces
@@ -346,7 +349,7 @@ async def hard_delete_workspace(self, workspace: Workspace) -> Optional[Workspac
346349
)
347350
return deleted_workspace
348351

349-
async def recover_workspace(self, workspace: Workspace) -> Optional[Workspace]:
352+
async def recover_workspace(self, workspace: WorkspaceRow) -> Optional[WorkspaceRow]:
350353
sql = text(
351354
"""
352355
UPDATE workspaces
@@ -460,20 +463,20 @@ async def get_alerts_with_prompt_and_output(
460463
)
461464
return prompts
462465

463-
async def get_workspaces(self) -> List[WorkspaceActive]:
466+
async def get_workspaces(self) -> List[WorkspaceWithSessionInfo]:
464467
sql = text(
465468
"""
466469
SELECT
467-
w.id, w.name, s.active_workspace_id
470+
w.id, w.name, s.id as session_id
468471
FROM workspaces w
469472
LEFT JOIN sessions s ON w.id = s.active_workspace_id
470473
WHERE w.deleted_at IS NULL
471474
"""
472475
)
473-
workspaces = await self._execute_select_pydantic_model(WorkspaceActive, sql)
476+
workspaces = await self._execute_select_pydantic_model(WorkspaceWithSessionInfo, sql)
474477
return workspaces
475478

476-
async def get_archived_workspaces(self) -> List[Workspace]:
479+
async def get_archived_workspaces(self) -> List[WorkspaceRow]:
477480
sql = text(
478481
"""
479482
SELECT
@@ -483,10 +486,10 @@ async def get_archived_workspaces(self) -> List[Workspace]:
483486
ORDER BY deleted_at DESC
484487
"""
485488
)
486-
workspaces = await self._execute_select_pydantic_model(Workspace, sql)
489+
workspaces = await self._execute_select_pydantic_model(WorkspaceRow, sql)
487490
return workspaces
488491

489-
async def get_workspace_by_name(self, name: str) -> Optional[Workspace]:
492+
async def get_workspace_by_name(self, name: str) -> Optional[WorkspaceRow]:
490493
sql = text(
491494
"""
492495
SELECT
@@ -495,13 +498,13 @@ async def get_workspace_by_name(self, name: str) -> Optional[Workspace]:
495498
WHERE name = :name AND deleted_at IS NULL
496499
"""
497500
)
498-
conditions = {"name": name}
501+
conditions = GetWorkspaceByNameConditions(name=name).get_conditions()
499502
workspaces = await self._exec_select_conditions_to_pydantic(
500-
Workspace, sql, conditions, should_raise=True
503+
WorkspaceRow, sql, conditions, should_raise=True
501504
)
502505
return workspaces[0] if workspaces else None
503506

504-
async def get_archived_workspace_by_name(self, name: str) -> Optional[Workspace]:
507+
async def get_archived_workspace_by_name(self, name: str) -> Optional[WorkspaceRow]:
505508
sql = text(
506509
"""
507510
SELECT
@@ -510,9 +513,9 @@ async def get_archived_workspace_by_name(self, name: str) -> Optional[Workspace]
510513
WHERE name = :name AND deleted_at IS NOT NULL
511514
"""
512515
)
513-
conditions = {"name": name}
516+
conditions = GetWorkspaceByNameConditions(name=name).get_conditions()
514517
workspaces = await self._exec_select_conditions_to_pydantic(
515-
Workspace, sql, conditions, should_raise=True
518+
WorkspaceRow, sql, conditions, should_raise=True
516519
)
517520
return workspaces[0] if workspaces else None
518521

src/codegate/db/models.py

+39-7
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,43 @@ class Prompt(BaseModel):
3030
workspace_id: Optional[str]
3131

3232

33-
WorskpaceNameStr = Annotated[
33+
class Setting(BaseModel):
34+
id: Any
35+
ip: Optional[Any]
36+
port: Optional[Any]
37+
llm_model: Optional[Any]
38+
system_prompt: Optional[Any]
39+
other_settings: Optional[Any]
40+
41+
42+
WorkspaceNameStr = Annotated[
3443
str,
3544
StringConstraints(
3645
strip_whitespace=True, to_lower=True, pattern=r"^[a-zA-Z0-9_-]+$", strict=True
3746
),
3847
]
3948

4049

41-
class Workspace(BaseModel):
50+
class WorkspaceRow(BaseModel):
51+
"""A workspace row entry.
52+
53+
Since our model currently includes instructions
54+
in the same table, this is returned as a single
55+
object.
56+
"""
57+
4258
id: str
43-
name: WorskpaceNameStr
59+
name: WorkspaceNameStr
4460
custom_instructions: Optional[str]
4561

4662

63+
class GetWorkspaceByNameConditions(BaseModel):
64+
name: WorkspaceNameStr
65+
66+
def get_conditions(self):
67+
return {"name": self.name}
68+
69+
4770
class Session(BaseModel):
4871
id: str
4972
active_workspace_id: str
@@ -81,15 +104,24 @@ class GetPromptWithOutputsRow(BaseModel):
81104
output_timestamp: Optional[Any]
82105

83106

84-
class WorkspaceActive(BaseModel):
107+
class WorkspaceWithSessionInfo(BaseModel):
108+
"""Returns a workspace ID with an optional
109+
session ID. If the session ID is None, then
110+
the workspace is not active.
111+
"""
112+
85113
id: str
86-
name: str
87-
active_workspace_id: Optional[str]
114+
name: WorkspaceNameStr
115+
session_id: Optional[str]
88116

89117

90118
class ActiveWorkspace(BaseModel):
119+
"""Returns a full active workspace object with the
120+
with the session information.
121+
"""
122+
91123
id: str
92-
name: str
124+
name: WorkspaceNameStr
93125
custom_instructions: Optional[str]
94126
session_id: str
95127
last_update: datetime.datetime

src/codegate/pipeline/cli/commands.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ async def _list_workspaces(self, flags: Dict[str, str], args: List[str]) -> str:
168168
respond_str = ""
169169
for workspace in workspaces:
170170
respond_str += f"- {workspace.name}"
171-
if workspace.active_workspace_id:
171+
if workspace.session_id:
172172
respond_str += " **(active)**"
173173
respond_str += "\n"
174174
return respond_str

src/codegate/workspaces/crud.py

+12-10
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from typing import List, Optional, Tuple
33

44
from codegate.db.connection import DbReader, DbRecorder
5-
from codegate.db.models import ActiveWorkspace, Session, Workspace, WorkspaceActive
5+
from codegate.db.models import ActiveWorkspace, Session, WorkspaceRow, WorkspaceWithSessionInfo
66

77

88
class WorkspaceCrudError(Exception):
@@ -28,7 +28,7 @@ class WorkspaceCrud:
2828
def __init__(self):
2929
self._db_reader = DbReader()
3030

31-
async def add_workspace(self, new_workspace_name: str) -> Workspace:
31+
async def add_workspace(self, new_workspace_name: str) -> WorkspaceRow:
3232
"""
3333
Add a workspace
3434
@@ -43,7 +43,9 @@ async def add_workspace(self, new_workspace_name: str) -> Workspace:
4343
workspace_created = await db_recorder.add_workspace(new_workspace_name)
4444
return workspace_created
4545

46-
async def rename_workspace(self, old_workspace_name: str, new_workspace_name: str) -> Workspace:
46+
async def rename_workspace(
47+
self, old_workspace_name: str, new_workspace_name: str
48+
) -> WorkspaceRow:
4749
"""
4850
Rename a workspace
4951
@@ -65,19 +67,19 @@ async def rename_workspace(self, old_workspace_name: str, new_workspace_name: st
6567
if not ws:
6668
raise WorkspaceDoesNotExistError(f"Workspace {old_workspace_name} does not exist.")
6769
db_recorder = DbRecorder()
68-
new_ws = Workspace(
70+
new_ws = WorkspaceRow(
6971
id=ws.id, name=new_workspace_name, custom_instructions=ws.custom_instructions
7072
)
7173
workspace_renamed = await db_recorder.update_workspace(new_ws)
7274
return workspace_renamed
7375

74-
async def get_workspaces(self) -> List[WorkspaceActive]:
76+
async def get_workspaces(self) -> List[WorkspaceWithSessionInfo]:
7577
"""
7678
Get all workspaces
7779
"""
7880
return await self._db_reader.get_workspaces()
7981

80-
async def get_archived_workspaces(self) -> List[Workspace]:
82+
async def get_archived_workspaces(self) -> List[WorkspaceRow]:
8183
"""
8284
Get all archived workspaces
8385
"""
@@ -91,7 +93,7 @@ async def get_active_workspace(self) -> Optional[ActiveWorkspace]:
9193

9294
async def _is_workspace_active(
9395
self, workspace_name: str
94-
) -> Tuple[bool, Optional[Session], Optional[Workspace]]:
96+
) -> Tuple[bool, Optional[Session], Optional[WorkspaceRow]]:
9597
"""
9698
Check if the workspace is active alongside the session and workspace objects
9799
"""
@@ -137,13 +139,13 @@ async def recover_workspace(self, workspace_name: str):
137139

138140
async def update_workspace_custom_instructions(
139141
self, workspace_name: str, custom_instr_lst: List[str]
140-
) -> Workspace:
142+
) -> WorkspaceRow:
141143
selected_workspace = await self._db_reader.get_workspace_by_name(workspace_name)
142144
if not selected_workspace:
143145
raise WorkspaceDoesNotExistError(f"Workspace {workspace_name} does not exist.")
144146

145147
custom_instructions = " ".join(custom_instr_lst)
146-
workspace_update = Workspace(
148+
workspace_update = WorkspaceRow(
147149
id=selected_workspace.id,
148150
name=selected_workspace.name,
149151
custom_instructions=custom_instructions,
@@ -195,7 +197,7 @@ async def hard_delete_workspace(self, workspace_name: str):
195197
raise WorkspaceCrudError(f"Error deleting workspace {workspace_name}")
196198
return
197199

198-
async def get_workspace_by_name(self, workspace_name: str) -> Workspace:
200+
async def get_workspace_by_name(self, workspace_name: str) -> WorkspaceRow:
199201
workspace = await self._db_reader.get_workspace_by_name(workspace_name)
200202
if not workspace:
201203
raise WorkspaceDoesNotExistError(f"Workspace {workspace_name} does not exist.")

tests/pipeline/workspace/test_workspace.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
import pytest
44

5-
from codegate.db.models import Workspace as WorkspaceModel
6-
from codegate.db.models import WorkspaceActive
5+
from codegate.db.models import WorkspaceRow as WorkspaceModel
6+
from codegate.db.models import WorkspaceWithSessionInfo
77
from codegate.pipeline.cli.commands import Workspace
88

99

@@ -17,16 +17,16 @@
1717
(
1818
[
1919
# We'll make a MagicMock that simulates a workspace
20-
# with 'name' attribute and 'active_workspace_id' set
21-
WorkspaceActive(id="1", name="Workspace1", active_workspace_id="100")
20+
# with 'name' attribute and 'session_id' set
21+
WorkspaceWithSessionInfo(id="1", name="Workspace1", session_id="100")
2222
],
2323
"- Workspace1 **(active)**\n",
2424
),
2525
# Case 3: Multiple workspaces, second one active
2626
(
2727
[
28-
WorkspaceActive(id="1", name="Workspace1", active_workspace_id=None),
29-
WorkspaceActive(id="2", name="Workspace2", active_workspace_id="200"),
28+
WorkspaceWithSessionInfo(id="1", name="Workspace1", session_id=None),
29+
WorkspaceWithSessionInfo(id="2", name="Workspace2", session_id="200"),
3030
],
3131
"- Workspace1\n- Workspace2 **(active)**\n",
3232
),

0 commit comments

Comments
 (0)