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

Commit c23a481

Browse files
author
Luke Hinds
authored
Merge pull request #515 from stacklok/issue-512
Remove sqlc and its classes
2 parents 246c9cd + a5de607 commit c23a481

File tree

13 files changed

+134
-243
lines changed

13 files changed

+134
-243
lines changed

poetry.lock

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ litellm = "^1.57.1"
1616
llama_cpp_python = ">=0.3.2"
1717
cryptography = "^44.0.0"
1818
sqlalchemy = "^2.0.28"
19-
greenlet = "^3.0.3"
2019
aiosqlite = "^0.20.0"
2120
ollama = ">=0.4.4"
2221
pydantic-settings = "^2.7.1"
@@ -27,8 +26,8 @@ tree-sitter-java = ">=0.23.5"
2726
tree-sitter-javascript = ">=0.23.1"
2827
tree-sitter-python = ">=0.23.6"
2928
tree-sitter-rust = ">=0.23.2"
30-
3129
sqlite-vec-sl-tmp = "^0.0.4"
30+
3231
[tool.poetry.group.dev.dependencies]
3332
pytest = ">=7.4.0"
3433
pytest-cov = ">=4.1.0"

sql/queries/queries.sql

Lines changed: 0 additions & 24 deletions
This file was deleted.

sqlc.yaml

Lines changed: 0 additions & 20 deletions
This file was deleted.

src/codegate/dashboard/post_processing.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
PartialConversation,
1313
QuestionAnswer,
1414
)
15-
from codegate.db.queries import GetAlertsWithPromptAndOutputRow, GetPromptWithOutputsRow
15+
from codegate.db.models import GetAlertsWithPromptAndOutputRow, GetPromptWithOutputsRow
1616

1717
logger = structlog.get_logger("codegate")
1818

@@ -183,7 +183,7 @@ async def parse_get_prompt_with_output(
183183

184184
def parse_question_answer(input_text: str) -> str:
185185
# given a string, detect if we have a pattern of "Context: xxx \n\nQuery: xxx" and strip it
186-
pattern = r'^Context:.*?\n\n\s*Query:\s*(.*)$'
186+
pattern = r"^Context:.*?\n\n\s*Query:\s*(.*)$"
187187

188188
# Search using the regex pattern
189189
match = re.search(pattern, input_text, re.DOTALL)
@@ -226,7 +226,8 @@ async def match_conversations(
226226
if partial_conversation.question_answer.answer is not None:
227227
first_partial_conversation = partial_conversation
228228
partial_conversation.question_answer.question.message = parse_question_answer(
229-
partial_conversation.question_answer.question.message)
229+
partial_conversation.question_answer.question.message
230+
)
230231
questions_answers.append(partial_conversation.question_answer)
231232

232233
# only add conversation if we have some answers

src/codegate/db/connection.py

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
import asyncio
22
import json
33
from pathlib import Path
4-
from typing import List, Optional
4+
from typing import List, Optional, Type
55

66
import structlog
77
from pydantic import BaseModel
88
from sqlalchemy import TextClause, text
99
from sqlalchemy.ext.asyncio import create_async_engine
1010

1111
from codegate.db.fim_cache import FimCache
12-
from codegate.db.models import Alert, Output, Prompt
13-
from codegate.db.queries import (
14-
AsyncQuerier,
12+
from codegate.db.models import (
13+
Alert,
1514
GetAlertsWithPromptAndOutputRow,
1615
GetPromptWithOutputsRow,
16+
Output,
17+
Prompt,
1718
)
1819
from codegate.pipeline.base import PipelineContext
1920

@@ -83,11 +84,9 @@ async def init_db(self):
8384
await self._async_db_engine.dispose()
8485

8586
async def _execute_update_pydantic_model(
86-
self, model: BaseModel, sql_command: TextClause #
87+
self, model: BaseModel, sql_command: TextClause
8788
) -> Optional[BaseModel]:
88-
# There are create method in queries.py automatically generated by sqlc
89-
# However, the methods are buggy for Pydancti and don't work as expected.
90-
# Manually writing the SQL query to insert Pydantic models.
89+
"""Execute an update or insert command for a Pydantic model."""
9190
async with self._async_db_engine.begin() as conn:
9291
try:
9392
result = await conn.execute(sql_command, model.model_dump())
@@ -117,8 +116,9 @@ async def record_request(self, prompt_params: Optional[Prompt] = None) -> Option
117116
# logger.debug(f"Recorded request: {recorded_request}")
118117
return recorded_request # type: ignore
119118

120-
async def update_request(self, initial_id: str,
121-
prompt_params: Optional[Prompt] = None) -> Optional[Prompt]:
119+
async def update_request(
120+
self, initial_id: str, prompt_params: Optional[Prompt] = None
121+
) -> Optional[Prompt]:
122122
if prompt_params is None:
123123
return None
124124
prompt_params.id = initial_id # overwrite the initial id of the request
@@ -135,8 +135,9 @@ async def update_request(self, initial_id: str,
135135
# logger.debug(f"Recorded request: {recorded_request}")
136136
return updated_request # type: ignore
137137

138-
async def record_outputs(self, outputs: List[Output],
139-
initial_id: Optional[str]) -> Optional[Output]:
138+
async def record_outputs(
139+
self, outputs: List[Output], initial_id: Optional[str]
140+
) -> Optional[Output]:
140141
if not outputs:
141142
return
142143

@@ -216,7 +217,7 @@ def _should_record_context(self, context: Optional[PipelineContext]) -> tuple:
216217

217218
# If it's not a FIM prompt, we don't need to check anything else.
218219
if context.input_request.type != "fim":
219-
return True, 'add', '' # Default to add if not FIM, since no cache check is required
220+
return True, "add", "" # Default to add if not FIM, since no cache check is required
220221

221222
return fim_cache.could_store_fim_request(context) # type: ignore
222223

@@ -229,7 +230,7 @@ async def record_context(self, context: Optional[PipelineContext]) -> None:
229230
if not should_record:
230231
logger.info("Skipping record of context, not needed")
231232
return
232-
if action == 'add':
233+
if action == "add":
233234
await self.record_request(context.input_request)
234235
await self.record_outputs(context.output_responses, None)
235236
await self.record_alerts(context.alerts_raised, None)
@@ -257,18 +258,61 @@ class DbReader(DbCodeGate):
257258
def __init__(self, sqlite_path: Optional[str] = None):
258259
super().__init__(sqlite_path)
259260

261+
async def _execute_select_pydantic_model(
262+
self, model_type: Type[BaseModel], sql_command: TextClause
263+
) -> Optional[BaseModel]:
264+
async with self._async_db_engine.begin() as conn:
265+
try:
266+
result = await conn.execute(sql_command)
267+
if not result:
268+
return None
269+
rows = [model_type(**row._asdict()) for row in result.fetchall() if row]
270+
return rows
271+
except Exception as e:
272+
logger.error(f"Failed to select model: {model_type}.", error=str(e))
273+
return None
274+
260275
async def get_prompts_with_output(self) -> List[GetPromptWithOutputsRow]:
261-
conn = await self._async_db_engine.connect()
262-
querier = AsyncQuerier(conn)
263-
prompts = [prompt async for prompt in querier.get_prompt_with_outputs()]
264-
await conn.close()
276+
sql = text(
277+
"""
278+
SELECT
279+
p.id, p.timestamp, p.provider, p.request, p.type,
280+
o.id as output_id,
281+
o.output,
282+
o.timestamp as output_timestamp
283+
FROM prompts p
284+
LEFT JOIN outputs o ON p.id = o.prompt_id
285+
ORDER BY o.timestamp DESC
286+
"""
287+
)
288+
prompts = await self._execute_select_pydantic_model(GetPromptWithOutputsRow, sql)
265289
return prompts
266290

267291
async def get_alerts_with_prompt_and_output(self) -> List[GetAlertsWithPromptAndOutputRow]:
268-
conn = await self._async_db_engine.connect()
269-
querier = AsyncQuerier(conn)
270-
prompts = [prompt async for prompt in querier.get_alerts_with_prompt_and_output()]
271-
await conn.close()
292+
sql = text(
293+
"""
294+
SELECT
295+
a.id,
296+
a.prompt_id,
297+
a.code_snippet,
298+
a.trigger_string,
299+
a.trigger_type,
300+
a.trigger_category,
301+
a.timestamp,
302+
p.timestamp as prompt_timestamp,
303+
p.provider,
304+
p.request,
305+
p.type,
306+
o.id as output_id,
307+
o.output,
308+
o.timestamp as output_timestamp
309+
FROM alerts a
310+
LEFT JOIN prompts p ON p.id = a.prompt_id
311+
LEFT JOIN outputs o ON p.id = o.prompt_id
312+
ORDER BY a.timestamp DESC
313+
"""
314+
)
315+
prompts = await self._execute_select_pydantic_model(GetAlertsWithPromptAndOutputRow, sql)
272316
return prompts
273317

274318

src/codegate/db/fim_cache.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,9 @@ def _add_cache_entry(self, hash_key: str, context: PipelineContext):
9696
if alert.trigger_category == AlertSeverity.CRITICAL.value
9797
]
9898
new_cache = CachedFim(
99-
timestamp=context.input_request.timestamp, critical_alerts=critical_alerts,
100-
initial_id=context.input_request.id
99+
timestamp=context.input_request.timestamp,
100+
critical_alerts=critical_alerts,
101+
initial_id=context.input_request.id,
101102
)
102103
self.cache[hash_key] = new_cache
103104
logger.info(f"Added cache entry for hash key: {hash_key}")
@@ -115,8 +116,9 @@ def _update_cache_entry(self, hash_key: str, context: PipelineContext):
115116
]
116117
# Update the entry in the cache with new critical alerts but keep the old timestamp.
117118
updated_cache = CachedFim(
118-
timestamp=existing_entry.timestamp, critical_alerts=critical_alerts,
119-
initial_id=existing_entry.initial_id
119+
timestamp=existing_entry.timestamp,
120+
critical_alerts=critical_alerts,
121+
initial_id=existing_entry.initial_id,
120122
)
121123
self.cache[hash_key] = updated_cache
122124
logger.info(f"Updated cache entry for hash key: {hash_key}")
@@ -148,22 +150,25 @@ def _is_cached_entry_old(self, context: PipelineContext, cached_entry: CachedFim
148150
def could_store_fim_request(self, context: PipelineContext):
149151
if not context.input_request:
150152
logger.warning("No input request found. Skipping creating a mapping entry")
151-
return False, '', ''
153+
return False, "", ""
152154
# Couldn't process the user message. Skip creating a mapping entry.
153155
message = self._extract_message_from_fim_request(context.input_request.request)
154156
if message is None:
155157
logger.warning(f"Couldn't read FIM message: {message}. Will not record to DB.")
156-
return False, '', ''
158+
return False, "", ""
157159

158160
hash_key = self._calculate_hash_key(message, context.input_request.provider) # type: ignore
159161
cached_entry = self.cache.get(hash_key, None)
160-
if cached_entry is None or self._is_cached_entry_old(
161-
context, cached_entry) or self._are_new_alerts_present(context, cached_entry):
162+
if (
163+
cached_entry is None
164+
or self._is_cached_entry_old(context, cached_entry)
165+
or self._are_new_alerts_present(context, cached_entry)
166+
):
162167
cached_entry = self._add_cache_entry(hash_key, context)
163168
if cached_entry is None:
164169
logger.warning("Failed to add cache entry")
165-
return False, '', ''
166-
return True, 'add', cached_entry.initial_id
170+
return False, "", ""
171+
return True, "add", cached_entry.initial_id
167172

168173
self._update_cache_entry(hash_key, context)
169-
return True, 'update', cached_entry.initial_id
174+
return True, "update", cached_entry.initial_id

src/codegate/db/models.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
# Code generated by sqlc. DO NOT EDIT.
2-
# versions:
3-
# sqlc v1.27.0
41
from typing import Any, Optional
52

63
import pydantic
@@ -38,3 +35,34 @@ class Setting(pydantic.BaseModel):
3835
llm_model: Optional[Any]
3936
system_prompt: Optional[Any]
4037
other_settings: Optional[Any]
38+
39+
40+
# Models for select queries
41+
42+
43+
class GetAlertsWithPromptAndOutputRow(pydantic.BaseModel):
44+
id: Any
45+
prompt_id: Any
46+
code_snippet: Optional[Any]
47+
trigger_string: Optional[Any]
48+
trigger_type: Any
49+
trigger_category: Optional[Any]
50+
timestamp: Any
51+
prompt_timestamp: Optional[Any]
52+
provider: Optional[Any]
53+
request: Optional[Any]
54+
type: Optional[Any]
55+
output_id: Optional[Any]
56+
output: Optional[Any]
57+
output_timestamp: Optional[Any]
58+
59+
60+
class GetPromptWithOutputsRow(pydantic.BaseModel):
61+
id: Any
62+
timestamp: Any
63+
provider: Optional[Any]
64+
request: Any
65+
type: Any
66+
output_id: Optional[Any]
67+
output: Optional[Any]
68+
output_timestamp: Optional[Any]

0 commit comments

Comments
 (0)