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
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,12 @@ OpenCodeZenSseEventReader.read(streamResponse.body(), event -> {
```

注意:上述两种 BodyHandler 为演示分类和流式解析的独立示例。实际运行时先检查 HTTP 状态码,403/429 不要进入 SSE 解析。`200 /models` 也不代表推理授权成功。


## 1.3.3: 403 诊断兼容性修复

- `OpenCodeZenUpstreamDiagnostics` 现在也识别实际观测到的 `detected abusive traffic` / `abusive traffic` 文案。与旧版 `Detect Abuse Traffic` 一样归类为 `ABUSE_REJECTED`;它不是可自动重试的错误。
- 添加按真实服务端措辞构造的分类回归测试,以及公开 API 编译契约测试;没有改变请求 Header、工具声明、代理出口或推理逻辑。
- 1.3.2 源码中 `OpenCodeZenRequestFactory.newEphemeralInferenceRequest(URI, ZenModelProtocol, boolean, String, Map)` 和 `OpenCodeZenProtocolPolicy.applyRequiredEnvelope(Map, ZenModelProtocol, boolean)` 都已存在。若只在恢复本地改动后报 `cannot find symbol`,请检查本地测试文件的导入、接收对象类型、参数类型,以及测试使用的依赖/模块 classpath。不能仅由该错误断定上游方法被删除。
- 如需同步此修复到已有本地两文件的相同未提交改动,请先备份、提交或 stash 当前工作区;普通 `git pull` 可能因工作区冲突而中止。不要使用 `git reset --hard` 丢弃用户改动。

2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ plugins {
}

group = "com.sharedlibs"
version = "1.3.2"
version = "1.3.3"

subprojects {
group = rootProject.group
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ public static Outcome classify(int statusCode, byte[] responseBody) {
String message = new String(bytes, 0, length, StandardCharsets.UTF_8)
.toLowerCase(Locale.ROOT);
if (message.contains("detect abuse traffic")
|| message.contains("abuse traffic")) {
|| message.contains("abuse traffic")
|| message.contains("abusive traffic")) {
return Outcome.ABUSE_REJECTED;
}
if (message.contains("freetiererror") || message.contains("free tier")) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.sharedlibs.opencodezen;

import com.sharedlibs.opencodezen.autoconfigure.OpenCodeZenRequestFactory;
import org.junit.jupiter.api.Test;
import java.net.URI;
import java.net.http.HttpRequest;
import java.util.LinkedHashMap;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;

/**
* This test intentionally compiles against public API signatures as a guard
* against consumer API regressions. It performs no network requests.
*/
class OpenCodeZenPublicApiContractTest {
@Test
void methodsReportedMissingAreActuallyExposedByTheCurrentSource() {
OpenCodeZenRequestFactory factory =
new OpenCodeZenRequestFactory(OpenCodeZenProtocolPolicy.USER_AGENT);
HttpRequest request = factory.newEphemeralInferenceRequest(
URI.create(OpenCodeZenProtocolPolicy.DEFAULT_BASE_URL + "/chat/completions"),
ZenModelProtocol.CHAT, true, null,
Map.of("model", "big-pickle"));
assertThat(request.method()).isEqualTo("POST");

Map<String, Object> payload = new LinkedHashMap<>();
payload.put("model", "big-pickle");
OpenCodeZenProtocolPolicy.applyRequiredEnvelope(payload, ZenModelProtocol.CHAT, true);
assertThat(payload).containsEntry("stream", true).containsKey("tools");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,28 @@ void detectsExplicitTrafficRejectionAndNeverRetries() {
assertThat(OpenCodeZenUpstreamDiagnostics.shouldRetryAutomatically(result)).isFalse();
}

@Test
void detectsAbusiveTrafficVariantBeforeGenericFreeTierError() {
// This wording was observed on a rejected network egress, not a JSON schema error.
String[] upstreamBodies = {
"{\"error\":{\"type\":\"FreeTierError\",\"message\":\"detected abusive traffic\"}}",
"{\"error\":{\"type\":\"FreeTierError\",\"message\":\"Forbidden: Detected Abusive Traffic\"}}",
"FreeTierError: current network has abusive traffic",
"Forbidden, OpenCode Free Model Gateway Detect Abuse Traffic"
};
for (String upstreamBody : upstreamBodies) {
var outcome = OpenCodeZenUpstreamDiagnostics.classify(
403, upstreamBody.getBytes(StandardCharsets.UTF_8));
assertThat(outcome)
.as("403 error classification for %s", upstreamBody)
.isEqualTo(OpenCodeZenUpstreamDiagnostics.Outcome.ABUSE_REJECTED);
assertThat(OpenCodeZenUpstreamDiagnostics.shouldRetryAutomatically(outcome)).isFalse();
}
assertThat(OpenCodeZenUpstreamDiagnostics.classify(403,
"FreeTierError".getBytes(StandardCharsets.UTF_8)))
.isEqualTo(OpenCodeZenUpstreamDiagnostics.Outcome.FREE_TIER_REJECTED);
}

@Test
void distinguishesFreeTierAndOtherHttpErrorsWithoutStoringBody() {
assertThat(OpenCodeZenUpstreamDiagnostics.classify(403,
Expand Down
Loading