mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-25 01:02:15 +00:00
fix(oauth): jsonify_object() for Prisma Json columns in persist_credential_to_db
Reported error after the cross-loop fix: Tokens obtained but DB persist failed: Unable to match input value to any allowed input type for the field. Parse errors: [... ``credential_values`` should be of any of the following types: ``JsonNullValueInput``, ``Json`` ...] Prisma's Json columns ingest pre-serialized JSON *strings*, not raw Python dicts. The ``/credentials`` endpoint wraps the payload with ``jsonify_object(...)`` (in ``litellm/proxy/utils.py``) which does ``json.dumps`` per nested-dict field. My persist helper was skipping that step and passing raw dicts — Prisma's binding layer then couldn't match them to the ``Json`` type. Route the ``credential_values`` + ``credential_info`` dicts through ``jsonify_object`` before the upsert. Same fix in both ChatGPT and Copilot ``db_authenticator.py``. Tests updated to assert the serialized string form (``json.loads(kwargs[...]["credential_values"])``). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c3932e06d4
commit
b22f988248
4 changed files with 32 additions and 12 deletions
|
|
@ -176,6 +176,7 @@ async def persist_credential_to_db(item: CredentialItem) -> None:
|
|||
# would bind the stale None reference rather than the live client.
|
||||
from litellm.proxy.common_utils.encrypt_decrypt_utils import encrypt_value_helper
|
||||
from litellm.proxy.proxy_server import prisma_client
|
||||
from litellm.proxy.utils import jsonify_object
|
||||
|
||||
if prisma_client is None:
|
||||
verbose_logger.debug(
|
||||
|
|
@ -191,19 +192,25 @@ async def persist_credential_to_db(item: CredentialItem) -> None:
|
|||
"type": CREDENTIAL_TYPE,
|
||||
"custom_llm_provider": "chatgpt",
|
||||
}
|
||||
# Prisma's Json columns want JSON-serialized strings, not raw dicts —
|
||||
# see credential_endpoints/endpoints.py for the canonical pattern.
|
||||
jsonified = jsonify_object(
|
||||
{
|
||||
"credential_values": encrypted_values,
|
||||
"credential_info": credential_info,
|
||||
}
|
||||
)
|
||||
await prisma_client.db.litellm_credentialstable.upsert(
|
||||
where={"credential_name": item.credential_name},
|
||||
data={
|
||||
"create": {
|
||||
"credential_name": item.credential_name,
|
||||
"credential_values": encrypted_values,
|
||||
"credential_info": credential_info,
|
||||
**jsonified,
|
||||
"created_by": "chatgpt_oauth_flow",
|
||||
"updated_by": "chatgpt_oauth_flow",
|
||||
},
|
||||
"update": {
|
||||
"credential_values": encrypted_values,
|
||||
"credential_info": credential_info,
|
||||
**jsonified,
|
||||
"updated_by": "chatgpt_oauth_flow",
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -180,6 +180,7 @@ async def persist_credential_to_db(item: CredentialItem) -> None:
|
|||
# would bind the stale None reference rather than the live client.
|
||||
from litellm.proxy.common_utils.encrypt_decrypt_utils import encrypt_value_helper
|
||||
from litellm.proxy.proxy_server import prisma_client
|
||||
from litellm.proxy.utils import jsonify_object
|
||||
|
||||
if prisma_client is None:
|
||||
verbose_logger.debug(
|
||||
|
|
@ -195,19 +196,24 @@ async def persist_credential_to_db(item: CredentialItem) -> None:
|
|||
"type": CREDENTIAL_TYPE,
|
||||
"custom_llm_provider": "github_copilot",
|
||||
}
|
||||
# Prisma's Json columns want JSON-serialized strings, not raw dicts.
|
||||
jsonified = jsonify_object(
|
||||
{
|
||||
"credential_values": encrypted_values,
|
||||
"credential_info": credential_info,
|
||||
}
|
||||
)
|
||||
await prisma_client.db.litellm_credentialstable.upsert(
|
||||
where={"credential_name": item.credential_name},
|
||||
data={
|
||||
"create": {
|
||||
"credential_name": item.credential_name,
|
||||
"credential_values": encrypted_values,
|
||||
"credential_info": credential_info,
|
||||
**jsonified,
|
||||
"created_by": "copilot_oauth_flow",
|
||||
"updated_by": "copilot_oauth_flow",
|
||||
},
|
||||
"update": {
|
||||
"credential_values": encrypted_values,
|
||||
"credential_info": credential_info,
|
||||
**jsonified,
|
||||
"updated_by": "copilot_oauth_flow",
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import json
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
|
@ -178,11 +179,12 @@ class TestPersistCredentialToDb:
|
|||
assert kwargs["where"] == {"credential_name": "test"}
|
||||
create = kwargs["data"]["create"]
|
||||
assert create["credential_name"] == "test"
|
||||
assert create["credential_values"] == {
|
||||
# Prisma Json columns receive pre-serialized JSON strings.
|
||||
assert json.loads(create["credential_values"]) == {
|
||||
"access_token": "enc(a)",
|
||||
"refresh_token": "enc(r)",
|
||||
}
|
||||
assert create["credential_info"] == {"type": CREDENTIAL_TYPE}
|
||||
assert json.loads(create["credential_info"]) == {"type": CREDENTIAL_TYPE}
|
||||
update = kwargs["data"]["update"]
|
||||
assert update["credential_values"] == create["credential_values"]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import json
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
|
@ -225,10 +226,14 @@ class TestPersistCredentialToDb:
|
|||
fake_prisma.db.litellm_credentialstable.upsert.assert_called_once()
|
||||
kwargs = fake_prisma.db.litellm_credentialstable.upsert.call_args.kwargs
|
||||
assert kwargs["where"] == {"credential_name": "c"}
|
||||
assert kwargs["data"]["create"]["credential_values"] == {
|
||||
# Prisma Json columns receive pre-serialized JSON strings.
|
||||
assert json.loads(kwargs["data"]["create"]["credential_values"]) == {
|
||||
"access_token": "enc(gho_abc)"
|
||||
}
|
||||
assert kwargs["data"]["create"]["credential_info"]["type"] == CREDENTIAL_TYPE
|
||||
assert (
|
||||
json.loads(kwargs["data"]["create"]["credential_info"])["type"]
|
||||
== CREDENTIAL_TYPE
|
||||
)
|
||||
|
||||
|
||||
class _Awaitable:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue