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

Commit b3a2ed4

Browse files
authored
Merge branch 'main' into run-on-main
2 parents 0d61209 + 705ebe5 commit b3a2ed4

File tree

6 files changed

+303
-13
lines changed

6 files changed

+303
-13
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ build: clean test
3434
image-build:
3535
DOCKER_BUILDKIT=1 $(CONTAINER_BUILD) \
3636
-f Dockerfile \
37-
--build-arg LATEST_RELEASE=$(curl -s "https://api.github.com/repos/stacklok/codegate-ui/releases/latest" | grep '"zipball_url":' | cut -d '"' -f 4) \
37+
--build-arg LATEST_RELEASE=$(shell curl -s "https://api.github.com/repos/stacklok/codegate-ui/releases/latest" | grep '"zipball_url":' | cut -d '"' -f 4) \
3838
--build-arg CODEGATE_VERSION="$(shell git describe --tags --abbrev=0)-$(shell git rev-parse --short HEAD)-dev" \
3939
-t codegate \
4040
. \

api/openapi.json

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,109 @@
305305
}
306306
}
307307
}
308+
},
309+
"/api/v1/workspaces/{workspace_name}/alerts": {
310+
"get": {
311+
"tags": [
312+
"CodeGate API",
313+
"Workspaces"
314+
],
315+
"summary": "Get Workspace Alerts",
316+
"description": "Get alerts for a workspace.",
317+
"operationId": "v1_get_workspace_alerts",
318+
"parameters": [
319+
{
320+
"name": "workspace_name",
321+
"in": "path",
322+
"required": true,
323+
"schema": {
324+
"type": "string",
325+
"title": "Workspace Name"
326+
}
327+
}
328+
],
329+
"responses": {
330+
"200": {
331+
"description": "Successful Response",
332+
"content": {
333+
"application/json": {
334+
"schema": {
335+
"type": "array",
336+
"items": {
337+
"anyOf": [
338+
{
339+
"$ref": "#/components/schemas/AlertConversation"
340+
},
341+
{
342+
"type": "null"
343+
}
344+
]
345+
},
346+
"title": "Response V1 Get Workspace Alerts"
347+
}
348+
}
349+
}
350+
},
351+
"422": {
352+
"description": "Validation Error",
353+
"content": {
354+
"application/json": {
355+
"schema": {
356+
"$ref": "#/components/schemas/HTTPValidationError"
357+
}
358+
}
359+
}
360+
}
361+
}
362+
}
363+
},
364+
"/api/v1/workspaces/{workspace_name}/messages": {
365+
"get": {
366+
"tags": [
367+
"CodeGate API",
368+
"Workspaces"
369+
],
370+
"summary": "Get Workspace Messages",
371+
"description": "Get messages for a workspace.",
372+
"operationId": "v1_get_workspace_messages",
373+
"parameters": [
374+
{
375+
"name": "workspace_name",
376+
"in": "path",
377+
"required": true,
378+
"schema": {
379+
"type": "string",
380+
"title": "Workspace Name"
381+
}
382+
}
383+
],
384+
"responses": {
385+
"200": {
386+
"description": "Successful Response",
387+
"content": {
388+
"application/json": {
389+
"schema": {
390+
"type": "array",
391+
"items": {
392+
"$ref": "#/components/schemas/Conversation"
393+
},
394+
"title": "Response V1 Get Workspace Messages"
395+
}
396+
}
397+
}
398+
},
399+
"422": {
400+
"description": "Validation Error",
401+
"content": {
402+
"application/json": {
403+
"schema": {
404+
"$ref": "#/components/schemas/HTTPValidationError"
405+
}
406+
}
407+
}
408+
}
409+
}
410+
}
308411
}
309412
},
310413
"components": {
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
"""add_on_delete_cascade
2+
3+
Revision ID: 4dec3e456c9e
4+
Revises: e6227073183d
5+
Create Date: 2025-01-21 08:20:12.221051+00:00
6+
7+
"""
8+
9+
from typing import Sequence, Union
10+
11+
from alembic import op
12+
13+
# revision identifiers, used by Alembic.
14+
revision: str = "4dec3e456c9e"
15+
down_revision: Union[str, None] = "e6227073183d"
16+
branch_labels: Union[str, Sequence[str], None] = None
17+
depends_on: Union[str, Sequence[str], None] = None
18+
19+
20+
def upgrade() -> None:
21+
# To add ON DELETE CASCADE to the foreign key constraint, we need to
22+
# rename the table, create a new table with the constraint, and copy
23+
# the data over.
24+
op.execute("ALTER TABLE prompts RENAME TO _prompts_old;")
25+
op.execute(
26+
"""
27+
CREATE TABLE prompts (
28+
id TEXT PRIMARY KEY, -- UUID stored as TEXT
29+
timestamp DATETIME NOT NULL,
30+
provider TEXT, -- VARCHAR(255)
31+
request TEXT NOT NULL, -- Record the full request that arrived to the server
32+
type TEXT NOT NULL, -- VARCHAR(50) (e.g. "fim", "chat")
33+
workspace_id TEXT NOT NULL,
34+
FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE
35+
);
36+
"""
37+
)
38+
op.execute("INSERT INTO prompts SELECT * FROM _prompts_old;")
39+
op.execute("DROP TABLE _prompts_old;")
40+
41+
# Doing the same for the sessions table
42+
op.execute("ALTER TABLE sessions RENAME TO _sessions_old;")
43+
op.execute(
44+
"""
45+
CREATE TABLE sessions (
46+
id TEXT PRIMARY KEY, -- UUID stored as TEXT
47+
active_workspace_id TEXT NOT NULL,
48+
last_update DATETIME NOT NULL,
49+
FOREIGN KEY (active_workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE
50+
);
51+
"""
52+
)
53+
op.execute("INSERT INTO sessions SELECT * FROM _sessions_old;")
54+
op.execute("DROP TABLE _sessions_old;")
55+
56+
# Doing the same for the output table
57+
op.execute("ALTER TABLE outputs RENAME TO _outputs_old;")
58+
op.execute(
59+
"""
60+
CREATE TABLE outputs (
61+
id TEXT PRIMARY KEY, -- UUID stored as TEXT
62+
prompt_id TEXT NOT NULL,
63+
timestamp DATETIME NOT NULL,
64+
output TEXT NOT NULL, -- Record the full response. If stream will be a list of objects
65+
FOREIGN KEY (prompt_id) REFERENCES prompts(id) ON DELETE CASCADE
66+
);
67+
"""
68+
)
69+
op.execute("INSERT INTO outputs SELECT * FROM _outputs_old;")
70+
op.execute("DROP TABLE _outputs_old;")
71+
72+
# Doing the same for the alerts table
73+
op.execute("ALTER TABLE alerts RENAME TO _alerts_old;")
74+
op.execute(
75+
"""
76+
CREATE TABLE alerts (
77+
id TEXT PRIMARY KEY, -- UUID stored as TEXT
78+
prompt_id TEXT NOT NULL,
79+
code_snippet TEXT,
80+
trigger_string TEXT, -- VARCHAR(255)
81+
trigger_type TEXT NOT NULL, -- VARCHAR(50)
82+
trigger_category TEXT,
83+
timestamp DATETIME NOT NULL,
84+
FOREIGN KEY (prompt_id) REFERENCES prompts(id) ON DELETE CASCADE
85+
);
86+
"""
87+
)
88+
op.execute("INSERT INTO alerts SELECT * FROM _alerts_old;")
89+
op.execute("DROP TABLE _alerts_old;")
90+
91+
# Dropping unused table
92+
op.execute("DROP TABLE settings;")
93+
94+
# Create indexes for foreign keys
95+
op.execute("CREATE INDEX idx_outputs_prompt_id ON outputs(prompt_id);")
96+
op.execute("CREATE INDEX idx_alerts_prompt_id ON alerts(prompt_id);")
97+
op.execute("CREATE INDEX idx_prompts_workspace_id ON prompts (workspace_id);")
98+
op.execute("CREATE INDEX idx_sessions_workspace_id ON sessions (active_workspace_id);")
99+
100+
101+
def downgrade() -> None:
102+
# Settings table
103+
op.execute(
104+
"""
105+
CREATE TABLE settings (
106+
id TEXT PRIMARY KEY, -- UUID stored as TEXT
107+
ip TEXT, -- VARCHAR(45)
108+
port INTEGER,
109+
llm_model TEXT, -- VARCHAR(255)
110+
system_prompt TEXT,
111+
other_settings TEXT -- JSON stored as TEXT
112+
);
113+
"""
114+
)

src/codegate/api/dashboard/dashboard.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import requests
55
import structlog
6-
from fastapi import APIRouter, Depends
6+
from fastapi import APIRouter, Depends, HTTPException
77
from fastapi.responses import StreamingResponse
88
from fastapi.routing import APIRoute
99

@@ -14,12 +14,15 @@
1414
)
1515
from codegate.api.dashboard.request_models import AlertConversation, Conversation
1616
from codegate.db.connection import DbReader, alert_queue
17+
from codegate.workspaces import crud
1718

1819
logger = structlog.get_logger("codegate")
1920

2021
dashboard_router = APIRouter()
2122
db_reader = None
2223

24+
wscrud = crud.WorkspaceCrud()
25+
2326

2427
def uniq_name(route: APIRoute):
2528
return f"v1_{route.name}"
@@ -48,9 +51,14 @@ def get_messages(db_reader: DbReader = Depends(get_db_reader)) -> List[Conversat
4851
"""
4952
Get all the messages from the database and return them as a list of conversations.
5053
"""
51-
prompts_outputs = asyncio.run(db_reader.get_prompts_with_output())
54+
try:
55+
active_ws = asyncio.run(wscrud.get_active_workspace())
56+
prompts_outputs = asyncio.run(db_reader.get_prompts_with_output(active_ws.id))
5257

53-
return asyncio.run(parse_messages_in_conversations(prompts_outputs))
58+
return asyncio.run(parse_messages_in_conversations(prompts_outputs))
59+
except Exception as e:
60+
logger.error(f"Error getting messages: {str(e)}")
61+
raise HTTPException(status_code=500, detail="Internal server error")
5462

5563

5664
@dashboard_router.get(
@@ -60,8 +68,15 @@ def get_alerts(db_reader: DbReader = Depends(get_db_reader)) -> List[Optional[Al
6068
"""
6169
Get all the messages from the database and return them as a list of conversations.
6270
"""
63-
alerts_prompt_output = asyncio.run(db_reader.get_alerts_with_prompt_and_output())
64-
return asyncio.run(parse_get_alert_conversation(alerts_prompt_output))
71+
try:
72+
active_ws = asyncio.run(wscrud.get_active_workspace())
73+
alerts_prompt_output = asyncio.run(
74+
db_reader.get_alerts_with_prompt_and_output(active_ws.id)
75+
)
76+
return asyncio.run(parse_get_alert_conversation(alerts_prompt_output))
77+
except Exception as e:
78+
logger.error(f"Error getting alerts: {str(e)}")
79+
raise HTTPException(status_code=500, detail="Internal server error")
6580

6681

6782
async def generate_sse_events() -> AsyncGenerator[str, None]:

src/codegate/api/v1.py

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
1+
from typing import List, Optional
2+
13
from fastapi import APIRouter, HTTPException, Response
24
from fastapi.routing import APIRoute
35
from pydantic import ValidationError
46

57
from codegate.api import v1_models
6-
from codegate.api.dashboard.dashboard import dashboard_router
7-
from codegate.db.connection import AlreadyExistsError
8+
from codegate.api.dashboard import dashboard
9+
from codegate.api.dashboard.request_models import AlertConversation, Conversation
10+
from codegate.db.connection import AlreadyExistsError, DbReader
811
from codegate.workspaces import crud
912

1013
v1 = APIRouter()
11-
v1.include_router(dashboard_router)
14+
v1.include_router(dashboard.dashboard_router)
1215
wscrud = crud.WorkspaceCrud()
1316

17+
# This is a singleton object
18+
dbreader = DbReader()
19+
1420

1521
def uniq_name(route: APIRoute):
1622
return f"v1_{route.name}"
@@ -94,3 +100,45 @@ async def delete_workspace(workspace_name: str):
94100
raise HTTPException(status_code=500, detail="Internal server error")
95101

96102
return Response(status_code=204)
103+
104+
105+
@v1.get(
106+
"/workspaces/{workspace_name}/alerts",
107+
tags=["Workspaces"],
108+
generate_unique_id_function=uniq_name,
109+
)
110+
async def get_workspace_alerts(workspace_name: str) -> List[Optional[AlertConversation]]:
111+
"""Get alerts for a workspace."""
112+
try:
113+
ws = await wscrud.get_workspace_by_name(workspace_name)
114+
except crud.WorkspaceDoesNotExistError:
115+
raise HTTPException(status_code=404, detail="Workspace does not exist")
116+
except Exception:
117+
raise HTTPException(status_code=500, detail="Internal server error")
118+
119+
try:
120+
alerts = await dbreader.get_alerts_with_prompt_and_output(ws.id)
121+
return await dashboard.parse_get_alert_conversation(alerts)
122+
except Exception:
123+
raise HTTPException(status_code=500, detail="Internal server error")
124+
125+
126+
@v1.get(
127+
"/workspaces/{workspace_name}/messages",
128+
tags=["Workspaces"],
129+
generate_unique_id_function=uniq_name,
130+
)
131+
async def get_workspace_messages(workspace_name: str) -> List[Conversation]:
132+
"""Get messages for a workspace."""
133+
try:
134+
ws = await wscrud.get_workspace_by_name(workspace_name)
135+
except crud.WorkspaceDoesNotExistError:
136+
raise HTTPException(status_code=404, detail="Workspace does not exist")
137+
except Exception:
138+
raise HTTPException(status_code=500, detail="Internal server error")
139+
140+
try:
141+
prompts_outputs = await dbreader.get_prompts_with_output(ws.id)
142+
return await dashboard.parse_messages_in_conversations(prompts_outputs)
143+
except Exception:
144+
raise HTTPException(status_code=500, detail="Internal server error")

0 commit comments

Comments
 (0)