Skip to content

Commit ec54cdc

Browse files
authored
Bug fixes and enhancements (#4)
1 parent 7b09dd3 commit ec54cdc

File tree

18 files changed

+113
-85
lines changed

18 files changed

+113
-85
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,10 @@ jobs:
2828
with:
2929
python-version: ${{ matrix.python-version }}
3030

31-
#- name: Test API Key
32-
# run: |
33-
# response=$(curl --fail -s -H "Authorization: Bearer ${OPENAI_API_KEY}" https://api.openai.com/v1/engines)
34-
# echo "$response"
35-
# if [ $? -ne 0 ]; then
36-
# echo "API key test failed!"
37-
# exit 1
38-
# fi
39-
# env:
40-
# OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
41-
42-
- name: Setup
31+
- name: Remove cloud-only modules and install Python client
4332
run: |
4433
set -e
34+
bash scripts/remove_cloud_modules.sh
4535
cd clients/python
4636
python -m pip install .[all]
4737
@@ -98,20 +88,6 @@ jobs:
9888
COMPOSE_DOCKER_CLI_BUILD: 1
9989
DOCKER_BUILDKIT: 1
10090

101-
#- name: Test list models
102-
# run: |
103-
# response=$(curl --fail -s http://localhost:6969/api/v1/models)
104-
# echo "$response"
105-
# if [ $? -ne 0 ]; then
106-
# echo "list models test failed!"
107-
# exit 1
108-
# fi
109-
110-
#- name: Get docker compose logs
111-
# if: always()
112-
# run: |
113-
# docker compose -f docker/compose.cpu.yml logs
114-
11591
- name: Pytest
11692
run: |
11793
set -e

CHANGELOG.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@
22

33
All notable changes to this project will be documented in this file.
44

5-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). [An example](https://github.com/standard/standard/blob/master/CHANGELOG.md).
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

7-
## [Unreleased]
7+
The version number mentioned here refers to the cloud version.
88

9-
This is for a future release.
9+
## [v0.1] - 2024-06-03
1010

11-
### CHANGED
12-
13-
### FIXED
11+
This is our first release 🚀

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
<!-- prettier-ignore -->
1010
> [!TIP]
11-
> [Explore our docs](https://docs.jamaibase.com)
11+
> [Explore our docs](#explore-the-documentation)
1212
1313
## Overview
1414

@@ -141,10 +141,10 @@ Get free LLM tokens on JamAI Base Cloud. [Sign up now.](https://cloud.jamaibase.
141141
$ docker compose -f docker/compose.nvidia.yml up --quiet-pull -d
142142
```
143143

144-
<!-- prettier-ignore -->
145-
> [!TIP]
146-
> By default, frontend and backend are accessible at ports 4000 and 6969.
147-
> You can change the ports exposed to host by setting env var like so `API_PORT=6970 FRONTEND_PORT=4001 docker compose -f docker/compose.cpu.yml up --quiet-pull -d`
144+
<!-- prettier-ignore -->
145+
> [!TIP]
146+
> By default, frontend and backend are accessible at ports 4000 and 6969.
147+
> You can change the ports exposed to host by setting env var like so `API_PORT=6970 FRONTEND_PORT=4001 docker compose -f docker/compose.cpu.yml up --quiet-pull -d`
148148
149149
4. Try the command below in your terminal, or open your browser and go to `localhost:4000`.
150150

@@ -156,6 +156,8 @@ Get free LLM tokens on JamAI Base Cloud. [Sign up now.](https://cloud.jamaibase.
156156

157157
- [API Documentation](https://jamaibase.readme.io)
158158
- [Platform Documentation](https://docs.jamaibase.com)
159+
- [Changelog](CHANGELOG.md)
160+
- [Versioning](VERSIONING.md)
159161

160162
## Examples
161163

VERSIONING.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Versioning
2+
3+
This document describes the versioning policy for this repository.
4+
5+
# TLDR
6+
7+
We follow [Semantic Versioning](https://semver.org/).
8+
9+
# Current Client SDK Versions (v0.x)
10+
11+
We will start our versioning at `v0.x.x`.
12+
13+
Major version zero (0.y.z) is for initial development. Anything may change at any time. The public API should not be considered stable.
14+
15+
# Future Client SDK Versions (v1.x)
16+
17+
SDK versions will follow this structure `<major>.<minor>.<patch>`:
18+
19+
- `major` is incremented for any backwards-incompatible updates made to our [Cloud service / backend](https://cloud.jamaibase.com/) and hence our SDK.
20+
- `minor` or `patch` is incremented for any backwards-compatible feature update, bug fix, enhancements made to the SDK libraries.

clients/python/src/jamaibase/protocol.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1206,7 +1206,13 @@ def handle_nulls_and_validate(self) -> Self:
12061206
if k in failed_cols:
12071207
d[k], state["original"] = None, d[k]
12081208
if d[k] is None:
1209-
if col.dtype == DtypeEnum.str_:
1209+
if col.dtype == DtypeEnum.int_:
1210+
d[k] = 0
1211+
elif col.dtype == DtypeEnum.float_:
1212+
d[k] = 0.0
1213+
elif col.dtype == DtypeEnum.bool_:
1214+
d[k] = False
1215+
elif col.dtype == DtypeEnum.str_:
12101216
# Store null string as ""
12111217
# https://github.com/lancedb/lancedb/issues/1160
12121218
d[k] = ""

clients/python/src/jamaibase/utils/io.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@
1414
import orjson
1515
import srsly
1616
import toml
17-
from jamaibase.utils.types import JSONInput, JSONOutput
1817
from PIL import ExifTags, Image
1918

19+
from jamaibase.utils.types import JSONInput, JSONOutput
20+
2021
logger = logging.getLogger(__name__)
2122

2223

@@ -49,7 +50,7 @@ def dump_json(data: JSONInput, path: str, **kwargs) -> str:
4950
Args:
5051
data (JSONInput): The data.
5152
path (str): Path to the file.
52-
**kwargs: Other keyword arguments to pass into `srsly.write_json`.
53+
**kwargs: Other keyword arguments to pass into `orjson.dumps`.
5354
5455
Returns:
5556
path (str): Path to the file.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.0.1"
1+
__version__ = "0.1.0"

scripts/remove_cloud_modules.sh

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
#!/usr/bin/env bash
22

3-
set -e
4-
53
find . -type f -name "cloud*.py" -delete
64
find . -type f -name "compose.*.cloud.yml" -delete
75
find . -type d -name "(cloud)" -exec rm -rf {} +
8-
rm services/app/ecosystem.config.cjs
9-
rm services/app/ecosystem.json
6+
rm -f services/app/ecosystem.config.cjs
7+
rm -f services/app/ecosystem.json

services/api/pyproject.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,14 @@ dependencies = [
7878
"lancedb~=0.6.13",
7979
"langchain-community~=0.0.25",
8080
"langchain~=0.1.10",
81-
"litellm~=1.35.15",
81+
"litellm~=1.40.0",
8282
"loguru~=0.7.2",
8383
"matplotlib",
8484
"numpy~=1.26.4",
85-
"openai~=1.23.1",
85+
"openai~=1.30.5",
8686
"openmeter~=1.0.0b50",
8787
"orjson~=3.9.15",
88+
"Pillow~=10.3.0",
8889
"pyarrow==15.0.0",
8990
"pycryptodomex~=3.20.0",
9091
"pydantic-settings>=2.2.1",
@@ -93,9 +94,11 @@ dependencies = [
9394
"python-multipart~=0.0.6",
9495
"redis[hiredis]~=5.0.4",
9596
"sqlmodel~=0.0.16",
97+
"srsly~=2.4.8",
9698
"stripe~=8.5.0",
9799
"tantivy~=0.21.0",
98100
"tiktoken~=0.6.0",
101+
"toml~=0.10.2",
99102
"tqdm~=4.66.2",
100103
"typer[all]~=0.9.0",
101104
"typing_extensions>=4.10.0",

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from filelock import FileLock
1313
from lancedb.table import LanceTable
1414
from loguru import logger
15-
from pydantic import BaseModel
1615
from sqlalchemy import desc
1716
from sqlmodel import Session, SQLModel, select
1817
from typing_extensions import Self
@@ -67,7 +66,7 @@ def __init__(
6766
self.db_url = Path(db_url)
6867
self.vector_db_url = Path(vector_db_url)
6968

70-
def lock(self, name: str, timeout: int = 60):
69+
def lock(self, name: str, timeout: int = 5):
7170
name = f"{self.lock_name_prefix}/{name}.lock"
7271
self.locks[name] = self.locks.get(name, FileLock(name, timeout=timeout))
7372
return self.locks[name]

services/api/src/owl/entrypoints/api.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
44
```shell
55
$ python -m owl.entrypoints.api
6+
$ JAMAI_API_BASE=http://localhost:6969/api TZ=Asia/Singapore python -m owl.entrypoints.api
67
```
78
"""
89

@@ -16,6 +17,7 @@
1617
from fastapi.exceptions import RequestValidationError
1718
from fastapi.middleware.cors import CORSMiddleware
1819
from fastapi.responses import ORJSONResponse
20+
from filelock import Timeout
1921
from loguru import logger
2022
from pydantic import SecretStr
2123
from pydantic_settings import BaseSettings, SettingsConfigDict
@@ -226,8 +228,8 @@ async def authenticate(request: Request, call_next):
226228
# Defaults
227229
project_id, org_id, org_tier = CONFIG.default_project, CONFIG.default_org, "free"
228230
token, openmeter_id = "", "default"
229-
quota_reset_at = ""
230-
quotas = defaultdict(lambda: 1.0)
231+
quotas, quota_reset_at = defaultdict(lambda: 1.0), ""
232+
db_storage_gb = file_storage_gb = 0.0
231233

232234
# --- OSS Mode --- #
233235
if CONFIG.service_key_plain == "":
@@ -380,9 +382,11 @@ async def authenticate(request: Request, call_next):
380382
)
381383
org_id, external_keys = org_info.id, org_info.external_keys
382384
openmeter_id = org_info.openmeter_id
383-
quota_reset_at = org_info.quota_reset_at
384385
org_tier = org_info.tier
385386
quotas = org_info.quotas
387+
quota_reset_at = org_info.quota_reset_at
388+
db_storage_gb = org_info.db_storage_gb
389+
file_storage_gb = org_info.file_storage_gb
386390
if openmeter_id is None:
387391
logger.warning(
388392
f"{request.state.id} - Organization {org_id} does not have OpenMeter ID."
@@ -399,14 +403,14 @@ async def authenticate(request: Request, call_next):
399403
# --- Set request state and headers --- #
400404
request.state.org_id = org_id
401405
request.state.project_id = project_id
402-
request.state.api_key = token
403-
request.state.openmeter_id = openmeter_id
404406
request.state.billing_manager = BillingManager(
405407
request=request,
408+
openmeter_id=openmeter_id,
406409
quotas=quotas,
407410
quota_reset_at=quota_reset_at,
411+
db_storage_gb=db_storage_gb,
412+
file_storage_gb=file_storage_gb,
408413
organization_tier=org_tier,
409-
openmeter_id=openmeter_id,
410414
organization_id=org_id,
411415
project_id=project_id,
412416
api_key=token,
@@ -457,6 +461,21 @@ async def health() -> Response:
457461
# --- Order of handlers does not matter --- #
458462

459463

464+
@app.exception_handler(Timeout)
465+
async def write_lock_timeout_exc_handler(request: Request, exc: Timeout):
466+
return ORJSONResponse(
467+
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
468+
content={
469+
"error": "write_lock_timeout",
470+
"message": "This table is currently busy. Please try again later.",
471+
"detail": str(exc),
472+
"request_id": request.state.id,
473+
"exception": exc.__class__.__name__,
474+
},
475+
headers={"Retry-After": 10},
476+
)
477+
478+
460479
@app.exception_handler(UpgradeTierError)
461480
async def upgrade_tier_exc_handler(request: Request, exc: UpgradeTierError):
462481
return ORJSONResponse(

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

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import asyncio
21
import pathlib
2+
from asyncio import sleep
33
from datetime import timedelta
44
from hashlib import blake2b
5+
from time import perf_counter
56
from typing import Annotated, Any
67

78
import numpy as np
@@ -122,7 +123,7 @@ async def periodic_reindex():
122123
lock = FileLock(f"{config.owl_db_dir}/periodic_reindex.lock", blocking=False)
123124
try:
124125
with lock:
125-
logger.info("Periodic Lance re-indexing started.")
126+
t0 = perf_counter()
126127
num_ok = num_skipped = num_failed = 0
127128
for session, table, meta, table_path in _iter_all_tables():
128129
if session is None:
@@ -136,14 +137,18 @@ async def periodic_reindex():
136137
except Exception:
137138
logger.exception(f"Periodic Lance re-indexing failed for table: {table_path}")
138139
num_failed += 1
140+
t = perf_counter() - t0
141+
# Hold the lock for a while to block other workers
142+
await sleep(max(0.0, (config.owl_reindex_period_sec - t) * 0.5))
139143
logger.info(
140144
(
141-
"Periodic Lance re-indexing completed "
142-
f"({num_ok:,d} OK, {num_skipped:,d} skipped, {num_failed:,d} failed)."
145+
f"Periodic Lance re-indexing completed (t={t:,.3f} s, "
146+
f"{num_ok:,d} OK, {num_skipped:,d} skipped, {num_failed:,d} failed)."
143147
)
144148
)
145149
except Timeout:
146-
logger.info("Periodic Lance re-indexing skipped.")
150+
# logger.info("Periodic Lance re-indexing skipped.")
151+
pass
147152
except Exception:
148153
logger.exception("Periodic Lance re-indexing encountered an error.")
149154

@@ -154,7 +159,7 @@ async def periodic_optimize():
154159
lock = FileLock(f"{config.owl_db_dir}/periodic_optimization.lock", blocking=False)
155160
try:
156161
with lock:
157-
logger.info("Periodic Lance optimization started.")
162+
t0 = perf_counter()
158163
num_ok = num_skipped = num_failed = 0
159164
for _, table, meta, table_path in _iter_all_tables():
160165
done = True
@@ -177,14 +182,18 @@ async def periodic_optimize():
177182
except Exception:
178183
logger.exception(f"Periodic Lance optimization failed for table: {table_path}")
179184
num_failed += 1
185+
t = perf_counter() - t0
186+
# Hold the lock for a while to block other workers
187+
await sleep(max(0.0, (config.owl_reindex_period_sec - t) * 0.5))
180188
logger.info(
181189
(
182-
"Periodic Lance optimization completed "
183-
f"({num_ok:,d} OK, {num_skipped:,d} skipped, {num_failed:,d} failed)."
190+
f"Periodic Lance optimization completed (t={t:,.3f} s, "
191+
f"{num_ok:,d} OK, {num_skipped:,d} skipped, {num_failed:,d} failed)."
184192
)
185193
)
186194
except Timeout:
187-
logger.info("Periodic Lance optimization skipped.")
195+
# logger.info("Periodic Lance optimization skipped.")
196+
pass
188197
except Exception:
189198
logger.exception("Periodic Lance optimization encountered an error.")
190199

services/api/tests/test_cloud_client.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,19 +81,13 @@ def test_get_and_list_organizations(client_cls: Type[Owl]):
8181
@pytest.mark.parametrize("client_cls", CLIENT_CLS)
8282
def test_create_flow(client_cls: Type[Owl]):
8383
owl = client_cls()
84-
# response = owl.delete_user(USER_ID_A)
85-
# assert isinstance(response, OkResponse)
86-
# response = owl.delete_user(USER_ID_B)
87-
# assert isinstance(response, OkResponse)
84+
response = owl.delete_user(USER_ID_A)
85+
assert isinstance(response, OkResponse)
86+
response = owl.delete_user(USER_ID_B)
87+
assert isinstance(response, OkResponse)
8888
# response = owl.delete_organization("org_f12d46652cfa3120006e44e2")
8989
# assert isinstance(response, OkResponse)
9090

91-
orgs = owl.list_organizations().items
92-
for org in orgs:
93-
org = owl.refresh_quota(org.id)
94-
print(org)
95-
exit()
96-
9791
# Create user
9892
duncan = _create_user(owl)
9993
print(f"User created: {duncan}\n")

services/app/build.sh

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@ vite build
2222

2323
# Copy the build files to the build directory
2424
rm -rf build
25-
mkdir build
26-
cp -r temp/* build/
27-
rm -rf temp
25+
mv temp build
2826

2927
# Reload PM2 app if not in dev mode
3028
if [ $DEV_MODE -eq 0 ]; then

0 commit comments

Comments
 (0)