diff --git a/CHANGELOG.md b/CHANGELOG.md index e27f5ed..33674e0 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 API support is planned for a future release and is not included in 0.1.0. + +### 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..1798a99 100644 --- a/README.md +++ b/README.md @@ -1,58 +1,96 @@ # 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. +[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) -**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. +The Qoder Cloud Agents Java SDK provides typed synchronous and asynchronous clients, with shared authentication, timeouts, retries, pagination, streaming and error handling. -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. +Version **0.1.0** supports the **Forward API**. **Managed API support is planned for a future release** and is not available in this version. -See the [examples](examples/README.md) for authentication and resource workflows, and the [maintenance guide](docs/maintaining.md) for contributor checks and version updates. +| 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 | -## Maven dependency +See [API coverage and resource guides](docs/forward-api-coverage.md) for the supported operations. -After installing the SDK locally or publishing it to your configured Maven repository, use: +- [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. ```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 +Requirements: -```sh -./mvnw clean install -./mvnw -f examples/pom.xml verify -python3 .github/scripts/check_package.py -``` +- 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. -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 -## PAT +Set `QODER_PAT` in your environment. This complete program connects to Global and lists the enabled models: ```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()); +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 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. +| 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. + +## PAT and Service Account authentication + +PAT and SA are parallel authentication modes: -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. +| 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 | -## Service Account +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; @@ -69,94 +107,144 @@ 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. +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). -The caller owns the credential. Keep it open until all clients using it are closed. Closing a client does not close its credential. +## Conversations and streaming -For a SAT managed by your application: +Create a Session using an existing Identity and Template, send a user message, and consume events through SSE: ```java -ForwardClient client = ForwardClient.builder() - .authMode(com.qoder.cloudagents.auth.AuthMode.SA) - .accessToken(System.getenv("QODER_ACCESS_TOKEN")) - .build(); +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; + } +} ``` -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. +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. -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. +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 -java.util.concurrent.CompletableFuture pending = - client.async().templates().retrieve("tmpl_example"); -pending.cancel(true); // when the caller no longer needs the result +import com.qoder.cloudagents.forward.Template; +import java.util.concurrent.CompletableFuture; + +CompletableFuture