-
Notifications
You must be signed in to change notification settings - Fork 10
Credentials: error handling for unsupported providers #707
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
94eb3b0
f749d69
284fd2f
c4d4f0c
2ced4bc
34e7fc2
5fcfa88
9451f33
df0124d
294f488
9a29ea8
66d85e9
077da6a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,46 @@ | ||
| Persist new credentials for the current organization and project. | ||
|
|
||
| Credentials are encrypted and stored securely for provider integrations (OpenAI, Langfuse, etc.). Only one credential per provider is allowed per organization-project combination. | ||
| 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. | ||
|
|
||
| ### Supported Providers: | ||
| - **LLM:** openai, sarvamai, google(gemini) | ||
| - **Observability:** langfuse | ||
| - **Audio:** elevenlabs | ||
|
|
||
| ### Examples: | ||
|
|
||
| #### Single Provider | ||
| ```json | ||
| { | ||
| "credential": { | ||
| "openai": { | ||
| "api_key": "sk-proj-..." | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| #### Multiple Providers | ||
| ```json | ||
| { | ||
| "credential": { | ||
| "openai": { | ||
| "api_key": "sk-proj-..." | ||
| }, | ||
| "google": { | ||
| "api_key": "AIzaSy..." | ||
| }, | ||
| "sarvamai": { | ||
| "api_key": "sarvam-..." | ||
| }, | ||
| "elevenlabs": { | ||
| "api_key": "sk_..." | ||
| }, | ||
| "langfuse": { | ||
| "public_key": "pk-lf-....", | ||
| "secret_key": "sk-lf-...", | ||
| "host": "https://cloud.langfuse.com" | ||
| } | ||
| } | ||
| } | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -90,39 +90,30 @@ def _validate_credential_list(cls, v: list[dict[str, dict[str, str]]] | None): | |
|
|
||
| if not isinstance(v, list): | ||
| raise TypeError( | ||
| "credential must be a list of single-key dicts (e.g., {'openai': {...}})." | ||
| "Credential must be a list of single-key dicts (e.g., {'openai': {...}})." | ||
| ) | ||
|
|
||
| errors: list[str] = [] | ||
| for item in v: | ||
| if not isinstance(item, dict): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this post filter required inside
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. while it may be hard to maintain, this helps us catch any issue early on with the request body when it comes to credentials being given, otherwise we will only get to know about it after crud function is hit and we put the verification in that
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sure we can take it up later |
||
| raise TypeError( | ||
| "Credential must be a dict with a single provider key like {'openai': {...}}." | ||
| ) | ||
| if len(item) != 1: | ||
| raise ValueError( | ||
| "Credential must have exactly one provider key like {'openai': {...}}." | ||
| ) | ||
|
|
||
| for idx, item in enumerate(v): | ||
| try: | ||
| if not isinstance(item, dict): | ||
| raise TypeError( | ||
| "must be a dict with a single provider key like {'openai': {...}}." | ||
| ) | ||
| if len(item) != 1: | ||
| raise ValueError( | ||
| "must have exactly one provider key like {'openai': {...}}." | ||
| ) | ||
| (provider_key,) = item.keys() | ||
| values = item[provider_key] | ||
|
|
||
| (provider_key,) = item.keys() | ||
| values = item[provider_key] | ||
| validate_provider(provider_key) | ||
|
|
||
| validate_provider(provider_key) | ||
| if not isinstance(values, dict): | ||
| raise ValueError( | ||
| f"Value for provider '{provider_key}' must be an object/dict." | ||
| ) | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| if not isinstance(values, dict): | ||
| raise TypeError( | ||
| f"value for provider '{provider_key}' must be an object/dict." | ||
| ) | ||
|
|
||
| validate_provider_credentials(provider_key, values) | ||
|
|
||
| except (TypeError, ValueError) as e: | ||
| errors.append(f"[{idx}] {e}") | ||
|
|
||
| if errors: | ||
| raise ValueError("credential validation failed:\n" + "\n".join(errors)) | ||
| validate_provider_credentials(provider_key, values) | ||
|
|
||
| return v | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: can we reuse
APIResponse.failure_response()instead of manually raising?Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use failure response directly for situations where we are raising 200 status code with a failure payload, this manual raising is needed for the status code and anyway the global exception handler wraps HTTPException into APIResponse.failure_response() automatically, so the client still gets a consistent APIResponse shape, just with the right non-200 status code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the error code can be sent in the metadata. but okay we can take that up later