sanbo110 Claude commited on
Commit
d7c05c2
·
1 Parent(s): f78578c

fix(zai_provider): 增强错误处理和调试日志,定位非流式响应处理失败

Browse files

- 在 chat_completion 中添加详细的请求/响应调试日志
- 捕获 transform_response 异常并记录上游响应内容预览
- 在 _handle_non_stream_response 中添加数据行跟踪机制
- 记录所有原始响应行,便于分析响应格式不匹配问题
- 添加内容提取结果的警告日志(当内容为空但收到数据时)

这些修改将帮助我们精确定位 Z.AI API 返回的响应格式,找出为什么
非流式请求在获取到200响应后仍然处理失败的根本原因。

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

Files changed (2) hide show
  1. CLAUDE.md +411 -0
  2. app/providers/zai_provider.py +37 -6
CLAUDE.md ADDED
@@ -0,0 +1,411 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with this repository.
4
+
5
+ ## Project Overview
6
+
7
+ This is an **OpenAI-compatible API proxy server** written in Python/FastAPI that provides unified access to multiple AI providers:
8
+ - **Z.AI** (GLM-4.5, GLM-4.6V, GLM-4.7 series)
9
+ - **K2Think** (MBZUAI-IFM/K2-Think)
10
+ - **LongCat** (LongCat-Flash, LongCat-Search)
11
+
12
+ The server implements the OpenAI API specification (`/v1/chat/completions`, `/v1/models`) and includes token pool management, provider routing, admin panel, and Docker deployment support.
13
+
14
+ ## High-Level Architecture
15
+
16
+ ### Core Components
17
+
18
+ ```
19
+ ┌─────────────────────────────────────────────────────────────┐
20
+ │ FastAPI Application │
21
+ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
22
+ │ │ OpenAI Router│ │ Admin Routes │ │ Static Files │ │
23
+ │ └──────┬───────┘ └──────┬───────┘ └──────────────────┘ │
24
+ │ │ │ │
25
+ │ └────────┬────────┘ │
26
+ │ ▼ │
27
+ │ ┌──────────────────────────────────────────────────┐ │
28
+ │ │ ProviderRouter (ProviderFactory) │ │
29
+ │ │ - Routes requests to appropriate provider │ │
30
+ │ │ - Manages model-to-provider mapping │ │
31
+ │ └────────┬─────────────────────────────────────────┘ │
32
+ │ │ │
33
+ │ ┏━━━━━━━━┼━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ │
34
+ │ ┃ ▼ ┃ │
35
+ │ ┃ ┌──────────────────┐ ┌──────────────────┐ ┃ │
36
+ │ ┃ │ ZAIProvider │ │ K2ThinkProvider │ ┃ │
37
+ │ ┃ │ │ │ │ ┃ │
38
+ │ ┃ │ ┌────────────┐ │ │ ┌────────────┐ │ ┃ │
39
+ │ ┃ │ │ Token Pool │ │ │ │ HTTP Client│ │ ┃ │
40
+ │ ┃ │ └────────────┘ │ │ └────────────┘ │ ┃ │
41
+ │ ┃ └──────────────────┘ └──────────────────┘ ┃ │
42
+ │ ┃ ┃ │
43
+ │ ┃ ┌──────────────────┐ ┃ │
44
+ │ ┃ │ LongCatProvider │ ┃ │
45
+ │ ┃ └──────────────────┘ ┃ │
46
+ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │
47
+ │ │
48
+ │ ┌─────────────────────┐ ┌─────────────────────┐ │
49
+ │ │ Token Database │ │ Logger │ │
50
+ │ │ (SQLite/JSON) │ │ (Loguru) │ │
51
+ │ └─────────────────────┘ └─────────────────────┘ │
52
+ └─────────────────────────────────────────────────────────────┘
53
+ ```
54
+
55
+ ### Request Flow
56
+
57
+ 1. **Client Request** → `app/core/openai.py:chat_completions()`
58
+ 2. **Auth Validation** → Check `AUTH_TOKEN` or skip if `SKIP_AUTH_TOKEN=true`
59
+ 3. **Provider Routing** → `ProviderRouter.route_request()`
60
+ 4. **Provider Selection** → `ProviderFactory.get_provider_for_model()`
61
+ 5. **Provider Processing** → Specific provider handles request:
62
+ - Token pool management (Z.AI)
63
+ - HTTP request formatting
64
+ - Response transformation
65
+ 6. **Response** → OpenAI-formatted streaming or non-streaming response
66
+
67
+ ### Key Modules
68
+
69
+ - **`app/core/`**: Application core, settings, OpenAI API router
70
+ - **`app/providers/`**: Provider implementations (base, ZAI, K2Think, LongCat)
71
+ - **`app/models/`**: Data schemas, token DB models, request logging
72
+ - **`app/services/`**: Token management, database operations
73
+ - **`app/utils/`**: Utilities (logger, token pool, reload config)
74
+ - **`app/admin/`**: Web admin panel and API
75
+ - **`app/templates/`**: Web UI templates
76
+
77
+ ## Common Development Commands
78
+
79
+ ### Local Development
80
+
81
+ ```bash
82
+ # Install dependencies
83
+ pip install -r requirements.txt
84
+
85
+ # Install with uv (recommended for this project)
86
+ uv pip install -r requirements.txt
87
+ uv pip install -e .
88
+
89
+ # Run development server
90
+ python main.py
91
+
92
+ # Run with hot reload (development only)
93
+ # Edit app/utils/reload_config.py to enable granian reload
94
+ ```
95
+
96
+ ### Testing
97
+
98
+ ```bash
99
+ # Run all tests
100
+ pytest
101
+
102
+ # Run specific test file
103
+ pytest tests/test_simple_signature.py
104
+
105
+ # Run with verbose output
106
+ pytest -v
107
+
108
+ # Run single test function
109
+ pytest tests/test_signature.py::test_function_name
110
+ ```
111
+
112
+ ### Code Quality
113
+
114
+ ```bash
115
+ # Lint with ruff
116
+ ruff check app/
117
+
118
+ # Format code
119
+ ruff format app/
120
+
121
+ # Type check (if pyright/mypy installed)
122
+ ```
123
+
124
+ ### Docker Deployment
125
+
126
+ ```bash
127
+ # Build and start
128
+ docker compose up -d
129
+
130
+ # Build with logging
131
+ docker compose up --build
132
+
133
+ # View logs
134
+ docker compose logs -f
135
+
136
+ # Stop services
137
+ docker compose down
138
+
139
+ # Restart specific service
140
+ docker compose restart api-server
141
+ ```
142
+
143
+ ### Database Management
144
+
145
+ ```bash
146
+ # Database is auto-initialized on startup
147
+ # Default location: data/tokens.db (via volume mapping)
148
+
149
+ # View database (if sqlite3 available)
150
+ sqlite3 data/tokens.db ".tables"
151
+ sqlite3 data/tokens.db "SELECT * FROM tokens;"
152
+
153
+ # Backup database
154
+ cp data/tokens.db tokens.db.backup.$(date +%Y%m%d)
155
+ ```
156
+
157
+ ## Configuration
158
+
159
+ ### Environment Variables
160
+
161
+ Create `.env` file based on `.env.example`:
162
+
163
+ ```bash
164
+ cp .env.example .env
165
+ ```
166
+
167
+ **Core Configuration:**
168
+ - **Authentication**: `AUTH_TOKEN`, `ADMIN_PASSWORD`, `SKIP_AUTH_TOKEN`
169
+ - **Server**: `LISTEN_PORT` (default 7860), `ROOT_PATH` (reverse proxy)
170
+ - **Features**: `ANONYMOUS_MODE`, `TOOL_SUPPORT`, `DEBUG_LOGGING`
171
+ - **Token Pool**: `TOKEN_FAILURE_THRESHOLD`, `TOKEN_RECOVERY_TIMEOUT`
172
+ - **Provider**: `DEFAULT_PROVIDER`, `LONGCAT_TOKEN`
173
+ - **Proxy**: `HTTP_PROXY`, `HTTPS_PROXY`, `SOCKS5_PROXY`
174
+
175
+ ### Model Mapping
176
+
177
+ Models are auto-mapped to providers in `app/core/config.py`:
178
+
179
+ ```python
180
+ provider_model_mapping = {
181
+ # Z.AI
182
+ "GLM-4.5": "zai",
183
+ "GLM-4.5-Thinking": "zai",
184
+ "GLM-4.7": "zai",
185
+ # K2Think
186
+ "MBZUAI-IFM/K2-Think": "k2think",
187
+ # LongCat
188
+ "LongCat-Flash": "longcat",
189
+ }
190
+ ```
191
+
192
+ ### Custom Provider Configuration
193
+
194
+ To add a new provider:
195
+
196
+ 1. Create `app/providers/new_provider.py` inheriting `BaseProvider`
197
+ 2. Implement required methods: `chat_completion()`, `transform_request()`, `transform_response()`
198
+ 3. Register in `ProviderFactory.initialize()` at line 36-54
199
+ 4. Add model mapping to config
200
+
201
+ ## API Endpoints
202
+
203
+ ### Chat Completions
204
+ ```
205
+ POST /v1/chat/completions
206
+ POST /hf/v1/chat/completions
207
+ ```
208
+
209
+ **Request (OpenAI format):**
210
+ ```json
211
+ {
212
+ "model": "GLM-4.5",
213
+ "messages": [{"role": "user", "content": "Hello"}],
214
+ "stream": true,
215
+ "tools": [...]
216
+ }
217
+ ```
218
+
219
+ **Response:** OpenAI-compatible streaming or non-streaming format
220
+
221
+ ### Model Listing
222
+ ```
223
+ GET /v1/models
224
+ GET /hf/v1/models
225
+ ```
226
+
227
+ **Response:**
228
+ ```json
229
+ {
230
+ "object": "list",
231
+ "data": [
232
+ {"id": "GLM-4.5", "owned_by": "zai"},
233
+ {"id": "MBZUAI-IFM/K2-Think", "owned_by": "k2think"}
234
+ ]
235
+ }
236
+ ```
237
+
238
+ ### Admin Panel
239
+ - **UI**: `http://localhost:7860/admin`
240
+ - **API**: `http://localhost:7860/admin/api`
241
+ - **Docs**: `http://localhost:7860/docs`
242
+ - **Health Check**: `http://localhost:7860/hf/v1/models`
243
+
244
+ ## Key Features
245
+
246
+ ### Token Pool Management
247
+ - **Automatic failure tracking**: Tokens failing 3+ times are marked unavailable
248
+ - **Recovery mechanism**: Failed tokens recover after 30 minutes
249
+ - **SQLite persistence**: Stored in `data/tokens.db`
250
+ - **Anonymous mode**: Works without tokens (limited functionality)
251
+
252
+ ### Multi-Provider Support
253
+ - **Dynamic routing**: Request automatically routed to correct provider
254
+ - **Unified API**: Single endpoint supports all providers
255
+ - **Extensible**: Easy to add new providers
256
+
257
+ ### Error Handling
258
+ - **Provider errors**: Wrapped in OpenAI-compatible error format
259
+ - **Token failures**: Automatic fallback to healthy tokens
260
+ - **Request validation**: Input validation and type checking
261
+
262
+ ### Monitoring & Logging
263
+ - **Structured logging**: Using Loguru with levels (DEBUG/INFO/WARNING/ERROR)
264
+ - **Request tracking**: All requests logged with model, provider, status
265
+ - **Live reload**: Development mode can auto-reload on code changes
266
+
267
+ ## Testing Providers
268
+
269
+ ### Z.AI (Default)
270
+ ```bash
271
+ curl -X POST http://localhost:7860/v1/chat/completions \
272
+ -H "Authorization: Bearer sk-your-api-key" \
273
+ -H "Content-Type: application/json" \
274
+ -d '{
275
+ "model": "GLM-4.5",
276
+ "messages": [{"role": "user", "content": "Hello"}],
277
+ "stream": true
278
+ }'
279
+ ```
280
+
281
+ ### Without Auth (Anonymous Mode)
282
+ ```bash
283
+ export SKIP_AUTH_TOKEN=true
284
+ export ANONYMOUS_MODE=true
285
+ python main.py
286
+ ```
287
+
288
+ ## Production Deployment
289
+
290
+ ### Security Checklist
291
+ - [ ] Change default `ADMIN_PASSWORD`
292
+ - [ ] Set strong `AUTH_TOKEN`
293
+ - [ ] Disable `DEBUG_LOGGING`
294
+ - [ ] Set `ANONYMOUS_MODE=false` (if requiring auth)
295
+ - [ ] Use HTTPS reverse proxy (Nginx)
296
+ - [ ] Limit network access with firewall rules
297
+
298
+ ### Nginx Configuration
299
+ See `NGINX_SETUP.md` and `nginx.conf.example` for production reverse proxy setup.
300
+
301
+ ### Resource Requirements
302
+ - **CPU**: 1-2 cores recommended
303
+ - **Memory**: 512MB-1GB minimum
304
+ - **Storage**: 100MB for code + database storage
305
+
306
+ ## Troubleshooting
307
+
308
+ ### Common Issues
309
+
310
+ **"unable to open database file"**
311
+ ```bash
312
+ mkdir -p data logs
313
+ chmod 755 data logs
314
+ ```
315
+
316
+ **Auth failures**
317
+ ```bash
318
+ # Check .env file for AUTH_TOKEN mismatch
319
+ echo $AUTH_TOKEN
320
+ # Verify client is sending correct Authorization header
321
+ ```
322
+
323
+ **Provider unavailable**
324
+ ```bash
325
+ # Check logs for provider-specific errors
326
+ docker compose logs -f | grep "ERROR"
327
+ # Verify API credentials for provider
328
+ ```
329
+
330
+ **Port already in use**
331
+ ```bash
332
+ # Change port in docker-compose.yml
333
+ # Or set environment variable
334
+ export LISTEN_PORT=7861
335
+ ```
336
+
337
+ ### Debug Mode
338
+ ```bash
339
+ export DEBUG_LOGGING=true
340
+ python main.py
341
+
342
+ # View real-time logs
343
+ tail -f logs/*.log
344
+ ```
345
+
346
+ ## Adding New Features
347
+
348
+ ### New Provider Implementation Checklist
349
+ 1. Create provider class in `app/providers/`
350
+ 2. Inherit from `BaseProvider`
351
+ 3. Implement:
352
+ - `get_supported_models()` → List[str]
353
+ - `transform_request()` → Dict (API format conversion)
354
+ - `transform_response()` → Dict/Generator (response conversion)
355
+ - `chat_completion()` → Async generator or dict
356
+ 4. Register in `ProviderFactory.initialize()`
357
+ 5. Add models to config mapping
358
+
359
+ ### Adding New API Endpoints
360
+ 1. Create router in `app/core/` or `app/admin/`
361
+ 2. Add endpoint with proper type hints
362
+ 3. Add to main app in `main.py` (include_router)
363
+ 4. Update docs if needed
364
+
365
+ ### Token Pool Customization
366
+ 1. Modify `app/utils/token_pool.py`
367
+ 2. Update token storage logic in `app/services/token_dao.py`
368
+ 3. Adjust failure thresholds in config
369
+
370
+ ## Directory Structure Reference
371
+
372
+ ```
373
+ zai/
374
+ ├── app/
375
+ │ ├── core/ # Core logic & config
376
+ │ ├── providers/ # AI provider implementations
377
+ │ ├── models/ # Schemas & DB models
378
+ │ ├── services/ # Business logic (token mgmt)
379
+ │ ├── utils/ # Helper utilities
380
+ │ ├── admin/ # Admin panel & API
381
+ │ └── templates/ # Web UI templates
382
+ ├── tests/ # Test files
383
+ ├── data/ # Persistent data (SQLite DB)
384
+ ├── logs/ # Application logs
385
+ ├── main.py # Entry point
386
+ ├── docker-compose.yml # Docker setup
387
+ ├── .env.example # Environment template
388
+ └── requirements.txt # Python dependencies
389
+ ```
390
+
391
+ ## Release Checklist
392
+
393
+ Before deploying new version:
394
+ - [ ] Run `pytest` - all tests pass
395
+ - [ ] Run `ruff check` - no linting errors
396
+ - [ ] Update version in `pyproject.toml`
397
+ - [ ] Test all providers (Z.AI, K2Think, LongCat)
398
+ - [ ] Update `.env.example` if new vars added
399
+ - [ ] Build Docker image & test
400
+ - [ ] Update README if API changes
401
+
402
+ ## References
403
+
404
+ - **Project Docs**: `README_DOCKER.md` (deployment)
405
+ - **API Spec**: OpenAI Chat Completions API
406
+ - **FastAPI Docs**: Built-in at `/docs` when running
407
+ - **Docker Hub**: `zyphrzero/z-ai2api-python`
408
+
409
+ ---
410
+
411
+ *This CLAUDE.md is relevant as of 2026-01-15. Last commit: f78578c*
app/providers/zai_provider.py CHANGED
@@ -661,6 +661,8 @@ class ZAIProvider(BaseProvider):
661
  "X-Signature": signature,
662
  }
663
 
 
 
664
  query_params = {
665
  "timestamp": str(timestamp_ms),
666
  "requestId": request_id,
@@ -701,6 +703,7 @@ class ZAIProvider(BaseProvider):
701
  try:
702
  # 转换请求
703
  transformed = await self.transform_request(request)
 
704
 
705
  # 根据请求类型返回响应
706
  if request.stream:
@@ -719,14 +722,28 @@ class ZAIProvider(BaseProvider):
719
  )
720
 
721
  if not response.is_success:
722
- error_msg = f"Z.AI API 错误: {response.status_code}"
 
 
723
  self.log_response(False, error_msg)
724
  return self.handle_error(Exception(error_msg))
725
 
726
- return await self.transform_response(response, request, transformed)
 
 
 
 
 
 
 
 
727
 
728
  except Exception as e:
729
- self.log_response(False, str(e))
 
 
 
 
730
  return self.handle_error(e, "请求处理")
731
 
732
 
@@ -1083,9 +1100,9 @@ class ZAIProvider(BaseProvider):
1083
  yield "data: [DONE]\n\n"
1084
 
1085
  async def _handle_non_stream_response(
1086
- self,
1087
- response: httpx.Response,
1088
- chat_id: str,
1089
  model: str
1090
  ) -> Dict[str, Any]:
1091
  """处理非流式响应
@@ -1102,12 +1119,19 @@ class ZAIProvider(BaseProvider):
1102
  "total_tokens": 0,
1103
  }
1104
 
 
 
 
1105
  try:
1106
  async for line in response.aiter_lines():
1107
  if not line:
1108
  continue
1109
 
1110
  line = line.strip()
 
 
 
 
1111
 
1112
  # 仅处理以 data: 开头的 SSE 行,其余行尝试作为错误/JSON 忽略
1113
  if not line.startswith("data:"):
@@ -1176,6 +1200,13 @@ class ZAIProvider(BaseProvider):
1176
  elif delta_content:
1177
  final_content += delta_content
1178
 
 
 
 
 
 
 
 
1179
  except Exception as e:
1180
  self.logger.error(f"❌ 非流式响应处理错误: {e}")
1181
  import traceback
 
661
  "X-Signature": signature,
662
  }
663
 
664
+ # 关键:从 transform_request 方法返回的数据中,token 同时用于 Authorization header 和 query params
665
+ # 但必须确保 URL 中的 token 与 header 中的 token 一致
666
  query_params = {
667
  "timestamp": str(timestamp_ms),
668
  "requestId": request_id,
 
703
  try:
704
  # 转换请求
705
  transformed = await self.transform_request(request)
706
+ self.logger.debug(f"[chat_completion] 转换后的请求: {transformed['url'][:100]}...")
707
 
708
  # 根据请求类型返回响应
709
  if request.stream:
 
722
  )
723
 
724
  if not response.is_success:
725
+ error_body = response.text[:500] if response.text else "无响应体"
726
+ error_msg = f"Z.AI API 错误: {response.status_code}, 响应: {error_body}"
727
+ self.logger.error(f"❌ 上游响应非成功: {error_msg}")
728
  self.log_response(False, error_msg)
729
  return self.handle_error(Exception(error_msg))
730
 
731
+ # 记录响应状态
732
+ self.logger.info(f"✅ 上游响应成功: {response.status_code}, Content-Length: {response.headers.get('content-length', 'N/A')}")
733
+ try:
734
+ return await self.transform_response(response, request, transformed)
735
+ except Exception as transform_error:
736
+ self.logger.error(f"❌ transform_response 失败: {transform_error}")
737
+ body_text = response.text[:1000] if response.text else "无响应体"
738
+ self.logger.error(f"❌ 上游响应内容预览: {body_text}")
739
+ raise
740
 
741
  except Exception as e:
742
+ error_str = str(e)
743
+ self.logger.error(f"❌ chat_completion 异常捕获: {type(e).__name__}: {error_str}")
744
+ import traceback
745
+ self.logger.error(f"❌ 详细堆栈: {traceback.format_exc()}")
746
+ self.log_response(False, error_str)
747
  return self.handle_error(e, "请求处理")
748
 
749
 
 
1100
  yield "data: [DONE]\n\n"
1101
 
1102
  async def _handle_non_stream_response(
1103
+ self,
1104
+ response: httpx.Response,
1105
+ chat_id: str,
1106
  model: str
1107
  ) -> Dict[str, Any]:
1108
  """处理非流式响应
 
1119
  "total_tokens": 0,
1120
  }
1121
 
1122
+ self.logger.info(f"[_handle_non_stream_response] 开始处理响应,Content-Type: {response.headers.get('content-type', '未知')}")
1123
+
1124
+ all_lines = []
1125
  try:
1126
  async for line in response.aiter_lines():
1127
  if not line:
1128
  continue
1129
 
1130
  line = line.strip()
1131
+ # 收集所有行用于调试
1132
+ if line:
1133
+ self.logger.debug(f"[_handle_non_stream_response] 原始行: {line[:200]}")
1134
+ all_lines.append(line)
1135
 
1136
  # 仅处理以 data: 开头的 SSE 行,其余行尝试作为错误/JSON 忽略
1137
  if not line.startswith("data:"):
 
1200
  elif delta_content:
1201
  final_content += delta_content
1202
 
1203
+ # 循环结束后,记录所有采集的线和内容
1204
+ self.logger.info(f"[_handle_non_stream_response] 处理完成,共 {len(all_lines)} 行数据")
1205
+ self.logger.debug(f"[_handle_non_stream_response] 最终内容长度: {len(final_content)}, 思考长度: {len(reasoning_content)}")
1206
+ if not final_content and not reasoning_content and len(all_lines) > 0:
1207
+ self.logger.warning(f"[_handle_non_stream_response] 警告:未提取到内容,但接收到 {len(all_lines)} 行数据")
1208
+ self.logger.warning(f"[_handle_non_stream_response] 前10行数据: {all_lines[:10]}")
1209
+
1210
  except Exception as e:
1211
  self.logger.error(f"❌ 非流式响应处理错误: {e}")
1212
  import traceback