Skip to content

Commit 25246e0

Browse files
jiahueiAmirBalwel
andcommitted
[Backend] Various updates ; Draft polylm library (#879)
Python SDK - jamaibase - Support context manager Backend - owl (API server) - Log invitation code rejection reason - Remove unsupported DB dialects - Only allow DB init in dev - Bump `owl` dependencies - Move owl scripts - Draft `polylm` library --- Co-authored-by: AmirBalwel <amir.balwel@embeddedllm.com>
1 parent 3982b99 commit 25246e0

13 files changed

Lines changed: 85 additions & 87 deletions

File tree

clients/python/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ unfixable = ["B"]
7171
"**/{tests,docs,tools}/*" = ["E402"]
7272

7373
[tool.ruff.lint.isort]
74-
known-first-party = ["jamaibase", "owl"]
74+
known-first-party = ["jamaibase", "owl", "polylm"]
7575

7676
[tool.ruff.lint.flake8-bugbear]
7777
# Allow default arguments like, e.g., `data: List[str] = fastapi.Query(None)`.

clients/python/src/jamaibase/client.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from datetime import datetime
55
from os.path import basename, split
66
from time import perf_counter
7-
from typing import Any, AsyncGenerator, BinaryIO, Generator, Literal, Type
7+
from typing import Any, AsyncGenerator, BinaryIO, Generator, Literal, Self, Type
88
from urllib.parse import quote
99
from warnings import warn
1010

@@ -192,6 +192,17 @@ async def close(self) -> None:
192192
"""
193193
await self.http_client.aclose()
194194

195+
async def __aenter__(self) -> Self:
196+
return self
197+
198+
async def __aexit__(self, exc_type, exc_value, traceback) -> None:
199+
if exc_type:
200+
logger.error(f"Client exited with exception: {exc_type.__name__}: {exc_value}")
201+
try:
202+
await self.close()
203+
except Exception as e:
204+
logger.error(f"Client failed to close: {e}")
205+
195206
@staticmethod
196207
def _filter_params(params: dict[str, Any] | BaseModel | None) -> dict[str, Any] | None:
197208
"""

clients/python/src/jamaibase/types/lm.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
AfterValidator,
77
BaseModel,
88
BeforeValidator,
9-
ConfigDict,
109
Field,
1110
field_validator,
1211
model_serializer,
@@ -730,11 +729,9 @@ def _sanitise_name(v: str) -> str:
730729
return re.sub(r"[^a-zA-Z0-9_-]", "_", v).strip()
731730

732731

733-
class ChatEntry(BaseModel):
732+
class ChatEntry(BaseModel, use_enum_values=True):
734733
"""Represents a message in the chat context."""
735734

736-
model_config = ConfigDict(use_enum_values=True)
737-
738735
role: ChatRole = Field(
739736
description="Who said the message?",
740737
)

docs/pgaudit_guide.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ spec:
3939
- customize audit_statement based on the level of DML statement you would want to monitor
4040
4141
```python
42-
async def _grant_auditor_priviledge(engine: AsyncEngine) -> bool:
42+
async def _grant_auditor_privilege(engine: AsyncEngine) -> bool:
4343
"""
4444
Apply the necessary grants to allow the auditor role to audit the database.
4545
"""

services/api/pyproject.toml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ unfixable = ["B"]
7575
"**/{tests,docs,tools}/*" = ["E402"]
7676

7777
[tool.ruff.lint.isort]
78-
known-first-party = ["jamaibase", "owl"]
78+
known-first-party = ["jamaibase", "owl", "polylm"]
7979

8080
[tool.ruff.lint.flake8-bugbear]
8181
# Allow default arguments like, e.g., `data: List[str] = fastapi.Query(None)`.
@@ -131,7 +131,7 @@ dependencies = [
131131
"fastapi[standard]~=0.115.0",
132132
"flower~=2.0.0",
133133
"gunicorn~=22.0.0",
134-
"httpx~=0.27.0",
134+
"httpx~=0.27",
135135
"itsdangerous~=2.2.0",
136136
"jamaibase>=0.4.1",
137137
"lancedb==0.12.0",
@@ -208,8 +208,7 @@ dependencies = [
208208
"typing_extensions~=4.14.0",
209209
"uuid-utils~=0.9.0",
210210
"uuid7~=0.1.0",
211-
# uvicorn 0.29.x shutdown seems unclean and 0.30.x child process sometimes dies
212-
"uvicorn[standard]~=0.28.0",
211+
"uvicorn[standard]~=0.28",
213212
"xmltodict~=0.14.0",
214213
]
215214
dynamic = ["version"]
File renamed without changes.

services/api/src/owl/configs/oss.py

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ class EnvConfig(BaseSettings):
2727
max_concurrency: int = 300
2828
db_init: bool | None = None # None means unset
2929
db_reset: bool = False
30-
db_init_max_users: int = 5
3130
cache_reset: bool = False
3231
enable_byok: bool = True
3332
disable_billing: bool = False
@@ -139,31 +138,25 @@ def check_alternate_service_key(self) -> Self:
139138

140139
@model_validator(mode="after")
141140
def validate_db_path(self) -> Self:
142-
"""
143-
Validates that `db_path` starts with either `rqlite+pyrqlite://` or `sqlite://` or `sqlite+libsql://` or `postgresql`.
144-
"""
145141
if not (
146-
self.db_path.startswith("rqlite+pyrqlite://")
147-
or self.db_path.startswith("sqlite://")
148-
or self.db_path.startswith("sqlite+libsql://")
149-
or self.db_path.startswith("postgresql")
142+
self.db_path.startswith("postgresql+psycopg://")
143+
# or self.db_path.startswith("sqlite+aiosqlite://")
144+
# or self.db_path.startswith("sqlite://")
145+
# or self.db_path.startswith("sqlite+libsql://")
146+
# or self.db_path.startswith("rqlite+pyrqlite://")
150147
):
151148
raise ValueError(f'`db_path` "{self.db_path}" has an invalid dialect.')
152149
return self
153150

154151
@property
155-
def db_dialect(self) -> Literal["rqlite", "libsql", "postgresql", "sqlite"]:
152+
def db_dialect(self) -> Literal["postgresql"]:
156153
"""
157-
Show the sqlite dialect that's in use based on the `db_path`.
154+
Show the dialect that's in use based on the `db_path`.
158155
"""
159-
if self.db_path.startswith("rqlite+pyrqlite://"):
160-
return "rqlite"
161-
elif self.db_path.startswith("sqlite+libsql://"):
162-
return "libsql"
163-
elif self.db_path.startswith("postgresql"):
156+
if self.db_path.startswith("postgresql"):
164157
return "postgresql"
165-
elif self.db_path.startswith("sqlite://"):
166-
return "sqlite"
158+
else:
159+
raise ValueError(f'`db_path` "{self.db_path}" has an invalid dialect.')
167160

168161
@cached_property
169162
def is_oss(self) -> bool:

services/api/src/owl/db/__init__.py

Lines changed: 10 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -97,37 +97,12 @@ def cached_text(query: str) -> TextClause:
9797
return text(query)
9898

9999

100-
async def reset_db(*, reset_max_users: int = 3):
101-
from sqlmodel import func, select
102-
103-
from owl.db.models import User
104-
100+
async def reset_db():
105101
# Only allow DB reset in dev with localhost
106102
if "@localhost:" not in ENV_CONFIG.db_path:
107103
raise ValueError("DB reset is only allowed in dev with localhost DB.")
108104

109105
async with async_session() as session:
110-
# As a safety measure, reset DB only if it has less than `init_max_users` users
111-
# Just in case we accidentally tried to nuke a prod DB
112-
user_table_exists = (
113-
await session.exec(
114-
text(
115-
(
116-
f"SELECT EXISTS ("
117-
f"SELECT FROM information_schema.tables WHERE table_schema = '{SCHEMA}' AND table_name = 'User'"
118-
");"
119-
)
120-
)
121-
)
122-
).scalar()
123-
if user_table_exists:
124-
user_count = (await session.exec(select(func.count(User.id)))).one()
125-
if user_count >= reset_max_users:
126-
logger.info(
127-
f"Found {user_count:,d} users, abort database reset (>= {reset_max_users} users)."
128-
)
129-
return
130-
131106
# Delete all tables
132107
logger.warning(f'Resetting database (dropping schema "{SCHEMA}")...')
133108
await session.exec(text(f"DROP SCHEMA IF EXISTS {SCHEMA} CASCADE"))
@@ -702,7 +677,7 @@ async def migrate_db():
702677
await CACHE.aclose()
703678

704679

705-
async def init_db(*, init_max_users: int = 3):
680+
async def init_db():
706681
from fastapi import Request
707682
from sqlmodel import func, select
708683
from starlette.datastructures import URL, Headers
@@ -721,19 +696,16 @@ async def init_db(*, init_max_users: int = 3):
721696
)
722697

723698
async with async_session() as session:
724-
# As a safety measure, init DB only if it has less than `init_max_users` users
725-
# Just in case we accidentally tried to nuke a prod DB
726699
user_count = (await session.exec(select(func.count(User.id)))).one()
727-
if user_count >= init_max_users:
728-
logger.info(
729-
f"Found {user_count:,d} users, abort database initialisation (>= {init_max_users} users)."
730-
)
731-
return
732-
733700
# Only enforce OSS check if db_init=False
734-
if ENV_CONFIG.is_oss and user_count != 0:
735-
logger.info("OSS mode: Skipping initialization (non-empty DB).")
736-
return
701+
if ENV_CONFIG.is_oss:
702+
if user_count != 0:
703+
logger.info("OSS mode: Skipping initialization (non-empty DB).")
704+
return
705+
else:
706+
# Only allow DB init in dev with localhost
707+
if "@localhost:" not in ENV_CONFIG.db_path:
708+
raise ValueError("DB init is only allowed in dev with localhost DB.")
737709

738710
logger.info("Initialising database...")
739711

services/api/src/owl/db/gen_table.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2423,6 +2423,7 @@ def _measure_ram() -> str:
24232423
logger.info(f'Importing table "{self.table_id}": Skipped S3 upload.')
24242424
prog.parse_data.progress = 100
24252425
await CACHE.set_progress(prog)
2426+
semaphore = Semaphore(S3_MAX_CONCURRENCY)
24262427

24272428
async def _upload(
24282429
old_uri: str,
@@ -2441,7 +2442,6 @@ async def _upload(
24412442
return (old_uri, new_uri)
24422443

24432444
uris_seen: dict[str, str] = {} # Old URI to new URI
2444-
semaphore = Semaphore(S3_MAX_CONCURRENCY)
24452445
upload_coros = []
24462446
for row in rows:
24472447
file_byte_cols = [c for c in row.keys() if c.endswith("__")]

0 commit comments

Comments
 (0)