{"record":{"id":"4edae61431acfb90","repo":"binary-husky/gpt_academic","slug":"error-4edae6","errorCode":null,"errorMessage":"用户取消了程序。","messagePattern":"用户取消了程序。","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"request_llms/bridge_chatgpt.py","lineNumber":214,"sourceCode":"        if len(delta) == 0:\n            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的内部调优参数","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/binary-husky/gpt_academic/blob/d6bde0fa54373309bd05823a49bda8da019d2c77/request_llms/bridge_chatgpt.py#L196-L232","documentation":"RuntimeError ('用户取消了程序。') raised by the watchdog in the ChatGPT streaming reader. observe_window[1] holds a timestamp that the Gradio UI refreshes on every tick; if it ages past watch_dog_patience (5 s) while delta content is still arriving, the bridge concludes the user pressed stop (or the UI thread died) and aborts the stream. It is a deliberate cooperative-cancellation check evaluated only when new content chunks arrive.","triggerScenarios":"User clicks the stop button in the gpt_academic UI during generation; the UI thread stops updating observe_window[1] (browser tab suspended, Gradio queue stalled); the caller passes observe_window with a stale [1] timestamp and never refreshes it while content streams.","commonSituations":"Intentional stop mid-answer; mobile browser backgrounding the tab freezes the Gradio polling; long generations on slow relays combined with an unresponsive UI thread; plugin code copying the observe_window pattern but forgetting the feeding thread.","solutions":["If you stopped generation deliberately, no fix is needed — re-ask the question.","Keep the gpt_academic browser tab in the foreground and responsive during generation so the UI keeps feeding the watchdog.","Custom callers must run a loop setting observe_window[1] = time.time() at least every few seconds for the whole stream.","For systematically slow setups, raise watch_dog_patience in request_llms/bridge_chatgpt.py."],"exampleFix":"# caller-side feeding loop\nimport threading, time\n\ndef feed_watchdog(observe_window, stop_event):\n    while not stop_event.is_set():\n        observe_window[1] = time.time()\n        time.sleep(1)","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    reply = predict_no_ui_long_connection(inputs, llm_kwargs, history, sys_prompt, observe_window=win)\nexcept RuntimeError as e:\n    if e.args[0] == '用户取消了程序。' and user_pressed_stop:\n        acknowledge_cancel()\n    else:\n        raise  # watchdog fired but nobody cancelled -> UI feeding problem","preventionTips":["Maintain the feeder thread updating observe_window[1] for the entire stream lifetime.","Join/stop the feeder thread when the request ends so it can't mask later cancellations.","Keep the Gradio tab foregrounded during long generations.","Correlate watchdog aborts with your own cancel flag to distinguish user stops from UI stalls."],"tags":["watchdog","cancellation","streaming","openai","ui"],"backgroundTag":null,"analyzedSha":"d6bde0fa54373309bd05823a49bda8da019d2c77","analyzedAt":"2026-08-14T22:48:35.038Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}