feat(nodes): add Baidu Qianfan ERNIE LLM node#1006
Conversation
|
Caution Review failedFailed to post review comments 📝 WalkthroughWalkthroughThis PR adds a complete Baidu Qianfan LLM node to RocketRide, enabling access to ERNIE chat models via Qianfan's OpenAI-compatible API. It includes the node runtime with configuration validation and client lifecycle, service definition with selectable profiles, model sync provider for discovering available ERNIE models, documentation, and comprehensive test coverage. ChangesBaidu Qianfan LLM Node Integration
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
No description provided. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@nodes/src/nodes/llm_baidu_qianfan/qianfan_client.py`:
- Around line 69-97: The APIConnectionError checks in is_retryable_error and
map_exception are unreachable because APIConnectionError subclasses APIError;
update both methods (is_retryable_error and map_exception) to check for
APIConnectionError before checking for APIError, preserving the existing return
values/messages for AuthenticationError, RateLimitError, APIConnectionError, and
APIError so connection-specific logic runs as intended.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 7130dc6d-1b61-4157-9c57-faedc6e54c80
⛔ Files ignored due to path filters (1)
packages/shared-ui/src/assets/nodes/baidu-qianfan.svgis excluded by!**/*.svg
📒 Files selected for processing (17)
docs/README-nodes.mdnodes/src/nodes/llm_baidu_qianfan/IGlobal.pynodes/src/nodes/llm_baidu_qianfan/IInstance.pynodes/src/nodes/llm_baidu_qianfan/README.mdnodes/src/nodes/llm_baidu_qianfan/__init__.pynodes/src/nodes/llm_baidu_qianfan/qianfan_client.pynodes/src/nodes/llm_baidu_qianfan/requirements.txtnodes/src/nodes/llm_baidu_qianfan/services.jsonnodes/test/framework/pipeline.pypackages/shared-ui/src/components/canvas/util/get-icon-path.tstools/SYNC_MODELS.mdtools/src/providers/baidu_qianfan.pytools/src/sync_models.config.jsontools/src/sync_models.pytools/test/markers.pytools/test/test_baidu_qianfan_provider.pytools/test/test_sync_live.py
asclearuc
left a comment
There was a problem hiding this comment.
Thanks @jimmyzhuu — this is a clean, complete provider integration (node runtime, ERNIE profiles, model-sync tooling, tests, and icon), and it follows the existing OpenAI-compatible node pattern well.
Before it can merge it needs a rebase onto develop, because #926 changed how node icons work and it rewrote the exact files your PR touches under packages/. Points to pay attention to during the rebase:
packages/shared-ui/src/assets/nodes/no longer exists. Move yourbaidu-qianfan.svgtonodes/src/nodes/llm_baidu_qianfan/baidu-qianfan.svg(next toservices.json). Keep"icon": "baidu-qianfan.svg"inservices.jsonunchanged — only the file location moves.get-icon-path.tsis gone, replaced byIcon.tsx, which auto-discoversnodes/src/nodes/**/*.svgat build time. Drop all your changes to it — there is no manual icon registration anymore.THEME_DYNAMIC_ICONSis gone. Theming is now automatic: a build step rewrites single-color SVGs tocurrentColor(so they follow the light/dark theme) and passes multicolor logos through unchanged. Your note about keeping Baidu out of the dynamic set no longer applies — instead, make sure the Baidu SVG is genuinely multicolor if you want it to keep its brand blue, otherwise it will be theme-tinted.docs/README-nodes.mdwas also edited by #926 — expect a conflict there and resolve it.
After rebasing, please run builder shared-ui:test and the icon audit (node scripts/audit-icons.mjs); the audit asserts every services.json icon exists inside its node folder.
There are also a few code findings to address for a clean PR — see the inline comments below. CodeRabbit's notes are worth a pass too.
| except APIStatusError as e: | ||
| status = getattr(e, 'status_code', None) or getattr(e, 'status', None) | ||
| try: | ||
| resp = getattr(e, 'response', None) | ||
| data = resp.json() if resp is not None else None | ||
| if isinstance(data, dict): | ||
| err = data.get('error') | ||
| if isinstance(err, dict): | ||
| etype = err.get('type') or err.get('code') | ||
| emsg = err.get('message') or data.get('message') | ||
| else: | ||
| etype = data.get('code') | ||
| emsg = data.get('message') | ||
| message = self._format_error(status, etype, emsg, str(e)) | ||
| else: | ||
| message = self._format_error(status, None, None, str(e)) | ||
| except Exception: | ||
| message = self._format_error(status, None, None, str(e)) | ||
| warning(message) | ||
| return | ||
| except AuthenticationError: | ||
| warning('Baidu Qianfan API key is invalid or unauthorized.') | ||
| return | ||
| except RateLimitError: | ||
| warning('Baidu Qianfan rate limit exceeded while validating the configuration.') | ||
| return | ||
| except APIConnectionError: | ||
| warning('Could not connect to Baidu Qianfan. Check the configured base URL and network access.') | ||
| return |
There was a problem hiding this comment.
these except branches can never run.
except APIStatusError (line 82) is listed before except AuthenticationError (line 102) and except RateLimitError (line 105). In the OpenAI SDK both AuthenticationError (401) and RateLimitError (429) are subclasses of APIStatusError, so the APIStatusError branch catches them first and the two dedicated handlers below are dead code. The friendly messages — "Baidu Qianfan API key is invalid or unauthorized." and the rate-limit message — never appear; a bad key at save time falls through to the generic formatted string instead.
Move the AuthenticationError and RateLimitError handlers above the APIStatusError handler so the specific cases are matched first. (APIConnectionError is fine — it is not a subclass of APIStatusError.) Your qianfan_client.py already orders these correctly; this is the one spot where the order slipped.
There was a problem hiding this comment.
Fixed in 07af0f2: validateConfig() now catches AuthenticationError and RateLimitError before APIStatusError, so the provider-specific API key and rate-limit warnings are reachable. Added focused regression coverage in nodes/test/test_baidu_qianfan_global_validation.py and verified it passes along with py_compile and the llm_baidu_qianfan contract tests.
| "baidu_qianfan.serverbase": { | ||
| "type": "string", | ||
| "title": "Base URL", | ||
| "description": "Qianfan OpenAI-compatible API base URL" | ||
| }, |
There was a problem hiding this comment.
surface both endpoints as a dropdown instead of a README copy-paste (and verify the international URL first).
Right now Base URL is a free-text field and the international endpoint is only documented as a raw URL in the README that users hand-copy.
Two problems: it's easy to mistype, and the documented domain https://api.baiduqianfan.ai/v1 is a different domain from the official qianfan.baidubce.com used everywhere else in this PR — the Base URL receives the user's API key, so an unverified domain is a credential-leak risk.
Since the runtime already reads serverbase directly as the URL, make it a labeled enum (no Python change needed), the same way the Qwen node offers regional endpoints:
"baidu_qianfan.serverbase": {
"type": "string",
"title": "Base URL",
"description": "Qianfan OpenAI-compatible API endpoint.",
"default": "https://qianfan.baidubce.com/v2",
"enum": [
["https://qianfan.baidubce.com/v2", "China (default)"],
["https://api.baiduqianfan.ai/v1", "International"]
]
},Then drop the "set the base URL to …" line from the README — the dropdown documents itself.
Before adding the international option, confirm api.baiduqianfan.ai is an official Baidu domain with a source. If you can't verify it, omit that second entry from both the enum and the README — don't ship an unverified endpoint as a selectable option, which is worse than a doc line.
There was a problem hiding this comment.
Fixed in 07af0f2: Base URL is now a labeled enum with China (default) and International options, and the README no longer asks users to copy-paste a raw endpoint. I verified https://api.baiduqianfan.ai/v1 against official Baidu AI Cloud Qianfan documentation before adding it:
89a5bc7 to
07af0f2
Compare
|
@asclearuc Thanks for your detailed review! Rebased this PR onto the latest
Verification run locally:
|
Summary
This PR adds first-class Baidu Qianfan / ERNIE support as a dedicated RocketRide LLM node.
Instead of requiring users to configure Baidu through the generic OpenAI-compatible node, the new
llm_baidu_qianfannode exposes Qianfan-specific defaults, provider branding, model profiles, runtime validation, sync-models support, and targeted tests.Closes #1005
Motivation
RocketRide already includes dedicated nodes for major LLM providers such as OpenAI, Anthropic, Gemini, Mistral, DeepSeek, Perplexity, xAI, and Qwen. Baidu Qianfan is an important provider for Chinese-language and enterprise AI workflows, and Qianfan now exposes OpenAI-compatible chat-completions endpoints.
A dedicated node improves the user experience in three ways:
What Changed
New node runtime
nodes/src/nodes/llm_baidu_qianfan/.IGlobal,IInstance, andqianfan_client.Chat.langchain_openai.ChatOpenAIagainst Qianfan's OpenAI-compatible API.https://qianfan.baidubce.com/v2.serverbaseconfigurable so users can switch endpoints, including Baidu's international endpoint when needed.Node metadata and profiles
services.jsonforllm_baidu_qianfan://.ernie-4.5-turbo-128kernie-4.5-turbo-32kernie-5.0-thinking-previewcustommodelSource: "provider".llm_baidu_qianfan.Shared UI
baidu-qianfan.svgin the shared UI icon resolver.THEME_DYNAMIC_ICONSbecause it is a colored brand asset and should not be filtered as a single-color icon.Model sync tooling
BaiduQianfanProvider.llm_baidu_qianfanin_PROVIDER_REGISTRYand_SERVICES_JSON_PATHS.sync_models.config.json.ROCKETRIDE_BAIDU_QIANFAN_KEYlive-test marker.tools/SYNC_MODELS.md.Implementation Notes
sk-. The runtime only validates that a non-empty key is configured because Baidu keys can use other formats.customremains protected from sync-models deprecation.Testing
Local focused tests:
PYTHONPATH=tools/src pytest tools/test/test_baidu_qianfan_provider.py -vPYTHONPATH=nodes/src uv run --with pytest --with pytest-asyncio --with pytest-timeout --with pytest-xdist --with pytest-mock --with json5 --with requests --with httpx --with openai pytest nodes/test/test_contracts.py -k llm_baidu_qianfan -vpython -m py_compile nodes/src/nodes/llm_baidu_qianfan/IGlobal.py nodes/src/nodes/llm_baidu_qianfan/qianfan_client.py tools/src/providers/baidu_qianfan.pygit diff --checkLive/API validation:
PYTHONPATH=tools/src uv run --with pytest --with json5 --with openai pytest tools/test/test_sync_live.py -k baidu_qianfan -vVerified this skips cleanly without credentials.
ROCKETRIDE_BAIDU_QIANFAN_KEY=*** PYTHONPATH=tools/src uv run --with pytest --with json5 --with openai pytest tools/test/test_sync_live.py -k baidu_qianfan -vVerified this passes with a real Qianfan key.
ernie-4.5-turbo-128k.ROCKETRIDE_BAIDU_QIANFAN_KEY=*** PYTHONPATH=tools/src uv run --with json5 --with openai python tools/src/sync_models.py --provider llm_baidu_qianfan --model-source providerVerified dry run reports no changes.
Local Environment Note
./builder nodes:test-contractswas not runnable in this local environment because the defaultpnpmis9.15.9, while this repository requirespnpm >=10.0.0. Direct pytest contract coverage for the new node passed.Security
Summary by CodeRabbit
New Features
Documentation