Skip to content

Commit c1f997c

Browse files
authored
Merge branch 'main' into enhancement/dynamic_batching
2 parents 86e555c + a912ba0 commit c1f997c

File tree

28 files changed

+1055
-118
lines changed

28 files changed

+1055
-118
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: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,46 @@
11
Persist new credentials for the current organization and project.
22

3-
Credentials are encrypted and stored securely for provider integrations (OpenAI, Langfuse, etc.). Only one credential per provider is allowed per organization-project combination.
3+
Credentials are encrypted and stored securely for provider integrations (OpenAI, Langfuse, etc.). Only one credential per provider is allowed per organization-project combination. You can send credentials for a single provider or multiple providers in one request. Refer to the examples below for the required input parameters for each provider.
4+
5+
### Supported Providers:
6+
- **LLM:** openai, sarvamai, google(gemini)
7+
- **Observability:** langfuse
8+
- **Audio:** elevenlabs
9+
10+
### Examples:
11+
12+
#### Single Provider
13+
```json
14+
{
15+
"credential": {
16+
"openai": {
17+
"api_key": "sk-proj-..."
18+
}
19+
}
20+
}
21+
```
22+
23+
#### Multiple Providers
24+
```json
25+
{
26+
"credential": {
27+
"openai": {
28+
"api_key": "sk-proj-..."
29+
},
30+
"google": {
31+
"api_key": "AIzaSy..."
32+
},
33+
"sarvamai": {
34+
"api_key": "sarvam-..."
35+
},
36+
"elevenlabs": {
37+
"api_key": "sk_..."
38+
},
39+
"langfuse": {
40+
"public_key": "pk-lf-....",
41+
"secret_key": "sk-lf-...",
42+
"host": "https://cloud.langfuse.com"
43+
}
44+
}
45+
}
46+
```

backend/app/api/docs/onboarding/onboarding.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@
3030
- We’ve also included a list of the providers currently supported by kaapi.
3131

3232
### Supported Providers
33-
- **LLM:** openai
33+
- **LLM:** openai, google, sarvamai
3434
- **Observability:** langfuse
35+
- **Audio:** elevenlabs
3536

3637
### Example: For sending multiple credentials -
3738
```
@@ -41,9 +42,24 @@
4142
"api_key": "sk-proj-..."
4243
}
4344
},
45+
{
46+
"google": {
47+
"api_key": "AIzaSy..."
48+
}
49+
},
50+
{
51+
"sarvamai": {
52+
"api_key": "sarvam-..."
53+
}
54+
},
55+
{
56+
"elevenlabs": {
57+
"api_key": "sk_..."
58+
}
59+
},
4460
{
4561
"langfuse": {
46-
"public_key": "pk-lf-...",
62+
"public_key": "pk-lf-....",
4763
"secret_key": "sk-lf-...",
4864
"host": "https://cloud.langfuse.com"
4965
}
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/credentials.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,11 @@ def read_provider_credential(
8585
provider: str,
8686
_current_user: AuthContextDep,
8787
):
88-
provider_enum = validate_provider(provider)
88+
try:
89+
provider_enum = validate_provider(provider)
90+
except ValueError as e:
91+
raise HTTPException(status_code=400, detail=str(e))
92+
8993
credential = get_provider_credential(
9094
session=session,
9195
org_id=_current_user.organization_.id,
@@ -143,7 +147,11 @@ def delete_provider_credential(
143147
provider: str,
144148
_current_user: AuthContextDep,
145149
):
146-
provider_enum = validate_provider(provider)
150+
try:
151+
provider_enum = validate_provider(provider)
152+
except ValueError as e:
153+
raise HTTPException(status_code=400, detail=str(e))
154+
147155
remove_provider_credential(
148156
session=session,
149157
org_id=_current_user.organization_.id,

backend/app/api/routes/documents.py

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

8989
storage = (
9090
get_cloud_storage(session=session, project_id=current_user.project_.id)
@@ -97,7 +97,7 @@ def list_docs(
9797
include_url=include_url,
9898
storage=storage,
9999
)
100-
return APIResponse.success_response(results)
100+
return APIResponse.success_response(results, metadata=dict(has_more=has_more))
101101

102102

103103
@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)

0 commit comments

Comments
 (0)