Skip to content
This repository was archived by the owner on Jun 5, 2025. It is now read-only.

Commit 5f90a25

Browse files
authored
Merge branch 'main' into run-on-main
2 parents e676b61 + 10900b3 commit 5f90a25

File tree

5 files changed

+256
-6
lines changed

5 files changed

+256
-6
lines changed

api/openapi.json

Lines changed: 149 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@
161161
"content": {
162162
"application/json": {
163163
"schema": {
164-
"$ref": "#/components/schemas/CreateWorkspaceRequest"
164+
"$ref": "#/components/schemas/CreateOrRenameWorkspaceRequest"
165165
}
166166
}
167167
},
@@ -408,6 +408,128 @@
408408
}
409409
}
410410
}
411+
},
412+
"/api/v1/workspaces/{workspace_name}/system-prompt": {
413+
"get": {
414+
"tags": [
415+
"CodeGate API",
416+
"Workspaces"
417+
],
418+
"summary": "Get Workspace System Prompt",
419+
"description": "Get the system prompt for a workspace.",
420+
"operationId": "v1_get_workspace_system_prompt",
421+
"parameters": [
422+
{
423+
"name": "workspace_name",
424+
"in": "path",
425+
"required": true,
426+
"schema": {
427+
"type": "string",
428+
"title": "Workspace Name"
429+
}
430+
}
431+
],
432+
"responses": {
433+
"200": {
434+
"description": "Successful Response",
435+
"content": {
436+
"application/json": {
437+
"schema": {
438+
"$ref": "#/components/schemas/SystemPrompt"
439+
}
440+
}
441+
}
442+
},
443+
"422": {
444+
"description": "Validation Error",
445+
"content": {
446+
"application/json": {
447+
"schema": {
448+
"$ref": "#/components/schemas/HTTPValidationError"
449+
}
450+
}
451+
}
452+
}
453+
}
454+
},
455+
"put": {
456+
"tags": [
457+
"CodeGate API",
458+
"Workspaces"
459+
],
460+
"summary": "Set Workspace System Prompt",
461+
"operationId": "v1_set_workspace_system_prompt",
462+
"parameters": [
463+
{
464+
"name": "workspace_name",
465+
"in": "path",
466+
"required": true,
467+
"schema": {
468+
"type": "string",
469+
"title": "Workspace Name"
470+
}
471+
}
472+
],
473+
"requestBody": {
474+
"required": true,
475+
"content": {
476+
"application/json": {
477+
"schema": {
478+
"$ref": "#/components/schemas/SystemPrompt"
479+
}
480+
}
481+
}
482+
},
483+
"responses": {
484+
"204": {
485+
"description": "Successful Response"
486+
},
487+
"422": {
488+
"description": "Validation Error",
489+
"content": {
490+
"application/json": {
491+
"schema": {
492+
"$ref": "#/components/schemas/HTTPValidationError"
493+
}
494+
}
495+
}
496+
}
497+
}
498+
},
499+
"delete": {
500+
"tags": [
501+
"CodeGate API",
502+
"Workspaces"
503+
],
504+
"summary": "Delete Workspace System Prompt",
505+
"operationId": "v1_delete_workspace_system_prompt",
506+
"parameters": [
507+
{
508+
"name": "workspace_name",
509+
"in": "path",
510+
"required": true,
511+
"schema": {
512+
"type": "string",
513+
"title": "Workspace Name"
514+
}
515+
}
516+
],
517+
"responses": {
518+
"204": {
519+
"description": "Successful Response"
520+
},
521+
"422": {
522+
"description": "Validation Error",
523+
"content": {
524+
"application/json": {
525+
"schema": {
526+
"$ref": "#/components/schemas/HTTPValidationError"
527+
}
528+
}
529+
}
530+
}
531+
}
532+
}
411533
}
412534
},
413535
"components": {
@@ -628,18 +750,29 @@
628750
"title": "Conversation",
629751
"description": "Represents a conversation."
630752
},
631-
"CreateWorkspaceRequest": {
753+
"CreateOrRenameWorkspaceRequest": {
632754
"properties": {
633755
"name": {
634756
"type": "string",
635757
"title": "Name"
758+
},
759+
"rename_to": {
760+
"anyOf": [
761+
{
762+
"type": "string"
763+
},
764+
{
765+
"type": "null"
766+
}
767+
],
768+
"title": "Rename To"
636769
}
637770
},
638771
"type": "object",
639772
"required": [
640773
"name"
641774
],
642-
"title": "CreateWorkspaceRequest"
775+
"title": "CreateOrRenameWorkspaceRequest"
643776
},
644777
"HTTPValidationError": {
645778
"properties": {
@@ -710,6 +843,19 @@
710843
"title": "QuestionAnswer",
711844
"description": "Represents a question and answer pair."
712845
},
846+
"SystemPrompt": {
847+
"properties": {
848+
"prompt": {
849+
"type": "string",
850+
"title": "Prompt"
851+
}
852+
},
853+
"type": "object",
854+
"required": [
855+
"prompt"
856+
],
857+
"title": "SystemPrompt"
858+
},
713859
"ValidationError": {
714860
"properties": {
715861
"loc": {

src/codegate/api/v1.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,18 @@ async def activate_workspace(request: v1_models.ActivateWorkspaceRequest, status
6161

6262

6363
@v1.post("/workspaces", tags=["Workspaces"], generate_unique_id_function=uniq_name, status_code=201)
64-
async def create_workspace(request: v1_models.CreateWorkspaceRequest) -> v1_models.Workspace:
64+
async def create_workspace(
65+
request: v1_models.CreateOrRenameWorkspaceRequest,
66+
) -> v1_models.Workspace:
6567
"""Create a new workspace."""
68+
if request.rename_to is not None:
69+
return await rename_workspace(request)
70+
return await create_new_workspace(request)
71+
72+
73+
async def create_new_workspace(
74+
request: v1_models.CreateOrRenameWorkspaceRequest,
75+
) -> v1_models.Workspace:
6676
# Input validation is done in the model
6777
try:
6878
_ = await wscrud.add_workspace(request.name)
@@ -83,6 +93,30 @@ async def create_workspace(request: v1_models.CreateWorkspaceRequest) -> v1_mode
8393
return v1_models.Workspace(name=request.name, is_active=False)
8494

8595

96+
async def rename_workspace(
97+
request: v1_models.CreateOrRenameWorkspaceRequest,
98+
) -> v1_models.Workspace:
99+
try:
100+
_ = await wscrud.rename_workspace(request.name, request.rename_to)
101+
except crud.WorkspaceDoesNotExistError:
102+
raise HTTPException(status_code=404, detail="Workspace does not exist")
103+
except AlreadyExistsError:
104+
raise HTTPException(status_code=409, detail="Workspace already exists")
105+
except ValidationError:
106+
raise HTTPException(
107+
status_code=400,
108+
detail=(
109+
"Invalid workspace name. " "Please use only alphanumeric characters and dashes"
110+
),
111+
)
112+
except crud.WorkspaceCrudError as e:
113+
raise HTTPException(status_code=400, detail=str(e))
114+
except Exception:
115+
raise HTTPException(status_code=500, detail="Internal server error")
116+
117+
return v1_models.Workspace(name=request.rename_to, is_active=False)
118+
119+
86120
@v1.delete(
87121
"/workspaces/{workspace_name}",
88122
tags=["Workspaces"],

src/codegate/api/v1_models.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,14 @@ def from_db_workspaces(
4848
)
4949

5050

51-
class CreateWorkspaceRequest(pydantic.BaseModel):
51+
class CreateOrRenameWorkspaceRequest(pydantic.BaseModel):
5252
name: str
5353

54+
# If set, rename the workspace to this name. Note that
55+
# the 'name' field is still required and the workspace
56+
# workspace must exist.
57+
rename_to: Optional[str] = None
58+
5459

5560
class ActivateWorkspaceRequest(pydantic.BaseModel):
5661
name: str

src/codegate/pipeline/cli/commands.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ def subcommands(self) -> Dict[str, Callable[[List[str]], Awaitable[str]]]:
154154
"add": self._add_workspace,
155155
"activate": self._activate_workspace,
156156
"remove": self._remove_workspace,
157+
"rename": self._rename_workspace,
157158
}
158159

159160
async def _list_workspaces(self, flags: Dict[str, str], args: List[str]) -> str:
@@ -193,6 +194,37 @@ async def _add_workspace(self, flags: Dict[str, str], args: List[str]) -> str:
193194

194195
return f"Workspace **{new_workspace_name}** has been added"
195196

197+
async def _rename_workspace(self, flags: Dict[str, str], args: List[str]) -> str:
198+
"""
199+
Rename a workspace
200+
"""
201+
if args is None or len(args) < 2:
202+
return (
203+
"Please provide a name and a new name. "
204+
"Use `codegate workspace rename workspace_name new_workspace_name`"
205+
)
206+
207+
old_workspace_name = args[0]
208+
new_workspace_name = args[1]
209+
if not old_workspace_name or not new_workspace_name:
210+
return (
211+
"Please provide a name and a new name. "
212+
"Use `codegate workspace rename workspace_name new_workspace_name`"
213+
)
214+
215+
try:
216+
await self.workspace_crud.rename_workspace(old_workspace_name, new_workspace_name)
217+
except crud.WorkspaceDoesNotExistError:
218+
return f"Workspace **{old_workspace_name}** does not exist"
219+
except AlreadyExistsError:
220+
return f"Workspace **{new_workspace_name}** already exists"
221+
except crud.WorkspaceCrudError:
222+
return "An error occurred while renaming the workspace"
223+
except Exception:
224+
return "An error occurred while renaming the workspace"
225+
226+
return f"Workspace **{old_workspace_name}** has been renamed to **{new_workspace_name}**"
227+
196228
async def _activate_workspace(self, flags: Dict[str, str], args: List[str]) -> str:
197229
"""
198230
Activate a workspace
@@ -249,7 +281,14 @@ def help(self) -> str:
249281
" - `workspace_name`\n\n"
250282
"- `activate`: Activate a workspace\n\n"
251283
" - *args*:\n\n"
252-
" - `workspace_name`"
284+
" - `workspace_name`\n\n"
285+
"- `remove`: Remove a workspace\n\n"
286+
" - *args*:\n\n"
287+
" - `workspace_name`\n\n"
288+
"- `rename`: Rename a workspace\n\n"
289+
" - *args*:\n\n"
290+
" - `workspace_name`\n"
291+
" - `new_workspace_name`\n\n"
253292
)
254293

255294

src/codegate/workspaces/crud.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,32 @@ 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:
47+
"""
48+
Rename a workspace
49+
50+
Args:
51+
old_name (str): The old name of the workspace
52+
new_name (str): The new name of the workspace
53+
"""
54+
if new_workspace_name == "":
55+
raise WorkspaceCrudError("Workspace name cannot be empty.")
56+
if old_workspace_name == "":
57+
raise WorkspaceCrudError("Workspace name cannot be empty.")
58+
if old_workspace_name in DEFAULT_WORKSPACE_NAME:
59+
raise WorkspaceCrudError("Cannot rename default workspace.")
60+
if new_workspace_name in RESERVED_WORKSPACE_KEYWORDS:
61+
raise WorkspaceCrudError(f"Workspace name {new_workspace_name} is reserved.")
62+
if old_workspace_name == new_workspace_name:
63+
raise WorkspaceCrudError("Old and new workspace names are the same.")
64+
ws = await self._db_reader.get_workspace_by_name(old_workspace_name)
65+
if not ws:
66+
raise WorkspaceDoesNotExistError(f"Workspace {old_workspace_name} does not exist.")
67+
db_recorder = DbRecorder()
68+
new_ws = Workspace(id=ws.id, name=new_workspace_name, system_prompt=ws.system_prompt)
69+
workspace_renamed = await db_recorder.update_workspace(new_ws)
70+
return workspace_renamed
71+
4672
async def get_workspaces(self) -> List[WorkspaceActive]:
4773
"""
4874
Get all workspaces

0 commit comments

Comments
 (0)