Skip to content

fix(perplexity): fix streaming errors and silent maxOutputTokens drop#3780

Open
rhawk301 wants to merge 2 commits into
chatboxai:mainfrom
rhawk301:main
Open

fix(perplexity): fix streaming errors and silent maxOutputTokens drop#3780
rhawk301 wants to merge 2 commits into
chatboxai:mainfrom
rhawk301:main

Conversation

@rhawk301

@rhawk301 rhawk301 commented Jul 3, 2026

Copy link
Copy Markdown

Summary

Fixes two independent bugs that break the Perplexity provider in Chatbox 1.21.1.

Bug 1 — getCallSettings() not overridden (maxOutputTokens silently dropped)

File: src/shared/providers/definitions/models/perplexity.ts

The Perplexity class extends AbstractAISDKModel but does not override getCallSettings(). Every other provider (Azure, Claude, DeepSeek, Gemini, Groq…) overrides this to forward temperature, topP, and maxOutputTokens to the AI SDK. Perplexity doesn't — they're silently dropped on every request.

Effect: user-configured maxTokens is always ignored. Perplexity's API now enforces max_tokens >= 16; a naive fix without a floor would cause HTTP 400 for any value 1–15.

Fix:

protected getCallSettings(_options: CallChatCompletionOptions): CallSettings {
  return {
    temperature: this.options.temperature,
    topP: this.options.topP,
    maxOutputTokens: this.options.maxOutputTokens != null
      ? Math.max(16, this.options.maxOutputTokens)
      : undefined,
  }
}

Also: LazyNumberInput for Perplexity's maxTokens in SessionSettings.tsx now has min={16} (was min={0}) to prevent UI entry of invalid values.


Bug 2 — stale @ai-sdk/perplexity with broken streaming schema

Chatbox bundles @ai-sdk/perplexity@3.0.17 which has a bug in perplexityChunkSchema:

// 3.0.17 (BROKEN) — role required on every chunk
delta: object({ role: literal("assistant"), content: string() })

Perplexity's streaming API only sends role in the first and last SSE chunks. Middle chunks omit role, failing zod-mini literal validation with:

AI_TypeValidationError: Invalid input: expected "assistant"
at choices[0].delta.role

This error fires even when the API call succeeds — the streaming parser fails on every non-trivial response.

Fix: bump @ai-sdk/perplexity to >=3.0.42 <4.0.0.

Note on <4.0.0: 4.x introduces LanguageModelV4, which is incompatible with the LanguageModelV3 interface used throughout this codebase (abstract-ai-sdk.ts).

Lockfile note: @ai-sdk/perplexity@3.0.42 was published 2026-07-02. pnpm's minimumReleaseAge: 10080 (7 days, in pnpm-workspace.yaml) means pnpm install will not pull it until 2026-07-09. Until then, maintainers can temporarily add @ai-sdk/perplexity to minimumReleaseAgeExclude in pnpm-workspace.yaml, or use the patch script below.


Immediate workaround (no rebuild required)

scripts/fix-perplexity-streaming.py applies the same schema fix directly to the installed app.asar using binary surgery — no Node, npm, or build tools needed:

# Check status
python3 scripts/fix-perplexity-streaming.py --status

# Apply fix (creates timestamped backup, re-signs app bundle)
python3 scripts/fix-perplexity-streaming.py

# Restore original if needed
python3 scripts/fix-perplexity-streaming.py --restore

Requires Python 3.8+ and macOS codesign. Patches both the main-process and renderer-process bundles (each ships its own copy of @ai-sdk/perplexity). Finds the renderer's vendor-ai.*.js by glob so it works across builds with different content-hash suffixes.


Test plan

  • pnpm exec vitest run src/shared/providers/definitions/models/perplexity.test.ts — 6 tests pass
  • pnpm test — full unit suite passes
  • Configure Perplexity provider → send a message → streaming response completes without AI_TypeValidationError
  • Set Perplexity maxTokens ≥ 16 → verify it is respected in the request
  • Set Perplexity maxTokens < 16 in UI → verify input is clamped to 16

Maintainer verification prompt

Paste into Claude (or any LLM) for an independent review of this PR:

Review this pull request to ChatBoxAI/ChatBox and verify the following:

CONTEXT:
- Chatbox is an Electron app using the Vercel AI SDK
- getCallSettings() is called by AbstractAISDKModel to build streamText() options
- @ai-sdk/perplexity runs in both the main (Node) and renderer (Chromium) processes
- Perplexity's streaming API sends delta.role ONLY in the first and last SSE chunks

CHANGES TO VERIFY:

1. src/shared/providers/definitions/models/perplexity.ts
   - Does getCallSettings() correctly forward temperature, topP, maxOutputTokens?
   - Is Math.max(16, maxOutputTokens) the right floor? (Perplexity API returns HTTP 400 for max_tokens < 16)
   - Is returning undefined when maxOutputTokens is null/undefined correct? (API uses its own default when field is omitted)

2. src/shared/providers/definitions/models/perplexity.test.ts
   - Do the 6 unit tests cover the meaningful edge cases?
   - Is the mock of @ai-sdk/perplexity correct for this test pattern?

3. src/renderer/modals/SessionSettings.tsx
   - min changed from 0 to: settings?.provider === ModelProviderEnum.Perplexity ? 16 : 0
   - Is this the right place for a provider-specific UI constraint in this codebase?

4. package.json
   - @ai-sdk/perplexity bumped from ^3.0.3 to >=3.0.42 <4.0.0
   - Is <4.0.0 correct? (4.x uses LanguageModelV4, incompatible with LanguageModelV3 in abstract-ai-sdk.ts)

5. scripts/fix-perplexity-streaming.py
   - Patches dist/main/main.js and dist/renderer/js/vendor-ai.*.js (glob for hash-suffixed filename)
   - Uses struct.pack('<IIII', 4, padded_len+8, padded_len+4, json_len_raw) for asar Chromium Pickle preamble
   - Runs codesign --force --deep --sign - after patching (required on macOS or app is silently killed)
   - Is the backup-before-patch + verify-before-install flow safe for users?

Description

Fixes two bugs in the Perplexity provider: missing getCallSettings() override causing silent parameter drops, and a stale @ai-sdk/perplexity version with a broken streaming schema.

Additional Notes

Both bugs were diagnosed against a live Perplexity API. Bug 2 affects every streaming response — the error fires even when the model answers correctly, because the streaming parser fails on middle chunks that omit delta.role.

Contributor Agreement

  • I agree to contribute all code submitted in this PR to the open-source community edition licensed under GPLv3 and the proprietary official edition without compensation.
  • I grant the official edition development team the rights to freely use, modify, and distribute this code, including for commercial purposes.
  • I confirm that this code is my original work.
  • I understand that the submitted code will be publicly released under the GPLv3 license.

Sean Ackley and others added 2 commits July 2, 2026 20:46
Perplexity class did not override getCallSettings(), so temperature, topP,
and maxOutputTokens were silently dropped from every request. Every other
provider (Claude, DeepSeek, Gemini, Groq…) overrides this method.

Also: Perplexity's API now enforces max_tokens >= 16. Apply Math.max(16, …)
so a user-configured value below that never triggers HTTP 400.

UI: LazyNumberInput for Perplexity's maxTokens now has min=16 (was 0).

Package: bump @ai-sdk/perplexity to >=3.0.42 <4.0.0. The 3.x series up to
3.0.41 shipped a streaming-schema bug where delta.role was required on every
SSE chunk; Perplexity's API only sends role in the first and last chunks,
causing AI_TypeValidationError on all middle chunks. 3.0.42 fixes this by
making role optional.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
scripts/fix-perplexity-streaming.py patches the installed Chatbox app.asar
directly so users can fix the streaming error today, without waiting for a
source build or official release.

The script:
- Requires only Python 3.8+ and macOS codesign (no Node.js, no npm)
- Auto-detects Chatbox.app in /Applications or ~/Applications
- Accepts --app PATH override for non-standard installs
- Patches both main-process (dist/main/main.js) and renderer-process
  (dist/renderer/js/vendor-ai.*.js) bundles — each ships its own copy
  of @ai-sdk/perplexity, both need the delta.role optional fix
- Finds the renderer file by glob (the hash suffix changes each build)
- Creates a timestamped backup before any change
- Re-signs the app bundle with an ad-hoc signature (required on macOS)
- Verifies all patches applied before committing the new asar to disk
- --status, --restore, --dry-run, --force flags

Bug patched: @ai-sdk/perplexity <=3.0.41 validates delta.role as required
on every SSE chunk. Perplexity's API only sends role on the first and last
chunks. Source fix is upgrading to >=3.0.42 (previous commit), but that
requires a pnpm-workspace minimumReleaseAge of 7 days (until 2026-07-09).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c605906e-a1b7-4233-9dae-a9283c4db667

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@themez themez left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

核心修复方向已经验证:补齐 getCallSettings() 是必要的,升级后的 Perplexity SDK 也确实修复了流式 chunk schema;补生成 lockfile 后,新增的 6 个定向测试可以通过。不过当前 PR 仍有安装阻断、热补丁失败处理和格式问题,另外 max_tokens 下限与当前官方文档不一致,请处理下列评论后再合并。

Comment thread package.json
"@ai-sdk/openai": "^3.0.53",
"@ai-sdk/openai-compatible": "^2.0.3",
"@ai-sdk/perplexity": "^3.0.3",
"@ai-sdk/perplexity": ">=3.0.42 <4.0.0",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] 请同步提交 pnpm-lock.yaml 当前 PR 只修改了 manifest,lockfile 仍记录 ^3.0.3 / 3.0.17。我在该 HEAD 上使用 Node 22、pnpm 10.33 运行 pnpm install --frozen-lockfile --ignore-scripts,会直接报 ERR_PNPM_OUTDATED_LOCKFILE;CI 环境默认使用 frozen lockfile,因此当前提交无法完成依赖安装。

finally:
tmp_path.unlink(missing_ok=True)

sign_app(app)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 签名失败不能继续报告成功。 sign_app()codesign 缺失或执行失败时返回 False,但这里忽略结果,随后仍输出 Done 并返回成功;脚本自己的说明也指出这种状态可能导致应用无法启动。至少应将签名失败视为硬错误并恢复刚创建的备份。现在 3.0.42 已超过 minimumReleaseAge 等待期,更建议删除这整个临时 asar 热补丁,只保留源码依赖升级。

const baseModel: ProviderModelInfo = { modelId: 'sonar-pro' }
const emptyOptions: CallChatCompletionOptions = {}

function makeModel(opts: {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 新增测试目前未通过 Biome 格式检查。 定向运行 pnpm exec biome check .../perplexity.test.ts 会在这里退出非零;formatter 会把参数类型整理为单行。请运行 pnpm format 并提交格式化结果。

return {
temperature: this.options.temperature,
topP: this.options.topP,
// Perplexity API rejects max_tokens < 16 with HTTP 400.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里的 16 下限需要补充可复现证据。当前 Perplexity 官方 Sonar API 文档给出的 max_tokens 范围是 0 < x <= 128000,与“1–15 必然返回 400”冲突:https://docs.perplexity.ai/api-reference/sonar-post 。请提供当前模型、请求和错误响应的复现;如果这是模型特有限制,应明确限定,否则不应静默把文档允许的用户值改写为 16。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants