원클릭으로
debug-sse-refactoring
Debugging SSE streaming failures after refactoring AI provider code, focusing on JSON null-safety and field ordering
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Debugging SSE streaming failures after refactoring AI provider code, focusing on JSON null-safety and field ordering
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
通过 Maven profiles 构建同时支持 javax.servlet-api 和 jakarta.servlet-api 的双版本 JAR,包含构建目录切换、finalName 自定义、依赖 jar 复制重命名
Architecture and implementation patterns for the AI API proxy/gateway module (switchproxy) that converts between OpenAI/Anthropic/Responses API formats, with IP access control, key-based auth, multi-host listening, HTML status page, Web admin UI, and tool config export
emp-script-ai 及其下游项目(travelagent、pf2023)所用 AI Mode XML schema 的撰写参考。覆盖 `<mode>` / `<step>` / `<prompt>` / `<apis>` / `<sqls>` / `<actions>` / `<paramChecks>` / `<ui>` 各块结构、prompt 数据源(sqlRef / api / action / CDATA)、函数调用 prompt(`apisCheck`)、URL / 请求占位符、输出自定义标签(`<day>`、`<rq>`、`<cid>`、`<enj>`、`<id>`、`<num>`、`<sersday>`、`<prices>`、`<gn>`)以及 step 控制属性(`innerCall`、`validateParams`、`multiOnlyUserMsg`、`action`、`actionSqlRef`)。
Setting up integrationTest framework with HSQLDB in-memory database and real AI APIs for emp-script-ai
Compare two SQL Server databases and sync missing tables and columns from source to target using sqlcmd and a Python script.
Procedure for adding a new AI provider to emp-script-ai (Java library wrapping multiple LLM providers via OpenAI-compatible interfaces)
| name | debug-sse-refactoring |
| description | Debugging SSE streaming failures after refactoring AI provider code, focusing on JSON null-safety and field ordering |
| source | auto-skill |
| extracted_at | 2026-06-17T07:21:35.326Z |
When refactoring AI provider implementations (extracting provider-specific logic into shared base classes in request/style/), SSE streaming can silently fail with "no response" symptoms.
The org.json.JSONObject library has a critical difference between getJSONObject() and optJSONObject():
json.has("field") returns true even if the value is nulljson.getJSONObject("field") throws JSONException if the value is nulljson.optJSONObject("field") returns null if the value is null (safe)Example bug pattern:
// BUGGY: Throws exception when usage=null
if (json.has("usage")) {
JSONObject usage = json.getJSONObject("usage"); // ← Throws!
this.setTokensUsage(usage);
}
// FIXED: Safe null handling
JSONObject usageObj = json.optJSONObject("usage");
if (usageObj != null) {
this.setTokensUsage(usageObj);
}
In OpenAI-compatible SSE streams (especially Qwen), every chunk may include "usage": null:
data: {"choices":[{"delta":{"content":"Hello"}}],"usage":null}
If the JSON parsing throws on usage:null:
RST=falsehandleLine checks !json.getBoolean("RST") → skips the chunkfullText stays empty → nothing saved to databaseWhen SSE fails after refactoring:
Systematic directory-level diff: Start by comparing entire source trees to identify all changed files
# Compare two project directories to find all differences
diff -rq old-project/src new-project/src | grep -v ".DS_Store"
This quickly reveals which files changed, including new files (extracted base classes) and deleted files.
Focus on the call chain: Trace the SSE execution path from entry point to lowest level:
AiStreamOrPost.processRequest() → executeRequest() → req.doStream()RequestAIBase.doStream() → handleLine() → extraceJson()Check field processing order: New code may check fields (like usage) before processing choices/delta, while old code processed choices first and ignored null fields
Look for null-safety violations: Search for getJSONObject(), getJSONArray(), getString() on optional fields that might be null
Verify with actual SSE data: Check provider documentation or logs for null fields in streaming chunks
Replace unsafe accessors with optional variants:
getJSONObject() → optJSONObject()getJSONArray() → optJSONArray()getString() → optString() (with default value)Always check for null before using the result.
Verified scope: This bug affected all 8 OpenAI-compatible providers (qwen, deepseek, openai, doubao, grok, tencent, openrouter, openaiCompat) since they all inherit from OpenAiRequestAI. A single fix in the base class resolved the issue for all providers.
When extracting provider-specific JSON parsing into shared base classes:
opt* methods for all optional fields