Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 15 additions & 34 deletions api/answer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1282,16 +1282,18 @@ def check_llm_connection(self) -> bool:
with self._lock:
logger.info(f'正在检查 {self.name} 连接...')
try:
# 初始化客户端
if self.http_proxy:
httpx_client = httpx.Client(proxy=self.http_proxy)
client = OpenAI(http_client=httpx_client, base_url=self.endpoint, api_key=self.key)
else:
client = OpenAI(base_url=self.endpoint, api_key=self.key)

# 发送一个简单的测试请求
# 限流等待
self._wait_for_interval()
self.last_request_time = time.time()

# 发送测试请求
completion = client.chat.completions.create(**self._completion_kwargs(
model=self.model,
messages=[
Expand All @@ -1300,43 +1302,22 @@ def check_llm_connection(self) -> bool:
'content': '你好,请回答:1+1 等于几?只回答数字。'
}
],
max_tokens=64
max_tokens=200 # 增大以支持可能返回的 reasoning_content
))

if completion.choices and completion.choices[0].message.content:
logger.info(f'{self.name} 连接检查成功')
return True
else:
logger.error(f'{self.name} 连接检查失败:未收到响应')
return False
# 统一检查响应
if completion.choices:
msg = completion.choices[0].message
if msg.content or getattr(msg, 'reasoning_content', None):
logger.info(f'{self.name} 连接检查成功')
return True
Comment on lines +1308 to +1313

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files:"
git ls-files | rg '(^|/)api/answer\.py$|(^|/)main\.py$' || true

echo
echo "api/answer.py relevant snippets:"
sed -n '1240,1290p' api/answer.py || true
sed -n '1298,1320p' api/answer.py || true

echo
echo "main.py relevant snippets:"
sed -n '200,245p' main.py || true

echo
echo "Search for _query_locked and connection check usages:"
rg -n "_query_locked|连接检查|reason" -S api/answer.py main.py || true

echo
echo "Git diff summary/name-only:"
git diff --stat || true
git diff -- api/answer.py main.py | sed -n '1,220p' || true

Repository: Samueli924/chaoxing

Length of output: 6218


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Behavioral probe for the string-validation logic: parse_answer_like logic from _query_locked
# and the connection-check condition as written, over a small sample of response variants.
python3 - <<'PY'
from pathlib import Path
import re

answer_py = Path('api/answer.py').read_text()
m = re.search(r'_query_locked\(.*?\):(?:\n\s+.*)*?(?=\n    def \w|\Z)', answer_py, re.S)
print("FOUND_QUERY_LOCKED=", m is not None)
if m:
    snippet = m.group(0)
    print(snippet)
    print("contains_reasoning_in_parse=", bool(re.search(r'reasoning_content', snippet, re.I)))
    print("contains_text_content=", bool(re.search(r'\.content\b', snippet, re.I)))
else:
    print(answer_py[1240*80:1270*80])

conn = re.search(r'if \s+completion\.choices:\s*\n((?:\s+.+\n)+)', answer_py)
if conn:
    print("FOUND_CONN_CHECK=true")
    print(conn.group(0))
PY

Repository: Samueli924/chaoxing

Length of output: 27070


🌐 Web query:

OpenAI Python ChatCompletionMessage reasoning_content attribute Content

💡 Result:

In the standard official OpenAI Python SDK, the ChatCompletionMessage object does not include a native reasoning_content attribute [1]. While some third-party model providers (such as DeepSeek or Grok) and local inference servers like vLLM include a reasoning_content field in their API responses to expose "thinking" or "reasoning" tokens, OpenAI has explicitly declined to incorporate this field as a standard part of their Python SDK's ChatCompletionMessage object [2][3][4][5]. Key technical points: 1. SDK Absence: The official openai-python library's ChatCompletionMessage class definition does not define reasoning_content [1]. Attempts to introduce this field into the official SDK via pull requests have been declined [2]. 2. Third-Party Usage: You may encounter this field when using the OpenAI Python client to communicate with third-party APIs that follow the OpenAI Chat Completions API format but extend it to include reasoning tokens [4][5]. In these scenarios, developers often access the field dynamically (e.g., via getattr or casting) because the SDK's type-hinting will not recognize it [6][4]. 3. OpenAI's Reasoning Approach: OpenAI handles reasoning models via their own specific APIs and architectural patterns (e.g., the Responses API or specific reasoning contexts) rather than by exposing raw reasoning tokens in the standard ChatCompletionMessage structure [7]. If you are working with a third-party service that returns reasoning_content, you can access it by treating the response message as a dictionary or using dynamic attribute access, but you should not expect it to be a permanent or supported feature of the official OpenAI Python SDK [6][4].

Citations:


Keep the AI provider startup check aligned with the real answer parsing.

check_llm_connection() returns success when only reasoning_content is present, because AI._query_locked() continues to parse completion.choices[0].message.content. A reasoning-only response can clear startup validation but fail as None when real questions are answered. Require usable answer content in the check, or parse reasoning_content in _query_locked() the same way.

🤖 Prompt for 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.

In `@api/answer.py` around lines 1308 - 1313, Align check_llm_connection() with
AI._query_locked() by requiring non-empty message.content before reporting a
successful provider connection; do not treat reasoning_content alone as
sufficient unless _query_locked() is also updated to use it as the answer
content.


# 发送一个简单的测试请求
self._wait_for_interval()
self.last_request_time = time.time()
completion = client.chat.completions.create(**self._completion_kwargs(
model=self.model,
messages=[
{
'role': 'user',
'content': '你好,请回答:1+1 等于几?只回答数字。'
}
],
max_tokens=200
))
logger.error(f'{self.name} 连接检查失败:未收到响应')
return False

if completion.choices:
msg = completion.choices[0].message
has_content = bool(msg.content)
has_reasoning = bool(getattr(msg, 'reasoning_content', None))
if has_content or has_reasoning:
logger.info(f'{self.name} 连接检查成功')
return True
logger.error(f'{self.name} 连接检查失败:未收到响应')
return False

except Exception as e:
logger.error(f'{self.name} 连接检查失败:{e}')
return False
except Exception as e:
logger.error(f'{self.name} 连接检查失败:{e}')
return False

class SiliconFlow(Tiku):

Expand Down
2 changes: 1 addition & 1 deletion api/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -936,7 +936,7 @@ def fetch_response_with_retry():
questions = decode_questions_info(_resp.text)

if _resp.status_code == 200 and questions.get("questions"):
return (_resp, questions)
return _resp, questions

logger.warning(
f"无效响应 (Code: {getattr(_resp, 'status_code', 'Unknown')}), 重试中...")
Expand Down
6 changes: 3 additions & 3 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,10 @@ def build_config_from_args(args):
"username": args.username,
"password": args.password,
"course_list": [item.strip() for item in args.list.split(",") if item.strip()] if args.list else None,
"speed": args.speed if args.speed else 1.0,
"speed": args.speed or 1.0,
"jobs": args.jobs,
"notopen_action": args.notopen_action if args.notopen_action else "retry",
"retry_interval": args.retry_interval if args.retry_interval else 1.0
"notopen_action": args.notopen_action or "retry",
"retry_interval": args.retry_interval or 1.0,
"add_learning_count": args.add_learning_count,
"target_count": args.target_count,
}
Expand Down