Client or integration
Codex App의 qwen_review 서브에이전트
Provider or upstream service
Modal OpenAI-compatible Chat Completions endpoint, SGLang backend, model family Qwen/Qwen3.8-2.4T-A95B
공통 Modal gateway에서 endpoint hostname을 model ID로 쓰는 경로와 전용 endpoint의 /v1에서 정식 모델명을 쓰는 경로 모두 같은 스트림 형태를 반환했습니다. hostname-as-model 자체는 원인이 아닙니다.
OpenCodex version
2.13.0: 동일한 후속 청크의 null 값을 허용하여 정상 동작
2.14.0 preview/stable: 회귀 시작
2.17.0: 실제 서브에이전트 호출에서 재현
2.19.0: 최신 소스에도 같은 엄격 검사 조건이 남아 있음
Endpoint or capability
OpenAI-compatible /v1/chat/completions, streaming tool calls
stream: false 응답에서는 id와 function.name이 유효한 문자열이며 정상 동작합니다.
Current behaviour
SGLang 스트림의 첫 tool-call delta에는 유효한 id와 function.name이 있습니다. 이후 arguments continuation delta에서는 이미 보낸 필드를 null로 보냅니다.
OpenCodex 2.14.0 이후 parser는 후속 청크의 null을 “문자열이 아닌 값”으로 판단하여 전체 도구 호출을 무효화합니다. Qwen 서브에이전트는 첫 도구 호출을 실행하기 전에 종료되어 “응답 없음”처럼 보입니다.
Expected behaviour
- continuation delta의
id: null, function.name: null, function.arguments: null은 “이번 delta에는 새 값이 없음”으로 취급해야 합니다.
- 첫 청크에서 받은 기존 값은 유지해야 합니다.
- 스트림 종료 시점까지 function name을 한 번도 받지 못한 호출은 기존처럼 거부해야 합니다. fail-closed 최종 검증은 유지해야 합니다.
Minimal redacted request or reproduction
# 첫 tool-call delta
data: {"choices":[{"delta":{"tool_calls":[{"index":0,"id":"call_redacted","function":{"name":"exec","arguments":""}}]}}]}
# arguments continuation delta
data: {"choices":[{"delta":{"tool_calls":[{"index":0,"id":null,"function":{"name":null,"arguments":"...chunk..."}}]}}]}
# 비교 절차
1. 위 스트림을 OpenCodex 2.13.0과 2.14.0 이상에 입력
2. 동일한 tool call이 실행되는지 확인
3. stream=false 응답과 비교
Actual response or error
stream disconnected before completion:
Provider stream error: upstream response contained invalid tool calls
OpenCodex 2.13.0: tool call 실행 및 최종 응답 성공
OpenCodex 2.17.0: invalid tool calls로 즉시 종료
reasoning effort=max: 동일 실패
reasoning effort=xhigh: 6/6 동일 실패
stream=false: 유효한 string 필드로 성공
reasoning effort나 Qwen 모델의 도구 선택 문제가 아니라 streaming parser 호환성 문제로 보입니다.
Upstream documentation
Suggested mapping or implementation notes
회귀를 도입한 것으로 보이는 커밋:
2.19.0에도 남아 있는 검사:
rawName !== undefined && typeof rawName !== "string"
rawArguments !== undefined && typeof rawArguments !== "string"
tc.id !== undefined && typeof tc.id !== "string"
중간 delta에서 null을 unset과 동일하게 처리하도록 다음처럼 바꾸고, 스트림 종료 후 name 존재 검증은 유지하는 방법을 제안합니다.
rawName != null && typeof rawName !== "string"
rawArguments != null && typeof rawArguments !== "string"
tc.id != null && typeof tc.id !== "string"
회귀 테스트:
- 첫 delta에 유효한 name/id, 후속 delta에 null → 성공
- 모든 delta에서 name이 null/누락 → 기존처럼 실패
Additional context and attachments
Checks
Client or integration
Codex App의
qwen_review서브에이전트Provider or upstream service
Modal OpenAI-compatible Chat Completions endpoint, SGLang backend, model family
Qwen/Qwen3.8-2.4T-A95B공통 Modal gateway에서 endpoint hostname을 model ID로 쓰는 경로와 전용 endpoint의
/v1에서 정식 모델명을 쓰는 경로 모두 같은 스트림 형태를 반환했습니다. hostname-as-model 자체는 원인이 아닙니다.OpenCodex version
2.13.0: 동일한 후속 청크의null값을 허용하여 정상 동작2.14.0preview/stable: 회귀 시작2.17.0: 실제 서브에이전트 호출에서 재현2.19.0: 최신 소스에도 같은 엄격 검사 조건이 남아 있음Endpoint or capability
OpenAI-compatible
/v1/chat/completions, streaming tool callsstream: false응답에서는id와function.name이 유효한 문자열이며 정상 동작합니다.Current behaviour
SGLang 스트림의 첫 tool-call delta에는 유효한
id와function.name이 있습니다. 이후 arguments continuation delta에서는 이미 보낸 필드를null로 보냅니다.OpenCodex 2.14.0 이후 parser는 후속 청크의
null을 “문자열이 아닌 값”으로 판단하여 전체 도구 호출을 무효화합니다. Qwen 서브에이전트는 첫 도구 호출을 실행하기 전에 종료되어 “응답 없음”처럼 보입니다.Expected behaviour
id: null,function.name: null,function.arguments: null은 “이번 delta에는 새 값이 없음”으로 취급해야 합니다.Minimal redacted request or reproduction
Actual response or error
reasoning effort나 Qwen 모델의 도구 선택 문제가 아니라 streaming parser 호환성 문제로 보입니다.
Upstream documentation
https://developers.openai.com/api/docs/guides/function-calling#streaming
Suggested mapping or implementation notes
회귀를 도입한 것으로 보이는 커밋:
2.19.0에도 남아 있는 검사:
중간 delta에서 null을 unset과 동일하게 처리하도록 다음처럼 바꾸고, 스트림 종료 후 name 존재 검증은 유지하는 방법을 제안합니다.
회귀 테스트:
Additional context and attachments
id:null/name:nullcontinuation으로 발생한 버전 회귀를 특정하지 않습니다.Checks