Skip to content

Commit 5372913

Browse files
authored
Merge branch 'main' into enahncement/polling_llmcall_db
2 parents 05e9f57 + a912ba0 commit 5372913

File tree

11 files changed

+772
-72
lines changed

11 files changed

+772
-72
lines changed
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
}

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/core/providers.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import logging
2-
from typing import Dict, List, Optional
2+
from typing import Dict, List
33
from enum import Enum
44
from dataclasses import dataclass
55

@@ -10,7 +10,6 @@ class Provider(str, Enum):
1010
"""Enumeration of supported credential providers."""
1111

1212
OPENAI = "openai"
13-
AWS = "aws"
1413
LANGFUSE = "langfuse"
1514
GOOGLE = "google"
1615
SARVAMAI = "sarvamai"
@@ -27,9 +26,6 @@ class ProviderConfig:
2726
# Provider configurations
2827
PROVIDER_CONFIGS: Dict[Provider, ProviderConfig] = {
2928
Provider.OPENAI: ProviderConfig(required_fields=["api_key"]),
30-
Provider.AWS: ProviderConfig(
31-
required_fields=["access_key_id", "secret_access_key", "region"]
32-
),
3329
Provider.LANGFUSE: ProviderConfig(
3430
required_fields=["secret_key", "public_key", "host"]
3531
),

backend/app/crud/credentials.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from app.core.util import now
1212
from app.models import Credential, CredsCreate, CredsUpdate
1313

14+
1415
logger = logging.getLogger(__name__)
1516

1617

@@ -27,9 +28,13 @@ def set_creds_for_org(
2728
raise HTTPException(400, "No credentials provided")
2829

2930
for provider, credentials in creds_add.credential.items():
30-
# Validate provider and credentials
31-
validate_provider(provider)
32-
validate_provider_credentials(provider, credentials)
31+
try:
32+
validate_provider_credentials(provider, credentials)
33+
except ValueError as e:
34+
logger.error(
35+
f"[set_creds_for_org] Validation error | project_id: {project_id}, provider: {provider}, error: {str(e)}"
36+
)
37+
raise HTTPException(status_code=400, detail=str(e))
3338

3439
# Encrypt entire credentials object
3540
encrypted_credentials = encrypt_credentials(credentials)
@@ -144,7 +149,10 @@ def get_provider_credential(
144149
Raises:
145150
HTTPException: If credentials are not found
146151
"""
147-
validate_provider(provider)
152+
try:
153+
validate_provider(provider)
154+
except ValueError as e:
155+
raise HTTPException(status_code=400, detail=str(e))
148156

149157
statement = select(Credential).where(
150158
Credential.organization_id == org_id,
@@ -176,8 +184,13 @@ def update_creds_for_org(
176184
if not creds_in.provider or not creds_in.credential:
177185
raise ValueError("Provider and credential must be provided")
178186

179-
validate_provider(creds_in.provider)
180-
validate_provider_credentials(creds_in.provider, creds_in.credential)
187+
try:
188+
validate_provider_credentials(creds_in.provider, creds_in.credential)
189+
except ValueError as e:
190+
logger.error(
191+
f"[update_creds_for_org] Validation error | organization_id: {org_id}, project_id: {project_id}, provider: {creds_in.provider}, error: {str(e)}"
192+
)
193+
raise HTTPException(status_code=400, detail=str(e))
181194

182195
# Encrypt the entire credentials object
183196
encrypted_credentials = encrypt_credentials(creds_in.credential)
@@ -216,8 +229,6 @@ def remove_provider_credential(
216229
Raises:
217230
HTTPException: If credentials not found or deletion fails
218231
"""
219-
validate_provider(provider)
220-
221232
# Verify credentials exist before attempting delete
222233
creds = get_provider_credential(
223234
session=session,

backend/app/models/onboarding.py

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -90,39 +90,30 @@ def _validate_credential_list(cls, v: list[dict[str, dict[str, str]]] | None):
9090

9191
if not isinstance(v, list):
9292
raise TypeError(
93-
"credential must be a list of single-key dicts (e.g., {'openai': {...}})."
93+
"Credential must be a list of single-key dicts (e.g., {'openai': {...}})."
9494
)
9595

96-
errors: list[str] = []
96+
for item in v:
97+
if not isinstance(item, dict):
98+
raise TypeError(
99+
"Credential must be a dict with a single provider key like {'openai': {...}}."
100+
)
101+
if len(item) != 1:
102+
raise ValueError(
103+
"Credential must have exactly one provider key like {'openai': {...}}."
104+
)
97105

98-
for idx, item in enumerate(v):
99-
try:
100-
if not isinstance(item, dict):
101-
raise TypeError(
102-
"must be a dict with a single provider key like {'openai': {...}}."
103-
)
104-
if len(item) != 1:
105-
raise ValueError(
106-
"must have exactly one provider key like {'openai': {...}}."
107-
)
106+
(provider_key,) = item.keys()
107+
values = item[provider_key]
108108

109-
(provider_key,) = item.keys()
110-
values = item[provider_key]
109+
validate_provider(provider_key)
111110

112-
validate_provider(provider_key)
111+
if not isinstance(values, dict):
112+
raise ValueError(
113+
f"Value for provider '{provider_key}' must be an object/dict."
114+
)
113115

114-
if not isinstance(values, dict):
115-
raise TypeError(
116-
f"value for provider '{provider_key}' must be an object/dict."
117-
)
118-
119-
validate_provider_credentials(provider_key, values)
120-
121-
except (TypeError, ValueError) as e:
122-
errors.append(f"[{idx}] {e}")
123-
124-
if errors:
125-
raise ValueError("credential validation failed:\n" + "\n".join(errors))
116+
validate_provider_credentials(provider_key, values)
126117

127118
return v
128119

0 commit comments

Comments
 (0)