Skip to content

[Bug]: BaiduYiyan provider crashes with JSONDecodeError on plain key (same pattern as #17204 / #17373) #17389

Description

@Harsh23Kashyap

Summary

BaiduYiyanChat.__init__ in rag/llm/chat_model.py does an unguarded json.loads(key). The Baidu Qianfan API requires a JSON key (the same protocol used by every other JSON-config provider: see conf/models/baidu.json for the model class). If a user pastes a plain (non-JSON) string as the Baidu API key, the constructor crashes with json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0) from inside rag/llm internals — exactly the same shape as the recently-fixed Azure (#17204 / PR #17215) and Bedrock (#17373 / PR #17377) bugs.

Affected call site

rag/llm/chat_model.py:1189BaiduYiyanChat.__init__ (the chat class for the Baidu Qianfan provider). One line:

key = json.loads(key)  # UNGUARDED — same pattern as the pre-fix Azure / Bedrock branches
ak = key.get("yiyan_ak", "")
sk = key.get("yiyan_sk", "")
self.client = qianfan.ChatCompletion(ak=ak, sk=sk)

The downstream .get("yiyan_ak", "") / .get("yiyan_sk", "") calls would mask the AttributeError if key were a non-dict, but the raw JSONDecodeError from json.loads on a plain string is what crashes today.

Reproduction

  1. In the RAGFlow UI, add an LLM provider with class BaiduYiyan (e.g. model ERNIE-4.0-8K).
  2. Paste a plain (non-JSON) string as the API key, e.g. bce-v3/ALTAK-.../....
  3. Save. The first chat call against the provider raises JSONDecodeError from json.loads(key) inside BaiduYiyanChat.__init__, before any network call is made.

Expected behavior

The provider should fail with a clear, actionable error — something like "BaiduYiyan requires a JSON key with yiyan_ak and yiyan_sk; see conf/models/baidu.json for the model class" — instead of a raw JSONDecodeError stack trace. The current behaviour is silent on what the user did wrong.

Why this matters

Proposed solution

Same shape as the Bedrock fix (PR #17377). Add a shared helper to rag/llm/key_utils.py:

def _resolve_qianfan_credentials(key):
    """Parse a BaiduYiyan (Baidu Qianfan) ``key`` and return it as a dict.

    The BaiduYiyan / Qianfan API requires a JSON object with at least
    ``yiyan_ak`` and ``yiyan_sk``. See ``conf/models/baidu.json`` for the
    model class.

    On non-JSON input we raise a clear :class:`ModelException` pointing
    at the required schema, instead of letting ``json.loads`` bubble up
    as ``json.decoder.JSONDecodeError`` from inside ``rag/llm`` internals.
    Mirrors ``_resolve_bedrock_credentials`` (#17373) and the Azure
    fix shape (#17215 / #17204).
    """
    # Same shape as _resolve_bedrock_credentials. See key_utils.py.
    ...

Wire it into chat_model.py:1189 in place of the bare json.loads(key).

Add a test_chat_model_qianfan_key_fallback.py mirroring the Bedrock test file: covers (a) plain string key raises a clear ModelException, (b) JSON dict with yiyan_ak and yiyan_sk parses, (c) JSON missing fields raises a clear error, (d) non-dict JSON raises a clear error, (e) end-to-end through BaiduYiyanChat.__init__.

Alternatives considered

  • Silent swallow + log warning. Rejected: the operator wouldn't see the error in the chat UI; the chat would just fail with no diagnostic.
  • Auto-detect a plain Baidu API key prefix (bce-v3/...) and try to wrap it. Rejected: a plain key alone is insufficient (no yiyan_ak / yiyan_sk); the heuristic would produce a half-broken state.
  • Schema validation at config-write time. Out of scope for this fix; would require a UI-side validator and a migration. The provider-init path catches the same class of errors and is where the existing Bedrock fix lives.

Scope

One call site, one new helper in key_utils.py, one new test file. No public API changes. No data-model changes. No migrations. Backward compatible: valid JSON keys behave identically to today; invalid keys now raise a ModelException with a clear message instead of a raw JSONDecodeError.

Acceptance criteria

  • A plain (non-JSON) BaiduYiyan key produces a ModelException with a message that includes the words "BaiduYiyan" or "Qianfan" and "JSON" and points at conf/models/baidu.json or the schema example.
  • A valid JSON dict with yiyan_ak and yiyan_sk continues to work (no behaviour change for happy path).
  • A JSON top-level non-dict (array, string, number) produces a ModelException, not an AttributeError from the downstream .get("yiyan_ak", "").
  • The bare json.loads(key) at chat_model.py:1189 is replaced by the helper.
  • New p0 regression tests cover the four scenarios above plus the end-to-end through BaiduYiyanChat.__init__.
  • ruff check + ruff format --check clean on all touched files.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions