From 1404bd1beec4a9f5a5816abc1deb77e4a05174d2 Mon Sep 17 00:00:00 2001 From: "yuechen.wyh" <300878504+moonyue-w@users.noreply.github.com> Date: Wed, 21 Oct 2026 19:50:32 +0800 Subject: [PATCH 1/2] chore(release): prepare Java SDK 0.1.0 and clarify installation --- CHANGELOG.md | 23 +++++- README.md | 132 +++++------------------------------ docs/client-configuration.md | 111 +++++++++++++++++++++++++++++ docs/forward-api-coverage.md | 10 ++- docs/live-testing.md | 13 +++- docs/maintaining.md | 15 +++- docs/releasing.md | 8 ++- examples/README.md | 10 ++- examples/pom.xml | 4 +- pom.xml | 2 +- 10 files changed, 201 insertions(+), 127 deletions(-) create mode 100644 docs/client-configuration.md diff --git a/CHANGELOG.md b/CHANGELOG.md index e27f5ed..3591b7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,27 @@ ## [Unreleased] +## [0.1.0] - 2026-10-21 + +First stable release of the Java SDK for the Forward API, using `com.qoder.sdk:qoder-cloud-agents-sdk:0.1.0`. + +### Features + +- Java 8 compatible synchronous and asynchronous clients, checked on Java 8, 11, 17, 21 and 25. +- Shared PAT, static SAT and custom credentials, plus `ServiceAccountCredential` for automatic SA Key exchange, SAT caching and refresh. Forward administrator/Identity SAT issuance and revocation use explicit per-call SA Keys. +- Typed Forward resource APIs for Models, Templates, Identities and configuration, Sessions and Threads, Memory Stores, Environments, Files, Skills, Vaults and Credentials, Schedules, Batches, Channels and Pairing, Usage and Drive. +- Session/Thread SSE subscriptions with cancellation and explicit resumption; Realtime conversations with text/PCM audio, typed events, acknowledged close and History. +- Shared request options, timeouts, retries, pagination, response metadata and typed errors, with resource-lifecycle examples and API guides. Managed APIs are outside this release's scope. + +### Changed + +- Promote the RC's API and runtime behavior without implementation changes; update installation, example and maintenance documentation for Maven Central consumption. + +### Migration + +- From `0.1.0-rc.1`, change only the dependency version to `0.1.0`. +- From the earlier internal `com.qoder:qca-sdk:0.1.0-SNAPSHOT`, use `com.qoder.sdk:qoder-cloud-agents-sdk:0.1.0`. Java imports remain under `com.qoder.cloudagents`. + ## [0.1.0-rc.1] - 2026-10-21 First public release candidate for installation and integration testing ahead of `0.1.0`. @@ -76,4 +97,4 @@ First public release candidate for installation and integration testing ahead of - Usage, Token management and Drive round-trip examples with installed-JAR consumer checks, scoped cleanup and storage credential isolation. - HTTP error request ID fallback from top-level and nested error bodies when response headers omit it. -This version can be built from source; Maven Central publication remains pending. See the [maintenance guide](docs/maintaining.md) for current checks and delivery boundaries. +This release candidate is available from Maven Central. See the [maintenance guide](docs/maintaining.md) for checks and delivery boundaries. diff --git a/README.md b/README.md index 1d43a39..822269e 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,43 @@ # Qoder Cloud Agents Java SDK -Java 8 compatible client for the **Forward API**. The repository follows the resource and authentication structure of the QCA Go, Python and TypeScript SDKs, using Maven and Java builders. +Java 8+ SDK for the **Forward API**, with typed synchronous and asynchronous clients. Supports PAT/SA authentication, pagination, retries, Session/Thread SSE and Realtime text/audio. Managed APIs are outside this SDK's scope. -**Status: source available; not yet published to Maven Central.** The current version is `0.1.0-rc.1`. Build from source to use the SDK until a public Maven release is available. +[Changelog](CHANGELOG.md) · [Releases](https://github.com/QoderAI/qoder-cloud-agents-sdk-java/releases) · [Maven Central](https://central.sonatype.com/artifact/com.qoder.sdk/qoder-cloud-agents-sdk) -The SDK provides synchronous and asynchronous clients for Models, Templates, Identities, Sessions and Threads, Memory Stores, Environments, Files, Skills, Vaults and Credentials, Schedules, Batches, Channels and Pairing, Usage, Service Account Tokens and Drive. Shared support includes PAT/SA authentication, timeouts, retries, pagination, typed errors and request IDs. Session/Thread SSE supports explicit resumption; Realtime supports text, PCM audio and History. The [API coverage](docs/forward-api-coverage.md) records the 123-operation baseline and its exclusions. Managed APIs are outside this project's scope. +## Installation -See the [examples](examples/README.md) for authentication and resource workflows, and the [maintenance guide](docs/maintaining.md) for contributor checks and version updates. - -## Maven dependency - -After installing the SDK locally or publishing it to your configured Maven repository, use: +Add the Maven dependency; published versions require no local SDK build or additional repository configuration. ```xml com.qoder.sdk qoder-cloud-agents-sdk - 0.1.0-rc.1 + 0.1.0 ``` -The Maven coordinates do not change Java imports, which remain under `com.qoder.cloudagents`. - -## Local build - -```sh -./mvnw clean install -./mvnw -f examples/pom.xml verify -python3 .github/scripts/check_package.py -``` +Compatible with Java 8, 11, 17, 21 and 25. See the [changelog](CHANGELOG.md) for upgrading from the RC or earlier internal coordinates. -Requires JDK 8 or later. JDK 17 is recommended for maintenance builds; compilation targets Java 8. The independent examples project consumes the installed JAR. See [CONTRIBUTING.md](CONTRIBUTING.md) for checks and repository organization; CI checks Java 8, 11, 17, 21 and 25. +## Quick start with PAT -## PAT +Set `QODER_PAT`, then create a client: ```java import com.qoder.cloudagents.ForwardClient; -import com.qoder.cloudagents.forward.Template; try (ForwardClient client = ForwardClient.builder() - .baseUrl("https://api.qoder.com/api/v1/forward") .pat(System.getenv("QODER_PAT")) .build()) { - Template template = client.templates().retrieve("tmpl_example"); - System.out.println(template.id()); - System.out.println(template.requestId()); + System.out.println(client.models().list().data().size()); } ``` -The default BaseURL is Global. For CN, use `https://api.qoder.com.cn/api/v1/forward`. `QODER_FORWARD_BASE_URL` provides the default when `.baseUrl(...)` is omitted. - -With no explicit credentials, PAT mode reads `QODER_ACCESS_TOKEN`, then `QODER_PAT`. Explicit `.pat(...)`, `.accessToken(...)` or `.credential(...)` takes precedence over environment tokens; configure only one explicit source. +The default endpoint is Global: `https://api.qoder.com/api/v1/forward`. For CN, add `.baseUrl("https://api.qoder.com.cn/api/v1/forward")` to the client builder. ## Service Account +Set `QODER_SA_KEY` and explicitly configure a credential: + ```java import com.qoder.cloudagents.ForwardClient; import com.qoder.cloudagents.auth.Region; @@ -69,94 +53,14 @@ try (ServiceAccountCredential credential = ServiceAccountCredential.builder() } ``` -The credential lazily exchanges the SA Key for a platform SAT, caches and refreshes the SAT before expiry, and shares concurrent refreshes. Business requests use the SAT; the Key only goes to the fixed regional exchange endpoint. Set both `Region.CN` and the CN BaseURL for CN requests. No SA credential is inferred merely because `QODER_SA_KEY` exists. - -The caller owns the credential. Keep it open until all clients using it are closed. Closing a client does not close its credential. - -For a SAT managed by your application: - -```java -ForwardClient client = ForwardClient.builder() - .authMode(com.qoder.cloudagents.auth.AuthMode.SA) - .accessToken(System.getenv("QODER_ACCESS_TOKEN")) - .build(); -``` - -Forward SAT management uses explicit per-call SA Keys through `serviceAccountTokens()` and `identities().serviceAccountTokens()`; see the [usage guide](docs/usage-sat-drive.md). A client without a business credential can call those methods; ordinary requests fail locally before network access until a business credential is configured. - -Close the client after use. A static SAT is not automatically renewed. SA mode does not fall back to `QODER_PAT`. For application-managed rotation, implement `Credential.getToken()` returning a `CompletableFuture`; it is called before each attempt and must be thread-safe and return promptly. Cancelling a request does not cancel a shared credential future. - -## Asynchronous calls and cancellation - -```java -java.util.concurrent.CompletableFuture pending = - client.async().templates().retrieve("tmpl_example"); -pending.cancel(true); // when the caller no longer needs the result -``` - -Cancellation propagates to the HTTP call and retry timer. The returned future follows Java's `CancellationException` contract. Interrupting a synchronous call cancels its request, preserves the interrupt flag and raises `ApiUserAbortException`. - -`client.async()` shares its parent's resources; closing the view is a no-op. Closing the parent cancels active requests. `ForwardClient.builder().buildAsync()` creates an independently owned, closeable async client. Keep the owning client open until futures finish. Additional futures created with application-side `thenApply(...)` follow normal Java cancellation behavior; cancel the SDK-returned future to abort its request. - -## Request options and errors - -```java -com.qoder.cloudagents.core.RequestOptions options = - com.qoder.cloudagents.core.RequestOptions.builder() - .timeout(java.time.Duration.ofSeconds(30)) - .totalTimeout(java.time.Duration.ofSeconds(60)) - .maxRetries(1) - .build(); -com.qoder.cloudagents.core.ApiResponse response = - client.templates().withResponse().retrieve("tmpl_example", options); -``` - -- Default connection timeout: 5 seconds. Default response-header wait and read/write idle timeouts: 10 minutes. `.timeout(...)` overrides the latter limits for one attempt. Optional `.totalTimeout(...)` covers credentials, all attempts, backoff and body reads. Timeouts must be positive. SSE disables read-idle timing after accepted headers; an optional subscription-wide total deadline remains active, including reconnects. See [SSE lifecycle](docs/session-streaming.md). -- Default maximum retries: 2, giving at most 3 attempts. The shared engine follows QCA safety rules: 409 is never retried; non-idempotent writes only qualify for 429 retries; non-repeatable bodies are not retried. Server retry hints remain subject to these checks. -- `ApiException` covers request failures. `ApiStatusException` exposes HTTP status, request ID, headers and parsed error body. Connection, timeout, cancellation and response parsing failures have corresponding subclasses. SA issuer failures use `ServiceAccountExchangeException`, exposing status/request ID without retaining the issuer body. Failed SA exchange does not switch to PAT. -- Models provide `requestId()`, `field(name)` for unknown fields and `rawJson()`. Unknown fields and numeric precision are preserved; invalid typed field access raises `ResponseValidationException`. Request metadata is separate from the JSON body. -- Template list parameters support `limit`, `status`, `afterId` or `beforeId`. Use `page.data()` for one page, `getNextPage()` / `getNextPageAsync()` for manual pagination, or `for (Template template : page)` for lazy synchronous iteration. Server order is retained; filters and request options follow subsequent pages. Check `hasNextPage()` before fetching; an exhausted page or repeated cursor raises `PaginationException`. - -## Template configuration - -```java -import com.qoder.cloudagents.forward.TemplateCreateParams; -import com.qoder.cloudagents.forward.TemplateUpdateParams; -import com.qoder.cloudagents.core.RequestOptions; - -Template template = client.templates().create( - TemplateCreateParams.builder() - .name("Support assistant") - .modelId("auto") - .environmentId("env_example") - .build(), - RequestOptions.builder().idempotencyKey(java.util.UUID.randomUUID().toString()).build()); - -client.templates().update(template.id(), TemplateUpdateParams.builder() - .description("Updated description") - .multiagent(null) // explicitly clear the multi-agent configuration - .build()); -``` - -An unset builder field is omitted. A setter with `null` writes JSON null; empty collections write `[]` or `{}`. The server decides which fields accept null. `omit("wire_name")` removes a previously set field. Requests and responses preserve unknown JSON fields; raw JSON can contain secrets and should not be logged. See [Template operations and configuration](docs/templates.md) and the [lifecycle example](examples/README.md). +The credential exchanges the SA Key for a SAT, then caches and refreshes it. For CN, use both `Region.CN` and the CN BaseURL. See [client configuration](docs/client-configuration.md) for static SATs, authentication precedence, timeouts, retries and cancellation. ## Documentation -- [Runnable examples and shared PAT/SA setup](examples/README.md) -- [Template operations, configuration and pagination](docs/templates.md) -- [Identity operations and configuration overrides](docs/identities.md) -- [Session HTTP operations, events, threads and Memory Store mounts](docs/sessions.md) -- [Session/Thread SSE, cancellation and resumable streaming](docs/session-streaming.md) -- [Memory Stores, content updates, versions and lifecycle rules](docs/memory-stores.md) -- [Environments, multipart upload and streaming file download](docs/environments-files.md) -- [Skills, versions, Vaults and Credentials](docs/skills-vaults.md) -- [Schedules, Runs and Batches](docs/schedules-batches.md) -- [Channels, Pairing and QR authorization](docs/channels.md) +- [Runnable resource examples and shared authentication setup](examples/README.md) +- [Forward API coverage and resource guides](docs/forward-api-coverage.md) +- [Session/Thread SSE](docs/session-streaming.md) and [Realtime text, audio and History](docs/realtime.md) - [Usage, Forward SAT management and Drive](docs/usage-sat-drive.md) -- [Forward API coverage](docs/forward-api-coverage.md) -- [Realtime text, PCM audio and History](docs/realtime.md) -- [Opt-in live testing](docs/live-testing.md) -- [Maintenance and release setup](docs/maintaining.md) -- [Changelog](CHANGELOG.md) +- [Live testing](docs/live-testing.md) -`./mvnw verify` also builds source and Javadoc JARs. API documentation is generated under `target/reports/apidocs/`; packages under `core.internal` are implementation details. +For source builds and contributions, see [CONTRIBUTING.md](CONTRIBUTING.md). Maintainers can follow the [maintenance](docs/maintaining.md) and [release](docs/releasing.md) guides. diff --git a/docs/client-configuration.md b/docs/client-configuration.md new file mode 100644 index 0000000..f263c38 --- /dev/null +++ b/docs/client-configuration.md @@ -0,0 +1,111 @@ +# Client configuration + +Shared configuration for the synchronous and asynchronous Forward clients. See the [README](../README.md) for installation and quick starts. + +## PAT + +```java +import com.qoder.cloudagents.ForwardClient; +import com.qoder.cloudagents.forward.Template; + +try (ForwardClient client = ForwardClient.builder() + .baseUrl("https://api.qoder.com/api/v1/forward") + .pat(System.getenv("QODER_PAT")) + .build()) { + Template template = client.templates().retrieve("tmpl_example"); + System.out.println(template.id()); + System.out.println(template.requestId()); +} +``` + +The default BaseURL is Global. For CN, use `https://api.qoder.com.cn/api/v1/forward`. `QODER_FORWARD_BASE_URL` provides the default when `.baseUrl(...)` is omitted. + +With no explicit credentials, PAT mode reads `QODER_ACCESS_TOKEN`, then `QODER_PAT`. Explicit `.pat(...)`, `.accessToken(...)` or `.credential(...)` takes precedence over environment tokens; configure only one explicit source. + +## Service Account + +```java +import com.qoder.cloudagents.ForwardClient; +import com.qoder.cloudagents.auth.Region; +import com.qoder.cloudagents.auth.ServiceAccountCredential; + +try (ServiceAccountCredential credential = ServiceAccountCredential.builder() + .saKey(System.getenv("QODER_SA_KEY")) + .region(Region.GLOBAL) + .scopes("qca.access", "forward.access") + .build(); + ForwardClient client = ForwardClient.builder().credential(credential).build()) { + System.out.println(client.models().list().data().size()); +} +``` + +The credential lazily exchanges the SA Key for a platform SAT, caches and refreshes the SAT before expiry, and shares concurrent refreshes. Business requests use the SAT; the Key only goes to the fixed regional exchange endpoint. Set both `Region.CN` and the CN BaseURL for CN requests. No SA credential is inferred merely because `QODER_SA_KEY` exists. + +The caller owns the credential. Keep it open until all clients using it are closed. Closing a client does not close its credential. + +For a SAT managed by your application: + +```java +ForwardClient client = ForwardClient.builder() + .authMode(com.qoder.cloudagents.auth.AuthMode.SA) + .accessToken(System.getenv("QODER_ACCESS_TOKEN")) + .build(); +``` + +Forward SAT management uses explicit per-call SA Keys through `serviceAccountTokens()` and `identities().serviceAccountTokens()`; see the [usage guide](usage-sat-drive.md). A client without a business credential can call those methods; ordinary requests fail locally before network access until a business credential is configured. + +Close the client after use. A static SAT is not automatically renewed. SA mode does not fall back to `QODER_PAT`. For application-managed rotation, implement `Credential.getToken()` returning a `CompletableFuture`; it is called before each attempt and must be thread-safe and return promptly. Cancelling a request does not cancel a shared credential future. + +## Asynchronous calls and cancellation + +```java +java.util.concurrent.CompletableFuture pending = + client.async().templates().retrieve("tmpl_example"); +pending.cancel(true); // when the caller no longer needs the result +``` + +Cancellation propagates to the HTTP call and retry timer. The returned future follows Java's `CancellationException` contract. Interrupting a synchronous call cancels its request, preserves the interrupt flag and raises `ApiUserAbortException`. + +`client.async()` shares its parent's resources; closing the view is a no-op. Closing the parent cancels active requests. `ForwardClient.builder().buildAsync()` creates an independently owned, closeable async client. Keep the owning client open until futures finish. Additional futures created with application-side `thenApply(...)` follow normal Java cancellation behavior; cancel the SDK-returned future to abort its request. + +## Request options and errors + +```java +com.qoder.cloudagents.core.RequestOptions options = + com.qoder.cloudagents.core.RequestOptions.builder() + .timeout(java.time.Duration.ofSeconds(30)) + .totalTimeout(java.time.Duration.ofSeconds(60)) + .maxRetries(1) + .build(); +com.qoder.cloudagents.core.ApiResponse response = + client.templates().withResponse().retrieve("tmpl_example", options); +``` + +- Default connection timeout: 5 seconds. Default response-header wait and read/write idle timeouts: 10 minutes. `.timeout(...)` overrides the latter limits for one attempt. Optional `.totalTimeout(...)` covers credentials, all attempts, backoff and body reads. Timeouts must be positive. SSE disables read-idle timing after accepted headers; an optional subscription-wide total deadline remains active, including reconnects. See [SSE lifecycle](session-streaming.md). +- Default maximum retries: 2, giving at most 3 attempts. The shared engine follows QCA safety rules: 409 is never retried; non-idempotent writes only qualify for 429 retries; non-repeatable bodies are not retried. Server retry hints remain subject to these checks. +- `ApiException` covers request failures. `ApiStatusException` exposes HTTP status, request ID, headers and parsed error body. Connection, timeout, cancellation and response parsing failures have corresponding subclasses. SA issuer failures use `ServiceAccountExchangeException`, exposing status/request ID without retaining the issuer body. Failed SA exchange does not switch to PAT. +- Models provide `requestId()`, `field(name)` for unknown fields and `rawJson()`. Unknown fields and numeric precision are preserved; invalid typed field access raises `ResponseValidationException`. Request metadata is separate from the JSON body. +- Template list parameters support `limit`, `status`, `afterId` or `beforeId`. Use `page.data()` for one page, `getNextPage()` / `getNextPageAsync()` for manual pagination, or `for (Template template : page)` for lazy synchronous iteration. Server order is retained; filters and request options follow subsequent pages. Check `hasNextPage()` before fetching; an exhausted page or repeated cursor raises `PaginationException`. + +## Template configuration + +```java +import com.qoder.cloudagents.forward.TemplateCreateParams; +import com.qoder.cloudagents.forward.TemplateUpdateParams; +import com.qoder.cloudagents.core.RequestOptions; + +Template template = client.templates().create( + TemplateCreateParams.builder() + .name("Support assistant") + .modelId("auto") + .environmentId("env_example") + .build(), + RequestOptions.builder().idempotencyKey(java.util.UUID.randomUUID().toString()).build()); + +client.templates().update(template.id(), TemplateUpdateParams.builder() + .description("Updated description") + .multiagent(null) // explicitly clear the multi-agent configuration + .build()); +``` + +An unset builder field is omitted. A setter with `null` writes JSON null; empty collections write `[]` or `{}`. The server decides which fields accept null. `omit("wire_name")` removes a previously set field. Requests and responses preserve unknown JSON fields; raw JSON can contain secrets and should not be logged. See [Template operations and configuration](templates.md) and the [lifecycle example](../examples/README.md). diff --git a/docs/forward-api-coverage.md b/docs/forward-api-coverage.md index 598b3d1..1d37039 100644 --- a/docs/forward-api-coverage.md +++ b/docs/forward-api-coverage.md @@ -6,10 +6,18 @@ HTTP BaseURL 为 CN `https://api.qoder.com.cn/api/v1/forward` 或 Global `https: 来源:TypeScript main `597ff047be4fb0afb72fc6bd024ce957cf3e1fc1` 的 `src/forward/api-inventory.json`(120 项),补充 `service-account-token.ts` 和 `identity-service-account-token.ts` 的 3 项;operation ID 集合与 Go main `735f13c8bf5888bf70b5a93b9ba89bd0e078d4b9` 的 `forward/testdata/api-contracts.json`(123 项)交叉核对一致。 -共 **123 个 HTTP 操作**:121 个普通 HTTP 操作、2 个 SSE 订阅操作;另有 1 个 WebSocket 连接入口。Java 资源方法见下表;当前尚未发布 Maven Central 版本。相同方法的同步/异步形式只计一个协议操作。 +共 **123 个 HTTP 操作**:121 个普通 HTTP 操作、2 个 SSE 订阅操作;另有 1 个 WebSocket 连接入口。Java 资源方法见下表;Maven 接入方式见 [README](../README.md#installation),已发布版本见 [Releases](https://github.com/QoderAI/qoder-cloud-agents-sdk-java/releases)。相同方法的同步/异步形式只计一个协议操作。 这里只盘点协议入口;参数、响应、分页、认证和重试行为由对应资源指南与契约测试说明。 +## 使用指南 + +- [客户端认证、超时、重试与取消](client-configuration.md) +- [Template](templates.md)、[Identity / Config](identities.md)、[Session / Thread](sessions.md) 和 [SSE](session-streaming.md) +- [Memory Store](memory-stores.md)、[Environment / File](environments-files.md)、[Skill / Vault](skills-vaults.md) +- [Schedule / Batch](schedules-batches.md)、[Channel / Pairing / QR](channels.md)、[Usage / SAT / Drive](usage-sat-drive.md) +- [Realtime](realtime.md) 和 [可运行示例](../examples/README.md) + ## HTTP 操作 | # | operation ID | Java 资源方法(省略 client 前缀) | HTTP | 相对路径 | 返回形式 | 状态 | diff --git a/docs/live-testing.md b/docs/live-testing.md index 7cd2074..a0e34ad 100644 --- a/docs/live-testing.md +++ b/docs/live-testing.md @@ -1,8 +1,17 @@ # Forward 真实环境验收 -`ForwardLiveCheck` 位于独立的 `examples` Maven 工程,消费本地安装的 SDK JAR。普通 `test` / `verify` 只运行模拟服务测试,不执行线上请求。 +`ForwardLiveCheck` 位于独立的 `examples` Maven 工程,消费 SDK JAR 依赖。普通 `test` / `verify` 只运行模拟服务测试,不执行线上请求。 -先构建、安装并验证消费者: +验证已发布版本时,检出对应发布标签,从空缓存解析并验证消费者,不先执行 SDK 的本地 `install`: + +```sh +SDK_ACCEPTANCE_CACHE=$(mktemp -d) +./mvnw -B -ntp -Dmaven.repo.local="$SDK_ACCEPTANCE_CACHE" -f examples/pom.xml clean verify +``` + +后续 `exec:java` 命令也传入 `-Dmaven.repo.local="$SDK_ACCEPTANCE_CACHE"`。记录实际使用的 Maven settings/镜像、下载来源及 JAR 摘要,避免把已有缓存或源码安装包当作公开发布包。 + +开发或验证尚未发布的版本时,先构建、安装并验证消费者: ```sh ./mvnw -B -ntp clean install diff --git a/docs/maintaining.md b/docs/maintaining.md index 47c62ee..471e37f 100644 --- a/docs/maintaining.md +++ b/docs/maintaining.md @@ -34,10 +34,21 @@ The SDK's embedded version is filtered from `${project.version}`; do not replace ```sh python3 .github/scripts/release_notes.py check # After adding the actual release entry: -python3 .github/scripts/release_notes.py extract --version 0.1.0-rc.1 +python3 .github/scripts/release_notes.py extract --version 0.1.0 ``` -The current version is `0.1.0-rc.1`. Acceptance records must include the resolved version, source SHA and JAR digest. Release versions must not be overwritten; a successful test of an older build is not evidence for a newer one. +The release version in this checkout is `0.1.0`. Also update the README dependency and version-specific maintenance examples. Acceptance records must include the resolved version, source SHA and JAR digest. Release versions must not be overwritten; a successful test of an older build is not evidence for a newer one. + +### RC qualification for 0.1.0 + +The `0.1.0-rc.1` package from source `7742942d6d9dc3f681cb974b45ecfffe5083ad4c` was downloaded from Maven Central and tested on Java 8, 11, 17, 21 and 25 on 2026-10-21. Its JAR SHA-256 was `4eb33d871d234fd80b0302b2bf03d90d9c121d87ad0d50eed098a28505afaa86`. + +- All 608 offline checks per JDK passed: 545 SDK regression tests, 62 example-consumer tests and one independent published-JAR/hash/Java 8 bytecode check, for 3,040 executions. +- The CN/Global PAT/SA live matrix ran 38 groups per JDK. The first run passed 184 of 190 groups. Six CN failures covered three Schedule reply checks, one Batch output check, one Realtime `work.failed` event and one Realtime PCM connection exception. +- Each failed case passed one targeted retry with the same package, configuration, assertions and JDK. Original failures were retained; no assertion was weakened. This is passing coverage after retries, not a clean first run or proof that the production failures were fixed. No cleanup failures or retained execution resources were reported. +- Channel/QR/Pairing external-platform flows, audio-device quality, long-duration stability and production fault injection were not part of that live matrix; see [live testing](live-testing.md). + +The `0.1.0` preparation changes version metadata and documentation, keeping the RC's implementation and test sources unchanged. The release workflow must still build, sign and independently verify the actual `0.1.0` package; RC acceptance does not replace those checks. ## Build candidates and releases diff --git a/docs/releasing.md b/docs/releasing.md index 5f479b3..ec51bd4 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -1,6 +1,8 @@ # Java SDK release setup -The current version is `com.qoder.sdk:qoder-cloud-agents-sdk:0.1.0-rc.1`. The manual [Release workflow](../.github/workflows/release.yml) implements publication using repository Actions secrets. Dispatching it from `main` authorizes publication after all automated checks pass; there is no separate environment approval. The Central account, namespace, signing key and repository settings below must be configured first. The GitHub repository and internal Maven access do not grant Maven Central publishing access. No public release has been made by adding this workflow. +The release version in this checkout is `com.qoder.sdk:qoder-cloud-agents-sdk:0.1.0`. The manual [Release workflow](../.github/workflows/release.yml) publishes using repository Actions secrets. Dispatching it from `main` authorizes publication after all automated checks pass; there is no separate environment approval. The Central account, namespace, signing key and repository settings below must be configured first. The GitHub repository and internal Maven access do not grant Maven Central publishing access. + +`0.1.0-rc.1` was published to Maven Central on 2026-10-21; see its [Release](https://github.com/QoderAI/qoder-cloud-agents-sdk-java/releases/tag/v0.1.0-rc.1) and [successful publication workflow](https://github.com/QoderAI/qoder-cloud-agents-sdk-java/actions/runs/39572349619). A version committed to a POM is not yet a published package: check the workflow's public verification and matching GitHub Release for each version. ## Optional GitHub protection @@ -68,11 +70,13 @@ The namespace variable is a maintainer's setup confirmation, not an automatic ow Prepare a PR that updates the root POM, the example project's version and SDK dependency, plus the changelog version section. Keep an empty `Unreleased` section. Review any version-specific install examples. Merge only after `quality` and all five Java checks succeed. The workflow does not bump versions or merge the PR. +For promotion from `0.1.0-rc.1` to `0.1.0`, review the [RC qualification and its limitations](maintaining.md#rc-qualification-for-010). Use the merged `0.1.0` main SHA and a new batch ID; leave recovery inputs empty. This creates a new version and tag, with its own signed bundle and public-package checks. Do not reuse the RC deployment, change its tag or copy/rename its JAR as the final release. + Open **Actions → Release → Run workflow**, select **main**, and verify the following inputs before dispatch. Successful preflight automatically proceeds to signing and publication; it does not pause for an additional approval. | Input | First publication | | --- | --- | -| `version` | The reviewed version without `v` or `SNAPSHOT`, for example `0.1.0-rc.1` | +| `version` | The reviewed version without `v` or `SNAPSHOT`, for example `0.1.0` | | `commit_sha` | Full 40-character lowercase SHA of the current GitHub main | | `batch_id` | 1–64 letters/digits/dots/underscores/hyphens, starting with a letter or digit | | `resume_run_id` | Empty | diff --git a/examples/README.md b/examples/README.md index e4cd4a0..21abe93 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,8 +1,14 @@ # Forward examples -This independent Maven project uses the installed SDK JAR. `ForwardReadOnlyExample` lists Models and the first Template page, then optionally retrieves `QODER_TEMPLATE_ID`, without mutating resources. `TemplateLifecycleExample` creates, updates, clones and retrieves isolated Templates, then archives every created ID. `IdentityConfigExample` creates an isolated Identity, configures it for an existing Template, retrieves effective configuration, then clears and deletes that Identity. All share PAT/SA setup through `ExampleClient`. +This independent Maven project consumes `com.qoder.sdk:qoder-cloud-agents-sdk` as a JAR dependency. `ForwardReadOnlyExample` lists Models and the first Template page, then optionally retrieves `QODER_TEMPLATE_ID`, without mutating resources. `TemplateLifecycleExample` creates, updates, clones and retrieves isolated Templates, then archives every created ID. `IdentityConfigExample` creates an isolated Identity, configures it for an existing Template, retrieves effective configuration, then clears and deletes that Identity. All share PAT/SA setup through `ExampleClient`. -From the repository root: +To use a published version, check out its matching release tag and run from the repository root. Maven resolves the SDK dependency without building SDK sources: + +```sh +./mvnw -B -ntp -f examples/pom.xml clean verify +``` + +For SDK development or an unpublished version, install the local SDK first: ```sh ./mvnw clean install diff --git a/examples/pom.xml b/examples/pom.xml index eb2f8ce..c2d95ef 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -2,11 +2,11 @@ 4.0.0 - com.qoder.examplesqca-forward-examples0.1.0-rc.1 + com.qoder.examplesqca-forward-examples0.1.0 8UTF-8 - com.qoder.sdkqoder-cloud-agents-sdk0.1.0-rc.1 + com.qoder.sdkqoder-cloud-agents-sdk0.1.0 org.junit.jupiterjunit-jupiter5.13.4test com.squareup.okhttp3mockwebserver5.5.0test diff --git a/pom.xml b/pom.xml index 7af3ed2..fe1bfcb 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.qoder.sdk qoder-cloud-agents-sdk - 0.1.0-rc.1 + 0.1.0 Qoder Cloud Agents Java SDK Java 8 compatible Qoder Cloud Agents Forward API client. https://github.com/QoderAI/qoder-cloud-agents-sdk-java From 5b36cebe73825a37c8e29cc0ab16d94ab3d0a35c Mon Sep 17 00:00:00 2001 From: "yuechen.wyh" <300878504+moonyue-w@users.noreply.github.com> Date: Wed, 21 Oct 2026 19:50:33 +0800 Subject: [PATCH 2/2] docs(java): explain supported workflows and planned Managed APIs --- CHANGELOG.md | 2 +- README.md | 226 +++++++++++++++++++++++++++++++---- docs/forward-api-coverage.md | 5 +- 3 files changed, 209 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3591b7f..33674e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ First stable release of the Java SDK for the Forward API, using `com.qoder.sdk:q - Shared PAT, static SAT and custom credentials, plus `ServiceAccountCredential` for automatic SA Key exchange, SAT caching and refresh. Forward administrator/Identity SAT issuance and revocation use explicit per-call SA Keys. - Typed Forward resource APIs for Models, Templates, Identities and configuration, Sessions and Threads, Memory Stores, Environments, Files, Skills, Vaults and Credentials, Schedules, Batches, Channels and Pairing, Usage and Drive. - Session/Thread SSE subscriptions with cancellation and explicit resumption; Realtime conversations with text/PCM audio, typed events, acknowledged close and History. -- Shared request options, timeouts, retries, pagination, response metadata and typed errors, with resource-lifecycle examples and API guides. Managed APIs are outside this release's scope. +- Shared request options, timeouts, retries, pagination, response metadata and typed errors, with resource-lifecycle examples and API guides. Managed API support is planned for a future release and is not included in 0.1.0. ### Changed diff --git a/README.md b/README.md index 822269e..1798a99 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,34 @@ # Qoder Cloud Agents Java SDK -Java 8+ SDK for the **Forward API**, with typed synchronous and asynchronous clients. Supports PAT/SA authentication, pagination, retries, Session/Thread SSE and Realtime text/audio. Managed APIs are outside this SDK's scope. - [Changelog](CHANGELOG.md) · [Releases](https://github.com/QoderAI/qoder-cloud-agents-sdk-java/releases) · [Maven Central](https://central.sonatype.com/artifact/com.qoder.sdk/qoder-cloud-agents-sdk) +The Qoder Cloud Agents Java SDK provides typed synchronous and asynchronous clients, with shared authentication, timeouts, retries, pagination, streaming and error handling. + +Version **0.1.0** supports the **Forward API**. **Managed API support is planned for a future release** and is not available in this version. + +| Capability | Resources | +| --- | --- | +| Agent configuration | Template, Identity and per-Identity configuration | +| Conversations | Session, Thread, event history and SSE subscriptions | +| Execution | Schedule, Schedule Run, Batch and Batch Task | +| Resources | Environment, File, Skill, Vault, Memory Store and Drive | +| Channels | Channel configuration, Pairing creation/deletion and QR authorization | +| Authentication and usage | PAT, Service Account credentials, Forward SAT issuance/revocation, Model and Usage | +| Realtime (Beta) | Text/audio WebSocket conversations and persisted History | + +See [API coverage and resource guides](docs/forward-api-coverage.md) for the supported operations. + +- [Installation](#installation) +- [Quick start](#quick-start) +- [PAT and Service Account authentication](#pat-and-service-account-authentication) +- [Conversations and streaming](#conversations-and-streaming) +- [Asynchronous calls and cancellation](#asynchronous-calls-and-cancellation) +- [Pagination](#pagination) +- [Timeouts, retries and errors](#timeouts-retries-and-errors) +- [Realtime](#realtime) +- [Examples and documentation](#examples-and-documentation) +- [Development](#development) + ## Installation Add the Maven dependency; published versions require no local SDK build or additional repository configuration. @@ -16,27 +41,56 @@ Add the Maven dependency; published versions require no local SDK build or addit ``` -Compatible with Java 8, 11, 17, 21 and 25. See the [changelog](CHANGELOG.md) for upgrading from the RC or earlier internal coordinates. +Requirements: -## Quick start with PAT +- Java **8** or later; CI covers Java **8, 11, 17, 21 and 25**. +- A PAT or Service Account credential for the target region, with access to the resources you call. -Set `QODER_PAT`, then create a client: +## Quick start + +Set `QODER_PAT` in your environment. This complete program connects to Global and lists the enabled models: ```java import com.qoder.cloudagents.ForwardClient; - -try (ForwardClient client = ForwardClient.builder() - .pat(System.getenv("QODER_PAT")) - .build()) { - System.out.println(client.models().list().data().size()); +import com.qoder.cloudagents.forward.Model; + +public class QuickStart { + public static void main(String[] args) { + try (ForwardClient client = ForwardClient.builder() + .pat(System.getenv("QODER_PAT")) + .build()) { + for (Model model : client.models().list().data()) { + if (model.isEnabled()) { + System.out.println(model.id()); + } + } + } + } } ``` -The default endpoint is Global: `https://api.qoder.com/api/v1/forward`. For CN, add `.baseUrl("https://api.qoder.com.cn/api/v1/forward")` to the client builder. +| Region | Forward BaseURL | +| --- | --- | +| Global (default) | `https://api.qoder.com/api/v1/forward` | +| CN | `https://api.qoder.com.cn/api/v1/forward` | + +For CN, add `.baseUrl("https://api.qoder.com.cn/api/v1/forward")` to the builder. If omitted, the SDK reads `QODER_FORWARD_BASE_URL`, then uses Global. BaseURLs include the full API root path. + +Clients are thread-safe and can be reused. Close the owning client when your application finishes using it. The following snippets assume an open `client`; replace resource IDs with IDs accessible to your credential. -## Service Account +## PAT and Service Account authentication -Set `QODER_SA_KEY` and explicitly configure a credential: +PAT and SA are parallel authentication modes: + +| Mode | Configuration | Token renewal | +| --- | --- | --- | +| PAT | `.pat(token)` | Managed by your application | +| SA Key | `.credential(ServiceAccountCredential)` | SDK exchanges, caches and refreshes the SAT on demand | +| Existing SAT | `.authMode(AuthMode.SA).accessToken(token)` | Managed by your application | + +With no explicit credentials, PAT mode reads `QODER_ACCESS_TOKEN`, then `QODER_PAT`. An explicit token or credential takes precedence over environment tokens; configure only one explicit source. The SDK does not load `.env` files or enable SA merely because `QODER_SA_KEY` is set. + +To use automatic SA exchange, set `QODER_SA_KEY` and configure a credential: ```java import com.qoder.cloudagents.ForwardClient; @@ -53,14 +107,144 @@ try (ServiceAccountCredential credential = ServiceAccountCredential.builder() } ``` -The credential exchanges the SA Key for a SAT, then caches and refreshes it. For CN, use both `Region.CN` and the CN BaseURL. See [client configuration](docs/client-configuration.md) for static SATs, authentication precedence, timeouts, retries and cancellation. +Business requests use the SAT; the SA Key is sent only to the exchange service. For CN, use both `Region.CN` and the CN BaseURL. Keep a shared credential open until all clients using it are closed. Failed SA authentication does not fall back to PAT. + +Forward also provides explicit administrator/Identity SAT issuance and revocation through `serviceAccountTokens()` and `identities().serviceAccountTokens()`. These methods take a per-call SA Key and do not retry automatically. See [SAT management](docs/usage-sat-drive.md) and [authentication configuration](docs/client-configuration.md). + +## Conversations and streaming + +Create a Session using an existing Identity and Template, send a user message, and consume events through SSE: + +```java +import com.qoder.cloudagents.core.RequestOptions; +import com.qoder.cloudagents.forward.*; +import com.qoder.cloudagents.streaming.EventStream; +import java.time.Duration; +import java.util.Collections; +import java.util.UUID; + +Session session = client.sessions().create( + SessionCreateParams.builder() + .identityId("idn_example") + .templateId("tmpl_example") + .title("SDK quick start") + .build(), + RequestOptions.builder().idempotencyKey(UUID.randomUUID().toString()).build()); + +SessionEventSendResponse sent = client.sessions().events().send(session.id(), + SessionEventSendParams.builder().events(Collections.singletonList( + SessionEventParam.builder() + .type("user.message") + .content(EventContent.text("Hello!")) + .build())).build(), + RequestOptions.builder().idempotencyKey(UUID.randomUUID().toString()).build()); + +// Start after the accepted input, excluding events from earlier turns. +String inputEventId = sent.data().get(0).id(); +try (EventStream stream = client.sessions().events().streamEvents( + session.id(), SessionEventStreamParams.builder().lastEventId(inputEventId).build(), + RequestOptions.builder().totalTimeout(Duration.ofMinutes(5)).build())) { + for (SessionEvent event : stream) { + System.out.println(event.type()); + // Inspect event.content() for message blocks. + if ("session.error".equals(event.type()) || "session.status_terminated".equals(event.type())) { + throw new IllegalStateException("Session stopped: " + event.type()); + } + if ("session.status_idle".equals(event.type())) break; + } +} +``` + +Reuse an idempotency key when retrying the same logical write. A successful send acknowledges input, not completed execution. An idle event can mean the turn finished or paused for input; inspect the event's stop reason in your application. The example bounds the subscription to five minutes; closing it does not cancel or archive the Session. + +Thread subscriptions use `sessions().threads().events()`. For explicit reconnects with the last delivered SSE ID, use `resumableStream(...)`; user messages are not automatically resent. See [Session operations](docs/sessions.md), [streaming and cancellation](docs/session-streaming.md) and the [runnable streaming example](examples/src/main/java/com/qoder/cloudagents/examples/SessionStreamingExample.java). + +## Asynchronous calls and cancellation + +The async view shares the client's authentication and transport: + +```java +import com.qoder.cloudagents.forward.Template; +import java.util.concurrent.CompletableFuture; + +CompletableFuture