Skip to content

Commit cb51ef7

Browse files
authored
Merge branch 'main' into enahncement/polling_llmcall_db
2 parents 2861753 + 7d310ce commit cb51ef7

File tree

28 files changed

+886
-53
lines changed

28 files changed

+886
-53
lines changed

backend/app/api/docs/config/list.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
query: Search string used for partial matching across configurations
2+
skip: Number of records to skip for pagination (default: 0)
3+
limit: Maximum number of records to return (default: 100, max: 100)
4+
15
Retrieve all configurations for the current project.
26

37
Returns a paginated list of configurations ordered by most recently
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
List all organizations.
22

3-
Returns paginated list of all organizations in the system.
3+
Returns paginated list of all organizations in the system. The response includes a `has_more` field in `metadata` indicating whether additional pages are available.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
List all projects for a given organization.
2+
3+
Returns all projects belonging to the specified organization ID. The organization must exist and be active.

backend/app/api/routes/config/config.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,17 @@ def create_config(
5353
def list_configs(
5454
current_user: AuthContextDep,
5555
session: SessionDep,
56+
query: str | None = Query(None, description="search query"),
5657
skip: int = Query(0, ge=0, description="Number of records to skip"),
5758
limit: int = Query(100, ge=1, le=100, description="Maximum records to return"),
58-
):
59+
) -> APIResponse[list[ConfigPublic]]:
5960
"""
6061
List all configurations for the current project.
6162
Ordered by updated_at in descending order.
6263
"""
6364
config_crud = ConfigCrud(session=session, project_id=current_user.project_.id)
64-
configs = config_crud.read_all(skip=skip, limit=limit)
65-
return APIResponse.success_response(
66-
data=configs,
67-
)
65+
configs, has_more = config_crud.read_all(query=query, skip=skip, limit=limit)
66+
return APIResponse.success_response(data=configs, metadata=dict(has_more=has_more))
6867

6968

7069
@router.get(

backend/app/api/routes/documents.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def list_docs(
8181
),
8282
):
8383
crud = DocumentCrud(session, current_user.project_.id)
84-
documents = crud.read_many(skip, limit)
84+
documents, has_more = crud.read_many(skip, limit)
8585

8686
storage = (
8787
get_cloud_storage(session=session, project_id=current_user.project_.id)
@@ -94,7 +94,7 @@ def list_docs(
9494
include_url=include_url,
9595
storage=storage,
9696
)
97-
return APIResponse.success_response(results)
97+
return APIResponse.success_response(results, metadata=dict(has_more=has_more))
9898

9999

100100
@router.post(

backend/app/api/routes/organization.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import logging
22
from typing import List
33

4-
from fastapi import APIRouter, Depends, HTTPException
4+
from fastapi import APIRouter, Depends, HTTPException, Query
55
from sqlalchemy import func
66
from sqlmodel import select
77

@@ -27,14 +27,19 @@
2727
response_model=APIResponse[List[OrganizationPublic]],
2828
description=load_description("organization/list.md"),
2929
)
30-
def read_organizations(session: SessionDep, skip: int = 0, limit: int = 100):
30+
def read_organizations(
31+
session: SessionDep,
32+
skip: int = Query(0, ge=0),
33+
limit: int = Query(100, ge=1, le=100),
34+
) -> APIResponse[List[OrganizationPublic]]:
3135
count_statement = select(func.count()).select_from(Organization)
3236
count = session.exec(count_statement).one()
3337

3438
statement = select(Organization).offset(skip).limit(limit)
3539
organizations = session.exec(statement).all()
3640

37-
return APIResponse.success_response(organizations)
41+
has_more = (skip + limit) < count
42+
return APIResponse.success_response(organizations, metadata={"has_more": has_more})
3843

3944

4045
# Create a new organization
@@ -44,7 +49,9 @@ def read_organizations(session: SessionDep, skip: int = 0, limit: int = 100):
4449
response_model=APIResponse[OrganizationPublic],
4550
description=load_description("organization/create.md"),
4651
)
47-
def create_new_organization(*, session: SessionDep, org_in: OrganizationCreate):
52+
def create_new_organization(
53+
*, session: SessionDep, org_in: OrganizationCreate
54+
) -> APIResponse[OrganizationPublic]:
4855
new_org = create_organization(session=session, org_create=org_in)
4956
return APIResponse.success_response(new_org)
5057

@@ -55,7 +62,9 @@ def create_new_organization(*, session: SessionDep, org_in: OrganizationCreate):
5562
response_model=APIResponse[OrganizationPublic],
5663
description=load_description("organization/get.md"),
5764
)
58-
def read_organization(*, session: SessionDep, org_id: int):
65+
def read_organization(
66+
*, session: SessionDep, org_id: int
67+
) -> APIResponse[OrganizationPublic]:
5968
"""
6069
Retrieve an organization by ID.
6170
"""
@@ -75,7 +84,7 @@ def read_organization(*, session: SessionDep, org_id: int):
7584
)
7685
def update_organization(
7786
*, session: SessionDep, org_id: int, org_in: OrganizationUpdate
78-
):
87+
) -> APIResponse[OrganizationPublic]:
7988
org = get_organization_by_id(session=session, org_id=org_id)
8089
if org is None:
8190
logger.error(
@@ -103,7 +112,7 @@ def update_organization(
103112
include_in_schema=False,
104113
description=load_description("organization/delete.md"),
105114
)
106-
def delete_organization(session: SessionDep, org_id: int):
115+
def delete_organization(session: SessionDep, org_id: int) -> APIResponse[None]:
107116
org = get_organization_by_id(session=session, org_id=org_id)
108117
if org is None:
109118
logger.error(

backend/app/api/routes/project.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
from app.crud.project import (
1212
create_project,
1313
get_project_by_id,
14+
get_projects_by_organization,
1415
)
16+
from app.crud.organization import validate_organization
1517
from app.utils import APIResponse, load_description
1618

1719
logger = logging.getLogger(__name__)
@@ -36,7 +38,8 @@ def read_projects(
3638
statement = select(Project).offset(skip).limit(limit)
3739
projects = session.exec(statement).all()
3840

39-
return APIResponse.success_response(projects)
41+
has_more = (skip + limit) < count
42+
return APIResponse.success_response(projects, metadata={"has_more": has_more})
4043

4144

4245
# Create a new project
@@ -112,3 +115,18 @@ def delete_project(session: SessionDep, project_id: int):
112115
f"[delete_project] Project deleted successfully | project_id={project_id}"
113116
)
114117
return APIResponse.success_response(None)
118+
119+
120+
# Get projects by organization
121+
@router.get(
122+
"/organization/{org_id}",
123+
dependencies=[Depends(require_permission(Permission.SUPERUSER))],
124+
response_model=APIResponse[List[ProjectPublic]],
125+
description=load_description("projects/list_by_org.md"),
126+
)
127+
def read_projects_by_organization(
128+
session: SessionDep, org_id: int
129+
) -> APIResponse[List[ProjectPublic]]:
130+
validate_organization(session=session, org_id=org_id)
131+
projects = get_projects_by_organization(session=session, org_id=org_id)
132+
return APIResponse.success_response(projects)

backend/app/crud/config/config.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -85,20 +85,31 @@ def read_one(self, config_id: UUID) -> Config | None:
8585
)
8686
return self.session.exec(statement).one_or_none()
8787

88-
def read_all(self, skip: int = 0, limit: int = 100) -> list[Config]:
88+
def read_all(
89+
self, query: str | None, skip: int = 0, limit: int = 100
90+
) -> tuple[list[Config], bool]:
91+
filters = [
92+
Config.project_id == self.project_id,
93+
Config.deleted_at.is_(None),
94+
]
95+
96+
if query:
97+
filters.append(Config.name.ilike(f"{query}%"))
98+
8999
statement = (
90100
select(Config)
91-
.where(
92-
and_(
93-
Config.project_id == self.project_id,
94-
Config.deleted_at.is_(None),
95-
)
96-
)
101+
.where(and_(*filters))
97102
.order_by(Config.updated_at.desc())
98103
.offset(skip)
99-
.limit(limit)
104+
.limit(limit + 1)
100105
)
101-
return self.session.exec(statement).all()
106+
configs = self.session.exec(statement).all()
107+
has_more = False
108+
if limit is not None and len(configs) > limit:
109+
has_more = True
110+
configs = configs[:limit]
111+
112+
return configs, has_more
102113

103114
def update_or_raise(self, config_id: UUID, config_update: ConfigUpdate) -> Config:
104115
config = self.exists_or_raise(config_id)

backend/app/crud/document/document.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,11 @@ def read_many(
3737
self,
3838
skip: int | None = None,
3939
limit: int | None = None,
40-
) -> list[Document]:
40+
) -> tuple[list[Document], bool]:
4141
statement = select(Document).where(
4242
and_(Document.project_id == self.project_id, Document.is_deleted.is_(False))
4343
)
44+
statement = statement.order_by(Document.inserted_at.desc())
4445

4546
if skip is not None:
4647
if skip < 0:
@@ -64,10 +65,16 @@ def read_many(
6465
exc_info=True,
6566
)
6667
raise
67-
statement = statement.limit(limit)
68+
statement = statement.limit(limit + 1)
6869

6970
documents = self.session.exec(statement).all()
70-
return documents
71+
72+
has_more = False
73+
if limit is not None and len(documents) > limit:
74+
has_more = True
75+
documents = documents[:limit]
76+
77+
return documents, has_more
7178

7279
def read_each(self, doc_ids: list[UUID]):
7380
statement = select(Document).where(

backend/app/crud/evaluations/batch.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,12 @@ def build_evaluation_jsonl(
106106
body: dict[str, Any] = {
107107
"model": config.model,
108108
"instructions": config.instructions,
109-
"temperature": config.temperature
110-
if config.temperature is not None
111-
else 0.01,
112109
"input": question, # Add input from dataset
113110
}
114111

112+
if "temperature" in config.model_fields_set:
113+
body["temperature"] = config.temperature
114+
115115
# Add reasoning only if provided
116116
if config.reasoning:
117117
body["reasoning"] = {"effort": config.reasoning}
@@ -189,7 +189,7 @@ def start_evaluation_batch(
189189
"description": f"Evaluation: {eval_run.run_name}",
190190
"completion_window": "24h",
191191
# Store complete config for reference
192-
"evaluation_config": config.model_dump(exclude_none=True),
192+
"evaluation_config": config.model_dump(exclude_unset=True),
193193
}
194194

195195
# Step 5: Start batch job using generic infrastructure

0 commit comments

Comments
 (0)