{"record":{"id":"474cc5e0f205695f","repo":"binary-husky/gpt_academic","slug":"json","errorCode":null,"errorMessage":"意外Json结构：","messagePattern":"意外Json结构：","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"request_llms/bridge_chatgpt.py","lineNumber":215,"sourceCode":"            is_termination_certain = False\n            if (has_choices) and (chunkjson['choices'][0].get('finish_reason', 'null') == 'stop'): is_termination_certain = True\n            if is_termination_certain: break\n            else: continue # 对于不符合规范的狗屎接口，这里需要继续\n\n        if (not has_content) and has_role: continue\n        if (not has_content) and (not has_role): continue # raise RuntimeError(\"发现不标准的第三方接口：\"+delta)\n        if has_content: # has_role = True/False\n            result += delta[\"content\"]\n            if not console_silence: print(delta[\"content\"], end='')\n            if observe_window is not None:\n                # 观测窗，把已经获取的数据显示出去\n                if len(observe_window) >= 1:\n                    observe_window[0] += delta[\"content\"]\n                # 看门狗，如果超过期限没有喂狗，则终止\n                if len(observe_window) >= 2:\n                    if (time.time()-observe_window[1]) > watch_dog_patience:\n                        raise RuntimeError(\"用户取消了程序。\")\n        else: raise RuntimeError(\"意外Json结构：\"+delta)\n\n    finish_reason = json_data.get('finish_reason', None) if json_data else None\n    if finish_reason == 'content_filter':\n        raise RuntimeError(\"由于提问含不合规内容被过滤。\")\n    if finish_reason == 'length':\n        raise ConnectionAbortedError(\"正常结束，但显示Token不足，导致输出不完整，请削减单次输入的文本量。\")\n\n    return result\n\n\ndef predict(inputs:str, llm_kwargs:dict, plugin_kwargs:dict, chatbot:ChatBotWithCookies,\n            history:list=[], system_prompt:str='', stream:bool=True, additional_fn:str=None):\n    \"\"\"\n    发送至chatGPT，流式获取输出。\n    用于基础的对话功能。\n    inputs 是本次问询的输入\n    top_p, temperature是chatGPT的内部调优参数\n    history 是之前的对话列表（注意无论是inputs还是history，内容太长了都会触发token数量溢出的错误）","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/binary-husky/gpt_academic/blob/d6bde0fa54373309bd05823a49bda8da019d2c77/request_llms/bridge_chatgpt.py#L197-L233","documentation":"RuntimeError ('意外Json结构：' + delta) raised when a streamed delta object parses as JSON, has no 'content' key, and also no 'role' key — i.e. the chunk's delta is neither content nor a role header, which contradicts the OpenAI chat-completions delta schema this bridge implements. The delta payload is appended to the message so the deviant structure is visible. It typically indicates a non-conformant third-party relay injecting extra delta fields.","triggerScenarios":"A relay/one-api channel forwards deltas containing only unexpected keys (e.g. {'reasoning_content': ...} without 'content', tool-call deltas, or vendor-specific fields) so decode_chunk sets has_content=False and has_role=False; the else branch then fires. Only reached when the chunk DID parse as choices-bearing JSON, so it is a schema mismatch, not a transport error.","commonSituations":"Third-party OpenAI-compatible gateways that add fields like reasoning_content, function_call, or empty deltas; newer model APIs (tool calling, o1-style reasoning) streamed through bridges that predate those delta types; partially-conforming one-api channels.","solutions":["Inspect the printed delta to identify the stray field; if it's reasoning_content or tool-call data, update gpt_academic to a version whose decode_chunk tolerates it.","Switch to the official OpenAI/Azure endpoint to confirm the relay is the source of the malformed delta.","If you control the bridge, extend decode_chunk to accept the extra delta shapes instead of raising (the commented-out line above shows raise was once relaxed).","Disable the non-standard feature (e.g. tool/reasoning mode) on the relay channel serving your key."],"exampleFix":"# before\nelse: raise RuntimeError(\"意外Json结构：\"+delta)\n\n# after (tolerate known extra keys)\nelse:\n    if any(k in delta for k in (\"reasoning_content\", \"tool_calls\", \"function_call\")):\n        continue\n    raise RuntimeError(\"意外Json结构：\" + str(delta))","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"def is_standard_chat_delta(delta: dict) -> bool:\n    \"\"\"OpenAI chat-completions deltas carry content or role; anything else is non-standard.\"\"\"\n    return isinstance(delta, dict) and ('content' in delta or 'role' in delta)","tryCatchPattern":"try:\n    reply = predict_no_ui_long_connection(...)\nexcept RuntimeError as e:\n    if '意外Json结构' in str(e):\n        log_nonstandard_delta(str(e))  # inspect which key the relay added\n        switch_to_official_endpoint()\n    else:\n        raise","preventionTips":["Prefer official OpenAI/Azure endpoints when delta-schema strictness matters.","Pin relay channels that pass OpenAI's delta schema unchanged; test new channels with a smoke chat before relying on them.","Upgrade gpt_academic when adopting tool-call or reasoning models so decode_chunk knows their delta shapes.","Log the offending delta verbatim — it identifies the misbehaving hop immediately."],"tags":["openai","json","streaming","relay","schema-mismatch"],"backgroundTag":null,"analyzedSha":"d6bde0fa54373309bd05823a49bda8da019d2c77","analyzedAt":"2026-08-14T22:48:35.038Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}