Skip to content

Commit 1754e43

Browse files
author
leoguillaume
committed
wip
1 parent f749b42 commit 1754e43

File tree

19 files changed

+376
-228
lines changed

19 files changed

+376
-228
lines changed

.github/compose.test.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: albert-api
2+
services:
3+
api:
4+
build:
5+
context: ..
6+
dockerfile: app/Dockerfile
7+
platform: linux/amd64
8+
restart: always
9+
environment:
10+
- COVERAGE_RCFILE=./app/.coveragerc
11+
- BRAVE_API_KEY=${BRAVE_API_KEY}
12+
- ALBERT_API_KEY=${ALBERT_API_KEY}
13+
ports:
14+
- 8000:8000
15+
volumes:
16+
- ./config.test.yml:/config.yml:ro
17+
depends_on:
18+
redis:
19+
condition: service_healthy
20+
postgres:
21+
condition: service_healthy
22+
qdrant:
23+
condition: service_healthy
24+
25+
postgres:
26+
extends:
27+
file: ../compose.yml
28+
service: postgres
29+
30+
redis:
31+
extends:
32+
file: ../compose.yml
33+
service: redis
34+
35+
qdrant:
36+
extends:
37+
file: ../compose.yml
38+
service: qdrant
39+
40+
volumes:
41+
postgres:
42+
redis:
43+
qdrant:

.github/config.test.yml

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
general:
2+
log_level: DEBUG
3+
4+
playground:
5+
api_url: http://api:8000
6+
max_api_key_expiration_days: 365
7+
cache_ttl: 1800
8+
database_url: postgresql://postgres:changeme@postgres:5432/playground
9+
10+
auth:
11+
master_username: master
12+
master_key: changeme
13+
14+
web_search:
15+
- type: brave
16+
model: albert-small
17+
args:
18+
api_key: ${BRAVE_API_KEY}
19+
20+
databases:
21+
- type: qdrant
22+
model: embeddings-small
23+
args:
24+
url: http://qdrant
25+
api_key: changeme
26+
prefer_grpc: True
27+
timeout: 10
28+
29+
- type: redis
30+
args:
31+
host: redis
32+
password: changeme
33+
34+
- type: sql
35+
args:
36+
url: postgresql+asyncpg://postgres:changeme@postgres:5432/api
37+
echo: True
38+
pool_size: 5
39+
max_overflow: 10
40+
pool_pre_ping: True
41+
connect_args: {"server_settings": {"statement_timeout": "120s"}}
42+
43+
models:
44+
- id: albert-large
45+
type: text-generation
46+
owned_by: test
47+
aliases: ["mistralai/Mistral-Small-3.1-24B-Instruct-2503"]
48+
clients:
49+
- model: mistralai/Mistral-Small-3.1-24B-Instruct-2503
50+
type: albert
51+
args:
52+
api_url: https://albert.api.etalab.gouv.fr
53+
api_key: ${ALBERT_API_KEY}
54+
timeout: 120
55+
56+
- id: albert-small
57+
type: text-generation
58+
aliases: ["meta-llama/Llama-3.1-8B-Instruct"]
59+
clients:
60+
- model: albert-small
61+
type: albert
62+
args:
63+
api_url: https://albert.api.etalab.gouv.fr
64+
api_key: ${ALBERT_API_KEY}
65+
timeout: 120
66+
67+
- id: embeddings-small
68+
type: text-embeddings-inference
69+
aliases: ["BAAI/bge-m3"]
70+
clients:
71+
- model: BAAI/bge-m3
72+
type: albert
73+
args:
74+
api_url: https://albert.api.etalab.gouv.fr
75+
api_key: ${ALBERT_API_KEY}
76+
timeout: 120
77+
78+
- id: audio-large
79+
type: automatic-speech-recognition
80+
aliases: ["openai/whisper-large-v3"]
81+
clients:
82+
- model: audio-large
83+
type: albert
84+
args:
85+
api_url: https://albert.api.etalab.gouv.fr
86+
api_key: ${ALBERT_API_KEY}
87+
timeout: 120
88+
89+
- id: rerank-small
90+
type: text-classification
91+
aliases: ["BAAI/bge-reranker-v2-m3"]
92+
clients:
93+
- model: rerank-small
94+
type: albert
95+
args:
96+
api_url: https://albert.api.etalab.gouv.fr
97+
api_key: ${ALBERT_API_KEY}
98+
timeout: 120
99+
100+

.github/workflows/run_tests.yml

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,22 @@ jobs:
1313

1414
steps:
1515
- uses: actions/checkout@v3
16-
17-
- name: Create config from template
18-
run: |
19-
echo "${{ secrets.CONFIG_YML }}" > config.yml
2016

2117
- name: Set up Docker Compose
2218
run: |
23-
docker compose --file compose.dev.yml up --detach
19+
docker compose --file ./.github/compose.test.yml up --detach
20+
env:
21+
BRAVE_API_KEY: ${{ secrets.BRAVE_API_KEY }}
22+
ALBERT_API_KEY: ${{ secrets.ALBERT_API_KEY }}
2423

2524
- name: Wait for API to start
2625
run: |
27-
sleep 30
26+
echo $(ls -la)
27+
for i in {1..30}; do
28+
curl -s http://localhost:8000/health -H "Authorization: Bearer changeme" > /dev/null && echo "API is ready" && break || echo "Waiting for API..." && sleep 2;
29+
done
2830
echo $(docker logs albert-api-api-1)
29-
31+
3032
- name: Wait for PostgreSQL
3133
run: |
3234
for i in {1..30}; do

app/endpoints/documents.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,16 @@ async def get_documents(
4646
if not context.documents: # no vector store available
4747
if collection:
4848
raise CollectionNotFoundException()
49-
data = []
50-
else:
51-
data = await context.documents.get_documents(
52-
session=session,
53-
collection_id=collection,
54-
limit=limit,
55-
offset=offset,
56-
user_id=request.app.state.user.id,
57-
)
49+
50+
return Documents(data=[])
51+
52+
data = await context.documents.get_documents(
53+
session=session,
54+
collection_id=collection,
55+
limit=limit,
56+
offset=offset,
57+
user_id=request.app.state.user.id,
58+
)
5859

5960
return Documents(data=data)
6061

app/helpers/_documentmanager.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
VectorParams,
2020
)
2121
from sqlalchemy import Integer, cast, delete, distinct, func, insert, or_, select, update
22-
from sqlalchemy.exc import IntegrityError, NoResultFound
22+
from sqlalchemy.exc import NoResultFound
2323
from sqlalchemy.ext.asyncio import AsyncSession
2424

2525
from app.clients.model import BaseModelClient as ModelClient
@@ -36,7 +36,6 @@
3636
from app.sql.models import User as UserTable
3737
from app.utils.exceptions import (
3838
ChunkingFailedException,
39-
CollectionAlreadyExistsException,
4039
CollectionNotFoundException,
4140
DocumentNotFoundException,
4241
NotImplementedException,
@@ -75,16 +74,13 @@ def __init__(
7574
async def create_collection(
7675
self, session: AsyncSession, user_id: int, name: str, visibility: CollectionVisibility, description: Optional[str] = None
7776
) -> int:
78-
try:
79-
result = await session.execute(
80-
statement=insert(table=CollectionTable)
81-
.values(name=name, user_id=user_id, visibility=visibility, description=description)
82-
.returning(CollectionTable.id)
83-
)
84-
collection_id = result.scalar_one()
85-
await session.commit()
86-
except IntegrityError as e:
87-
raise CollectionAlreadyExistsException()
77+
result = await session.execute(
78+
statement=insert(table=CollectionTable)
79+
.values(name=name, user_id=user_id, visibility=visibility, description=description)
80+
.returning(CollectionTable.id)
81+
)
82+
collection_id = result.scalar_one()
83+
await session.commit()
8884

8985
await self.qdrant.create_collection(
9086
collection_name=str(collection_id),

app/helpers/_identityaccessmanager.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
DeleteRoleWithUsersException,
1717
RoleAlreadyExistsException,
1818
RoleNotFoundException,
19-
TokenAlreadyExistsException,
2019
TokenNotFoundException,
2120
UserAlreadyExistsException,
2221
UserNotFoundException,
@@ -335,12 +334,9 @@ async def create_token(self, session: AsyncSession, user_id: int, name: str, exp
335334
raise UserNotFoundException()
336335

337336
# create the token
338-
try:
339-
result = await session.execute(statement=insert(table=TokenTable).values(user_id=user.id, name=name).returning(TokenTable.id))
340-
token_id = result.scalar_one()
341-
await session.commit()
342-
except IntegrityError:
343-
raise TokenAlreadyExistsException()
337+
result = await session.execute(statement=insert(table=TokenTable).values(user_id=user.id, name=name).returning(TokenTable.id))
338+
token_id = result.scalar_one()
339+
await session.commit()
344340

345341
# generate the token
346342
token = self._encode_token(user_id=user.id, token_id=token_id, expires_at=expires_at)

app/main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def create_app(db_func=get_db, *args, **kwargs) -> FastAPI:
4141
)
4242

4343
# Middlewares
44-
if not settings.general.disabled_middleware:
44+
if not settings.general.disabled_middlewares:
4545
app.add_middleware(middleware_class=UsagesMiddleware, db_func=get_db)
4646
app.instrumentator = Instrumentator().instrument(app=app)
4747

@@ -77,7 +77,7 @@ def create_app(db_func=get_db, *args, **kwargs) -> FastAPI:
7777
app.include_router(router=models.router, tags=[ROUTER__MODELS.title()], prefix="/v1")
7878

7979
if ROUTER__MONITORING not in settings.general.disabled_routers:
80-
if not settings.general.disabled_middleware:
80+
if not settings.general.disabled_middlewares:
8181
app.instrumentator.expose(app=app, should_gzip=True, tags=[ROUTER__MONITORING.title()], dependencies=[Depends(dependency=Authorization(permissions=[PermissionType.READ_METRIC]))], include_in_schema=settings.general.log_level == "DEBUG") # fmt: off
8282

8383
@app.get(path="/health", tags=[ROUTER__MONITORING.title()], include_in_schema=settings.general.log_level == "DEBUG", dependencies=[Security(dependency=Authorization())]) # fmt: off

app/schemas/core/settings.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ class General(ConfigBaseModel):
125125

126126
# Others
127127
disabled_routers: List[Literal[*ROUTERS]] = []
128-
disabled_middleware: bool = False
128+
disabled_middlewares: bool = False
129129
log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = "INFO"
130130

131131

@@ -175,7 +175,7 @@ def setup_config(cls, values) -> Any:
175175
# replace environment variables (pattern: ${VARIABLE_NAME})
176176
for match in set(re.findall(pattern=r"\${[A-Z_]+}", string=file_content)):
177177
variable = match.replace("${", "").replace("}", "")
178-
if not os.getenv(variable):
178+
if os.getenv(variable) is None or os.getenv(variable) == "":
179179
logging.warning(f"Environment variable {variable} not found or empty to replace {match}.")
180180
file_content = file_content.replace(match, os.getenv(variable, match))
181181

app/tests/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def get_test_db():
9696
yield db_session
9797

9898
# Create app with test config
99-
app = create_app(db_func=get_test_db, disabled_middleware=False)
99+
app = create_app(db_func=get_test_db)
100100

101101
# Exit the global cassette, requests done by app initialization
102102
# are recorded in the global cassette

app/utils/exceptions.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,6 @@ def __init__(self, detail: str = "Web search is not available."):
1919
super().__init__(status_code=400, detail=detail)
2020

2121

22-
class CollectionAlreadyExistsException(HTTPException):
23-
def __init__(self, detail: str = "Collection already exists."):
24-
super().__init__(status_code=400, detail=detail)
25-
26-
2722
class RoleAlreadyExistsException(HTTPException):
2823
def __init__(self, detail: str = "Role already exists."):
2924
super().__init__(status_code=400, detail=detail)
@@ -39,11 +34,6 @@ def __init__(self, detail: str = "User already exists."):
3934
super().__init__(status_code=400, detail=detail)
4035

4136

42-
class TokenAlreadyExistsException(HTTPException):
43-
def __init__(self, detail: str = "Token already exists."):
44-
super().__init__(status_code=400, detail=detail)
45-
46-
4737
# 403
4838
class InvalidPasswordException(HTTPException):
4939
def __init__(self, detail: str = "Invalid password."):

0 commit comments

Comments
 (0)