Skip to content

Commit 895263e

Browse files
foleydangclaude
andcommitted
feat: add AgentStudio SDK for managed agents
Add AgentStudio sub-SDK with full API coverage: - Agents: create/retrieve/update/list/listVersions/archive - Sessions: create/retrieve/update/list/archive/delete - SessionEvents: send/list/stream(SSE) - Environments: create/retrieve/update/list/archive/delete - Files: upload(multipart)/retrieve/list/delete - Skills: create(file auto-upload)/retrieve/list/delete + versions - Vaults + Credentials: full CRUD with typed CredentialAuth Key design: - AgentStudioClient with constructors + Builder pattern (apiKey+workspace for production, Builder for pre-env baseUrl) - workspace required (no default), region defaults to cn-beijing - CursorPage<T> with auto-pagination via Iterable - AgentStudioEventStream (SSE) with Message.getStopReason() - ClientEvents: 6 event constructors (userMessage, interrupt, toolConfirmation, customToolResult, toolResult, defineOutcome) - async support via client.async(Supplier) + CompletableFuture - ContentBlock polymorphic deserialization (text/image/audio/data/file/refusal/error) E2e verified: 18/18 tests pass via JAR against real API. Unit tests: 52 tests + 163 total SDK tests pass. Google Java Format: clean. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5eed3ed commit 895263e

48 files changed

Lines changed: 4661 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

samples/AgentStudioSimple.java

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import com.alibaba.dashscope.agentstudio.AgentStudioClient;
2+
import com.alibaba.dashscope.agentstudio.message.ClientEvents;
3+
import com.alibaba.dashscope.agentstudio.message.ContentBlock;
4+
import com.alibaba.dashscope.agentstudio.message.Message;
5+
import com.alibaba.dashscope.agentstudio.model.Agent;
6+
import com.alibaba.dashscope.agentstudio.model.Session;
7+
import com.alibaba.dashscope.agentstudio.param.AgentCreateParam;
8+
import com.alibaba.dashscope.agentstudio.param.SessionCreateParam;
9+
import com.alibaba.dashscope.agentstudio.resource.AgentStudioEventStream;
10+
import com.google.gson.JsonObject;
11+
import java.util.Collections;
12+
13+
/**
14+
* AgentStudio quick start: create agent, session, send message, stream reply.
15+
*
16+
* <p>Prerequisites: set DASHSCOPE_API_KEY env var, and either DASHSCOPE_WORKSPACE env var or pass
17+
* workspace to the constructor.
18+
*
19+
* <pre>
20+
* export DASHSCOPE_API_KEY=sk-xxx
21+
* export DASHSCOPE_WORKSPACE=ws_xxxxxxxxxxxx
22+
* </pre>
23+
*/
24+
public class AgentStudioSimple {
25+
26+
public static void main(String[] args) throws Exception {
27+
// Option 1: apiKey + workspace (production)
28+
AgentStudioClient client = new AgentStudioClient("sk-xxx", "ws_xxxxxxxxxxxx");
29+
30+
// Option 2: all from env vars (DASHSCOPE_API_KEY + DASHSCOPE_WORKSPACE)
31+
// AgentStudioClient client = new AgentStudioClient();
32+
33+
// Option 3: custom base URL
34+
// AgentStudioClient client = AgentStudioClient.builder()
35+
// .apiKey("sk-xxx")
36+
// .baseUrl("https://your-custom-host/api/v1/agentstudio")
37+
// .build();
38+
39+
// 1. Create Agent
40+
Agent agent =
41+
client
42+
.agents()
43+
.create(
44+
AgentCreateParam.builder()
45+
.name("demo-agent")
46+
.model("qwen-plus")
47+
.systemPrompt("你是一个简洁的助手。")
48+
.build());
49+
System.out.println("Agent: " + agent.getId());
50+
51+
// 2. Create Session
52+
Session session =
53+
client.sessions().create(SessionCreateParam.builder().agent(agent.getId()).build());
54+
System.out.println("Session: " + session.getId());
55+
56+
// 3. Send message
57+
client
58+
.sessions()
59+
.events()
60+
.send(session.getId(), Collections.singletonList(ClientEvents.userMessage("你好")));
61+
62+
// 4. Stream reply
63+
try (AgentStudioEventStream stream = client.sessions().events().stream(session.getId())) {
64+
for (Message event : stream) {
65+
if ("message".equals(event.getType()) && event.getContent() != null) {
66+
for (ContentBlock block : event.getContent()) {
67+
if (block instanceof ContentBlock.Text) {
68+
System.out.print(((ContentBlock.Text) block).getText());
69+
}
70+
}
71+
} else if ("session_status".equals(event.getType())) {
72+
JsonObject stopReason = event.getStopReason();
73+
if (stopReason != null) {
74+
System.out.println("\nstop_reason: " + stopReason.get("type"));
75+
}
76+
break;
77+
}
78+
}
79+
}
80+
81+
// Cleanup
82+
client.sessions().delete(session.getId());
83+
client.agents().archive(agent.getId());
84+
client.close();
85+
}
86+
}
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
// Copyright (c) Alibaba, Inc. and its affiliates.
2+
package com.alibaba.dashscope.agentstudio;
3+
4+
import com.alibaba.dashscope.agentstudio.resource.Agents;
5+
import com.alibaba.dashscope.agentstudio.resource.Environments;
6+
import com.alibaba.dashscope.agentstudio.resource.Files;
7+
import com.alibaba.dashscope.agentstudio.resource.Sessions;
8+
import com.alibaba.dashscope.agentstudio.resource.Skills;
9+
import com.alibaba.dashscope.agentstudio.resource.Vaults;
10+
import com.alibaba.dashscope.protocol.ConnectionOptions;
11+
import com.alibaba.dashscope.utils.Constants;
12+
import java.io.Closeable;
13+
import java.util.concurrent.CompletableFuture;
14+
import java.util.concurrent.ExecutorService;
15+
import java.util.concurrent.ForkJoinPool;
16+
import java.util.function.Supplier;
17+
18+
public class AgentStudioClient implements Closeable {
19+
private final Agents agents;
20+
private final Sessions sessions;
21+
private final Environments environments;
22+
private final Skills skills;
23+
private final Vaults vaults;
24+
private final Files files;
25+
private final String baseUrl;
26+
private final ExecutorService asyncExecutor;
27+
28+
/** All from env vars: DASHSCOPE_API_KEY, DASHSCOPE_WORKSPACE, DASHSCOPE_API_REGION. */
29+
public AgentStudioClient() {
30+
this(null, null, null, null, null);
31+
}
32+
33+
/**
34+
* Workspace only, apiKey from DASHSCOPE_API_KEY env var.
35+
*
36+
* @param workspace workspace ID (required)
37+
*/
38+
public AgentStudioClient(String workspace) {
39+
this(null, null, workspace, null, null);
40+
}
41+
42+
/**
43+
* Most common: apiKey + workspace for production.
44+
*
45+
* @param apiKey API Key (or set DASHSCOPE_API_KEY env var)
46+
* @param workspace workspace ID (required)
47+
*/
48+
public AgentStudioClient(String apiKey, String workspace) {
49+
this(apiKey, null, workspace, null, null);
50+
}
51+
52+
/**
53+
* Full constructor — prefer {@link #builder()} for readability.
54+
*
55+
* @param apiKey API Key (nullable → read from DASHSCOPE_API_KEY)
56+
* @param baseUrl full base URL (nullable → use workspace + region)
57+
* @param workspace workspace ID (required if baseUrl is null)
58+
* @param region region (nullable → default cn-beijing)
59+
* @param connectionOptions timeout / retry config (nullable)
60+
*/
61+
public AgentStudioClient(
62+
String apiKey,
63+
String baseUrl,
64+
String workspace,
65+
String region,
66+
ConnectionOptions connectionOptions) {
67+
if (apiKey != null && !apiKey.isEmpty()) {
68+
Constants.apiKey = apiKey;
69+
}
70+
this.baseUrl = resolveBaseUrl(baseUrl, workspace, region);
71+
this.agents = new Agents(this.baseUrl, connectionOptions);
72+
this.sessions = new Sessions(this.baseUrl, connectionOptions);
73+
this.environments = new Environments(this.baseUrl, connectionOptions);
74+
this.skills = new Skills(this.baseUrl, connectionOptions);
75+
this.vaults = new Vaults(this.baseUrl, connectionOptions);
76+
this.files = new Files(this.baseUrl, connectionOptions);
77+
this.asyncExecutor = ForkJoinPool.commonPool();
78+
}
79+
80+
/** Builder for advanced use cases (pre-env baseUrl, custom region, etc.). */
81+
public static Builder builder() {
82+
return new Builder();
83+
}
84+
85+
public static class Builder {
86+
private String apiKey;
87+
private String baseUrl;
88+
private String workspace;
89+
private String region;
90+
private ConnectionOptions connectionOptions;
91+
92+
public Builder apiKey(String apiKey) {
93+
this.apiKey = apiKey;
94+
return this;
95+
}
96+
97+
public Builder baseUrl(String baseUrl) {
98+
this.baseUrl = baseUrl;
99+
return this;
100+
}
101+
102+
public Builder workspace(String workspace) {
103+
this.workspace = workspace;
104+
return this;
105+
}
106+
107+
public Builder region(String region) {
108+
this.region = region;
109+
return this;
110+
}
111+
112+
public Builder connectionOptions(ConnectionOptions connectionOptions) {
113+
this.connectionOptions = connectionOptions;
114+
return this;
115+
}
116+
117+
public AgentStudioClient build() {
118+
return new AgentStudioClient(apiKey, baseUrl, workspace, region, connectionOptions);
119+
}
120+
}
121+
122+
public Agents agents() {
123+
return agents;
124+
}
125+
126+
public Sessions sessions() {
127+
return sessions;
128+
}
129+
130+
public Environments environments() {
131+
return environments;
132+
}
133+
134+
public Skills skills() {
135+
return skills;
136+
}
137+
138+
public Vaults vaults() {
139+
return vaults;
140+
}
141+
142+
public Files files() {
143+
return files;
144+
}
145+
146+
public String getBaseUrl() {
147+
return baseUrl;
148+
}
149+
150+
ExecutorService getAsyncExecutor() {
151+
return asyncExecutor;
152+
}
153+
154+
/**
155+
* Run any sync SDK call asynchronously.
156+
*
157+
* <pre>{@code
158+
* client.async(() -> client.agents().create(param))
159+
* .thenAccept(agent -> System.out.println(agent.getId()));
160+
* }</pre>
161+
*/
162+
public <T> CompletableFuture<T> async(Supplier<T> supplier) {
163+
return CompletableFuture.supplyAsync(supplier, asyncExecutor);
164+
}
165+
166+
@Override
167+
public void close() {
168+
sessions.events().close();
169+
asyncExecutor.shutdownNow();
170+
}
171+
172+
private static String resolveBaseUrl(String explicitUrl, String workspace, String region) {
173+
if (explicitUrl != null && !explicitUrl.isEmpty()) {
174+
return explicitUrl;
175+
}
176+
String envUrl = System.getenv(AgentStudioConstants.ENV_BASE_URL);
177+
if (envUrl == null || envUrl.isEmpty()) {
178+
envUrl = System.getenv(AgentStudioConstants.ENV_BASE_URL_ALT);
179+
}
180+
if (envUrl != null && !envUrl.isEmpty()) {
181+
return envUrl;
182+
}
183+
String ws = workspace;
184+
if (ws == null || ws.isEmpty()) {
185+
ws = System.getenv(AgentStudioConstants.ENV_WORKSPACE);
186+
}
187+
if (ws == null || ws.isEmpty()) {
188+
throw new IllegalArgumentException(
189+
"workspace is required. Pass it to the constructor, "
190+
+ "set DASHSCOPE_WORKSPACE env var, or set DASHSCOPE_AGENTSTUDIO_URL.");
191+
}
192+
String r = region;
193+
if (r == null || r.isEmpty()) {
194+
r = System.getenv(AgentStudioConstants.ENV_REGION);
195+
}
196+
if (r == null || r.isEmpty()) {
197+
r = AgentStudioConstants.DEFAULT_REGION;
198+
}
199+
return String.format(AgentStudioConstants.BASE_URL_TEMPLATE, ws, r);
200+
}
201+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) Alibaba, Inc. and its affiliates.
2+
package com.alibaba.dashscope.agentstudio;
3+
4+
public final class AgentStudioConstants {
5+
public static final String BASE_URL_TEMPLATE =
6+
"https://%s.%s.maas.aliyuncs.com/api/v1/agentstudio";
7+
public static final String DEFAULT_REGION = "cn-beijing";
8+
public static final int DEFAULT_TIMEOUT_MS = 600_000;
9+
public static final int DEFAULT_CONNECT_TIMEOUT_MS = 10_000;
10+
public static final String ENV_BASE_URL = "DASHSCOPE_AGENTSTUDIO_URL";
11+
public static final String ENV_BASE_URL_ALT = "AGENTSTUDIO_URL";
12+
public static final String ENV_WORKSPACE = "DASHSCOPE_WORKSPACE";
13+
public static final String ENV_REGION = "DASHSCOPE_API_REGION";
14+
15+
private AgentStudioConstants() {}
16+
}

0 commit comments

Comments
 (0)