diff --git a/README.md b/README.md index 4c169ca..33fef68 100644 --- a/README.md +++ b/README.md @@ -227,4 +227,6 @@ service.shutdownExecutor(); ## License -Apache License 2.0. +This project is licensed under the Apache License 2.0. See [LICENSE](./LICENSE). +For third-party open-source software notices, see +[THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md). diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md new file mode 100644 index 0000000..b45aae7 --- /dev/null +++ b/THIRD_PARTY_NOTICES.md @@ -0,0 +1,41 @@ +# Third-Party Notices + +This repository contains code that is derived from or structurally adapted +from third-party open-source projects. + +## Anthropic self-hosted worker SDK + +Portions of the self-hosted worker lifecycle and local agent tool +implementations under +`src/main/java/com/volcengine/ark/runtime/selfhosted`, including the work +poller, environment worker, session tool runner, skill initializer, local +tools, and tool-result store, are structurally adapted from Anthropic's +self-hosted worker SDK implementations: + +- https://github.com/anthropics/anthropic-sdk-go +- https://github.com/anthropics/anthropic-sdk-python + +The upstream projects are licensed under the MIT License. The MIT copyright +and permission notice is preserved below as required by that license. + +```text +Copyright 2023 Anthropic, PBC. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` diff --git a/examples/README.md b/examples/README.md index 364c223..c3683c6 100644 --- a/examples/README.md +++ b/examples/README.md @@ -28,6 +28,9 @@ pom on purpose — install the SDK first, then build the examples separately. | `EnvironmentsLifecycleExample` | Managed-Agents: Environment lifecycle — Create/Get/List/Update/Delete (cloud + unrestricted networking) | | `SessionsLoopExample` | Managed-Agents: end-to-end agent loop — Agent + Env + Session, send user.message, stream events until idle | | `MemoryStoresLifecycleExample` | Managed-Agents: MemoryStore + nested Memory CRUD | +| `SelfHostedWorkerExample` | Managed-Agents: self-hosted worker poll / handle loop | + +`SelfHostedWorkerExample` uses the client's production default `https://ark.cn-beijing.volces.com/api/v3`. The Managed-Agents examples additionally accept `ARK_MODEL_ID` for the model id (falls back to a `${YOUR_MODEL_ID}` placeholder that will 400 at runtime). diff --git a/examples/src/main/java/com/volcengine/ark/runtime/examples/SelfHostedWorkerExample.java b/examples/src/main/java/com/volcengine/ark/runtime/examples/SelfHostedWorkerExample.java new file mode 100644 index 0000000..ad9f667 --- /dev/null +++ b/examples/src/main/java/com/volcengine/ark/runtime/examples/SelfHostedWorkerExample.java @@ -0,0 +1,63 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +// Retrofit 2.9 may report an illegal reflective access warning on JDK 9-16. +// The warning is not a startup failure. To suppress it when running with Maven: +// +// MAVEN_OPTS="--add-opens=java.base/java.lang.invoke=ALL-UNNAMED" \ +// mvn -q -f examples/pom.xml exec:java \ +// -Dexec.mainClass=com.volcengine.ark.runtime.examples.SelfHostedWorkerExample + +package com.volcengine.ark.runtime.examples; + +import com.volcengine.ark.runtime.selfhosted.EnvironmentWorker; +import com.volcengine.ark.runtime.selfhosted.SelfHostedClient; + +public final class SelfHostedWorkerExample { + private SelfHostedWorkerExample() { + } + + public static void main(String[] args) { + String environmentId = requiredEnv("MA_ENVIRONMENT_ID"); + String workerId = System.getenv("MA_WORKER_ID"); + String workdir = envOrDefault("MA_WORKDIR", "."); + SelfHostedClient.Builder clientBuilder = new SelfHostedClient.Builder() + .apiKey(requiredEnv("ARK_API_KEY")); + String baseUrl = System.getenv("ARK_BASE_URL"); + if (baseUrl != null && !baseUrl.isEmpty()) { + clientBuilder.baseUrl(baseUrl); + } + System.out.printf( + "starting self-hosted worker base_url=%s environment_id=%s worker_id=%s workdir=%s%n", + baseUrl == null || baseUrl.isEmpty() ? SelfHostedClient.DEFAULT_BASE_URL : baseUrl, + environmentId, + workerId == null || workerId.isEmpty() ? "auto" : workerId, + workdir); + SelfHostedClient client = clientBuilder.build(); + EnvironmentWorker worker = new EnvironmentWorker( + client, + new EnvironmentWorker.Options() + .environmentId(environmentId) + .workerId(workerId) + .workdir(workdir)); + Runtime.getRuntime().addShutdownHook(new Thread(worker::close, "ark-self-hosted-worker-shutdown")); + try { + worker.run(); + } finally { + worker.close(); + } + } + + private static String requiredEnv(String name) { + String value = System.getenv(name); + if (value == null || value.isEmpty()) { + throw new IllegalArgumentException(name + " is required"); + } + return value; + } + + private static String envOrDefault(String name, String defaultValue) { + String value = System.getenv(name); + return value == null || value.isEmpty() ? defaultValue : value; + } +} diff --git a/examples/src/main/java/com/volcengine/ark/runtime/examples/SessionsLoopExample.java b/examples/src/main/java/com/volcengine/ark/runtime/examples/SessionsLoopExample.java index 9d972c2..cf21485 100644 --- a/examples/src/main/java/com/volcengine/ark/runtime/examples/SessionsLoopExample.java +++ b/examples/src/main/java/com/volcengine/ark/runtime/examples/SessionsLoopExample.java @@ -13,15 +13,15 @@ import com.volcengine.ark.runtime.models.environment.NetworkingConfig; import com.volcengine.ark.runtime.models.environment.NetworkingType; import com.volcengine.ark.runtime.models.session.AgentIdentifier; +import com.volcengine.ark.runtime.models.session.ContentBlockType; import com.volcengine.ark.runtime.models.session.CreateSessionRequest; -import com.volcengine.ark.runtime.models.session.IncomingEventParams; -import com.volcengine.ark.runtime.models.session.IncomingEventParamsType; +import com.volcengine.ark.runtime.models.session.ManagedAgentsEventParams; +import com.volcengine.ark.runtime.models.session.ManagedAgentsEventParamsType; +import com.volcengine.ark.runtime.models.session.ManagedAgentsMessageContentBlock; +import com.volcengine.ark.runtime.models.session.ManagedAgentsTextBlock; +import com.volcengine.ark.runtime.models.session.ManagedAgentsUserMessageEventParams; import com.volcengine.ark.runtime.models.session.SendSessionEventsRequest; import com.volcengine.ark.runtime.models.session.Session; -import com.volcengine.ark.runtime.models.session.TurnInputContent; -import com.volcengine.ark.runtime.models.session.TurnInputContentType; -import com.volcengine.ark.runtime.models.session.TurnInputTextContent; -import com.volcengine.ark.runtime.models.session.UserMessageEventParams; import com.volcengine.ark.runtime.service.ArkService; import java.io.BufferedReader; import java.io.InputStreamReader; @@ -103,14 +103,14 @@ public static void main(String[] args) throws Exception { Thread sender = new Thread(() -> { try { Thread.sleep(500); - TurnInputTextContent textBlock = new TurnInputTextContent(); - textBlock.setType(TurnInputContentType.TEXT); + ManagedAgentsTextBlock textBlock = new ManagedAgentsTextBlock(); + textBlock.setType(ContentBlockType.TEXT); textBlock.setText("What's the tallest mountain? One sentence."); - UserMessageEventParams msg = new UserMessageEventParams(); - msg.setType(IncomingEventParamsType.USER_MESSAGE); - msg.setContent(Arrays.asList(textBlock)); + ManagedAgentsUserMessageEventParams msg = new ManagedAgentsUserMessageEventParams(); + msg.setType(ManagedAgentsEventParamsType.USER_MESSAGE); + msg.setContent(Arrays.asList(textBlock)); SendSessionEventsRequest req = new SendSessionEventsRequest(); - req.setEvents(Arrays.asList(msg)); + req.setEvents(Arrays.asList(msg)); service.sendSessionEvents(sess.getId(), req); } catch (InterruptedException e) { Thread.currentThread().interrupt(); diff --git a/pom.xml b/pom.xml index d1ca07c..027c94c 100644 --- a/pom.xml +++ b/pom.xml @@ -48,6 +48,7 @@ 2.0.0 4.12.0 1.84 + 4.13.2 @@ -117,12 +118,23 @@ junit junit - 4.13.2 + ${junit-version} test + + + ${project.basedir} + META-INF + false + + LICENSE + THIRD_PARTY_NOTICES.md + + + org.apache.maven.plugins diff --git a/src/main/java/com/volcengine/ark/runtime/models/agent/Agent.java b/src/main/java/com/volcengine/ark/runtime/models/agent/Agent.java index cddbad9..090021a 100644 --- a/src/main/java/com/volcengine/ark/runtime/models/agent/Agent.java +++ b/src/main/java/com/volcengine/ark/runtime/models/agent/Agent.java @@ -44,6 +44,7 @@ Agent.JSON_PROPERTY_MULTIAGENT, Agent.JSON_PROPERTY_METADATA, Agent.JSON_PROPERTY_TAGS, + Agent.JSON_PROPERTY_DISPLAY_NAME, Agent.JSON_PROPERTY_CREATED_AT, Agent.JSON_PROPERTY_UPDATED_AT }) @@ -134,6 +135,10 @@ public static TypeEnum fromValue(String value) { @javax.annotation.Nullable private List tags; + public static final String JSON_PROPERTY_DISPLAY_NAME = "display_name"; + @javax.annotation.Nullable + private String displayName; + public static final String JSON_PROPERTY_CREATED_AT = "created_at"; @javax.annotation.Nonnull private String createdAt; @@ -510,6 +515,31 @@ public void setTags(@javax.annotation.Nullable List tags) { this.tags = tags; } + public Agent displayName(@javax.annotation.Nullable String displayName) { + + this.displayName = displayName; + return this; + } + + /** + * 展示名。 + * @return displayName + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_DISPLAY_NAME, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getDisplayName() { + return displayName; + } + + + @JsonProperty(value = JSON_PROPERTY_DISPLAY_NAME, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setDisplayName(@javax.annotation.Nullable String displayName) { + this.displayName = displayName; + } + public Agent createdAt(@javax.annotation.Nonnull String createdAt) { this.createdAt = createdAt; @@ -583,13 +613,14 @@ public boolean equals(Object o) { Objects.equals(this.multiagent, agent.multiagent) && Objects.equals(this.metadata, agent.metadata) && Objects.equals(this.tags, agent.tags) && + Objects.equals(this.displayName, agent.displayName) && Objects.equals(this.createdAt, agent.createdAt) && Objects.equals(this.updatedAt, agent.updatedAt); } @Override public int hashCode() { - return Objects.hash(id, type, name, description, version, model, system, tools, mcpServers, skills, multiagent, metadata, tags, createdAt, updatedAt); + return Objects.hash(id, type, name, description, version, model, system, tools, mcpServers, skills, multiagent, metadata, tags, displayName, createdAt, updatedAt); } @Override @@ -609,6 +640,7 @@ public String toString() { sb.append(" multiagent: ").append(toIndentedString(multiagent)).append("\n"); sb.append(" metadata: ").append(toIndentedString(metadata)).append("\n"); sb.append(" tags: ").append(toIndentedString(tags)).append("\n"); + sb.append(" displayName: ").append(toIndentedString(displayName)).append("\n"); sb.append(" createdAt: ").append(toIndentedString(createdAt)).append("\n"); sb.append(" updatedAt: ").append(toIndentedString(updatedAt)).append("\n"); sb.append("}"); @@ -687,6 +719,10 @@ public Agent.Builder tags(List tags) { this.instance.tags = tags; return this; } + public Agent.Builder displayName(String displayName) { + this.instance.displayName = displayName; + return this; + } public Agent.Builder createdAt(String createdAt) { this.instance.createdAt = createdAt; return this; @@ -742,6 +778,7 @@ public Agent.Builder toBuilder() { .multiagent(getMultiagent()) .metadata(getMetadata()) .tags(getTags()) + .displayName(getDisplayName()) .createdAt(getCreatedAt()) .updatedAt(getUpdatedAt()); } diff --git a/src/main/java/com/volcengine/ark/runtime/models/agent/AgentRef.java b/src/main/java/com/volcengine/ark/runtime/models/agent/AgentRef.java index 45c8187..003a1a6 100644 --- a/src/main/java/com/volcengine/ark/runtime/models/agent/AgentRef.java +++ b/src/main/java/com/volcengine/ark/runtime/models/agent/AgentRef.java @@ -19,6 +19,8 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; /** @@ -27,7 +29,15 @@ @JsonPropertyOrder({ AgentRef.JSON_PROPERTY_TYPE, AgentRef.JSON_PROPERTY_ID, - AgentRef.JSON_PROPERTY_VERSION + AgentRef.JSON_PROPERTY_VERSION, + AgentRef.JSON_PROPERTY_NAME, + AgentRef.JSON_PROPERTY_DESCRIPTION, + AgentRef.JSON_PROPERTY_MODEL, + AgentRef.JSON_PROPERTY_SYSTEM, + AgentRef.JSON_PROPERTY_TOOLS, + AgentRef.JSON_PROPERTY_MCP_SERVERS, + AgentRef.JSON_PROPERTY_SKILLS, + AgentRef.JSON_PROPERTY_DISPLAY_NAME }) @javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") public class AgentRef { @@ -43,6 +53,38 @@ public class AgentRef { @javax.annotation.Nullable private Integer version; + public static final String JSON_PROPERTY_NAME = "name"; + @javax.annotation.Nullable + private String name; + + public static final String JSON_PROPERTY_DESCRIPTION = "description"; + @javax.annotation.Nullable + private String description; + + public static final String JSON_PROPERTY_MODEL = "model"; + @javax.annotation.Nullable + private ModelConfig model; + + public static final String JSON_PROPERTY_SYSTEM = "system"; + @javax.annotation.Nullable + private String system; + + public static final String JSON_PROPERTY_TOOLS = "tools"; + @javax.annotation.Nullable + private List tools; + + public static final String JSON_PROPERTY_MCP_SERVERS = "mcp_servers"; + @javax.annotation.Nullable + private List mcpServers; + + public static final String JSON_PROPERTY_SKILLS = "skills"; + @javax.annotation.Nullable + private List skills; + + public static final String JSON_PROPERTY_DISPLAY_NAME = "display_name"; + @javax.annotation.Nullable + private String displayName; + public AgentRef() { } @@ -121,6 +163,230 @@ public void setVersion(@javax.annotation.Nullable Integer version) { this.version = version; } + public AgentRef name(@javax.annotation.Nullable String name) { + + this.name = name; + return this; + } + + /** + * Session 响应中冻结的成员 Agent 名称。 + * @return name + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_NAME, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getName() { + return name; + } + + + @JsonProperty(value = JSON_PROPERTY_NAME, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setName(@javax.annotation.Nullable String name) { + this.name = name; + } + + public AgentRef description(@javax.annotation.Nullable String description) { + + this.description = description; + return this; + } + + /** + * Session 响应中冻结的成员 Agent 描述。 + * @return description + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_DESCRIPTION, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getDescription() { + return description; + } + + + @JsonProperty(value = JSON_PROPERTY_DESCRIPTION, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setDescription(@javax.annotation.Nullable String description) { + this.description = description; + } + + public AgentRef model(@javax.annotation.Nullable ModelConfig model) { + + this.model = model; + return this; + } + + /** + * Session 响应中冻结的成员 Agent 模型配置。 + * @return model + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_MODEL, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public ModelConfig getModel() { + return model; + } + + + @JsonProperty(value = JSON_PROPERTY_MODEL, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setModel(@javax.annotation.Nullable ModelConfig model) { + this.model = model; + } + + public AgentRef system(@javax.annotation.Nullable String system) { + + this.system = system; + return this; + } + + /** + * Session 响应中冻结的成员 Agent system prompt。 + * @return system + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_SYSTEM, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getSystem() { + return system; + } + + + @JsonProperty(value = JSON_PROPERTY_SYSTEM, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setSystem(@javax.annotation.Nullable String system) { + this.system = system; + } + + public AgentRef tools(@javax.annotation.Nullable List tools) { + + this.tools = tools; + return this; + } + + public AgentRef addToolsItem(ToolItem toolsItem) { + if (this.tools == null) { + this.tools = new ArrayList<>(); + } + this.tools.add(toolsItem); + return this; + } + + /** + * Session 响应中冻结的成员 Agent 工具配置。 + * @return tools + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_TOOLS, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + + public List getTools() { + return tools; + } + + + @JsonProperty(value = JSON_PROPERTY_TOOLS, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + public void setTools(@javax.annotation.Nullable List tools) { + this.tools = tools; + } + + public AgentRef mcpServers(@javax.annotation.Nullable List mcpServers) { + + this.mcpServers = mcpServers; + return this; + } + + public AgentRef addMcpServersItem(MCPServer mcpServersItem) { + if (this.mcpServers == null) { + this.mcpServers = new ArrayList<>(); + } + this.mcpServers.add(mcpServersItem); + return this; + } + + /** + * Session 响应中冻结的成员 Agent MCP servers。 + * @return mcpServers + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_MCP_SERVERS, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + + public List getMcpServers() { + return mcpServers; + } + + + @JsonProperty(value = JSON_PROPERTY_MCP_SERVERS, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + public void setMcpServers(@javax.annotation.Nullable List mcpServers) { + this.mcpServers = mcpServers; + } + + public AgentRef skills(@javax.annotation.Nullable List skills) { + + this.skills = skills; + return this; + } + + public AgentRef addSkillsItem(AgentSkillRef skillsItem) { + if (this.skills == null) { + this.skills = new ArrayList<>(); + } + this.skills.add(skillsItem); + return this; + } + + /** + * Session 响应中冻结的成员 Agent skills。 + * @return skills + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_SKILLS, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + + public List getSkills() { + return skills; + } + + + @JsonProperty(value = JSON_PROPERTY_SKILLS, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + public void setSkills(@javax.annotation.Nullable List skills) { + this.skills = skills; + } + + public AgentRef displayName(@javax.annotation.Nullable String displayName) { + + this.displayName = displayName; + return this; + } + + /** + * Session 响应中冻结的成员 Agent 展示名。 + * @return displayName + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_DISPLAY_NAME, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getDisplayName() { + return displayName; + } + + + @JsonProperty(value = JSON_PROPERTY_DISPLAY_NAME, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setDisplayName(@javax.annotation.Nullable String displayName) { + this.displayName = displayName; + } + @Override public boolean equals(Object o) { @@ -133,12 +399,20 @@ public boolean equals(Object o) { AgentRef agentRef = (AgentRef) o; return Objects.equals(this.type, agentRef.type) && Objects.equals(this.id, agentRef.id) && - Objects.equals(this.version, agentRef.version); + Objects.equals(this.version, agentRef.version) && + Objects.equals(this.name, agentRef.name) && + Objects.equals(this.description, agentRef.description) && + Objects.equals(this.model, agentRef.model) && + Objects.equals(this.system, agentRef.system) && + Objects.equals(this.tools, agentRef.tools) && + Objects.equals(this.mcpServers, agentRef.mcpServers) && + Objects.equals(this.skills, agentRef.skills) && + Objects.equals(this.displayName, agentRef.displayName); } @Override public int hashCode() { - return Objects.hash(type, id, version); + return Objects.hash(type, id, version, name, description, model, system, tools, mcpServers, skills, displayName); } @Override @@ -148,6 +422,14 @@ public String toString() { sb.append(" type: ").append(toIndentedString(type)).append("\n"); sb.append(" id: ").append(toIndentedString(id)).append("\n"); sb.append(" version: ").append(toIndentedString(version)).append("\n"); + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" description: ").append(toIndentedString(description)).append("\n"); + sb.append(" model: ").append(toIndentedString(model)).append("\n"); + sb.append(" system: ").append(toIndentedString(system)).append("\n"); + sb.append(" tools: ").append(toIndentedString(tools)).append("\n"); + sb.append(" mcpServers: ").append(toIndentedString(mcpServers)).append("\n"); + sb.append(" skills: ").append(toIndentedString(skills)).append("\n"); + sb.append(" displayName: ").append(toIndentedString(displayName)).append("\n"); sb.append("}"); return sb.toString(); } @@ -184,6 +466,38 @@ public AgentRef.Builder version(Integer version) { this.instance.version = version; return this; } + public AgentRef.Builder name(String name) { + this.instance.name = name; + return this; + } + public AgentRef.Builder description(String description) { + this.instance.description = description; + return this; + } + public AgentRef.Builder model(ModelConfig model) { + this.instance.model = model; + return this; + } + public AgentRef.Builder system(String system) { + this.instance.system = system; + return this; + } + public AgentRef.Builder tools(List tools) { + this.instance.tools = tools; + return this; + } + public AgentRef.Builder mcpServers(List mcpServers) { + this.instance.mcpServers = mcpServers; + return this; + } + public AgentRef.Builder skills(List skills) { + this.instance.skills = skills; + return this; + } + public AgentRef.Builder displayName(String displayName) { + this.instance.displayName = displayName; + return this; + } /** @@ -220,7 +534,15 @@ public AgentRef.Builder toBuilder() { return new AgentRef.Builder() .type(getType()) .id(getId()) - .version(getVersion()); + .version(getVersion()) + .name(getName()) + .description(getDescription()) + .model(getModel()) + .system(getSystem()) + .tools(getTools()) + .mcpServers(getMcpServers()) + .skills(getSkills()) + .displayName(getDisplayName()); } diff --git a/src/main/java/com/volcengine/ark/runtime/models/agent/AgentSkillRef.java b/src/main/java/com/volcengine/ark/runtime/models/agent/AgentSkillRef.java index 136e2bf..05c5bdc 100644 --- a/src/main/java/com/volcengine/ark/runtime/models/agent/AgentSkillRef.java +++ b/src/main/java/com/volcengine/ark/runtime/models/agent/AgentSkillRef.java @@ -27,7 +27,8 @@ @JsonPropertyOrder({ AgentSkillRef.JSON_PROPERTY_TYPE, AgentSkillRef.JSON_PROPERTY_SKILL_ID, - AgentSkillRef.JSON_PROPERTY_VERSION + AgentSkillRef.JSON_PROPERTY_VERSION, + AgentSkillRef.JSON_PROPERTY_USE_LATEST }) @javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") public class AgentSkillRef { @@ -43,6 +44,10 @@ public class AgentSkillRef { @javax.annotation.Nullable private String version; + public static final String JSON_PROPERTY_USE_LATEST = "use_latest"; + @javax.annotation.Nullable + private Boolean useLatest; + public AgentSkillRef() { } @@ -121,6 +126,31 @@ public void setVersion(@javax.annotation.Nullable String version) { this.version = version; } + public AgentSkillRef useLatest(@javax.annotation.Nullable Boolean useLatest) { + + this.useLatest = useLatest; + return this; + } + + /** + * Session 快照中标识创建时用户选择的是使用最新版本。 + * @return useLatest + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_USE_LATEST, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public Boolean getUseLatest() { + return useLatest; + } + + + @JsonProperty(value = JSON_PROPERTY_USE_LATEST, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setUseLatest(@javax.annotation.Nullable Boolean useLatest) { + this.useLatest = useLatest; + } + @Override public boolean equals(Object o) { @@ -133,12 +163,13 @@ public boolean equals(Object o) { AgentSkillRef agentSkillRef = (AgentSkillRef) o; return Objects.equals(this.type, agentSkillRef.type) && Objects.equals(this.skillId, agentSkillRef.skillId) && - Objects.equals(this.version, agentSkillRef.version); + Objects.equals(this.version, agentSkillRef.version) && + Objects.equals(this.useLatest, agentSkillRef.useLatest); } @Override public int hashCode() { - return Objects.hash(type, skillId, version); + return Objects.hash(type, skillId, version, useLatest); } @Override @@ -148,6 +179,7 @@ public String toString() { sb.append(" type: ").append(toIndentedString(type)).append("\n"); sb.append(" skillId: ").append(toIndentedString(skillId)).append("\n"); sb.append(" version: ").append(toIndentedString(version)).append("\n"); + sb.append(" useLatest: ").append(toIndentedString(useLatest)).append("\n"); sb.append("}"); return sb.toString(); } @@ -184,6 +216,10 @@ public AgentSkillRef.Builder version(String version) { this.instance.version = version; return this; } + public AgentSkillRef.Builder useLatest(Boolean useLatest) { + this.instance.useLatest = useLatest; + return this; + } /** @@ -220,7 +256,8 @@ public AgentSkillRef.Builder toBuilder() { return new AgentSkillRef.Builder() .type(getType()) .skillId(getSkillId()) - .version(getVersion()); + .version(getVersion()) + .useLatest(getUseLatest()); } diff --git a/src/main/java/com/volcengine/ark/runtime/models/agent/CreateAgentRequest.java b/src/main/java/com/volcengine/ark/runtime/models/agent/CreateAgentRequest.java index a812c48..e5fb8c3 100644 --- a/src/main/java/com/volcengine/ark/runtime/models/agent/CreateAgentRequest.java +++ b/src/main/java/com/volcengine/ark/runtime/models/agent/CreateAgentRequest.java @@ -32,6 +32,7 @@ CreateAgentRequest.JSON_PROPERTY_NAME, CreateAgentRequest.JSON_PROPERTY_MODEL, CreateAgentRequest.JSON_PROPERTY_DESCRIPTION, + CreateAgentRequest.JSON_PROPERTY_DISPLAY_NAME, CreateAgentRequest.JSON_PROPERTY_SYSTEM, CreateAgentRequest.JSON_PROPERTY_MCP_SERVERS, CreateAgentRequest.JSON_PROPERTY_TOOLS, @@ -54,6 +55,10 @@ public class CreateAgentRequest { @javax.annotation.Nullable private String description; + public static final String JSON_PROPERTY_DISPLAY_NAME = "display_name"; + @javax.annotation.Nullable + private String displayName; + public static final String JSON_PROPERTY_SYSTEM = "system"; @javax.annotation.Nullable private String system; @@ -160,6 +165,31 @@ public void setDescription(@javax.annotation.Nullable String description) { this.description = description; } + public CreateAgentRequest displayName(@javax.annotation.Nullable String displayName) { + + this.displayName = displayName; + return this; + } + + /** + * 展示名。 + * @return displayName + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_DISPLAY_NAME, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getDisplayName() { + return displayName; + } + + + @JsonProperty(value = JSON_PROPERTY_DISPLAY_NAME, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setDisplayName(@javax.annotation.Nullable String displayName) { + this.displayName = displayName; + } + public CreateAgentRequest system(@javax.annotation.Nullable String system) { this.system = system; @@ -388,6 +418,7 @@ public boolean equals(Object o) { return Objects.equals(this.name, createAgentRequest.name) && Objects.equals(this.model, createAgentRequest.model) && Objects.equals(this.description, createAgentRequest.description) && + Objects.equals(this.displayName, createAgentRequest.displayName) && Objects.equals(this.system, createAgentRequest.system) && Objects.equals(this.mcpServers, createAgentRequest.mcpServers) && Objects.equals(this.tools, createAgentRequest.tools) && @@ -399,7 +430,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(name, model, description, system, mcpServers, tools, skills, multiagent, metadata, tags); + return Objects.hash(name, model, description, displayName, system, mcpServers, tools, skills, multiagent, metadata, tags); } @Override @@ -409,6 +440,7 @@ public String toString() { sb.append(" name: ").append(toIndentedString(name)).append("\n"); sb.append(" model: ").append(toIndentedString(model)).append("\n"); sb.append(" description: ").append(toIndentedString(description)).append("\n"); + sb.append(" displayName: ").append(toIndentedString(displayName)).append("\n"); sb.append(" system: ").append(toIndentedString(system)).append("\n"); sb.append(" mcpServers: ").append(toIndentedString(mcpServers)).append("\n"); sb.append(" tools: ").append(toIndentedString(tools)).append("\n"); @@ -452,6 +484,10 @@ public CreateAgentRequest.Builder description(String description) { this.instance.description = description; return this; } + public CreateAgentRequest.Builder displayName(String displayName) { + this.instance.displayName = displayName; + return this; + } public CreateAgentRequest.Builder system(String system) { this.instance.system = system; return this; @@ -517,6 +553,7 @@ public CreateAgentRequest.Builder toBuilder() { .name(getName()) .model(getModel()) .description(getDescription()) + .displayName(getDisplayName()) .system(getSystem()) .mcpServers(getMcpServers()) .tools(getTools()) diff --git a/src/main/java/com/volcengine/ark/runtime/models/agent/CustomToolInputSchema.java b/src/main/java/com/volcengine/ark/runtime/models/agent/CustomToolInputSchema.java new file mode 100644 index 0000000..fba0aeb --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/models/agent/CustomToolInputSchema.java @@ -0,0 +1,247 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +/* + * Ark Managed Agents Agent API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.volcengine.ark.runtime.models.agent; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * Custom tool 的输入 JSON Schema。 + */ +@JsonPropertyOrder({ + CustomToolInputSchema.JSON_PROPERTY_TYPE, + CustomToolInputSchema.JSON_PROPERTY_PROPERTIES, + CustomToolInputSchema.JSON_PROPERTY_REQUIRED +}) +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") +public class CustomToolInputSchema { + public static final String JSON_PROPERTY_TYPE = "type"; + @javax.annotation.Nullable + private String type; + + public static final String JSON_PROPERTY_PROPERTIES = "properties"; + @javax.annotation.Nullable + private Map properties; + + public static final String JSON_PROPERTY_REQUIRED = "required"; + @javax.annotation.Nullable + private List required; + + public CustomToolInputSchema() { + } + + public CustomToolInputSchema type(@javax.annotation.Nullable String type) { + + this.type = type; + return this; + } + + /** + * JSON Schema 顶层类型;缺省按 `\"object\"` 处理。 + * @return type + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_TYPE, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getType() { + return type; + } + + + @JsonProperty(value = JSON_PROPERTY_TYPE, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setType(@javax.annotation.Nullable String type) { + this.type = type; + } + + public CustomToolInputSchema properties(@javax.annotation.Nullable Map properties) { + + this.properties = properties; + return this; + } + + public CustomToolInputSchema putPropertiesItem(String key, Object propertiesItem) { + if (this.properties == null) { + this.properties = new HashMap<>(); + } + this.properties.put(key, propertiesItem); + return this; + } + + /** + * JSON Schema properties 对象。 + * @return properties + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_PROPERTIES, required = false) + @JsonInclude(content = JsonInclude.Include.ALWAYS, value = JsonInclude.Include.NON_EMPTY) + + public Map getProperties() { + return properties; + } + + + @JsonProperty(value = JSON_PROPERTY_PROPERTIES, required = false) + @JsonInclude(content = JsonInclude.Include.ALWAYS, value = JsonInclude.Include.NON_EMPTY) + public void setProperties(@javax.annotation.Nullable Map properties) { + this.properties = properties; + } + + public CustomToolInputSchema required(@javax.annotation.Nullable List required) { + + this.required = required; + return this; + } + + public CustomToolInputSchema addRequiredItem(String requiredItem) { + if (this.required == null) { + this.required = new ArrayList<>(); + } + this.required.add(requiredItem); + return this; + } + + /** + * 必填属性名列表。 + * @return required + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_REQUIRED, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + + public List getRequired() { + return required; + } + + + @JsonProperty(value = JSON_PROPERTY_REQUIRED, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + public void setRequired(@javax.annotation.Nullable List required) { + this.required = required; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CustomToolInputSchema customToolInputSchema = (CustomToolInputSchema) o; + return Objects.equals(this.type, customToolInputSchema.type) && + Objects.equals(this.properties, customToolInputSchema.properties) && + Objects.equals(this.required, customToolInputSchema.required); + } + + @Override + public int hashCode() { + return Objects.hash(type, properties, required); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class CustomToolInputSchema {\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" properties: ").append(toIndentedString(properties)).append("\n"); + sb.append(" required: ").append(toIndentedString(required)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + return o == null ? "null" : o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private CustomToolInputSchema instance; + + public Builder() { + this(new CustomToolInputSchema()); + } + + protected Builder(CustomToolInputSchema instance) { + this.instance = instance; + } + + public CustomToolInputSchema.Builder type(String type) { + this.instance.type = type; + return this; + } + public CustomToolInputSchema.Builder properties(Map properties) { + this.instance.properties = properties; + return this; + } + public CustomToolInputSchema.Builder required(List required) { + this.instance.required = required; + return this; + } + + + /** + * returns a built CustomToolInputSchema instance. + * + * The builder is not reusable. + */ + public CustomToolInputSchema build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field. + */ + public static CustomToolInputSchema.Builder builder() { + return new CustomToolInputSchema.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public CustomToolInputSchema.Builder toBuilder() { + return new CustomToolInputSchema.Builder() + .type(getType()) + .properties(getProperties()) + .required(getRequired()); + } + + +} diff --git a/src/main/java/com/volcengine/ark/runtime/models/agent/ListRequest.java b/src/main/java/com/volcengine/ark/runtime/models/agent/ListRequest.java index d1e4e8c..deff74d 100644 --- a/src/main/java/com/volcengine/ark/runtime/models/agent/ListRequest.java +++ b/src/main/java/com/volcengine/ark/runtime/models/agent/ListRequest.java @@ -27,6 +27,8 @@ @JsonPropertyOrder({ ListRequest.JSON_PROPERTY_LIMIT, ListRequest.JSON_PROPERTY_PAGE, + ListRequest.JSON_PROPERTY_NAME, + ListRequest.JSON_PROPERTY_DISPLAY_NAME, ListRequest.JSON_PROPERTY_CREATED_AT_GTE, ListRequest.JSON_PROPERTY_CREATED_AT_LTE }) @@ -40,6 +42,14 @@ public class ListRequest { @javax.annotation.Nullable private String page; + public static final String JSON_PROPERTY_NAME = "name"; + @javax.annotation.Nullable + private String name; + + public static final String JSON_PROPERTY_DISPLAY_NAME = "display_name"; + @javax.annotation.Nullable + private String displayName; + public static final String JSON_PROPERTY_CREATED_AT_GTE = "created_at_gte"; @javax.annotation.Nullable private String createdAtGte; @@ -101,6 +111,56 @@ public void setPage(@javax.annotation.Nullable String page) { this.page = page; } + public ListRequest name(@javax.annotation.Nullable String name) { + + this.name = name; + return this; + } + + /** + * 按名称过滤。 + * @return name + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_NAME, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getName() { + return name; + } + + + @JsonProperty(value = JSON_PROPERTY_NAME, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setName(@javax.annotation.Nullable String name) { + this.name = name; + } + + public ListRequest displayName(@javax.annotation.Nullable String displayName) { + + this.displayName = displayName; + return this; + } + + /** + * 按展示名过滤。 + * @return displayName + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_DISPLAY_NAME, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getDisplayName() { + return displayName; + } + + + @JsonProperty(value = JSON_PROPERTY_DISPLAY_NAME, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setDisplayName(@javax.annotation.Nullable String displayName) { + this.displayName = displayName; + } + public ListRequest createdAtGte(@javax.annotation.Nullable String createdAtGte) { this.createdAtGte = createdAtGte; @@ -163,13 +223,15 @@ public boolean equals(Object o) { ListRequest listRequest = (ListRequest) o; return Objects.equals(this.limit, listRequest.limit) && Objects.equals(this.page, listRequest.page) && + Objects.equals(this.name, listRequest.name) && + Objects.equals(this.displayName, listRequest.displayName) && Objects.equals(this.createdAtGte, listRequest.createdAtGte) && Objects.equals(this.createdAtLte, listRequest.createdAtLte); } @Override public int hashCode() { - return Objects.hash(limit, page, createdAtGte, createdAtLte); + return Objects.hash(limit, page, name, displayName, createdAtGte, createdAtLte); } @Override @@ -178,6 +240,8 @@ public String toString() { sb.append("class ListRequest {\n"); sb.append(" limit: ").append(toIndentedString(limit)).append("\n"); sb.append(" page: ").append(toIndentedString(page)).append("\n"); + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" displayName: ").append(toIndentedString(displayName)).append("\n"); sb.append(" createdAtGte: ").append(toIndentedString(createdAtGte)).append("\n"); sb.append(" createdAtLte: ").append(toIndentedString(createdAtLte)).append("\n"); sb.append("}"); @@ -212,6 +276,14 @@ public ListRequest.Builder page(String page) { this.instance.page = page; return this; } + public ListRequest.Builder name(String name) { + this.instance.name = name; + return this; + } + public ListRequest.Builder displayName(String displayName) { + this.instance.displayName = displayName; + return this; + } public ListRequest.Builder createdAtGte(String createdAtGte) { this.instance.createdAtGte = createdAtGte; return this; @@ -256,6 +328,8 @@ public ListRequest.Builder toBuilder() { return new ListRequest.Builder() .limit(getLimit()) .page(getPage()) + .name(getName()) + .displayName(getDisplayName()) .createdAtGte(getCreatedAtGte()) .createdAtLte(getCreatedAtLte()); } diff --git a/src/main/java/com/volcengine/ark/runtime/models/agent/ModelConfig.java b/src/main/java/com/volcengine/ark/runtime/models/agent/ModelConfig.java index 917d559..a818214 100644 --- a/src/main/java/com/volcengine/ark/runtime/models/agent/ModelConfig.java +++ b/src/main/java/com/volcengine/ark/runtime/models/agent/ModelConfig.java @@ -19,6 +19,8 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; /** @@ -26,7 +28,12 @@ */ @JsonPropertyOrder({ ModelConfig.JSON_PROPERTY_ID, - ModelConfig.JSON_PROPERTY_SPEED + ModelConfig.JSON_PROPERTY_SPEED, + ModelConfig.JSON_PROPERTY_TOKEN_LIMITS, + ModelConfig.JSON_PROPERTY_INPUT_MODALITIES, + ModelConfig.JSON_PROPERTY_PROVIDER, + ModelConfig.JSON_PROPERTY_THINKING, + ModelConfig.JSON_PROPERTY_REASONING_EFFORT }) @javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") public class ModelConfig { @@ -38,6 +45,26 @@ public class ModelConfig { @javax.annotation.Nullable private ModelSpeed speed; + public static final String JSON_PROPERTY_TOKEN_LIMITS = "token_limits"; + @javax.annotation.Nullable + private TokenLimits tokenLimits; + + public static final String JSON_PROPERTY_INPUT_MODALITIES = "input_modalities"; + @javax.annotation.Nullable + private List inputModalities; + + public static final String JSON_PROPERTY_PROVIDER = "provider"; + @javax.annotation.Nullable + private String provider; + + public static final String JSON_PROPERTY_THINKING = "thinking"; + @javax.annotation.Nullable + private String thinking; + + public static final String JSON_PROPERTY_REASONING_EFFORT = "reasoning_effort"; + @javax.annotation.Nullable + private String reasoningEffort; + public ModelConfig() { } @@ -91,6 +118,139 @@ public void setSpeed(@javax.annotation.Nullable ModelSpeed speed) { this.speed = speed; } + public ModelConfig tokenLimits(@javax.annotation.Nullable TokenLimits tokenLimits) { + + this.tokenLimits = tokenLimits; + return this; + } + + /** + * 模型 token 限制快照。 + * @return tokenLimits + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_TOKEN_LIMITS, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public TokenLimits getTokenLimits() { + return tokenLimits; + } + + + @JsonProperty(value = JSON_PROPERTY_TOKEN_LIMITS, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setTokenLimits(@javax.annotation.Nullable TokenLimits tokenLimits) { + this.tokenLimits = tokenLimits; + } + + public ModelConfig inputModalities(@javax.annotation.Nullable List inputModalities) { + + this.inputModalities = inputModalities; + return this; + } + + public ModelConfig addInputModalitiesItem(String inputModalitiesItem) { + if (this.inputModalities == null) { + this.inputModalities = new ArrayList<>(); + } + this.inputModalities.add(inputModalitiesItem); + return this; + } + + /** + * 底模支持的输入模态列表。 + * @return inputModalities + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_INPUT_MODALITIES, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + + public List getInputModalities() { + return inputModalities; + } + + + @JsonProperty(value = JSON_PROPERTY_INPUT_MODALITIES, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + public void setInputModalities(@javax.annotation.Nullable List inputModalities) { + this.inputModalities = inputModalities; + } + + public ModelConfig provider(@javax.annotation.Nullable String provider) { + + this.provider = provider; + return this; + } + + /** + * 模型提供方。 + * @return provider + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_PROVIDER, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getProvider() { + return provider; + } + + + @JsonProperty(value = JSON_PROPERTY_PROVIDER, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setProvider(@javax.annotation.Nullable String provider) { + this.provider = provider; + } + + public ModelConfig thinking(@javax.annotation.Nullable String thinking) { + + this.thinking = thinking; + return this; + } + + /** + * thinking 配置。 + * @return thinking + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_THINKING, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getThinking() { + return thinking; + } + + + @JsonProperty(value = JSON_PROPERTY_THINKING, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setThinking(@javax.annotation.Nullable String thinking) { + this.thinking = thinking; + } + + public ModelConfig reasoningEffort(@javax.annotation.Nullable String reasoningEffort) { + + this.reasoningEffort = reasoningEffort; + return this; + } + + /** + * 推理努力程度。 + * @return reasoningEffort + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_REASONING_EFFORT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getReasoningEffort() { + return reasoningEffort; + } + + + @JsonProperty(value = JSON_PROPERTY_REASONING_EFFORT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setReasoningEffort(@javax.annotation.Nullable String reasoningEffort) { + this.reasoningEffort = reasoningEffort; + } + @Override public boolean equals(Object o) { @@ -102,12 +262,17 @@ public boolean equals(Object o) { } ModelConfig modelConfig = (ModelConfig) o; return Objects.equals(this.id, modelConfig.id) && - Objects.equals(this.speed, modelConfig.speed); + Objects.equals(this.speed, modelConfig.speed) && + Objects.equals(this.tokenLimits, modelConfig.tokenLimits) && + Objects.equals(this.inputModalities, modelConfig.inputModalities) && + Objects.equals(this.provider, modelConfig.provider) && + Objects.equals(this.thinking, modelConfig.thinking) && + Objects.equals(this.reasoningEffort, modelConfig.reasoningEffort); } @Override public int hashCode() { - return Objects.hash(id, speed); + return Objects.hash(id, speed, tokenLimits, inputModalities, provider, thinking, reasoningEffort); } @Override @@ -116,6 +281,11 @@ public String toString() { sb.append("class ModelConfig {\n"); sb.append(" id: ").append(toIndentedString(id)).append("\n"); sb.append(" speed: ").append(toIndentedString(speed)).append("\n"); + sb.append(" tokenLimits: ").append(toIndentedString(tokenLimits)).append("\n"); + sb.append(" inputModalities: ").append(toIndentedString(inputModalities)).append("\n"); + sb.append(" provider: ").append(toIndentedString(provider)).append("\n"); + sb.append(" thinking: ").append(toIndentedString(thinking)).append("\n"); + sb.append(" reasoningEffort: ").append(toIndentedString(reasoningEffort)).append("\n"); sb.append("}"); return sb.toString(); } @@ -148,6 +318,26 @@ public ModelConfig.Builder speed(ModelSpeed speed) { this.instance.speed = speed; return this; } + public ModelConfig.Builder tokenLimits(TokenLimits tokenLimits) { + this.instance.tokenLimits = tokenLimits; + return this; + } + public ModelConfig.Builder inputModalities(List inputModalities) { + this.instance.inputModalities = inputModalities; + return this; + } + public ModelConfig.Builder provider(String provider) { + this.instance.provider = provider; + return this; + } + public ModelConfig.Builder thinking(String thinking) { + this.instance.thinking = thinking; + return this; + } + public ModelConfig.Builder reasoningEffort(String reasoningEffort) { + this.instance.reasoningEffort = reasoningEffort; + return this; + } /** @@ -183,7 +373,12 @@ public static ModelConfig.Builder builder() { public ModelConfig.Builder toBuilder() { return new ModelConfig.Builder() .id(getId()) - .speed(getSpeed()); + .speed(getSpeed()) + .tokenLimits(getTokenLimits()) + .inputModalities(getInputModalities()) + .provider(getProvider()) + .thinking(getThinking()) + .reasoningEffort(getReasoningEffort()); } diff --git a/src/main/java/com/volcengine/ark/runtime/models/agent/TokenLimits.java b/src/main/java/com/volcengine/ark/runtime/models/agent/TokenLimits.java new file mode 100644 index 0000000..35ab5ba --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/models/agent/TokenLimits.java @@ -0,0 +1,227 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +/* + * Ark Managed Agents Agent API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.volcengine.ark.runtime.models.agent; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.util.Objects; + +/** + * 模型上下文与输入输出 token 限制。 + */ +@JsonPropertyOrder({ + TokenLimits.JSON_PROPERTY_CONTEXT_WINDOW, + TokenLimits.JSON_PROPERTY_MAX_INPUT_TOKEN_LENGTH, + TokenLimits.JSON_PROPERTY_MAX_OUTPUT_TOKEN_LENGTH +}) +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") +public class TokenLimits { + public static final String JSON_PROPERTY_CONTEXT_WINDOW = "context_window"; + @javax.annotation.Nullable + private Long contextWindow; + + public static final String JSON_PROPERTY_MAX_INPUT_TOKEN_LENGTH = "max_input_token_length"; + @javax.annotation.Nullable + private Long maxInputTokenLength; + + public static final String JSON_PROPERTY_MAX_OUTPUT_TOKEN_LENGTH = "max_output_token_length"; + @javax.annotation.Nullable + private Long maxOutputTokenLength; + + public TokenLimits() { + } + + public TokenLimits contextWindow(@javax.annotation.Nullable Long contextWindow) { + + this.contextWindow = contextWindow; + return this; + } + + /** + * 模型上下文窗口。 + * @return contextWindow + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_CONTEXT_WINDOW, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public Long getContextWindow() { + return contextWindow; + } + + + @JsonProperty(value = JSON_PROPERTY_CONTEXT_WINDOW, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setContextWindow(@javax.annotation.Nullable Long contextWindow) { + this.contextWindow = contextWindow; + } + + public TokenLimits maxInputTokenLength(@javax.annotation.Nullable Long maxInputTokenLength) { + + this.maxInputTokenLength = maxInputTokenLength; + return this; + } + + /** + * 最大输入 token 长度。 + * @return maxInputTokenLength + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_MAX_INPUT_TOKEN_LENGTH, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public Long getMaxInputTokenLength() { + return maxInputTokenLength; + } + + + @JsonProperty(value = JSON_PROPERTY_MAX_INPUT_TOKEN_LENGTH, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setMaxInputTokenLength(@javax.annotation.Nullable Long maxInputTokenLength) { + this.maxInputTokenLength = maxInputTokenLength; + } + + public TokenLimits maxOutputTokenLength(@javax.annotation.Nullable Long maxOutputTokenLength) { + + this.maxOutputTokenLength = maxOutputTokenLength; + return this; + } + + /** + * 最大输出 token 长度。 + * @return maxOutputTokenLength + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_MAX_OUTPUT_TOKEN_LENGTH, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public Long getMaxOutputTokenLength() { + return maxOutputTokenLength; + } + + + @JsonProperty(value = JSON_PROPERTY_MAX_OUTPUT_TOKEN_LENGTH, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setMaxOutputTokenLength(@javax.annotation.Nullable Long maxOutputTokenLength) { + this.maxOutputTokenLength = maxOutputTokenLength; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TokenLimits tokenLimits = (TokenLimits) o; + return Objects.equals(this.contextWindow, tokenLimits.contextWindow) && + Objects.equals(this.maxInputTokenLength, tokenLimits.maxInputTokenLength) && + Objects.equals(this.maxOutputTokenLength, tokenLimits.maxOutputTokenLength); + } + + @Override + public int hashCode() { + return Objects.hash(contextWindow, maxInputTokenLength, maxOutputTokenLength); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class TokenLimits {\n"); + sb.append(" contextWindow: ").append(toIndentedString(contextWindow)).append("\n"); + sb.append(" maxInputTokenLength: ").append(toIndentedString(maxInputTokenLength)).append("\n"); + sb.append(" maxOutputTokenLength: ").append(toIndentedString(maxOutputTokenLength)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + return o == null ? "null" : o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private TokenLimits instance; + + public Builder() { + this(new TokenLimits()); + } + + protected Builder(TokenLimits instance) { + this.instance = instance; + } + + public TokenLimits.Builder contextWindow(Long contextWindow) { + this.instance.contextWindow = contextWindow; + return this; + } + public TokenLimits.Builder maxInputTokenLength(Long maxInputTokenLength) { + this.instance.maxInputTokenLength = maxInputTokenLength; + return this; + } + public TokenLimits.Builder maxOutputTokenLength(Long maxOutputTokenLength) { + this.instance.maxOutputTokenLength = maxOutputTokenLength; + return this; + } + + + /** + * returns a built TokenLimits instance. + * + * The builder is not reusable. + */ + public TokenLimits build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field. + */ + public static TokenLimits.Builder builder() { + return new TokenLimits.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public TokenLimits.Builder toBuilder() { + return new TokenLimits.Builder() + .contextWindow(getContextWindow()) + .maxInputTokenLength(getMaxInputTokenLength()) + .maxOutputTokenLength(getMaxOutputTokenLength()); + } + + +} diff --git a/src/main/java/com/volcengine/ark/runtime/models/agent/ToolItem.java b/src/main/java/com/volcengine/ark/runtime/models/agent/ToolItem.java index 0f8b431..f69988f 100644 --- a/src/main/java/com/volcengine/ark/runtime/models/agent/ToolItem.java +++ b/src/main/java/com/volcengine/ark/runtime/models/agent/ToolItem.java @@ -24,7 +24,7 @@ import java.util.Objects; /** - * 一条工具配置。按 `type` 分三类: - `agent_toolset_<date>`:内置工具集(当前默认 `agent_toolset_20260701`; 存量 `agent_toolset_20260401` 仍兼容) - `mcp_toolset`:来自 `mcp_servers[]` 的工具集 - `custom`:客户端执行的自定义工具 所有变体字段合并在一个 model 里,未使用的字段留空即可(proto oneof 风格;wire 上就是同一个 JSON 对象按 `type` 决定语义)。 + * 一条工具配置。按 `type` 分三类: - `agent_toolset_<date>`:内置工具集(当前默认 `agent_toolset_20260701`; 存量 `agent_toolset_20260401` 仍兼容) - `mcp_toolset`:来自 `mcp_servers[]` 的工具集 - `evolution`:自演进类工具 - `custom`:客户端执行的自定义工具 所有变体字段合并在一个 model 里,未使用的字段留空即可(proto oneof 风格;wire 上就是同一个 JSON 对象按 `type` 决定语义)。 */ @JsonPropertyOrder({ ToolItem.JSON_PROPERTY_TYPE, @@ -63,7 +63,7 @@ public class ToolItem { public static final String JSON_PROPERTY_INPUT_SCHEMA = "input_schema"; @javax.annotation.Nullable - private String inputSchema; + private CustomToolInputSchema inputSchema; public ToolItem() { } @@ -226,28 +226,28 @@ public void setDescription(@javax.annotation.Nullable String description) { this.description = description; } - public ToolItem inputSchema(@javax.annotation.Nullable String inputSchema) { + public ToolItem inputSchema(@javax.annotation.Nullable CustomToolInputSchema inputSchema) { this.inputSchema = inputSchema; return this; } /** - * `custom` 专用;承载 JSON Schema 的字符串形态 (wire 上是 JSON-encoded string,非 nested object)。 + * `custom` 专用;承载 JSON Schema 对象。 * @return inputSchema */ @javax.annotation.Nullable @JsonProperty(value = JSON_PROPERTY_INPUT_SCHEMA, required = false) @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) - public String getInputSchema() { + public CustomToolInputSchema getInputSchema() { return inputSchema; } @JsonProperty(value = JSON_PROPERTY_INPUT_SCHEMA, required = false) @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) - public void setInputSchema(@javax.annotation.Nullable String inputSchema) { + public void setInputSchema(@javax.annotation.Nullable CustomToolInputSchema inputSchema) { this.inputSchema = inputSchema; } @@ -334,7 +334,7 @@ public ToolItem.Builder description(String description) { this.instance.description = description; return this; } - public ToolItem.Builder inputSchema(String inputSchema) { + public ToolItem.Builder inputSchema(CustomToolInputSchema inputSchema) { this.instance.inputSchema = inputSchema; return this; } diff --git a/src/main/java/com/volcengine/ark/runtime/models/agent/UpdateAgentRequest.java b/src/main/java/com/volcengine/ark/runtime/models/agent/UpdateAgentRequest.java index 9b5b566..33fd831 100644 --- a/src/main/java/com/volcengine/ark/runtime/models/agent/UpdateAgentRequest.java +++ b/src/main/java/com/volcengine/ark/runtime/models/agent/UpdateAgentRequest.java @@ -31,6 +31,7 @@ @JsonPropertyOrder({ UpdateAgentRequest.JSON_PROPERTY_VERSION, UpdateAgentRequest.JSON_PROPERTY_NAME, + UpdateAgentRequest.JSON_PROPERTY_DISPLAY_NAME, UpdateAgentRequest.JSON_PROPERTY_MODEL, UpdateAgentRequest.JSON_PROPERTY_DESCRIPTION, UpdateAgentRequest.JSON_PROPERTY_SYSTEM, @@ -38,7 +39,8 @@ UpdateAgentRequest.JSON_PROPERTY_TOOLS, UpdateAgentRequest.JSON_PROPERTY_SKILLS, UpdateAgentRequest.JSON_PROPERTY_MULTIAGENT, - UpdateAgentRequest.JSON_PROPERTY_METADATA + UpdateAgentRequest.JSON_PROPERTY_METADATA, + UpdateAgentRequest.JSON_PROPERTY_TAGS }) @javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") public class UpdateAgentRequest { @@ -50,6 +52,10 @@ public class UpdateAgentRequest { @javax.annotation.Nullable private String name; + public static final String JSON_PROPERTY_DISPLAY_NAME = "display_name"; + @javax.annotation.Nullable + private String displayName; + public static final String JSON_PROPERTY_MODEL = "model"; @javax.annotation.Nullable private ModelConfig model; @@ -82,6 +88,10 @@ public class UpdateAgentRequest { @javax.annotation.Nullable private Map metadata; + public static final String JSON_PROPERTY_TAGS = "tags"; + @javax.annotation.Nullable + private List tags; + public UpdateAgentRequest() { } @@ -135,6 +145,31 @@ public void setName(@javax.annotation.Nullable String name) { this.name = name; } + public UpdateAgentRequest displayName(@javax.annotation.Nullable String displayName) { + + this.displayName = displayName; + return this; + } + + /** + * 展示名。 + * @return displayName + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_DISPLAY_NAME, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getDisplayName() { + return displayName; + } + + + @JsonProperty(value = JSON_PROPERTY_DISPLAY_NAME, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setDisplayName(@javax.annotation.Nullable String displayName) { + this.displayName = displayName; + } + public UpdateAgentRequest model(@javax.annotation.Nullable ModelConfig model) { this.model = model; @@ -367,6 +402,39 @@ public void setMetadata(@javax.annotation.Nullable Map metadata) this.metadata = metadata; } + public UpdateAgentRequest tags(@javax.annotation.Nullable List tags) { + + this.tags = tags; + return this; + } + + public UpdateAgentRequest addTagsItem(Tag tagsItem) { + if (this.tags == null) { + this.tags = new ArrayList<>(); + } + this.tags.add(tagsItem); + return this; + } + + /** + * 资源标签(整体替换)。 + * @return tags + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_TAGS, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + + public List getTags() { + return tags; + } + + + @JsonProperty(value = JSON_PROPERTY_TAGS, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + public void setTags(@javax.annotation.Nullable List tags) { + this.tags = tags; + } + @Override public boolean equals(Object o) { @@ -379,6 +447,7 @@ public boolean equals(Object o) { UpdateAgentRequest updateAgentRequest = (UpdateAgentRequest) o; return Objects.equals(this.version, updateAgentRequest.version) && Objects.equals(this.name, updateAgentRequest.name) && + Objects.equals(this.displayName, updateAgentRequest.displayName) && Objects.equals(this.model, updateAgentRequest.model) && Objects.equals(this.description, updateAgentRequest.description) && Objects.equals(this.system, updateAgentRequest.system) && @@ -386,12 +455,13 @@ public boolean equals(Object o) { Objects.equals(this.tools, updateAgentRequest.tools) && Objects.equals(this.skills, updateAgentRequest.skills) && Objects.equals(this.multiagent, updateAgentRequest.multiagent) && - Objects.equals(this.metadata, updateAgentRequest.metadata); + Objects.equals(this.metadata, updateAgentRequest.metadata) && + Objects.equals(this.tags, updateAgentRequest.tags); } @Override public int hashCode() { - return Objects.hash(version, name, model, description, system, mcpServers, tools, skills, multiagent, metadata); + return Objects.hash(version, name, displayName, model, description, system, mcpServers, tools, skills, multiagent, metadata, tags); } @Override @@ -400,6 +470,7 @@ public String toString() { sb.append("class UpdateAgentRequest {\n"); sb.append(" version: ").append(toIndentedString(version)).append("\n"); sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" displayName: ").append(toIndentedString(displayName)).append("\n"); sb.append(" model: ").append(toIndentedString(model)).append("\n"); sb.append(" description: ").append(toIndentedString(description)).append("\n"); sb.append(" system: ").append(toIndentedString(system)).append("\n"); @@ -408,6 +479,7 @@ public String toString() { sb.append(" skills: ").append(toIndentedString(skills)).append("\n"); sb.append(" multiagent: ").append(toIndentedString(multiagent)).append("\n"); sb.append(" metadata: ").append(toIndentedString(metadata)).append("\n"); + sb.append(" tags: ").append(toIndentedString(tags)).append("\n"); sb.append("}"); return sb.toString(); } @@ -440,6 +512,10 @@ public UpdateAgentRequest.Builder name(String name) { this.instance.name = name; return this; } + public UpdateAgentRequest.Builder displayName(String displayName) { + this.instance.displayName = displayName; + return this; + } public UpdateAgentRequest.Builder model(ModelConfig model) { this.instance.model = model; return this; @@ -472,6 +548,10 @@ public UpdateAgentRequest.Builder metadata(Map metadata) { this.instance.metadata = metadata; return this; } + public UpdateAgentRequest.Builder tags(List tags) { + this.instance.tags = tags; + return this; + } /** @@ -508,6 +588,7 @@ public UpdateAgentRequest.Builder toBuilder() { return new UpdateAgentRequest.Builder() .version(getVersion()) .name(getName()) + .displayName(getDisplayName()) .model(getModel()) .description(getDescription()) .system(getSystem()) @@ -515,7 +596,8 @@ public UpdateAgentRequest.Builder toBuilder() { .tools(getTools()) .skills(getSkills()) .multiagent(getMultiagent()) - .metadata(getMetadata()); + .metadata(getMetadata()) + .tags(getTags()); } diff --git a/src/main/java/com/volcengine/ark/runtime/models/environment/AckRequest.java b/src/main/java/com/volcengine/ark/runtime/models/environment/AckRequest.java new file mode 100644 index 0000000..925c04e --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/models/environment/AckRequest.java @@ -0,0 +1,227 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +/* + * Ark Managed Agents Environment API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.volcengine.ark.runtime.models.environment; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.util.Objects; + +/** + * AckRequest + */ +@JsonPropertyOrder({ + AckRequest.JSON_PROPERTY_ENVIRONMENT_ID, + AckRequest.JSON_PROPERTY_WORK_ID, + AckRequest.JSON_PROPERTY_ARK_WORKER_I_D +}) +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") +public class AckRequest { + public static final String JSON_PROPERTY_ENVIRONMENT_ID = "environmentId"; + @javax.annotation.Nonnull + private String environmentId; + + public static final String JSON_PROPERTY_WORK_ID = "workId"; + @javax.annotation.Nonnull + private String workId; + + public static final String JSON_PROPERTY_ARK_WORKER_I_D = "Ark-Worker-ID"; + @javax.annotation.Nullable + private String arkWorkerID; + + public AckRequest() { + } + + public AckRequest environmentId(@javax.annotation.Nonnull String environmentId) { + + this.environmentId = environmentId; + return this; + } + + /** + * Get environmentId + * @return environmentId + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_ENVIRONMENT_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getEnvironmentId() { + return environmentId; + } + + + @JsonProperty(value = JSON_PROPERTY_ENVIRONMENT_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setEnvironmentId(@javax.annotation.Nonnull String environmentId) { + this.environmentId = environmentId; + } + + public AckRequest workId(@javax.annotation.Nonnull String workId) { + + this.workId = workId; + return this; + } + + /** + * Get workId + * @return workId + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_WORK_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getWorkId() { + return workId; + } + + + @JsonProperty(value = JSON_PROPERTY_WORK_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setWorkId(@javax.annotation.Nonnull String workId) { + this.workId = workId; + } + + public AckRequest arkWorkerID(@javax.annotation.Nullable String arkWorkerID) { + + this.arkWorkerID = arkWorkerID; + return this; + } + + /** + * Worker 实例 ID,用于控制面记录 work 归属和排障。 + * @return arkWorkerID + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_ARK_WORKER_I_D, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getArkWorkerID() { + return arkWorkerID; + } + + + @JsonProperty(value = JSON_PROPERTY_ARK_WORKER_I_D, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setArkWorkerID(@javax.annotation.Nullable String arkWorkerID) { + this.arkWorkerID = arkWorkerID; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AckRequest ackRequest = (AckRequest) o; + return Objects.equals(this.environmentId, ackRequest.environmentId) && + Objects.equals(this.workId, ackRequest.workId) && + Objects.equals(this.arkWorkerID, ackRequest.arkWorkerID); + } + + @Override + public int hashCode() { + return Objects.hash(environmentId, workId, arkWorkerID); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class AckRequest {\n"); + sb.append(" environmentId: ").append(toIndentedString(environmentId)).append("\n"); + sb.append(" workId: ").append(toIndentedString(workId)).append("\n"); + sb.append(" arkWorkerID: ").append(toIndentedString(arkWorkerID)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + return o == null ? "null" : o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private AckRequest instance; + + public Builder() { + this(new AckRequest()); + } + + protected Builder(AckRequest instance) { + this.instance = instance; + } + + public AckRequest.Builder environmentId(String environmentId) { + this.instance.environmentId = environmentId; + return this; + } + public AckRequest.Builder workId(String workId) { + this.instance.workId = workId; + return this; + } + public AckRequest.Builder arkWorkerID(String arkWorkerID) { + this.instance.arkWorkerID = arkWorkerID; + return this; + } + + + /** + * returns a built AckRequest instance. + * + * The builder is not reusable. + */ + public AckRequest build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field. + */ + public static AckRequest.Builder builder() { + return new AckRequest.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public AckRequest.Builder toBuilder() { + return new AckRequest.Builder() + .environmentId(getEnvironmentId()) + .workId(getWorkId()) + .arkWorkerID(getArkWorkerID()); + } + + +} diff --git a/src/main/java/com/volcengine/ark/runtime/models/environment/EnvConfig.java b/src/main/java/com/volcengine/ark/runtime/models/environment/EnvConfig.java index 9e76892..97c12f9 100644 --- a/src/main/java/com/volcengine/ark/runtime/models/environment/EnvConfig.java +++ b/src/main/java/com/volcengine/ark/runtime/models/environment/EnvConfig.java @@ -30,7 +30,9 @@ EnvConfig.JSON_PROPERTY_TYPE, EnvConfig.JSON_PROPERTY_NETWORKING, EnvConfig.JSON_PROPERTY_PACKAGES, - EnvConfig.JSON_PROPERTY_ENV + EnvConfig.JSON_PROPERTY_ENV, + EnvConfig.JSON_PROPERTY_SETUP_SCRIPT, + EnvConfig.JSON_PROPERTY_TOS }) @javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") public class EnvConfig { @@ -50,6 +52,14 @@ public class EnvConfig { @javax.annotation.Nullable private Map env; + public static final String JSON_PROPERTY_SETUP_SCRIPT = "setup_script"; + @javax.annotation.Nullable + private String setupScript; + + public static final String JSON_PROPERTY_TOS = "tos"; + @javax.annotation.Nullable + private TosConfig tos; + public EnvConfig() { } @@ -161,6 +171,56 @@ public void setEnv(@javax.annotation.Nullable Map env) { this.env = env; } + public EnvConfig setupScript(@javax.annotation.Nullable String setupScript) { + + this.setupScript = setupScript; + return this; + } + + /** + * 沙箱启动阶段执行的初始化脚本。 + * @return setupScript + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_SETUP_SCRIPT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getSetupScript() { + return setupScript; + } + + + @JsonProperty(value = JSON_PROPERTY_SETUP_SCRIPT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setSetupScript(@javax.annotation.Nullable String setupScript) { + this.setupScript = setupScript; + } + + public EnvConfig tos(@javax.annotation.Nullable TosConfig tos) { + + this.tos = tos; + return this; + } + + /** + * Environment outputs 的 TOS 存储配置。 + * @return tos + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_TOS, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public TosConfig getTos() { + return tos; + } + + + @JsonProperty(value = JSON_PROPERTY_TOS, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setTos(@javax.annotation.Nullable TosConfig tos) { + this.tos = tos; + } + @Override public boolean equals(Object o) { @@ -174,12 +234,14 @@ public boolean equals(Object o) { return Objects.equals(this.type, envConfig.type) && Objects.equals(this.networking, envConfig.networking) && Objects.equals(this.packages, envConfig.packages) && - Objects.equals(this.env, envConfig.env); + Objects.equals(this.env, envConfig.env) && + Objects.equals(this.setupScript, envConfig.setupScript) && + Objects.equals(this.tos, envConfig.tos); } @Override public int hashCode() { - return Objects.hash(type, networking, packages, env); + return Objects.hash(type, networking, packages, env, setupScript, tos); } @Override @@ -190,6 +252,8 @@ public String toString() { sb.append(" networking: ").append(toIndentedString(networking)).append("\n"); sb.append(" packages: ").append(toIndentedString(packages)).append("\n"); sb.append(" env: ").append(toIndentedString(env)).append("\n"); + sb.append(" setupScript: ").append(toIndentedString(setupScript)).append("\n"); + sb.append(" tos: ").append(toIndentedString(tos)).append("\n"); sb.append("}"); return sb.toString(); } @@ -230,6 +294,14 @@ public EnvConfig.Builder env(Map env) { this.instance.env = env; return this; } + public EnvConfig.Builder setupScript(String setupScript) { + this.instance.setupScript = setupScript; + return this; + } + public EnvConfig.Builder tos(TosConfig tos) { + this.instance.tos = tos; + return this; + } /** @@ -267,7 +339,9 @@ public EnvConfig.Builder toBuilder() { .type(getType()) .networking(getNetworking()) .packages(getPackages()) - .env(getEnv()); + .env(getEnv()) + .setupScript(getSetupScript()) + .tos(getTos()); } diff --git a/src/main/java/com/volcengine/ark/runtime/models/environment/Environment.java b/src/main/java/com/volcengine/ark/runtime/models/environment/Environment.java index 378a46a..b44ae4a 100644 --- a/src/main/java/com/volcengine/ark/runtime/models/environment/Environment.java +++ b/src/main/java/com/volcengine/ark/runtime/models/environment/Environment.java @@ -21,7 +21,9 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.annotation.JsonValue; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -37,7 +39,8 @@ Environment.JSON_PROPERTY_METADATA, Environment.JSON_PROPERTY_SCOPE, Environment.JSON_PROPERTY_CREATED_AT, - Environment.JSON_PROPERTY_UPDATED_AT + Environment.JSON_PROPERTY_UPDATED_AT, + Environment.JSON_PROPERTY_OVERRIDDEN_FIELDS }) @javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") public class Environment { @@ -110,6 +113,10 @@ public static TypeEnum fromValue(String value) { @javax.annotation.Nonnull private String updatedAt; + public static final String JSON_PROPERTY_OVERRIDDEN_FIELDS = "overridden_fields"; + @javax.annotation.Nullable + private List overriddenFields; + public Environment() { } @@ -346,6 +353,39 @@ public void setUpdatedAt(@javax.annotation.Nonnull String updatedAt) { this.updatedAt = updatedAt; } + public Environment overriddenFields(@javax.annotation.Nullable List overriddenFields) { + + this.overriddenFields = overriddenFields; + return this; + } + + public Environment addOverriddenFieldsItem(String overriddenFieldsItem) { + if (this.overriddenFields == null) { + this.overriddenFields = new ArrayList<>(); + } + this.overriddenFields.add(overriddenFieldsItem); + return this; + } + + /** + * Session 使用 EnvironmentWithOverrides 时,本次被覆写的 config 子字段。 + * @return overriddenFields + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_OVERRIDDEN_FIELDS, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + + public List getOverriddenFields() { + return overriddenFields; + } + + + @JsonProperty(value = JSON_PROPERTY_OVERRIDDEN_FIELDS, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + public void setOverriddenFields(@javax.annotation.Nullable List overriddenFields) { + this.overriddenFields = overriddenFields; + } + @Override public boolean equals(Object o) { @@ -364,12 +404,13 @@ public boolean equals(Object o) { Objects.equals(this.metadata, environment.metadata) && Objects.equals(this.scope, environment.scope) && Objects.equals(this.createdAt, environment.createdAt) && - Objects.equals(this.updatedAt, environment.updatedAt); + Objects.equals(this.updatedAt, environment.updatedAt) && + Objects.equals(this.overriddenFields, environment.overriddenFields); } @Override public int hashCode() { - return Objects.hash(id, type, name, description, config, metadata, scope, createdAt, updatedAt); + return Objects.hash(id, type, name, description, config, metadata, scope, createdAt, updatedAt, overriddenFields); } @Override @@ -385,6 +426,7 @@ public String toString() { sb.append(" scope: ").append(toIndentedString(scope)).append("\n"); sb.append(" createdAt: ").append(toIndentedString(createdAt)).append("\n"); sb.append(" updatedAt: ").append(toIndentedString(updatedAt)).append("\n"); + sb.append(" overriddenFields: ").append(toIndentedString(overriddenFields)).append("\n"); sb.append("}"); return sb.toString(); } @@ -445,6 +487,10 @@ public Environment.Builder updatedAt(String updatedAt) { this.instance.updatedAt = updatedAt; return this; } + public Environment.Builder overriddenFields(List overriddenFields) { + this.instance.overriddenFields = overriddenFields; + return this; + } /** @@ -487,7 +533,8 @@ public Environment.Builder toBuilder() { .metadata(getMetadata()) .scope(getScope()) .createdAt(getCreatedAt()) - .updatedAt(getUpdatedAt()); + .updatedAt(getUpdatedAt()) + .overriddenFields(getOverriddenFields()); } diff --git a/src/main/java/com/volcengine/ark/runtime/models/environment/EnvironmentWithOverrides.java b/src/main/java/com/volcengine/ark/runtime/models/environment/EnvironmentWithOverrides.java new file mode 100644 index 0000000..9f3a951 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/models/environment/EnvironmentWithOverrides.java @@ -0,0 +1,262 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +/* + * Ark Managed Agents Environment API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.volcengine.ark.runtime.models.environment; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.Objects; + +/** + * CreateSession 时的 Environment 覆写引用;以已有 environment 为底,只覆写 运行时 config。 + */ +@JsonPropertyOrder({ + EnvironmentWithOverrides.JSON_PROPERTY_TYPE, + EnvironmentWithOverrides.JSON_PROPERTY_ID, + EnvironmentWithOverrides.JSON_PROPERTY_CONFIG +}) +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") +public class EnvironmentWithOverrides { + /** + * 固定 `\"environment_with_overrides\"`。 + */ + public enum TypeEnum { + ENVIRONMENT_WITH_OVERRIDES(String.valueOf("environment_with_overrides")); + + private String value; + + TypeEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static TypeEnum fromValue(String value) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equalsIgnoreCase(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + public static final String JSON_PROPERTY_TYPE = "type"; + @javax.annotation.Nonnull + private TypeEnum type; + + public static final String JSON_PROPERTY_ID = "id"; + @javax.annotation.Nonnull + private String id; + + public static final String JSON_PROPERTY_CONFIG = "config"; + @javax.annotation.Nullable + private EnvConfig config; + + public EnvironmentWithOverrides() { + } + + public EnvironmentWithOverrides type(@javax.annotation.Nonnull TypeEnum type) { + + this.type = type; + return this; + } + + /** + * 固定 `\"environment_with_overrides\"`。 + * @return type + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_TYPE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public TypeEnum getType() { + return type; + } + + + @JsonProperty(value = JSON_PROPERTY_TYPE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setType(@javax.annotation.Nonnull TypeEnum type) { + this.type = type; + } + + public EnvironmentWithOverrides id(@javax.annotation.Nonnull String id) { + + this.id = id; + return this; + } + + /** + * Base Environment ID。 + * @return id + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getId() { + return id; + } + + + @JsonProperty(value = JSON_PROPERTY_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setId(@javax.annotation.Nonnull String id) { + this.id = id; + } + + public EnvironmentWithOverrides config(@javax.annotation.Nullable EnvConfig config) { + + this.config = config; + return this; + } + + /** + * 运行时配置覆写;省略表示继承 base。 + * @return config + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_CONFIG, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public EnvConfig getConfig() { + return config; + } + + + @JsonProperty(value = JSON_PROPERTY_CONFIG, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setConfig(@javax.annotation.Nullable EnvConfig config) { + this.config = config; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + EnvironmentWithOverrides environmentWithOverrides = (EnvironmentWithOverrides) o; + return Objects.equals(this.type, environmentWithOverrides.type) && + Objects.equals(this.id, environmentWithOverrides.id) && + Objects.equals(this.config, environmentWithOverrides.config); + } + + @Override + public int hashCode() { + return Objects.hash(type, id, config); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class EnvironmentWithOverrides {\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" config: ").append(toIndentedString(config)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + return o == null ? "null" : o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private EnvironmentWithOverrides instance; + + public Builder() { + this(new EnvironmentWithOverrides()); + } + + protected Builder(EnvironmentWithOverrides instance) { + this.instance = instance; + } + + public EnvironmentWithOverrides.Builder type(TypeEnum type) { + this.instance.type = type; + return this; + } + public EnvironmentWithOverrides.Builder id(String id) { + this.instance.id = id; + return this; + } + public EnvironmentWithOverrides.Builder config(EnvConfig config) { + this.instance.config = config; + return this; + } + + + /** + * returns a built EnvironmentWithOverrides instance. + * + * The builder is not reusable. + */ + public EnvironmentWithOverrides build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field. + */ + public static EnvironmentWithOverrides.Builder builder() { + return new EnvironmentWithOverrides.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public EnvironmentWithOverrides.Builder toBuilder() { + return new EnvironmentWithOverrides.Builder() + .type(getType()) + .id(getId()) + .config(getConfig()); + } + + +} diff --git a/src/main/java/com/volcengine/ark/runtime/models/environment/EnvironmentWorkPoll200Response.java b/src/main/java/com/volcengine/ark/runtime/models/environment/EnvironmentWorkPoll200Response.java new file mode 100644 index 0000000..12f32e5 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/models/environment/EnvironmentWorkPoll200Response.java @@ -0,0 +1,643 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +/* + * Ark Managed Agents Environment API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.volcengine.ark.runtime.models.environment; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * EnvironmentWorkPoll200Response + */ +@JsonPropertyOrder({ + EnvironmentWorkPoll200Response.JSON_PROPERTY_ID, + EnvironmentWorkPoll200Response.JSON_PROPERTY_ACKNOWLEDGED_AT, + EnvironmentWorkPoll200Response.JSON_PROPERTY_CREATED_AT, + EnvironmentWorkPoll200Response.JSON_PROPERTY_DATA, + EnvironmentWorkPoll200Response.JSON_PROPERTY_ENVIRONMENT_ID, + EnvironmentWorkPoll200Response.JSON_PROPERTY_LATEST_HEARTBEAT_AT, + EnvironmentWorkPoll200Response.JSON_PROPERTY_TAGS, + "secret", + EnvironmentWorkPoll200Response.JSON_PROPERTY_STARTED_AT, + EnvironmentWorkPoll200Response.JSON_PROPERTY_STATE, + EnvironmentWorkPoll200Response.JSON_PROPERTY_STOP_REQUESTED_AT, + EnvironmentWorkPoll200Response.JSON_PROPERTY_STOPPED_AT, + EnvironmentWorkPoll200Response.JSON_PROPERTY_TYPE +}) +@JsonTypeName("EnvironmentWork_poll_200_response") +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") +public class EnvironmentWorkPoll200Response { + public static final String JSON_PROPERTY_ID = "id"; + @javax.annotation.Nonnull + private String id; + + public static final String JSON_PROPERTY_ACKNOWLEDGED_AT = "acknowledged_at"; + @javax.annotation.Nullable + private String acknowledgedAt; + + public static final String JSON_PROPERTY_CREATED_AT = "created_at"; + @javax.annotation.Nonnull + private String createdAt; + + public static final String JSON_PROPERTY_DATA = "data"; + @javax.annotation.Nonnull + private WorkData data; + + public static final String JSON_PROPERTY_ENVIRONMENT_ID = "environment_id"; + @javax.annotation.Nonnull + private String environmentId; + + public static final String JSON_PROPERTY_LATEST_HEARTBEAT_AT = "latest_heartbeat_at"; + @javax.annotation.Nullable + private String latestHeartbeatAt; + + public static final String JSON_PROPERTY_TAGS = "tags"; + @javax.annotation.Nullable + private List tags; + + @javax.annotation.Nullable + private String secret; + + public static final String JSON_PROPERTY_STARTED_AT = "started_at"; + @javax.annotation.Nullable + private String startedAt; + + public static final String JSON_PROPERTY_STATE = "state"; + @javax.annotation.Nonnull + private WorkState state; + + public static final String JSON_PROPERTY_STOP_REQUESTED_AT = "stop_requested_at"; + @javax.annotation.Nullable + private String stopRequestedAt; + + public static final String JSON_PROPERTY_STOPPED_AT = "stopped_at"; + @javax.annotation.Nullable + private String stoppedAt; + + /** + * 对象类型,固定为 `work`。 + */ + public enum TypeEnum { + WORK(String.valueOf("work")); + + private String value; + + TypeEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static TypeEnum fromValue(String value) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equalsIgnoreCase(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + public static final String JSON_PROPERTY_TYPE = "type"; + @javax.annotation.Nonnull + private TypeEnum type; + + public EnvironmentWorkPoll200Response() { + } + + public EnvironmentWorkPoll200Response id(@javax.annotation.Nonnull String id) { + + this.id = id; + return this; + } + + /** + * Work ID。 + * @return id + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getId() { + return id; + } + + + @JsonProperty(value = JSON_PROPERTY_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setId(@javax.annotation.Nonnull String id) { + this.id = id; + } + + public EnvironmentWorkPoll200Response acknowledgedAt(@javax.annotation.Nullable String acknowledgedAt) { + + this.acknowledgedAt = acknowledgedAt; + return this; + } + + /** + * Work ack 时间,RFC 3339。 + * @return acknowledgedAt + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_ACKNOWLEDGED_AT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getAcknowledgedAt() { + return acknowledgedAt; + } + + + @JsonProperty(value = JSON_PROPERTY_ACKNOWLEDGED_AT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setAcknowledgedAt(@javax.annotation.Nullable String acknowledgedAt) { + this.acknowledgedAt = acknowledgedAt; + } + + public EnvironmentWorkPoll200Response createdAt(@javax.annotation.Nonnull String createdAt) { + + this.createdAt = createdAt; + return this; + } + + /** + * Work 创建时间,RFC 3339。 + * @return createdAt + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_CREATED_AT, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getCreatedAt() { + return createdAt; + } + + + @JsonProperty(value = JSON_PROPERTY_CREATED_AT, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setCreatedAt(@javax.annotation.Nonnull String createdAt) { + this.createdAt = createdAt; + } + + public EnvironmentWorkPoll200Response data(@javax.annotation.Nonnull WorkData data) { + + this.data = data; + return this; + } + + /** + * 业务载荷。 + * @return data + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_DATA, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public WorkData getData() { + return data; + } + + + @JsonProperty(value = JSON_PROPERTY_DATA, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setData(@javax.annotation.Nonnull WorkData data) { + this.data = data; + } + + public EnvironmentWorkPoll200Response environmentId(@javax.annotation.Nonnull String environmentId) { + + this.environmentId = environmentId; + return this; + } + + /** + * Environment ID。 + * @return environmentId + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_ENVIRONMENT_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getEnvironmentId() { + return environmentId; + } + + + @JsonProperty(value = JSON_PROPERTY_ENVIRONMENT_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setEnvironmentId(@javax.annotation.Nonnull String environmentId) { + this.environmentId = environmentId; + } + + public EnvironmentWorkPoll200Response latestHeartbeatAt(@javax.annotation.Nullable String latestHeartbeatAt) { + + this.latestHeartbeatAt = latestHeartbeatAt; + return this; + } + + /** + * 最近 heartbeat 时间,RFC 3339。 + * @return latestHeartbeatAt + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_LATEST_HEARTBEAT_AT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getLatestHeartbeatAt() { + return latestHeartbeatAt; + } + + + @JsonProperty(value = JSON_PROPERTY_LATEST_HEARTBEAT_AT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setLatestHeartbeatAt(@javax.annotation.Nullable String latestHeartbeatAt) { + this.latestHeartbeatAt = latestHeartbeatAt; + } + + public EnvironmentWorkPoll200Response tags(@javax.annotation.Nullable List tags) { + + this.tags = tags; + return this; + } + + public EnvironmentWorkPoll200Response addTagsItem(VolcTag tagsItem) { + if (this.tags == null) { + this.tags = new ArrayList<>(); + } + this.tags.add(tagsItem); + return this; + } + + /** + * Work 标签。 + * @return tags + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_TAGS, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + + public List getTags() { + return tags; + } + + + @JsonProperty(value = JSON_PROPERTY_TAGS, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + public void setTags(@javax.annotation.Nullable List tags) { + this.tags = tags; + } + + public EnvironmentWorkPoll200Response secret(@javax.annotation.Nullable String secret) { + + this.secret = secret; + return this; + } + + /** + * Work secret;仅 poll 时返回,ack / stop 响应会抹掉。 + * @return secret + */ + @javax.annotation.Nullable + @JsonProperty(value = "secret", required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getSecret() { + return secret; + } + + + @JsonProperty(value = "secret", required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setSecret(@javax.annotation.Nullable String secret) { + this.secret = secret; + } + + public EnvironmentWorkPoll200Response startedAt(@javax.annotation.Nullable String startedAt) { + + this.startedAt = startedAt; + return this; + } + + /** + * Work 开始时间,RFC 3339。 + * @return startedAt + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_STARTED_AT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getStartedAt() { + return startedAt; + } + + + @JsonProperty(value = JSON_PROPERTY_STARTED_AT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setStartedAt(@javax.annotation.Nullable String startedAt) { + this.startedAt = startedAt; + } + + public EnvironmentWorkPoll200Response state(@javax.annotation.Nonnull WorkState state) { + + this.state = state; + return this; + } + + /** + * Work 生命周期状态。 + * @return state + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_STATE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public WorkState getState() { + return state; + } + + + @JsonProperty(value = JSON_PROPERTY_STATE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setState(@javax.annotation.Nonnull WorkState state) { + this.state = state; + } + + public EnvironmentWorkPoll200Response stopRequestedAt(@javax.annotation.Nullable String stopRequestedAt) { + + this.stopRequestedAt = stopRequestedAt; + return this; + } + + /** + * 控制面请求停止的时间,RFC 3339。 + * @return stopRequestedAt + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_STOP_REQUESTED_AT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getStopRequestedAt() { + return stopRequestedAt; + } + + + @JsonProperty(value = JSON_PROPERTY_STOP_REQUESTED_AT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setStopRequestedAt(@javax.annotation.Nullable String stopRequestedAt) { + this.stopRequestedAt = stopRequestedAt; + } + + public EnvironmentWorkPoll200Response stoppedAt(@javax.annotation.Nullable String stoppedAt) { + + this.stoppedAt = stoppedAt; + return this; + } + + /** + * Work 停止时间,RFC 3339。 + * @return stoppedAt + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_STOPPED_AT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getStoppedAt() { + return stoppedAt; + } + + + @JsonProperty(value = JSON_PROPERTY_STOPPED_AT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setStoppedAt(@javax.annotation.Nullable String stoppedAt) { + this.stoppedAt = stoppedAt; + } + + public EnvironmentWorkPoll200Response type(@javax.annotation.Nonnull TypeEnum type) { + + this.type = type; + return this; + } + + /** + * 对象类型,固定为 `work`。 + * @return type + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_TYPE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public TypeEnum getType() { + return type; + } + + + @JsonProperty(value = JSON_PROPERTY_TYPE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setType(@javax.annotation.Nonnull TypeEnum type) { + this.type = type; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + EnvironmentWorkPoll200Response environmentWorkPoll200Response = (EnvironmentWorkPoll200Response) o; + return Objects.equals(this.id, environmentWorkPoll200Response.id) && + Objects.equals(this.acknowledgedAt, environmentWorkPoll200Response.acknowledgedAt) && + Objects.equals(this.createdAt, environmentWorkPoll200Response.createdAt) && + Objects.equals(this.data, environmentWorkPoll200Response.data) && + Objects.equals(this.environmentId, environmentWorkPoll200Response.environmentId) && + Objects.equals(this.latestHeartbeatAt, environmentWorkPoll200Response.latestHeartbeatAt) && + Objects.equals(this.tags, environmentWorkPoll200Response.tags) && + Objects.equals(this.secret, environmentWorkPoll200Response.secret) && + Objects.equals(this.startedAt, environmentWorkPoll200Response.startedAt) && + Objects.equals(this.state, environmentWorkPoll200Response.state) && + Objects.equals(this.stopRequestedAt, environmentWorkPoll200Response.stopRequestedAt) && + Objects.equals(this.stoppedAt, environmentWorkPoll200Response.stoppedAt) && + Objects.equals(this.type, environmentWorkPoll200Response.type); + } + + @Override + public int hashCode() { + return Objects.hash(id, acknowledgedAt, createdAt, data, environmentId, latestHeartbeatAt, tags, secret, startedAt, state, stopRequestedAt, stoppedAt, type); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class EnvironmentWorkPoll200Response {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" acknowledgedAt: ").append(toIndentedString(acknowledgedAt)).append("\n"); + sb.append(" createdAt: ").append(toIndentedString(createdAt)).append("\n"); + sb.append(" data: ").append(toIndentedString(data)).append("\n"); + sb.append(" environmentId: ").append(toIndentedString(environmentId)).append("\n"); + sb.append(" latestHeartbeatAt: ").append(toIndentedString(latestHeartbeatAt)).append("\n"); + sb.append(" tags: ").append(toIndentedString(tags)).append("\n"); + sb.append(" secret: [REDACTED]\n"); + sb.append(" startedAt: ").append(toIndentedString(startedAt)).append("\n"); + sb.append(" state: ").append(toIndentedString(state)).append("\n"); + sb.append(" stopRequestedAt: ").append(toIndentedString(stopRequestedAt)).append("\n"); + sb.append(" stoppedAt: ").append(toIndentedString(stoppedAt)).append("\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + return o == null ? "null" : o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private EnvironmentWorkPoll200Response instance; + + public Builder() { + this(new EnvironmentWorkPoll200Response()); + } + + protected Builder(EnvironmentWorkPoll200Response instance) { + this.instance = instance; + } + + public EnvironmentWorkPoll200Response.Builder id(String id) { + this.instance.id = id; + return this; + } + public EnvironmentWorkPoll200Response.Builder acknowledgedAt(String acknowledgedAt) { + this.instance.acknowledgedAt = acknowledgedAt; + return this; + } + public EnvironmentWorkPoll200Response.Builder createdAt(String createdAt) { + this.instance.createdAt = createdAt; + return this; + } + public EnvironmentWorkPoll200Response.Builder data(WorkData data) { + this.instance.data = data; + return this; + } + public EnvironmentWorkPoll200Response.Builder environmentId(String environmentId) { + this.instance.environmentId = environmentId; + return this; + } + public EnvironmentWorkPoll200Response.Builder latestHeartbeatAt(String latestHeartbeatAt) { + this.instance.latestHeartbeatAt = latestHeartbeatAt; + return this; + } + public EnvironmentWorkPoll200Response.Builder tags(List tags) { + this.instance.tags = tags; + return this; + } + public EnvironmentWorkPoll200Response.Builder secret(String secret) { + this.instance.secret = secret; + return this; + } + public EnvironmentWorkPoll200Response.Builder startedAt(String startedAt) { + this.instance.startedAt = startedAt; + return this; + } + public EnvironmentWorkPoll200Response.Builder state(WorkState state) { + this.instance.state = state; + return this; + } + public EnvironmentWorkPoll200Response.Builder stopRequestedAt(String stopRequestedAt) { + this.instance.stopRequestedAt = stopRequestedAt; + return this; + } + public EnvironmentWorkPoll200Response.Builder stoppedAt(String stoppedAt) { + this.instance.stoppedAt = stoppedAt; + return this; + } + public EnvironmentWorkPoll200Response.Builder type(TypeEnum type) { + this.instance.type = type; + return this; + } + + + /** + * returns a built EnvironmentWorkPoll200Response instance. + * + * The builder is not reusable. + */ + public EnvironmentWorkPoll200Response build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field. + */ + public static EnvironmentWorkPoll200Response.Builder builder() { + return new EnvironmentWorkPoll200Response.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public EnvironmentWorkPoll200Response.Builder toBuilder() { + return new EnvironmentWorkPoll200Response.Builder() + .id(getId()) + .acknowledgedAt(getAcknowledgedAt()) + .createdAt(getCreatedAt()) + .data(getData()) + .environmentId(getEnvironmentId()) + .latestHeartbeatAt(getLatestHeartbeatAt()) + .tags(getTags()) + .secret(getSecret()) + .startedAt(getStartedAt()) + .state(getState()) + .stopRequestedAt(getStopRequestedAt()) + .stoppedAt(getStoppedAt()) + .type(getType()); + } + + +} diff --git a/src/main/java/com/volcengine/ark/runtime/models/environment/HeartbeatRequest.java b/src/main/java/com/volcengine/ark/runtime/models/environment/HeartbeatRequest.java new file mode 100644 index 0000000..f7b365e --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/models/environment/HeartbeatRequest.java @@ -0,0 +1,264 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +/* + * Ark Managed Agents Environment API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.volcengine.ark.runtime.models.environment; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.util.Objects; + +/** + * HeartbeatRequest + */ +@JsonPropertyOrder({ + HeartbeatRequest.JSON_PROPERTY_ENVIRONMENT_ID, + HeartbeatRequest.JSON_PROPERTY_WORK_ID, + HeartbeatRequest.JSON_PROPERTY_DESIRED_TTL_SECONDS, + HeartbeatRequest.JSON_PROPERTY_EXPECTED_LAST_HEARTBEAT +}) +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") +public class HeartbeatRequest { + public static final String JSON_PROPERTY_ENVIRONMENT_ID = "environmentId"; + @javax.annotation.Nonnull + private String environmentId; + + public static final String JSON_PROPERTY_WORK_ID = "workId"; + @javax.annotation.Nonnull + private String workId; + + public static final String JSON_PROPERTY_DESIRED_TTL_SECONDS = "desired_ttl_seconds"; + @javax.annotation.Nullable + private Long desiredTtlSeconds; + + public static final String JSON_PROPERTY_EXPECTED_LAST_HEARTBEAT = "expected_last_heartbeat"; + @javax.annotation.Nullable + private String expectedLastHeartbeat; + + public HeartbeatRequest() { + } + + public HeartbeatRequest environmentId(@javax.annotation.Nonnull String environmentId) { + + this.environmentId = environmentId; + return this; + } + + /** + * Get environmentId + * @return environmentId + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_ENVIRONMENT_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getEnvironmentId() { + return environmentId; + } + + + @JsonProperty(value = JSON_PROPERTY_ENVIRONMENT_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setEnvironmentId(@javax.annotation.Nonnull String environmentId) { + this.environmentId = environmentId; + } + + public HeartbeatRequest workId(@javax.annotation.Nonnull String workId) { + + this.workId = workId; + return this; + } + + /** + * Get workId + * @return workId + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_WORK_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getWorkId() { + return workId; + } + + + @JsonProperty(value = JSON_PROPERTY_WORK_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setWorkId(@javax.annotation.Nonnull String workId) { + this.workId = workId; + } + + public HeartbeatRequest desiredTtlSeconds(@javax.annotation.Nullable Long desiredTtlSeconds) { + + this.desiredTtlSeconds = desiredTtlSeconds; + return this; + } + + /** + * 期望刷新后的 TTL 秒数。 + * @return desiredTtlSeconds + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_DESIRED_TTL_SECONDS, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public Long getDesiredTtlSeconds() { + return desiredTtlSeconds; + } + + + @JsonProperty(value = JSON_PROPERTY_DESIRED_TTL_SECONDS, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setDesiredTtlSeconds(@javax.annotation.Nullable Long desiredTtlSeconds) { + this.desiredTtlSeconds = desiredTtlSeconds; + } + + public HeartbeatRequest expectedLastHeartbeat(@javax.annotation.Nullable String expectedLastHeartbeat) { + + this.expectedLastHeartbeat = expectedLastHeartbeat; + return this; + } + + /** + * Worker 上一次看到的 last_heartbeat,用于并发校验。 + * @return expectedLastHeartbeat + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_EXPECTED_LAST_HEARTBEAT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getExpectedLastHeartbeat() { + return expectedLastHeartbeat; + } + + + @JsonProperty(value = JSON_PROPERTY_EXPECTED_LAST_HEARTBEAT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setExpectedLastHeartbeat(@javax.annotation.Nullable String expectedLastHeartbeat) { + this.expectedLastHeartbeat = expectedLastHeartbeat; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + HeartbeatRequest heartbeatRequest = (HeartbeatRequest) o; + return Objects.equals(this.environmentId, heartbeatRequest.environmentId) && + Objects.equals(this.workId, heartbeatRequest.workId) && + Objects.equals(this.desiredTtlSeconds, heartbeatRequest.desiredTtlSeconds) && + Objects.equals(this.expectedLastHeartbeat, heartbeatRequest.expectedLastHeartbeat); + } + + @Override + public int hashCode() { + return Objects.hash(environmentId, workId, desiredTtlSeconds, expectedLastHeartbeat); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class HeartbeatRequest {\n"); + sb.append(" environmentId: ").append(toIndentedString(environmentId)).append("\n"); + sb.append(" workId: ").append(toIndentedString(workId)).append("\n"); + sb.append(" desiredTtlSeconds: ").append(toIndentedString(desiredTtlSeconds)).append("\n"); + sb.append(" expectedLastHeartbeat: ").append(toIndentedString(expectedLastHeartbeat)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + return o == null ? "null" : o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private HeartbeatRequest instance; + + public Builder() { + this(new HeartbeatRequest()); + } + + protected Builder(HeartbeatRequest instance) { + this.instance = instance; + } + + public HeartbeatRequest.Builder environmentId(String environmentId) { + this.instance.environmentId = environmentId; + return this; + } + public HeartbeatRequest.Builder workId(String workId) { + this.instance.workId = workId; + return this; + } + public HeartbeatRequest.Builder desiredTtlSeconds(Long desiredTtlSeconds) { + this.instance.desiredTtlSeconds = desiredTtlSeconds; + return this; + } + public HeartbeatRequest.Builder expectedLastHeartbeat(String expectedLastHeartbeat) { + this.instance.expectedLastHeartbeat = expectedLastHeartbeat; + return this; + } + + + /** + * returns a built HeartbeatRequest instance. + * + * The builder is not reusable. + */ + public HeartbeatRequest build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field. + */ + public static HeartbeatRequest.Builder builder() { + return new HeartbeatRequest.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public HeartbeatRequest.Builder toBuilder() { + return new HeartbeatRequest.Builder() + .environmentId(getEnvironmentId()) + .workId(getWorkId()) + .desiredTtlSeconds(getDesiredTtlSeconds()) + .expectedLastHeartbeat(getExpectedLastHeartbeat()); + } + + +} diff --git a/src/main/java/com/volcengine/ark/runtime/models/environment/HeartbeatWorkResponse.java b/src/main/java/com/volcengine/ark/runtime/models/environment/HeartbeatWorkResponse.java new file mode 100644 index 0000000..caecbed --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/models/environment/HeartbeatWorkResponse.java @@ -0,0 +1,336 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +/* + * Ark Managed Agents Environment API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.volcengine.ark.runtime.models.environment; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.Objects; + +/** + * Heartbeat work 的响应体。 + */ +@JsonPropertyOrder({ + HeartbeatWorkResponse.JSON_PROPERTY_LAST_HEARTBEAT, + HeartbeatWorkResponse.JSON_PROPERTY_LEASE_EXTENDED, + HeartbeatWorkResponse.JSON_PROPERTY_STATE, + HeartbeatWorkResponse.JSON_PROPERTY_TTL_SECONDS, + HeartbeatWorkResponse.JSON_PROPERTY_TYPE +}) +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") +public class HeartbeatWorkResponse { + public static final String JSON_PROPERTY_LAST_HEARTBEAT = "last_heartbeat"; + @javax.annotation.Nonnull + private String lastHeartbeat; + + public static final String JSON_PROPERTY_LEASE_EXTENDED = "lease_extended"; + @javax.annotation.Nonnull + private Boolean leaseExtended; + + public static final String JSON_PROPERTY_STATE = "state"; + @javax.annotation.Nonnull + private WorkState state; + + public static final String JSON_PROPERTY_TTL_SECONDS = "ttl_seconds"; + @javax.annotation.Nonnull + private Long ttlSeconds; + + /** + * 对象类型,固定为 `work_heartbeat`。 + */ + public enum TypeEnum { + WORK_HEARTBEAT(String.valueOf("work_heartbeat")); + + private String value; + + TypeEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static TypeEnum fromValue(String value) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equalsIgnoreCase(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + public static final String JSON_PROPERTY_TYPE = "type"; + @javax.annotation.Nonnull + private TypeEnum type; + + public HeartbeatWorkResponse() { + } + + public HeartbeatWorkResponse lastHeartbeat(@javax.annotation.Nonnull String lastHeartbeat) { + + this.lastHeartbeat = lastHeartbeat; + return this; + } + + /** + * 控制面接受的 heartbeat 时间,RFC 3339。 + * @return lastHeartbeat + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_LAST_HEARTBEAT, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getLastHeartbeat() { + return lastHeartbeat; + } + + + @JsonProperty(value = JSON_PROPERTY_LAST_HEARTBEAT, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setLastHeartbeat(@javax.annotation.Nonnull String lastHeartbeat) { + this.lastHeartbeat = lastHeartbeat; + } + + public HeartbeatWorkResponse leaseExtended(@javax.annotation.Nonnull Boolean leaseExtended) { + + this.leaseExtended = leaseExtended; + return this; + } + + /** + * Lease 是否被刷新。 + * @return leaseExtended + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_LEASE_EXTENDED, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public Boolean getLeaseExtended() { + return leaseExtended; + } + + + @JsonProperty(value = JSON_PROPERTY_LEASE_EXTENDED, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setLeaseExtended(@javax.annotation.Nonnull Boolean leaseExtended) { + this.leaseExtended = leaseExtended; + } + + public HeartbeatWorkResponse state(@javax.annotation.Nonnull WorkState state) { + + this.state = state; + return this; + } + + /** + * Work 生命周期状态。 + * @return state + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_STATE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public WorkState getState() { + return state; + } + + + @JsonProperty(value = JSON_PROPERTY_STATE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setState(@javax.annotation.Nonnull WorkState state) { + this.state = state; + } + + public HeartbeatWorkResponse ttlSeconds(@javax.annotation.Nonnull Long ttlSeconds) { + + this.ttlSeconds = ttlSeconds; + return this; + } + + /** + * Lease TTL 秒数。 + * @return ttlSeconds + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_TTL_SECONDS, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public Long getTtlSeconds() { + return ttlSeconds; + } + + + @JsonProperty(value = JSON_PROPERTY_TTL_SECONDS, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setTtlSeconds(@javax.annotation.Nonnull Long ttlSeconds) { + this.ttlSeconds = ttlSeconds; + } + + public HeartbeatWorkResponse type(@javax.annotation.Nonnull TypeEnum type) { + + this.type = type; + return this; + } + + /** + * 对象类型,固定为 `work_heartbeat`。 + * @return type + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_TYPE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public TypeEnum getType() { + return type; + } + + + @JsonProperty(value = JSON_PROPERTY_TYPE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setType(@javax.annotation.Nonnull TypeEnum type) { + this.type = type; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + HeartbeatWorkResponse heartbeatWorkResponse = (HeartbeatWorkResponse) o; + return Objects.equals(this.lastHeartbeat, heartbeatWorkResponse.lastHeartbeat) && + Objects.equals(this.leaseExtended, heartbeatWorkResponse.leaseExtended) && + Objects.equals(this.state, heartbeatWorkResponse.state) && + Objects.equals(this.ttlSeconds, heartbeatWorkResponse.ttlSeconds) && + Objects.equals(this.type, heartbeatWorkResponse.type); + } + + @Override + public int hashCode() { + return Objects.hash(lastHeartbeat, leaseExtended, state, ttlSeconds, type); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class HeartbeatWorkResponse {\n"); + sb.append(" lastHeartbeat: ").append(toIndentedString(lastHeartbeat)).append("\n"); + sb.append(" leaseExtended: ").append(toIndentedString(leaseExtended)).append("\n"); + sb.append(" state: ").append(toIndentedString(state)).append("\n"); + sb.append(" ttlSeconds: ").append(toIndentedString(ttlSeconds)).append("\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + return o == null ? "null" : o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private HeartbeatWorkResponse instance; + + public Builder() { + this(new HeartbeatWorkResponse()); + } + + protected Builder(HeartbeatWorkResponse instance) { + this.instance = instance; + } + + public HeartbeatWorkResponse.Builder lastHeartbeat(String lastHeartbeat) { + this.instance.lastHeartbeat = lastHeartbeat; + return this; + } + public HeartbeatWorkResponse.Builder leaseExtended(Boolean leaseExtended) { + this.instance.leaseExtended = leaseExtended; + return this; + } + public HeartbeatWorkResponse.Builder state(WorkState state) { + this.instance.state = state; + return this; + } + public HeartbeatWorkResponse.Builder ttlSeconds(Long ttlSeconds) { + this.instance.ttlSeconds = ttlSeconds; + return this; + } + public HeartbeatWorkResponse.Builder type(TypeEnum type) { + this.instance.type = type; + return this; + } + + + /** + * returns a built HeartbeatWorkResponse instance. + * + * The builder is not reusable. + */ + public HeartbeatWorkResponse build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field. + */ + public static HeartbeatWorkResponse.Builder builder() { + return new HeartbeatWorkResponse.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public HeartbeatWorkResponse.Builder toBuilder() { + return new HeartbeatWorkResponse.Builder() + .lastHeartbeat(getLastHeartbeat()) + .leaseExtended(getLeaseExtended()) + .state(getState()) + .ttlSeconds(getTtlSeconds()) + .type(getType()); + } + + +} diff --git a/src/main/java/com/volcengine/ark/runtime/models/environment/PollRequest.java b/src/main/java/com/volcengine/ark/runtime/models/environment/PollRequest.java new file mode 100644 index 0000000..9a575d9 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/models/environment/PollRequest.java @@ -0,0 +1,264 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +/* + * Ark Managed Agents Environment API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.volcengine.ark.runtime.models.environment; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.util.Objects; + +/** + * PollRequest + */ +@JsonPropertyOrder({ + PollRequest.JSON_PROPERTY_ENVIRONMENT_ID, + PollRequest.JSON_PROPERTY_BLOCK_MS, + PollRequest.JSON_PROPERTY_RECLAIM_OLDER_THAN_MS, + PollRequest.JSON_PROPERTY_ARK_WORKER_I_D +}) +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") +public class PollRequest { + public static final String JSON_PROPERTY_ENVIRONMENT_ID = "environmentId"; + @javax.annotation.Nonnull + private String environmentId; + + public static final String JSON_PROPERTY_BLOCK_MS = "block_ms"; + @javax.annotation.Nullable + private Long blockMs; + + public static final String JSON_PROPERTY_RECLAIM_OLDER_THAN_MS = "reclaim_older_than_ms"; + @javax.annotation.Nullable + private Long reclaimOlderThanMs; + + public static final String JSON_PROPERTY_ARK_WORKER_I_D = "Ark-Worker-ID"; + @javax.annotation.Nullable + private String arkWorkerID; + + public PollRequest() { + } + + public PollRequest environmentId(@javax.annotation.Nonnull String environmentId) { + + this.environmentId = environmentId; + return this; + } + + /** + * Get environmentId + * @return environmentId + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_ENVIRONMENT_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getEnvironmentId() { + return environmentId; + } + + + @JsonProperty(value = JSON_PROPERTY_ENVIRONMENT_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setEnvironmentId(@javax.annotation.Nonnull String environmentId) { + this.environmentId = environmentId; + } + + public PollRequest blockMs(@javax.annotation.Nullable Long blockMs) { + + this.blockMs = blockMs; + return this; + } + + /** + * 长轮询阻塞时长,单位毫秒。 + * @return blockMs + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_BLOCK_MS, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public Long getBlockMs() { + return blockMs; + } + + + @JsonProperty(value = JSON_PROPERTY_BLOCK_MS, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setBlockMs(@javax.annotation.Nullable Long blockMs) { + this.blockMs = blockMs; + } + + public PollRequest reclaimOlderThanMs(@javax.annotation.Nullable Long reclaimOlderThanMs) { + + this.reclaimOlderThanMs = reclaimOlderThanMs; + return this; + } + + /** + * 允许控制面回收超过指定时长未 heartbeat 的 work,单位毫秒。 + * @return reclaimOlderThanMs + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_RECLAIM_OLDER_THAN_MS, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public Long getReclaimOlderThanMs() { + return reclaimOlderThanMs; + } + + + @JsonProperty(value = JSON_PROPERTY_RECLAIM_OLDER_THAN_MS, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setReclaimOlderThanMs(@javax.annotation.Nullable Long reclaimOlderThanMs) { + this.reclaimOlderThanMs = reclaimOlderThanMs; + } + + public PollRequest arkWorkerID(@javax.annotation.Nullable String arkWorkerID) { + + this.arkWorkerID = arkWorkerID; + return this; + } + + /** + * Worker 实例 ID,用于控制面记录 work 归属和排障。 + * @return arkWorkerID + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_ARK_WORKER_I_D, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getArkWorkerID() { + return arkWorkerID; + } + + + @JsonProperty(value = JSON_PROPERTY_ARK_WORKER_I_D, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setArkWorkerID(@javax.annotation.Nullable String arkWorkerID) { + this.arkWorkerID = arkWorkerID; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PollRequest pollRequest = (PollRequest) o; + return Objects.equals(this.environmentId, pollRequest.environmentId) && + Objects.equals(this.blockMs, pollRequest.blockMs) && + Objects.equals(this.reclaimOlderThanMs, pollRequest.reclaimOlderThanMs) && + Objects.equals(this.arkWorkerID, pollRequest.arkWorkerID); + } + + @Override + public int hashCode() { + return Objects.hash(environmentId, blockMs, reclaimOlderThanMs, arkWorkerID); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class PollRequest {\n"); + sb.append(" environmentId: ").append(toIndentedString(environmentId)).append("\n"); + sb.append(" blockMs: ").append(toIndentedString(blockMs)).append("\n"); + sb.append(" reclaimOlderThanMs: ").append(toIndentedString(reclaimOlderThanMs)).append("\n"); + sb.append(" arkWorkerID: ").append(toIndentedString(arkWorkerID)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + return o == null ? "null" : o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private PollRequest instance; + + public Builder() { + this(new PollRequest()); + } + + protected Builder(PollRequest instance) { + this.instance = instance; + } + + public PollRequest.Builder environmentId(String environmentId) { + this.instance.environmentId = environmentId; + return this; + } + public PollRequest.Builder blockMs(Long blockMs) { + this.instance.blockMs = blockMs; + return this; + } + public PollRequest.Builder reclaimOlderThanMs(Long reclaimOlderThanMs) { + this.instance.reclaimOlderThanMs = reclaimOlderThanMs; + return this; + } + public PollRequest.Builder arkWorkerID(String arkWorkerID) { + this.instance.arkWorkerID = arkWorkerID; + return this; + } + + + /** + * returns a built PollRequest instance. + * + * The builder is not reusable. + */ + public PollRequest build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field. + */ + public static PollRequest.Builder builder() { + return new PollRequest.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public PollRequest.Builder toBuilder() { + return new PollRequest.Builder() + .environmentId(getEnvironmentId()) + .blockMs(getBlockMs()) + .reclaimOlderThanMs(getReclaimOlderThanMs()) + .arkWorkerID(getArkWorkerID()); + } + + +} diff --git a/src/main/java/com/volcengine/ark/runtime/models/environment/StopWorkBody.java b/src/main/java/com/volcengine/ark/runtime/models/environment/StopWorkBody.java new file mode 100644 index 0000000..a36edd6 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/models/environment/StopWorkBody.java @@ -0,0 +1,153 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +/* + * Ark Managed Agents Environment API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.volcengine.ark.runtime.models.environment; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.util.Objects; + +/** + * Stop work 的请求体。 + */ +@JsonPropertyOrder({ + StopWorkBody.JSON_PROPERTY_FORCE +}) +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") +public class StopWorkBody { + public static final String JSON_PROPERTY_FORCE = "force"; + @javax.annotation.Nullable + private Boolean force; + + public StopWorkBody() { + } + + public StopWorkBody force(@javax.annotation.Nullable Boolean force) { + + this.force = force; + return this; + } + + /** + * 是否强制停止。 + * @return force + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_FORCE, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public Boolean getForce() { + return force; + } + + + @JsonProperty(value = JSON_PROPERTY_FORCE, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setForce(@javax.annotation.Nullable Boolean force) { + this.force = force; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + StopWorkBody stopWorkBody = (StopWorkBody) o; + return Objects.equals(this.force, stopWorkBody.force); + } + + @Override + public int hashCode() { + return Objects.hash(force); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class StopWorkBody {\n"); + sb.append(" force: ").append(toIndentedString(force)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + return o == null ? "null" : o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private StopWorkBody instance; + + public Builder() { + this(new StopWorkBody()); + } + + protected Builder(StopWorkBody instance) { + this.instance = instance; + } + + public StopWorkBody.Builder force(Boolean force) { + this.instance.force = force; + return this; + } + + + /** + * returns a built StopWorkBody instance. + * + * The builder is not reusable. + */ + public StopWorkBody build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field. + */ + public static StopWorkBody.Builder builder() { + return new StopWorkBody.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public StopWorkBody.Builder toBuilder() { + return new StopWorkBody.Builder() + .force(getForce()); + } + + +} diff --git a/src/main/java/com/volcengine/ark/runtime/models/environment/TosConfig.java b/src/main/java/com/volcengine/ark/runtime/models/environment/TosConfig.java new file mode 100644 index 0000000..7d45946 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/models/environment/TosConfig.java @@ -0,0 +1,190 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +/* + * Ark Managed Agents Environment API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.volcengine.ark.runtime.models.environment; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.util.Objects; + +/** + * Environment 产物存储位置。设置后 outputs 文件会注册到用户指定的 TOS bucket/prefix;不设置则走方舟默认存储。 + */ +@JsonPropertyOrder({ + TosConfig.JSON_PROPERTY_BUCKET, + TosConfig.JSON_PROPERTY_PREFIX +}) +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") +public class TosConfig { + public static final String JSON_PROPERTY_BUCKET = "bucket"; + @javax.annotation.Nullable + private String bucket; + + public static final String JSON_PROPERTY_PREFIX = "prefix"; + @javax.annotation.Nullable + private String prefix; + + public TosConfig() { + } + + public TosConfig bucket(@javax.annotation.Nullable String bucket) { + + this.bucket = bucket; + return this; + } + + /** + * TOS bucket 名称。 + * @return bucket + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_BUCKET, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getBucket() { + return bucket; + } + + + @JsonProperty(value = JSON_PROPERTY_BUCKET, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setBucket(@javax.annotation.Nullable String bucket) { + this.bucket = bucket; + } + + public TosConfig prefix(@javax.annotation.Nullable String prefix) { + + this.prefix = prefix; + return this; + } + + /** + * TOS 前缀。 + * @return prefix + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_PREFIX, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getPrefix() { + return prefix; + } + + + @JsonProperty(value = JSON_PROPERTY_PREFIX, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setPrefix(@javax.annotation.Nullable String prefix) { + this.prefix = prefix; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TosConfig tosConfig = (TosConfig) o; + return Objects.equals(this.bucket, tosConfig.bucket) && + Objects.equals(this.prefix, tosConfig.prefix); + } + + @Override + public int hashCode() { + return Objects.hash(bucket, prefix); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class TosConfig {\n"); + sb.append(" bucket: ").append(toIndentedString(bucket)).append("\n"); + sb.append(" prefix: ").append(toIndentedString(prefix)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + return o == null ? "null" : o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private TosConfig instance; + + public Builder() { + this(new TosConfig()); + } + + protected Builder(TosConfig instance) { + this.instance = instance; + } + + public TosConfig.Builder bucket(String bucket) { + this.instance.bucket = bucket; + return this; + } + public TosConfig.Builder prefix(String prefix) { + this.instance.prefix = prefix; + return this; + } + + + /** + * returns a built TosConfig instance. + * + * The builder is not reusable. + */ + public TosConfig build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field. + */ + public static TosConfig.Builder builder() { + return new TosConfig.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public TosConfig.Builder toBuilder() { + return new TosConfig.Builder() + .bucket(getBucket()) + .prefix(getPrefix()); + } + + +} diff --git a/src/main/java/com/volcengine/ark/runtime/models/environment/VolcTag.java b/src/main/java/com/volcengine/ark/runtime/models/environment/VolcTag.java new file mode 100644 index 0000000..e1e3c84 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/models/environment/VolcTag.java @@ -0,0 +1,190 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +/* + * Ark Managed Agents Environment API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.volcengine.ark.runtime.models.environment; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.util.Objects; + +/** + * Work 关联的标签。 + */ +@JsonPropertyOrder({ + VolcTag.JSON_PROPERTY_KEY, + VolcTag.JSON_PROPERTY_VALUE +}) +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") +public class VolcTag { + public static final String JSON_PROPERTY_KEY = "key"; + @javax.annotation.Nonnull + private String key; + + public static final String JSON_PROPERTY_VALUE = "value"; + @javax.annotation.Nullable + private String value; + + public VolcTag() { + } + + public VolcTag key(@javax.annotation.Nonnull String key) { + + this.key = key; + return this; + } + + /** + * 标签 key。 + * @return key + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_KEY, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getKey() { + return key; + } + + + @JsonProperty(value = JSON_PROPERTY_KEY, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setKey(@javax.annotation.Nonnull String key) { + this.key = key; + } + + public VolcTag value(@javax.annotation.Nullable String value) { + + this.value = value; + return this; + } + + /** + * 标签 value。 + * @return value + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_VALUE, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getValue() { + return value; + } + + + @JsonProperty(value = JSON_PROPERTY_VALUE, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setValue(@javax.annotation.Nullable String value) { + this.value = value; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + VolcTag volcTag = (VolcTag) o; + return Objects.equals(this.key, volcTag.key) && + Objects.equals(this.value, volcTag.value); + } + + @Override + public int hashCode() { + return Objects.hash(key, value); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class VolcTag {\n"); + sb.append(" key: ").append(toIndentedString(key)).append("\n"); + sb.append(" value: ").append(toIndentedString(value)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + return o == null ? "null" : o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private VolcTag instance; + + public Builder() { + this(new VolcTag()); + } + + protected Builder(VolcTag instance) { + this.instance = instance; + } + + public VolcTag.Builder key(String key) { + this.instance.key = key; + return this; + } + public VolcTag.Builder value(String value) { + this.instance.value = value; + return this; + } + + + /** + * returns a built VolcTag instance. + * + * The builder is not reusable. + */ + public VolcTag build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field. + */ + public static VolcTag.Builder builder() { + return new VolcTag.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public VolcTag.Builder toBuilder() { + return new VolcTag.Builder() + .key(getKey()) + .value(getValue()); + } + + +} diff --git a/src/main/java/com/volcengine/ark/runtime/models/environment/WorkData.java b/src/main/java/com/volcengine/ark/runtime/models/environment/WorkData.java new file mode 100644 index 0000000..474b9cb --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/models/environment/WorkData.java @@ -0,0 +1,190 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +/* + * Ark Managed Agents Environment API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.volcengine.ark.runtime.models.environment; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.util.Objects; + +/** + * Work 的业务载荷。 + */ +@JsonPropertyOrder({ + WorkData.JSON_PROPERTY_ID, + WorkData.JSON_PROPERTY_TYPE +}) +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") +public class WorkData { + public static final String JSON_PROPERTY_ID = "id"; + @javax.annotation.Nonnull + private String id; + + public static final String JSON_PROPERTY_TYPE = "type"; + @javax.annotation.Nonnull + private String type; + + public WorkData() { + } + + public WorkData id(@javax.annotation.Nonnull String id) { + + this.id = id; + return this; + } + + /** + * 业务对象 ID,例如 session ID。 + * @return id + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getId() { + return id; + } + + + @JsonProperty(value = JSON_PROPERTY_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setId(@javax.annotation.Nonnull String id) { + this.id = id; + } + + public WorkData type(@javax.annotation.Nonnull String type) { + + this.type = type; + return this; + } + + /** + * 业务载荷类型,例如 `session`。 + * @return type + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_TYPE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getType() { + return type; + } + + + @JsonProperty(value = JSON_PROPERTY_TYPE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setType(@javax.annotation.Nonnull String type) { + this.type = type; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + WorkData workData = (WorkData) o; + return Objects.equals(this.id, workData.id) && + Objects.equals(this.type, workData.type); + } + + @Override + public int hashCode() { + return Objects.hash(id, type); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class WorkData {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + return o == null ? "null" : o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private WorkData instance; + + public Builder() { + this(new WorkData()); + } + + protected Builder(WorkData instance) { + this.instance = instance; + } + + public WorkData.Builder id(String id) { + this.instance.id = id; + return this; + } + public WorkData.Builder type(String type) { + this.instance.type = type; + return this; + } + + + /** + * returns a built WorkData instance. + * + * The builder is not reusable. + */ + public WorkData build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field. + */ + public static WorkData.Builder builder() { + return new WorkData.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public WorkData.Builder toBuilder() { + return new WorkData.Builder() + .id(getId()) + .type(getType()); + } + + +} diff --git a/src/main/java/com/volcengine/ark/runtime/models/environment/WorkItem.java b/src/main/java/com/volcengine/ark/runtime/models/environment/WorkItem.java new file mode 100644 index 0000000..9a0fe2d --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/models/environment/WorkItem.java @@ -0,0 +1,641 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +/* + * Ark Managed Agents Environment API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.volcengine.ark.runtime.models.environment; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Worker queue 中的一条 work。 + */ +@JsonPropertyOrder({ + WorkItem.JSON_PROPERTY_ID, + WorkItem.JSON_PROPERTY_ACKNOWLEDGED_AT, + WorkItem.JSON_PROPERTY_CREATED_AT, + WorkItem.JSON_PROPERTY_DATA, + WorkItem.JSON_PROPERTY_ENVIRONMENT_ID, + WorkItem.JSON_PROPERTY_LATEST_HEARTBEAT_AT, + WorkItem.JSON_PROPERTY_TAGS, + "secret", + WorkItem.JSON_PROPERTY_STARTED_AT, + WorkItem.JSON_PROPERTY_STATE, + WorkItem.JSON_PROPERTY_STOP_REQUESTED_AT, + WorkItem.JSON_PROPERTY_STOPPED_AT, + WorkItem.JSON_PROPERTY_TYPE +}) +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") +public class WorkItem { + public static final String JSON_PROPERTY_ID = "id"; + @javax.annotation.Nonnull + private String id; + + public static final String JSON_PROPERTY_ACKNOWLEDGED_AT = "acknowledged_at"; + @javax.annotation.Nullable + private String acknowledgedAt; + + public static final String JSON_PROPERTY_CREATED_AT = "created_at"; + @javax.annotation.Nonnull + private String createdAt; + + public static final String JSON_PROPERTY_DATA = "data"; + @javax.annotation.Nonnull + private WorkData data; + + public static final String JSON_PROPERTY_ENVIRONMENT_ID = "environment_id"; + @javax.annotation.Nonnull + private String environmentId; + + public static final String JSON_PROPERTY_LATEST_HEARTBEAT_AT = "latest_heartbeat_at"; + @javax.annotation.Nullable + private String latestHeartbeatAt; + + public static final String JSON_PROPERTY_TAGS = "tags"; + @javax.annotation.Nullable + private List tags; + + @javax.annotation.Nullable + private String secret; + + public static final String JSON_PROPERTY_STARTED_AT = "started_at"; + @javax.annotation.Nullable + private String startedAt; + + public static final String JSON_PROPERTY_STATE = "state"; + @javax.annotation.Nonnull + private WorkState state; + + public static final String JSON_PROPERTY_STOP_REQUESTED_AT = "stop_requested_at"; + @javax.annotation.Nullable + private String stopRequestedAt; + + public static final String JSON_PROPERTY_STOPPED_AT = "stopped_at"; + @javax.annotation.Nullable + private String stoppedAt; + + /** + * 对象类型,固定为 `work`。 + */ + public enum TypeEnum { + WORK(String.valueOf("work")); + + private String value; + + TypeEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static TypeEnum fromValue(String value) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equalsIgnoreCase(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + public static final String JSON_PROPERTY_TYPE = "type"; + @javax.annotation.Nonnull + private TypeEnum type; + + public WorkItem() { + } + + public WorkItem id(@javax.annotation.Nonnull String id) { + + this.id = id; + return this; + } + + /** + * Work ID。 + * @return id + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getId() { + return id; + } + + + @JsonProperty(value = JSON_PROPERTY_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setId(@javax.annotation.Nonnull String id) { + this.id = id; + } + + public WorkItem acknowledgedAt(@javax.annotation.Nullable String acknowledgedAt) { + + this.acknowledgedAt = acknowledgedAt; + return this; + } + + /** + * Work ack 时间,RFC 3339。 + * @return acknowledgedAt + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_ACKNOWLEDGED_AT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getAcknowledgedAt() { + return acknowledgedAt; + } + + + @JsonProperty(value = JSON_PROPERTY_ACKNOWLEDGED_AT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setAcknowledgedAt(@javax.annotation.Nullable String acknowledgedAt) { + this.acknowledgedAt = acknowledgedAt; + } + + public WorkItem createdAt(@javax.annotation.Nonnull String createdAt) { + + this.createdAt = createdAt; + return this; + } + + /** + * Work 创建时间,RFC 3339。 + * @return createdAt + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_CREATED_AT, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getCreatedAt() { + return createdAt; + } + + + @JsonProperty(value = JSON_PROPERTY_CREATED_AT, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setCreatedAt(@javax.annotation.Nonnull String createdAt) { + this.createdAt = createdAt; + } + + public WorkItem data(@javax.annotation.Nonnull WorkData data) { + + this.data = data; + return this; + } + + /** + * 业务载荷。 + * @return data + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_DATA, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public WorkData getData() { + return data; + } + + + @JsonProperty(value = JSON_PROPERTY_DATA, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setData(@javax.annotation.Nonnull WorkData data) { + this.data = data; + } + + public WorkItem environmentId(@javax.annotation.Nonnull String environmentId) { + + this.environmentId = environmentId; + return this; + } + + /** + * Environment ID。 + * @return environmentId + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_ENVIRONMENT_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getEnvironmentId() { + return environmentId; + } + + + @JsonProperty(value = JSON_PROPERTY_ENVIRONMENT_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setEnvironmentId(@javax.annotation.Nonnull String environmentId) { + this.environmentId = environmentId; + } + + public WorkItem latestHeartbeatAt(@javax.annotation.Nullable String latestHeartbeatAt) { + + this.latestHeartbeatAt = latestHeartbeatAt; + return this; + } + + /** + * 最近 heartbeat 时间,RFC 3339。 + * @return latestHeartbeatAt + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_LATEST_HEARTBEAT_AT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getLatestHeartbeatAt() { + return latestHeartbeatAt; + } + + + @JsonProperty(value = JSON_PROPERTY_LATEST_HEARTBEAT_AT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setLatestHeartbeatAt(@javax.annotation.Nullable String latestHeartbeatAt) { + this.latestHeartbeatAt = latestHeartbeatAt; + } + + public WorkItem tags(@javax.annotation.Nullable List tags) { + + this.tags = tags; + return this; + } + + public WorkItem addTagsItem(VolcTag tagsItem) { + if (this.tags == null) { + this.tags = new ArrayList<>(); + } + this.tags.add(tagsItem); + return this; + } + + /** + * Work 标签。 + * @return tags + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_TAGS, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + + public List getTags() { + return tags; + } + + + @JsonProperty(value = JSON_PROPERTY_TAGS, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + public void setTags(@javax.annotation.Nullable List tags) { + this.tags = tags; + } + + public WorkItem secret(@javax.annotation.Nullable String secret) { + + this.secret = secret; + return this; + } + + /** + * Work secret;仅 poll 时返回,ack / stop 响应会抹掉。 + * @return secret + */ + @javax.annotation.Nullable + @JsonProperty(value = "secret", required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getSecret() { + return secret; + } + + + @JsonProperty(value = "secret", required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setSecret(@javax.annotation.Nullable String secret) { + this.secret = secret; + } + + public WorkItem startedAt(@javax.annotation.Nullable String startedAt) { + + this.startedAt = startedAt; + return this; + } + + /** + * Work 开始时间,RFC 3339。 + * @return startedAt + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_STARTED_AT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getStartedAt() { + return startedAt; + } + + + @JsonProperty(value = JSON_PROPERTY_STARTED_AT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setStartedAt(@javax.annotation.Nullable String startedAt) { + this.startedAt = startedAt; + } + + public WorkItem state(@javax.annotation.Nonnull WorkState state) { + + this.state = state; + return this; + } + + /** + * Work 生命周期状态。 + * @return state + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_STATE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public WorkState getState() { + return state; + } + + + @JsonProperty(value = JSON_PROPERTY_STATE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setState(@javax.annotation.Nonnull WorkState state) { + this.state = state; + } + + public WorkItem stopRequestedAt(@javax.annotation.Nullable String stopRequestedAt) { + + this.stopRequestedAt = stopRequestedAt; + return this; + } + + /** + * 控制面请求停止的时间,RFC 3339。 + * @return stopRequestedAt + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_STOP_REQUESTED_AT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getStopRequestedAt() { + return stopRequestedAt; + } + + + @JsonProperty(value = JSON_PROPERTY_STOP_REQUESTED_AT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setStopRequestedAt(@javax.annotation.Nullable String stopRequestedAt) { + this.stopRequestedAt = stopRequestedAt; + } + + public WorkItem stoppedAt(@javax.annotation.Nullable String stoppedAt) { + + this.stoppedAt = stoppedAt; + return this; + } + + /** + * Work 停止时间,RFC 3339。 + * @return stoppedAt + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_STOPPED_AT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getStoppedAt() { + return stoppedAt; + } + + + @JsonProperty(value = JSON_PROPERTY_STOPPED_AT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setStoppedAt(@javax.annotation.Nullable String stoppedAt) { + this.stoppedAt = stoppedAt; + } + + public WorkItem type(@javax.annotation.Nonnull TypeEnum type) { + + this.type = type; + return this; + } + + /** + * 对象类型,固定为 `work`。 + * @return type + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_TYPE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public TypeEnum getType() { + return type; + } + + + @JsonProperty(value = JSON_PROPERTY_TYPE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setType(@javax.annotation.Nonnull TypeEnum type) { + this.type = type; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + WorkItem workItem = (WorkItem) o; + return Objects.equals(this.id, workItem.id) && + Objects.equals(this.acknowledgedAt, workItem.acknowledgedAt) && + Objects.equals(this.createdAt, workItem.createdAt) && + Objects.equals(this.data, workItem.data) && + Objects.equals(this.environmentId, workItem.environmentId) && + Objects.equals(this.latestHeartbeatAt, workItem.latestHeartbeatAt) && + Objects.equals(this.tags, workItem.tags) && + Objects.equals(this.secret, workItem.secret) && + Objects.equals(this.startedAt, workItem.startedAt) && + Objects.equals(this.state, workItem.state) && + Objects.equals(this.stopRequestedAt, workItem.stopRequestedAt) && + Objects.equals(this.stoppedAt, workItem.stoppedAt) && + Objects.equals(this.type, workItem.type); + } + + @Override + public int hashCode() { + return Objects.hash(id, acknowledgedAt, createdAt, data, environmentId, latestHeartbeatAt, tags, secret, startedAt, state, stopRequestedAt, stoppedAt, type); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class WorkItem {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" acknowledgedAt: ").append(toIndentedString(acknowledgedAt)).append("\n"); + sb.append(" createdAt: ").append(toIndentedString(createdAt)).append("\n"); + sb.append(" data: ").append(toIndentedString(data)).append("\n"); + sb.append(" environmentId: ").append(toIndentedString(environmentId)).append("\n"); + sb.append(" latestHeartbeatAt: ").append(toIndentedString(latestHeartbeatAt)).append("\n"); + sb.append(" tags: ").append(toIndentedString(tags)).append("\n"); + sb.append(" secret: [REDACTED]\n"); + sb.append(" startedAt: ").append(toIndentedString(startedAt)).append("\n"); + sb.append(" state: ").append(toIndentedString(state)).append("\n"); + sb.append(" stopRequestedAt: ").append(toIndentedString(stopRequestedAt)).append("\n"); + sb.append(" stoppedAt: ").append(toIndentedString(stoppedAt)).append("\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + return o == null ? "null" : o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private WorkItem instance; + + public Builder() { + this(new WorkItem()); + } + + protected Builder(WorkItem instance) { + this.instance = instance; + } + + public WorkItem.Builder id(String id) { + this.instance.id = id; + return this; + } + public WorkItem.Builder acknowledgedAt(String acknowledgedAt) { + this.instance.acknowledgedAt = acknowledgedAt; + return this; + } + public WorkItem.Builder createdAt(String createdAt) { + this.instance.createdAt = createdAt; + return this; + } + public WorkItem.Builder data(WorkData data) { + this.instance.data = data; + return this; + } + public WorkItem.Builder environmentId(String environmentId) { + this.instance.environmentId = environmentId; + return this; + } + public WorkItem.Builder latestHeartbeatAt(String latestHeartbeatAt) { + this.instance.latestHeartbeatAt = latestHeartbeatAt; + return this; + } + public WorkItem.Builder tags(List tags) { + this.instance.tags = tags; + return this; + } + public WorkItem.Builder secret(String secret) { + this.instance.secret = secret; + return this; + } + public WorkItem.Builder startedAt(String startedAt) { + this.instance.startedAt = startedAt; + return this; + } + public WorkItem.Builder state(WorkState state) { + this.instance.state = state; + return this; + } + public WorkItem.Builder stopRequestedAt(String stopRequestedAt) { + this.instance.stopRequestedAt = stopRequestedAt; + return this; + } + public WorkItem.Builder stoppedAt(String stoppedAt) { + this.instance.stoppedAt = stoppedAt; + return this; + } + public WorkItem.Builder type(TypeEnum type) { + this.instance.type = type; + return this; + } + + + /** + * returns a built WorkItem instance. + * + * The builder is not reusable. + */ + public WorkItem build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field. + */ + public static WorkItem.Builder builder() { + return new WorkItem.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public WorkItem.Builder toBuilder() { + return new WorkItem.Builder() + .id(getId()) + .acknowledgedAt(getAcknowledgedAt()) + .createdAt(getCreatedAt()) + .data(getData()) + .environmentId(getEnvironmentId()) + .latestHeartbeatAt(getLatestHeartbeatAt()) + .tags(getTags()) + .secret(getSecret()) + .startedAt(getStartedAt()) + .state(getState()) + .stopRequestedAt(getStopRequestedAt()) + .stoppedAt(getStoppedAt()) + .type(getType()); + } + + +} diff --git a/src/main/java/com/volcengine/ark/runtime/models/environment/WorkState.java b/src/main/java/com/volcengine/ark/runtime/models/environment/WorkState.java new file mode 100644 index 0000000..1d40505 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/models/environment/WorkState.java @@ -0,0 +1,63 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +/* + * Ark Managed Agents Environment API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.volcengine.ark.runtime.models.environment; + + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * Work 生命周期状态。 + */ +public enum WorkState { + + QUEUED("queued"), + + STARTING("starting"), + + ACTIVE("active"), + + STOPPING("stopping"), + + STOPPED("stopped"); + + private String value; + + WorkState(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static WorkState fromValue(String value) { + for (WorkState b : WorkState.values()) { + if (b.value.equalsIgnoreCase(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/models/session/AgentRef.java b/src/main/java/com/volcengine/ark/runtime/models/session/AgentRef.java index 0b0d95a..84416ab 100644 --- a/src/main/java/com/volcengine/ark/runtime/models/session/AgentRef.java +++ b/src/main/java/com/volcengine/ark/runtime/models/session/AgentRef.java @@ -16,97 +16,101 @@ package com.volcengine.ark.runtime.models.session; -import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.annotation.JsonValue; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Objects; /** - * Agent 引用(对象形态):`type: \"agent\"` + id + optional version。 与 CreateSessionRequest.agent 联合使用。 + * Agent 引用(对象形态):`type: \"agent\"` 或 `\"agent_with_overrides\"`。 MA wire 上两种对象形态都走同一个 JSON object 承载,避免 SDK 生成复杂 union。 */ @JsonPropertyOrder({ AgentRef.JSON_PROPERTY_TYPE, AgentRef.JSON_PROPERTY_ID, - AgentRef.JSON_PROPERTY_VERSION + AgentRef.JSON_PROPERTY_VERSION, + AgentRef.JSON_PROPERTY_SYSTEM, + AgentRef.JSON_PROPERTY_TOOLS, + AgentRef.JSON_PROPERTY_MCP_SERVERS, + AgentRef.JSON_PROPERTY_SKILLS, + AgentRef.JSON_PROPERTY_MULTIAGENT, + AgentRef.JSON_PROPERTY_DISPLAY_NAME, + AgentRef.JSON_PROPERTY_MODEL }) @javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") public class AgentRef { - /** - * 固定 `\"agent\"`。 - */ - public enum TypeEnum { - AGENT(String.valueOf("agent")); - - private String value; - - TypeEnum(String value) { - this.value = value; - } - - @JsonValue - public String getValue() { - return value; - } - - @Override - public String toString() { - return String.valueOf(value); - } - - @JsonCreator - public static TypeEnum fromValue(String value) { - for (TypeEnum b : TypeEnum.values()) { - if (b.value.equalsIgnoreCase(value)) { - return b; - } - } - throw new IllegalArgumentException("Unexpected value '" + value + "'"); - } - } - public static final String JSON_PROPERTY_TYPE = "type"; @javax.annotation.Nonnull - private TypeEnum type; + private String type; public static final String JSON_PROPERTY_ID = "id"; - @javax.annotation.Nonnull + @javax.annotation.Nullable private String id; public static final String JSON_PROPERTY_VERSION = "version"; @javax.annotation.Nullable private Integer version; + public static final String JSON_PROPERTY_SYSTEM = "system"; + @javax.annotation.Nullable + private String system; + + public static final String JSON_PROPERTY_TOOLS = "tools"; + @javax.annotation.Nullable + private List> tools; + + public static final String JSON_PROPERTY_MCP_SERVERS = "mcp_servers"; + @javax.annotation.Nullable + private List> mcpServers; + + public static final String JSON_PROPERTY_SKILLS = "skills"; + @javax.annotation.Nullable + private List> skills; + + public static final String JSON_PROPERTY_MULTIAGENT = "multiagent"; + @javax.annotation.Nullable + private Map multiagent; + + public static final String JSON_PROPERTY_DISPLAY_NAME = "display_name"; + @javax.annotation.Nullable + private String displayName; + + public static final String JSON_PROPERTY_MODEL = "model"; + @javax.annotation.Nullable + private ModelOverrides model; + public AgentRef() { } - public AgentRef type(@javax.annotation.Nonnull TypeEnum type) { + public AgentRef type(@javax.annotation.Nonnull String type) { this.type = type; return this; } /** - * 固定 `\"agent\"`。 + * `\"agent\"` 或 `\"agent_with_overrides\"`。 * @return type */ @javax.annotation.Nonnull @JsonProperty(value = JSON_PROPERTY_TYPE, required = true) @JsonInclude(value = JsonInclude.Include.ALWAYS) - public TypeEnum getType() { + public String getType() { return type; } @JsonProperty(value = JSON_PROPERTY_TYPE, required = true) @JsonInclude(value = JsonInclude.Include.ALWAYS) - public void setType(@javax.annotation.Nonnull TypeEnum type) { + public void setType(@javax.annotation.Nonnull String type) { this.type = type; } - public AgentRef id(@javax.annotation.Nonnull String id) { + public AgentRef id(@javax.annotation.Nullable String id) { this.id = id; return this; @@ -116,18 +120,18 @@ public AgentRef id(@javax.annotation.Nonnull String id) { * Agent ID。 * @return id */ - @javax.annotation.Nonnull - @JsonProperty(value = JSON_PROPERTY_ID, required = true) - @JsonInclude(value = JsonInclude.Include.ALWAYS) + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_ID, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) public String getId() { return id; } - @JsonProperty(value = JSON_PROPERTY_ID, required = true) - @JsonInclude(value = JsonInclude.Include.ALWAYS) - public void setId(@javax.annotation.Nonnull String id) { + @JsonProperty(value = JSON_PROPERTY_ID, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setId(@javax.annotation.Nullable String id) { this.id = id; } @@ -156,6 +160,213 @@ public void setVersion(@javax.annotation.Nullable Integer version) { this.version = version; } + public AgentRef system(@javax.annotation.Nullable String system) { + + this.system = system; + return this; + } + + /** + * System prompt 覆写。 + * @return system + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_SYSTEM, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getSystem() { + return system; + } + + + @JsonProperty(value = JSON_PROPERTY_SYSTEM, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setSystem(@javax.annotation.Nullable String system) { + this.system = system; + } + + public AgentRef tools(@javax.annotation.Nullable List> tools) { + + this.tools = tools; + return this; + } + + public AgentRef addToolsItem(Map toolsItem) { + if (this.tools == null) { + this.tools = new ArrayList<>(); + } + this.tools.add(toolsItem); + return this; + } + + /** + * 工具配置覆写。 + * @return tools + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_TOOLS, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + + public List> getTools() { + return tools; + } + + + @JsonProperty(value = JSON_PROPERTY_TOOLS, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + public void setTools(@javax.annotation.Nullable List> tools) { + this.tools = tools; + } + + public AgentRef mcpServers(@javax.annotation.Nullable List> mcpServers) { + + this.mcpServers = mcpServers; + return this; + } + + public AgentRef addMcpServersItem(Map mcpServersItem) { + if (this.mcpServers == null) { + this.mcpServers = new ArrayList<>(); + } + this.mcpServers.add(mcpServersItem); + return this; + } + + /** + * MCP server 配置覆写。 + * @return mcpServers + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_MCP_SERVERS, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + + public List> getMcpServers() { + return mcpServers; + } + + + @JsonProperty(value = JSON_PROPERTY_MCP_SERVERS, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + public void setMcpServers(@javax.annotation.Nullable List> mcpServers) { + this.mcpServers = mcpServers; + } + + public AgentRef skills(@javax.annotation.Nullable List> skills) { + + this.skills = skills; + return this; + } + + public AgentRef addSkillsItem(Map skillsItem) { + if (this.skills == null) { + this.skills = new ArrayList<>(); + } + this.skills.add(skillsItem); + return this; + } + + /** + * Skill 配置覆写。 + * @return skills + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_SKILLS, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + + public List> getSkills() { + return skills; + } + + + @JsonProperty(value = JSON_PROPERTY_SKILLS, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + public void setSkills(@javax.annotation.Nullable List> skills) { + this.skills = skills; + } + + public AgentRef multiagent(@javax.annotation.Nullable Map multiagent) { + + this.multiagent = multiagent; + return this; + } + + public AgentRef putMultiagentItem(String key, Object multiagentItem) { + if (this.multiagent == null) { + this.multiagent = new HashMap<>(); + } + this.multiagent.put(key, multiagentItem); + return this; + } + + /** + * 多 Agent 配置覆写。 + * @return multiagent + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_MULTIAGENT, required = false) + @JsonInclude(content = JsonInclude.Include.ALWAYS, value = JsonInclude.Include.NON_EMPTY) + + public Map getMultiagent() { + return multiagent; + } + + + @JsonProperty(value = JSON_PROPERTY_MULTIAGENT, required = false) + @JsonInclude(content = JsonInclude.Include.ALWAYS, value = JsonInclude.Include.NON_EMPTY) + public void setMultiagent(@javax.annotation.Nullable Map multiagent) { + this.multiagent = multiagent; + } + + public AgentRef displayName(@javax.annotation.Nullable String displayName) { + + this.displayName = displayName; + return this; + } + + /** + * Session 响应中冻结的 Agent 展示名。 + * @return displayName + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_DISPLAY_NAME, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getDisplayName() { + return displayName; + } + + + @JsonProperty(value = JSON_PROPERTY_DISPLAY_NAME, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setDisplayName(@javax.annotation.Nullable String displayName) { + this.displayName = displayName; + } + + public AgentRef model(@javax.annotation.Nullable ModelOverrides model) { + + this.model = model; + return this; + } + + /** + * 模型运行参数覆写。 + * @return model + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_MODEL, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public ModelOverrides getModel() { + return model; + } + + + @JsonProperty(value = JSON_PROPERTY_MODEL, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setModel(@javax.annotation.Nullable ModelOverrides model) { + this.model = model; + } + @Override public boolean equals(Object o) { @@ -168,12 +379,19 @@ public boolean equals(Object o) { AgentRef agentRef = (AgentRef) o; return Objects.equals(this.type, agentRef.type) && Objects.equals(this.id, agentRef.id) && - Objects.equals(this.version, agentRef.version); + Objects.equals(this.version, agentRef.version) && + Objects.equals(this.system, agentRef.system) && + Objects.equals(this.tools, agentRef.tools) && + Objects.equals(this.mcpServers, agentRef.mcpServers) && + Objects.equals(this.skills, agentRef.skills) && + Objects.equals(this.multiagent, agentRef.multiagent) && + Objects.equals(this.displayName, agentRef.displayName) && + Objects.equals(this.model, agentRef.model); } @Override public int hashCode() { - return Objects.hash(type, id, version); + return Objects.hash(type, id, version, system, tools, mcpServers, skills, multiagent, displayName, model); } @Override @@ -183,6 +401,13 @@ public String toString() { sb.append(" type: ").append(toIndentedString(type)).append("\n"); sb.append(" id: ").append(toIndentedString(id)).append("\n"); sb.append(" version: ").append(toIndentedString(version)).append("\n"); + sb.append(" system: ").append(toIndentedString(system)).append("\n"); + sb.append(" tools: ").append(toIndentedString(tools)).append("\n"); + sb.append(" mcpServers: ").append(toIndentedString(mcpServers)).append("\n"); + sb.append(" skills: ").append(toIndentedString(skills)).append("\n"); + sb.append(" multiagent: ").append(toIndentedString(multiagent)).append("\n"); + sb.append(" displayName: ").append(toIndentedString(displayName)).append("\n"); + sb.append(" model: ").append(toIndentedString(model)).append("\n"); sb.append("}"); return sb.toString(); } @@ -207,7 +432,7 @@ protected Builder(AgentRef instance) { this.instance = instance; } - public AgentRef.Builder type(TypeEnum type) { + public AgentRef.Builder type(String type) { this.instance.type = type; return this; } @@ -219,6 +444,34 @@ public AgentRef.Builder version(Integer version) { this.instance.version = version; return this; } + public AgentRef.Builder system(String system) { + this.instance.system = system; + return this; + } + public AgentRef.Builder tools(List> tools) { + this.instance.tools = tools; + return this; + } + public AgentRef.Builder mcpServers(List> mcpServers) { + this.instance.mcpServers = mcpServers; + return this; + } + public AgentRef.Builder skills(List> skills) { + this.instance.skills = skills; + return this; + } + public AgentRef.Builder multiagent(Map multiagent) { + this.instance.multiagent = multiagent; + return this; + } + public AgentRef.Builder displayName(String displayName) { + this.instance.displayName = displayName; + return this; + } + public AgentRef.Builder model(ModelOverrides model) { + this.instance.model = model; + return this; + } /** @@ -255,7 +508,14 @@ public AgentRef.Builder toBuilder() { return new AgentRef.Builder() .type(getType()) .id(getId()) - .version(getVersion()); + .version(getVersion()) + .system(getSystem()) + .tools(getTools()) + .mcpServers(getMcpServers()) + .skills(getSkills()) + .multiagent(getMultiagent()) + .displayName(getDisplayName()) + .model(getModel()); } diff --git a/src/main/java/com/volcengine/ark/runtime/models/session/CreateSessionRequest.java b/src/main/java/com/volcengine/ark/runtime/models/session/CreateSessionRequest.java index 6314930..a3cc087 100644 --- a/src/main/java/com/volcengine/ark/runtime/models/session/CreateSessionRequest.java +++ b/src/main/java/com/volcengine/ark/runtime/models/session/CreateSessionRequest.java @@ -29,6 +29,7 @@ @JsonPropertyOrder({ CreateSessionRequest.JSON_PROPERTY_AGENT, CreateSessionRequest.JSON_PROPERTY_ENVIRONMENT_ID, + CreateSessionRequest.JSON_PROPERTY_ENVIRONMENT, CreateSessionRequest.JSON_PROPERTY_TAGS, CreateSessionRequest.JSON_PROPERTY_RESOURCES, CreateSessionRequest.JSON_PROPERTY_TITLE, @@ -41,9 +42,13 @@ public class CreateSessionRequest { private AgentIdentifier agent; public static final String JSON_PROPERTY_ENVIRONMENT_ID = "environment_id"; - @javax.annotation.Nonnull + @javax.annotation.Nullable private String environmentId; + public static final String JSON_PROPERTY_ENVIRONMENT = "environment"; + @javax.annotation.Nullable + private EnvironmentWithOverrides environment; + public static final String JSON_PROPERTY_TAGS = "tags"; @javax.annotation.Nullable private List tags; @@ -88,31 +93,56 @@ public void setAgent(@javax.annotation.Nonnull AgentIdentifier agent) { this.agent = agent; } - public CreateSessionRequest environmentId(@javax.annotation.Nonnull String environmentId) { + public CreateSessionRequest environmentId(@javax.annotation.Nullable String environmentId) { this.environmentId = environmentId; return this; } /** - * 关联的 Environment ID。 + * 关联的 Environment ID。与 `environment` 二选一。 * @return environmentId */ - @javax.annotation.Nonnull - @JsonProperty(value = JSON_PROPERTY_ENVIRONMENT_ID, required = true) - @JsonInclude(value = JsonInclude.Include.ALWAYS) + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_ENVIRONMENT_ID, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) public String getEnvironmentId() { return environmentId; } - @JsonProperty(value = JSON_PROPERTY_ENVIRONMENT_ID, required = true) - @JsonInclude(value = JsonInclude.Include.ALWAYS) - public void setEnvironmentId(@javax.annotation.Nonnull String environmentId) { + @JsonProperty(value = JSON_PROPERTY_ENVIRONMENT_ID, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setEnvironmentId(@javax.annotation.Nullable String environmentId) { this.environmentId = environmentId; } + public CreateSessionRequest environment(@javax.annotation.Nullable EnvironmentWithOverrides environment) { + + this.environment = environment; + return this; + } + + /** + * 关联 Environment 的覆写引用。与 `environment_id` 二选一。 + * @return environment + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_ENVIRONMENT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public EnvironmentWithOverrides getEnvironment() { + return environment; + } + + + @JsonProperty(value = JSON_PROPERTY_ENVIRONMENT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setEnvironment(@javax.annotation.Nullable EnvironmentWithOverrides environment) { + this.environment = environment; + } + public CreateSessionRequest tags(@javax.annotation.Nullable List tags) { this.tags = tags; @@ -249,6 +279,7 @@ public boolean equals(Object o) { CreateSessionRequest createSessionRequest = (CreateSessionRequest) o; return Objects.equals(this.agent, createSessionRequest.agent) && Objects.equals(this.environmentId, createSessionRequest.environmentId) && + Objects.equals(this.environment, createSessionRequest.environment) && Objects.equals(this.tags, createSessionRequest.tags) && Objects.equals(this.resources, createSessionRequest.resources) && Objects.equals(this.title, createSessionRequest.title) && @@ -257,7 +288,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(agent, environmentId, tags, resources, title, vaultIds); + return Objects.hash(agent, environmentId, environment, tags, resources, title, vaultIds); } @Override @@ -266,6 +297,7 @@ public String toString() { sb.append("class CreateSessionRequest {\n"); sb.append(" agent: ").append(toIndentedString(agent)).append("\n"); sb.append(" environmentId: ").append(toIndentedString(environmentId)).append("\n"); + sb.append(" environment: ").append(toIndentedString(environment)).append("\n"); sb.append(" tags: ").append(toIndentedString(tags)).append("\n"); sb.append(" resources: ").append(toIndentedString(resources)).append("\n"); sb.append(" title: ").append(toIndentedString(title)).append("\n"); @@ -302,6 +334,10 @@ public CreateSessionRequest.Builder environmentId(String environmentId) { this.instance.environmentId = environmentId; return this; } + public CreateSessionRequest.Builder environment(EnvironmentWithOverrides environment) { + this.instance.environment = environment; + return this; + } public CreateSessionRequest.Builder tags(List tags) { this.instance.tags = tags; return this; @@ -354,6 +390,7 @@ public CreateSessionRequest.Builder toBuilder() { return new CreateSessionRequest.Builder() .agent(getAgent()) .environmentId(getEnvironmentId()) + .environment(getEnvironment()) .tags(getTags()) .resources(getResources()) .title(getTitle()) diff --git a/src/main/java/com/volcengine/ark/runtime/models/session/EnvironmentConfigOverride.java b/src/main/java/com/volcengine/ark/runtime/models/session/EnvironmentConfigOverride.java new file mode 100644 index 0000000..e53a1e5 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/models/session/EnvironmentConfigOverride.java @@ -0,0 +1,348 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +/* + * Ark Managed Agents Session API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.volcengine.ark.runtime.models.session; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * Environment 覆写时使用的运行环境配置。 + */ +@JsonPropertyOrder({ + EnvironmentConfigOverride.JSON_PROPERTY_TYPE, + EnvironmentConfigOverride.JSON_PROPERTY_NETWORKING, + EnvironmentConfigOverride.JSON_PROPERTY_PACKAGES, + EnvironmentConfigOverride.JSON_PROPERTY_ENV, + EnvironmentConfigOverride.JSON_PROPERTY_SETUP_SCRIPT, + EnvironmentConfigOverride.JSON_PROPERTY_TOS +}) +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") +public class EnvironmentConfigOverride { + public static final String JSON_PROPERTY_TYPE = "type"; + @javax.annotation.Nonnull + private String type; + + public static final String JSON_PROPERTY_NETWORKING = "networking"; + @javax.annotation.Nullable + private EnvironmentNetworkingConfig networking; + + public static final String JSON_PROPERTY_PACKAGES = "packages"; + @javax.annotation.Nullable + private EnvironmentPackagesConfig packages; + + public static final String JSON_PROPERTY_ENV = "env"; + @javax.annotation.Nullable + private Map env; + + public static final String JSON_PROPERTY_SETUP_SCRIPT = "setup_script"; + @javax.annotation.Nullable + private String setupScript; + + public static final String JSON_PROPERTY_TOS = "tos"; + @javax.annotation.Nullable + private EnvironmentTosConfig tos; + + public EnvironmentConfigOverride() { + } + + public EnvironmentConfigOverride type(@javax.annotation.Nonnull String type) { + + this.type = type; + return this; + } + + /** + * 运行环境类型。 + * @return type + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_TYPE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getType() { + return type; + } + + + @JsonProperty(value = JSON_PROPERTY_TYPE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setType(@javax.annotation.Nonnull String type) { + this.type = type; + } + + public EnvironmentConfigOverride networking(@javax.annotation.Nullable EnvironmentNetworkingConfig networking) { + + this.networking = networking; + return this; + } + + /** + * 容器出网策略。 + * @return networking + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_NETWORKING, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public EnvironmentNetworkingConfig getNetworking() { + return networking; + } + + + @JsonProperty(value = JSON_PROPERTY_NETWORKING, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setNetworking(@javax.annotation.Nullable EnvironmentNetworkingConfig networking) { + this.networking = networking; + } + + public EnvironmentConfigOverride packages(@javax.annotation.Nullable EnvironmentPackagesConfig packages) { + + this.packages = packages; + return this; + } + + /** + * 启动时预装的依赖包。 + * @return packages + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_PACKAGES, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public EnvironmentPackagesConfig getPackages() { + return packages; + } + + + @JsonProperty(value = JSON_PROPERTY_PACKAGES, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setPackages(@javax.annotation.Nullable EnvironmentPackagesConfig packages) { + this.packages = packages; + } + + public EnvironmentConfigOverride env(@javax.annotation.Nullable Map env) { + + this.env = env; + return this; + } + + public EnvironmentConfigOverride putEnvItem(String key, String envItem) { + if (this.env == null) { + this.env = new HashMap<>(); + } + this.env.put(key, envItem); + return this; + } + + /** + * 容器环境变量。 + * @return env + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_ENV, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + + public Map getEnv() { + return env; + } + + + @JsonProperty(value = JSON_PROPERTY_ENV, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + public void setEnv(@javax.annotation.Nullable Map env) { + this.env = env; + } + + public EnvironmentConfigOverride setupScript(@javax.annotation.Nullable String setupScript) { + + this.setupScript = setupScript; + return this; + } + + /** + * 沙箱启动脚本。 + * @return setupScript + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_SETUP_SCRIPT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getSetupScript() { + return setupScript; + } + + + @JsonProperty(value = JSON_PROPERTY_SETUP_SCRIPT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setSetupScript(@javax.annotation.Nullable String setupScript) { + this.setupScript = setupScript; + } + + public EnvironmentConfigOverride tos(@javax.annotation.Nullable EnvironmentTosConfig tos) { + + this.tos = tos; + return this; + } + + /** + * Environment outputs 的 TOS 存储配置。 + * @return tos + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_TOS, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public EnvironmentTosConfig getTos() { + return tos; + } + + + @JsonProperty(value = JSON_PROPERTY_TOS, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setTos(@javax.annotation.Nullable EnvironmentTosConfig tos) { + this.tos = tos; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + EnvironmentConfigOverride environmentConfigOverride = (EnvironmentConfigOverride) o; + return Objects.equals(this.type, environmentConfigOverride.type) && + Objects.equals(this.networking, environmentConfigOverride.networking) && + Objects.equals(this.packages, environmentConfigOverride.packages) && + Objects.equals(this.env, environmentConfigOverride.env) && + Objects.equals(this.setupScript, environmentConfigOverride.setupScript) && + Objects.equals(this.tos, environmentConfigOverride.tos); + } + + @Override + public int hashCode() { + return Objects.hash(type, networking, packages, env, setupScript, tos); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class EnvironmentConfigOverride {\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" networking: ").append(toIndentedString(networking)).append("\n"); + sb.append(" packages: ").append(toIndentedString(packages)).append("\n"); + sb.append(" env: ").append(toIndentedString(env)).append("\n"); + sb.append(" setupScript: ").append(toIndentedString(setupScript)).append("\n"); + sb.append(" tos: ").append(toIndentedString(tos)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + return o == null ? "null" : o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private EnvironmentConfigOverride instance; + + public Builder() { + this(new EnvironmentConfigOverride()); + } + + protected Builder(EnvironmentConfigOverride instance) { + this.instance = instance; + } + + public EnvironmentConfigOverride.Builder type(String type) { + this.instance.type = type; + return this; + } + public EnvironmentConfigOverride.Builder networking(EnvironmentNetworkingConfig networking) { + this.instance.networking = networking; + return this; + } + public EnvironmentConfigOverride.Builder packages(EnvironmentPackagesConfig packages) { + this.instance.packages = packages; + return this; + } + public EnvironmentConfigOverride.Builder env(Map env) { + this.instance.env = env; + return this; + } + public EnvironmentConfigOverride.Builder setupScript(String setupScript) { + this.instance.setupScript = setupScript; + return this; + } + public EnvironmentConfigOverride.Builder tos(EnvironmentTosConfig tos) { + this.instance.tos = tos; + return this; + } + + + /** + * returns a built EnvironmentConfigOverride instance. + * + * The builder is not reusable. + */ + public EnvironmentConfigOverride build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field. + */ + public static EnvironmentConfigOverride.Builder builder() { + return new EnvironmentConfigOverride.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public EnvironmentConfigOverride.Builder toBuilder() { + return new EnvironmentConfigOverride.Builder() + .type(getType()) + .networking(getNetworking()) + .packages(getPackages()) + .env(getEnv()) + .setupScript(getSetupScript()) + .tos(getTos()); + } + + +} diff --git a/src/main/java/com/volcengine/ark/runtime/models/session/EnvironmentNetworkingConfig.java b/src/main/java/com/volcengine/ark/runtime/models/session/EnvironmentNetworkingConfig.java new file mode 100644 index 0000000..38bf383 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/models/session/EnvironmentNetworkingConfig.java @@ -0,0 +1,274 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +/* + * Ark Managed Agents Session API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.volcengine.ark.runtime.models.session; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Environment 覆写时使用的容器出网策略。 + */ +@JsonPropertyOrder({ + EnvironmentNetworkingConfig.JSON_PROPERTY_TYPE, + EnvironmentNetworkingConfig.JSON_PROPERTY_ALLOW_MCP_SERVERS, + EnvironmentNetworkingConfig.JSON_PROPERTY_ALLOW_PACKAGE_MANAGERS, + EnvironmentNetworkingConfig.JSON_PROPERTY_ALLOWED_HOSTS +}) +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") +public class EnvironmentNetworkingConfig { + public static final String JSON_PROPERTY_TYPE = "type"; + @javax.annotation.Nonnull + private String type; + + public static final String JSON_PROPERTY_ALLOW_MCP_SERVERS = "allow_mcp_servers"; + @javax.annotation.Nullable + private Boolean allowMcpServers; + + public static final String JSON_PROPERTY_ALLOW_PACKAGE_MANAGERS = "allow_package_managers"; + @javax.annotation.Nullable + private Boolean allowPackageManagers; + + public static final String JSON_PROPERTY_ALLOWED_HOSTS = "allowed_hosts"; + @javax.annotation.Nullable + private List allowedHosts; + + public EnvironmentNetworkingConfig() { + } + + public EnvironmentNetworkingConfig type(@javax.annotation.Nonnull String type) { + + this.type = type; + return this; + } + + /** + * 出网策略类型。 + * @return type + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_TYPE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getType() { + return type; + } + + + @JsonProperty(value = JSON_PROPERTY_TYPE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setType(@javax.annotation.Nonnull String type) { + this.type = type; + } + + public EnvironmentNetworkingConfig allowMcpServers(@javax.annotation.Nullable Boolean allowMcpServers) { + + this.allowMcpServers = allowMcpServers; + return this; + } + + /** + * 是否允许出网到 MCP servers。 + * @return allowMcpServers + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_ALLOW_MCP_SERVERS, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public Boolean getAllowMcpServers() { + return allowMcpServers; + } + + + @JsonProperty(value = JSON_PROPERTY_ALLOW_MCP_SERVERS, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setAllowMcpServers(@javax.annotation.Nullable Boolean allowMcpServers) { + this.allowMcpServers = allowMcpServers; + } + + public EnvironmentNetworkingConfig allowPackageManagers(@javax.annotation.Nullable Boolean allowPackageManagers) { + + this.allowPackageManagers = allowPackageManagers; + return this; + } + + /** + * 是否允许访问包管理器。 + * @return allowPackageManagers + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_ALLOW_PACKAGE_MANAGERS, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public Boolean getAllowPackageManagers() { + return allowPackageManagers; + } + + + @JsonProperty(value = JSON_PROPERTY_ALLOW_PACKAGE_MANAGERS, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setAllowPackageManagers(@javax.annotation.Nullable Boolean allowPackageManagers) { + this.allowPackageManagers = allowPackageManagers; + } + + public EnvironmentNetworkingConfig allowedHosts(@javax.annotation.Nullable List allowedHosts) { + + this.allowedHosts = allowedHosts; + return this; + } + + public EnvironmentNetworkingConfig addAllowedHostsItem(String allowedHostsItem) { + if (this.allowedHosts == null) { + this.allowedHosts = new ArrayList<>(); + } + this.allowedHosts.add(allowedHostsItem); + return this; + } + + /** + * 显式允许的出网域名。 + * @return allowedHosts + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_ALLOWED_HOSTS, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + + public List getAllowedHosts() { + return allowedHosts; + } + + + @JsonProperty(value = JSON_PROPERTY_ALLOWED_HOSTS, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + public void setAllowedHosts(@javax.annotation.Nullable List allowedHosts) { + this.allowedHosts = allowedHosts; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + EnvironmentNetworkingConfig environmentNetworkingConfig = (EnvironmentNetworkingConfig) o; + return Objects.equals(this.type, environmentNetworkingConfig.type) && + Objects.equals(this.allowMcpServers, environmentNetworkingConfig.allowMcpServers) && + Objects.equals(this.allowPackageManagers, environmentNetworkingConfig.allowPackageManagers) && + Objects.equals(this.allowedHosts, environmentNetworkingConfig.allowedHosts); + } + + @Override + public int hashCode() { + return Objects.hash(type, allowMcpServers, allowPackageManagers, allowedHosts); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class EnvironmentNetworkingConfig {\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" allowMcpServers: ").append(toIndentedString(allowMcpServers)).append("\n"); + sb.append(" allowPackageManagers: ").append(toIndentedString(allowPackageManagers)).append("\n"); + sb.append(" allowedHosts: ").append(toIndentedString(allowedHosts)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + return o == null ? "null" : o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private EnvironmentNetworkingConfig instance; + + public Builder() { + this(new EnvironmentNetworkingConfig()); + } + + protected Builder(EnvironmentNetworkingConfig instance) { + this.instance = instance; + } + + public EnvironmentNetworkingConfig.Builder type(String type) { + this.instance.type = type; + return this; + } + public EnvironmentNetworkingConfig.Builder allowMcpServers(Boolean allowMcpServers) { + this.instance.allowMcpServers = allowMcpServers; + return this; + } + public EnvironmentNetworkingConfig.Builder allowPackageManagers(Boolean allowPackageManagers) { + this.instance.allowPackageManagers = allowPackageManagers; + return this; + } + public EnvironmentNetworkingConfig.Builder allowedHosts(List allowedHosts) { + this.instance.allowedHosts = allowedHosts; + return this; + } + + + /** + * returns a built EnvironmentNetworkingConfig instance. + * + * The builder is not reusable. + */ + public EnvironmentNetworkingConfig build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field. + */ + public static EnvironmentNetworkingConfig.Builder builder() { + return new EnvironmentNetworkingConfig.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public EnvironmentNetworkingConfig.Builder toBuilder() { + return new EnvironmentNetworkingConfig.Builder() + .type(getType()) + .allowMcpServers(getAllowMcpServers()) + .allowPackageManagers(getAllowPackageManagers()) + .allowedHosts(getAllowedHosts()); + } + + +} diff --git a/src/main/java/com/volcengine/ark/runtime/models/session/EnvironmentPackagesConfig.java b/src/main/java/com/volcengine/ark/runtime/models/session/EnvironmentPackagesConfig.java new file mode 100644 index 0000000..b6a4203 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/models/session/EnvironmentPackagesConfig.java @@ -0,0 +1,460 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +/* + * Ark Managed Agents Session API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.volcengine.ark.runtime.models.session; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Environment 覆写时使用的预装依赖包配置。 + */ +@JsonPropertyOrder({ + EnvironmentPackagesConfig.JSON_PROPERTY_TYPE, + EnvironmentPackagesConfig.JSON_PROPERTY_PIP, + EnvironmentPackagesConfig.JSON_PROPERTY_APT, + EnvironmentPackagesConfig.JSON_PROPERTY_NPM, + EnvironmentPackagesConfig.JSON_PROPERTY_CARGO, + EnvironmentPackagesConfig.JSON_PROPERTY_GEM, + EnvironmentPackagesConfig.JSON_PROPERTY_GO +}) +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") +public class EnvironmentPackagesConfig { + /** + * 固定 `\"packages\"`。 + */ + public enum TypeEnum { + PACKAGES(String.valueOf("packages")); + + private String value; + + TypeEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static TypeEnum fromValue(String value) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equalsIgnoreCase(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + public static final String JSON_PROPERTY_TYPE = "type"; + @javax.annotation.Nullable + private TypeEnum type; + + public static final String JSON_PROPERTY_PIP = "pip"; + @javax.annotation.Nullable + private List pip; + + public static final String JSON_PROPERTY_APT = "apt"; + @javax.annotation.Nullable + private List apt; + + public static final String JSON_PROPERTY_NPM = "npm"; + @javax.annotation.Nullable + private List npm; + + public static final String JSON_PROPERTY_CARGO = "cargo"; + @javax.annotation.Nullable + private List cargo; + + public static final String JSON_PROPERTY_GEM = "gem"; + @javax.annotation.Nullable + private List gem; + + public static final String JSON_PROPERTY_GO = "go"; + @javax.annotation.Nullable + private List go; + + public EnvironmentPackagesConfig() { + } + + public EnvironmentPackagesConfig type(@javax.annotation.Nullable TypeEnum type) { + + this.type = type; + return this; + } + + /** + * 固定 `\"packages\"`。 + * @return type + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_TYPE, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public TypeEnum getType() { + return type; + } + + + @JsonProperty(value = JSON_PROPERTY_TYPE, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setType(@javax.annotation.Nullable TypeEnum type) { + this.type = type; + } + + public EnvironmentPackagesConfig pip(@javax.annotation.Nullable List pip) { + + this.pip = pip; + return this; + } + + public EnvironmentPackagesConfig addPipItem(String pipItem) { + if (this.pip == null) { + this.pip = new ArrayList<>(); + } + this.pip.add(pipItem); + return this; + } + + /** + * pip 依赖。 + * @return pip + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_PIP, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + + public List getPip() { + return pip; + } + + + @JsonProperty(value = JSON_PROPERTY_PIP, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + public void setPip(@javax.annotation.Nullable List pip) { + this.pip = pip; + } + + public EnvironmentPackagesConfig apt(@javax.annotation.Nullable List apt) { + + this.apt = apt; + return this; + } + + public EnvironmentPackagesConfig addAptItem(String aptItem) { + if (this.apt == null) { + this.apt = new ArrayList<>(); + } + this.apt.add(aptItem); + return this; + } + + /** + * apt 依赖。 + * @return apt + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_APT, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + + public List getApt() { + return apt; + } + + + @JsonProperty(value = JSON_PROPERTY_APT, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + public void setApt(@javax.annotation.Nullable List apt) { + this.apt = apt; + } + + public EnvironmentPackagesConfig npm(@javax.annotation.Nullable List npm) { + + this.npm = npm; + return this; + } + + public EnvironmentPackagesConfig addNpmItem(String npmItem) { + if (this.npm == null) { + this.npm = new ArrayList<>(); + } + this.npm.add(npmItem); + return this; + } + + /** + * npm 依赖。 + * @return npm + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_NPM, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + + public List getNpm() { + return npm; + } + + + @JsonProperty(value = JSON_PROPERTY_NPM, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + public void setNpm(@javax.annotation.Nullable List npm) { + this.npm = npm; + } + + public EnvironmentPackagesConfig cargo(@javax.annotation.Nullable List cargo) { + + this.cargo = cargo; + return this; + } + + public EnvironmentPackagesConfig addCargoItem(String cargoItem) { + if (this.cargo == null) { + this.cargo = new ArrayList<>(); + } + this.cargo.add(cargoItem); + return this; + } + + /** + * cargo 依赖。 + * @return cargo + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_CARGO, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + + public List getCargo() { + return cargo; + } + + + @JsonProperty(value = JSON_PROPERTY_CARGO, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + public void setCargo(@javax.annotation.Nullable List cargo) { + this.cargo = cargo; + } + + public EnvironmentPackagesConfig gem(@javax.annotation.Nullable List gem) { + + this.gem = gem; + return this; + } + + public EnvironmentPackagesConfig addGemItem(String gemItem) { + if (this.gem == null) { + this.gem = new ArrayList<>(); + } + this.gem.add(gemItem); + return this; + } + + /** + * gem 依赖。 + * @return gem + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_GEM, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + + public List getGem() { + return gem; + } + + + @JsonProperty(value = JSON_PROPERTY_GEM, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + public void setGem(@javax.annotation.Nullable List gem) { + this.gem = gem; + } + + public EnvironmentPackagesConfig go(@javax.annotation.Nullable List go) { + + this.go = go; + return this; + } + + public EnvironmentPackagesConfig addGoItem(String goItem) { + if (this.go == null) { + this.go = new ArrayList<>(); + } + this.go.add(goItem); + return this; + } + + /** + * go module 依赖。 + * @return go + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_GO, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + + public List getGo() { + return go; + } + + + @JsonProperty(value = JSON_PROPERTY_GO, required = false) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + public void setGo(@javax.annotation.Nullable List go) { + this.go = go; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + EnvironmentPackagesConfig environmentPackagesConfig = (EnvironmentPackagesConfig) o; + return Objects.equals(this.type, environmentPackagesConfig.type) && + Objects.equals(this.pip, environmentPackagesConfig.pip) && + Objects.equals(this.apt, environmentPackagesConfig.apt) && + Objects.equals(this.npm, environmentPackagesConfig.npm) && + Objects.equals(this.cargo, environmentPackagesConfig.cargo) && + Objects.equals(this.gem, environmentPackagesConfig.gem) && + Objects.equals(this.go, environmentPackagesConfig.go); + } + + @Override + public int hashCode() { + return Objects.hash(type, pip, apt, npm, cargo, gem, go); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class EnvironmentPackagesConfig {\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" pip: ").append(toIndentedString(pip)).append("\n"); + sb.append(" apt: ").append(toIndentedString(apt)).append("\n"); + sb.append(" npm: ").append(toIndentedString(npm)).append("\n"); + sb.append(" cargo: ").append(toIndentedString(cargo)).append("\n"); + sb.append(" gem: ").append(toIndentedString(gem)).append("\n"); + sb.append(" go: ").append(toIndentedString(go)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + return o == null ? "null" : o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private EnvironmentPackagesConfig instance; + + public Builder() { + this(new EnvironmentPackagesConfig()); + } + + protected Builder(EnvironmentPackagesConfig instance) { + this.instance = instance; + } + + public EnvironmentPackagesConfig.Builder type(TypeEnum type) { + this.instance.type = type; + return this; + } + public EnvironmentPackagesConfig.Builder pip(List pip) { + this.instance.pip = pip; + return this; + } + public EnvironmentPackagesConfig.Builder apt(List apt) { + this.instance.apt = apt; + return this; + } + public EnvironmentPackagesConfig.Builder npm(List npm) { + this.instance.npm = npm; + return this; + } + public EnvironmentPackagesConfig.Builder cargo(List cargo) { + this.instance.cargo = cargo; + return this; + } + public EnvironmentPackagesConfig.Builder gem(List gem) { + this.instance.gem = gem; + return this; + } + public EnvironmentPackagesConfig.Builder go(List go) { + this.instance.go = go; + return this; + } + + + /** + * returns a built EnvironmentPackagesConfig instance. + * + * The builder is not reusable. + */ + public EnvironmentPackagesConfig build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field. + */ + public static EnvironmentPackagesConfig.Builder builder() { + return new EnvironmentPackagesConfig.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public EnvironmentPackagesConfig.Builder toBuilder() { + return new EnvironmentPackagesConfig.Builder() + .type(getType()) + .pip(getPip()) + .apt(getApt()) + .npm(getNpm()) + .cargo(getCargo()) + .gem(getGem()) + .go(getGo()); + } + + +} diff --git a/src/main/java/com/volcengine/ark/runtime/models/session/EnvironmentTosConfig.java b/src/main/java/com/volcengine/ark/runtime/models/session/EnvironmentTosConfig.java new file mode 100644 index 0000000..f42ab46 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/models/session/EnvironmentTosConfig.java @@ -0,0 +1,190 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +/* + * Ark Managed Agents Session API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.volcengine.ark.runtime.models.session; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.util.Objects; + +/** + * Environment 覆写时使用的 TOS 配置。 + */ +@JsonPropertyOrder({ + EnvironmentTosConfig.JSON_PROPERTY_BUCKET, + EnvironmentTosConfig.JSON_PROPERTY_PREFIX +}) +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") +public class EnvironmentTosConfig { + public static final String JSON_PROPERTY_BUCKET = "bucket"; + @javax.annotation.Nullable + private String bucket; + + public static final String JSON_PROPERTY_PREFIX = "prefix"; + @javax.annotation.Nullable + private String prefix; + + public EnvironmentTosConfig() { + } + + public EnvironmentTosConfig bucket(@javax.annotation.Nullable String bucket) { + + this.bucket = bucket; + return this; + } + + /** + * TOS bucket 名称。 + * @return bucket + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_BUCKET, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getBucket() { + return bucket; + } + + + @JsonProperty(value = JSON_PROPERTY_BUCKET, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setBucket(@javax.annotation.Nullable String bucket) { + this.bucket = bucket; + } + + public EnvironmentTosConfig prefix(@javax.annotation.Nullable String prefix) { + + this.prefix = prefix; + return this; + } + + /** + * TOS 前缀。 + * @return prefix + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_PREFIX, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getPrefix() { + return prefix; + } + + + @JsonProperty(value = JSON_PROPERTY_PREFIX, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setPrefix(@javax.annotation.Nullable String prefix) { + this.prefix = prefix; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + EnvironmentTosConfig environmentTosConfig = (EnvironmentTosConfig) o; + return Objects.equals(this.bucket, environmentTosConfig.bucket) && + Objects.equals(this.prefix, environmentTosConfig.prefix); + } + + @Override + public int hashCode() { + return Objects.hash(bucket, prefix); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class EnvironmentTosConfig {\n"); + sb.append(" bucket: ").append(toIndentedString(bucket)).append("\n"); + sb.append(" prefix: ").append(toIndentedString(prefix)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + return o == null ? "null" : o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private EnvironmentTosConfig instance; + + public Builder() { + this(new EnvironmentTosConfig()); + } + + protected Builder(EnvironmentTosConfig instance) { + this.instance = instance; + } + + public EnvironmentTosConfig.Builder bucket(String bucket) { + this.instance.bucket = bucket; + return this; + } + public EnvironmentTosConfig.Builder prefix(String prefix) { + this.instance.prefix = prefix; + return this; + } + + + /** + * returns a built EnvironmentTosConfig instance. + * + * The builder is not reusable. + */ + public EnvironmentTosConfig build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field. + */ + public static EnvironmentTosConfig.Builder builder() { + return new EnvironmentTosConfig.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public EnvironmentTosConfig.Builder toBuilder() { + return new EnvironmentTosConfig.Builder() + .bucket(getBucket()) + .prefix(getPrefix()); + } + + +} diff --git a/src/main/java/com/volcengine/ark/runtime/models/session/EnvironmentWithOverrides.java b/src/main/java/com/volcengine/ark/runtime/models/session/EnvironmentWithOverrides.java new file mode 100644 index 0000000..b6488fe --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/models/session/EnvironmentWithOverrides.java @@ -0,0 +1,262 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +/* + * Ark Managed Agents Session API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.volcengine.ark.runtime.models.session; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.Objects; + +/** + * CreateSession 时的 Environment 覆写引用。 + */ +@JsonPropertyOrder({ + EnvironmentWithOverrides.JSON_PROPERTY_TYPE, + EnvironmentWithOverrides.JSON_PROPERTY_ID, + EnvironmentWithOverrides.JSON_PROPERTY_CONFIG +}) +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") +public class EnvironmentWithOverrides { + /** + * 固定 `\"environment_with_overrides\"`。 + */ + public enum TypeEnum { + ENVIRONMENT_WITH_OVERRIDES(String.valueOf("environment_with_overrides")); + + private String value; + + TypeEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static TypeEnum fromValue(String value) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equalsIgnoreCase(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + public static final String JSON_PROPERTY_TYPE = "type"; + @javax.annotation.Nonnull + private TypeEnum type; + + public static final String JSON_PROPERTY_ID = "id"; + @javax.annotation.Nonnull + private String id; + + public static final String JSON_PROPERTY_CONFIG = "config"; + @javax.annotation.Nullable + private EnvironmentConfigOverride config; + + public EnvironmentWithOverrides() { + } + + public EnvironmentWithOverrides type(@javax.annotation.Nonnull TypeEnum type) { + + this.type = type; + return this; + } + + /** + * 固定 `\"environment_with_overrides\"`。 + * @return type + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_TYPE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public TypeEnum getType() { + return type; + } + + + @JsonProperty(value = JSON_PROPERTY_TYPE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setType(@javax.annotation.Nonnull TypeEnum type) { + this.type = type; + } + + public EnvironmentWithOverrides id(@javax.annotation.Nonnull String id) { + + this.id = id; + return this; + } + + /** + * Base Environment ID。 + * @return id + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getId() { + return id; + } + + + @JsonProperty(value = JSON_PROPERTY_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setId(@javax.annotation.Nonnull String id) { + this.id = id; + } + + public EnvironmentWithOverrides config(@javax.annotation.Nullable EnvironmentConfigOverride config) { + + this.config = config; + return this; + } + + /** + * 运行时配置覆写。 + * @return config + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_CONFIG, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public EnvironmentConfigOverride getConfig() { + return config; + } + + + @JsonProperty(value = JSON_PROPERTY_CONFIG, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setConfig(@javax.annotation.Nullable EnvironmentConfigOverride config) { + this.config = config; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + EnvironmentWithOverrides environmentWithOverrides = (EnvironmentWithOverrides) o; + return Objects.equals(this.type, environmentWithOverrides.type) && + Objects.equals(this.id, environmentWithOverrides.id) && + Objects.equals(this.config, environmentWithOverrides.config); + } + + @Override + public int hashCode() { + return Objects.hash(type, id, config); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class EnvironmentWithOverrides {\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" config: ").append(toIndentedString(config)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + return o == null ? "null" : o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private EnvironmentWithOverrides instance; + + public Builder() { + this(new EnvironmentWithOverrides()); + } + + protected Builder(EnvironmentWithOverrides instance) { + this.instance = instance; + } + + public EnvironmentWithOverrides.Builder type(TypeEnum type) { + this.instance.type = type; + return this; + } + public EnvironmentWithOverrides.Builder id(String id) { + this.instance.id = id; + return this; + } + public EnvironmentWithOverrides.Builder config(EnvironmentConfigOverride config) { + this.instance.config = config; + return this; + } + + + /** + * returns a built EnvironmentWithOverrides instance. + * + * The builder is not reusable. + */ + public EnvironmentWithOverrides build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field. + */ + public static EnvironmentWithOverrides.Builder builder() { + return new EnvironmentWithOverrides.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public EnvironmentWithOverrides.Builder toBuilder() { + return new EnvironmentWithOverrides.Builder() + .type(getType()) + .id(getId()) + .config(getConfig()); + } + + +} diff --git a/src/main/java/com/volcengine/ark/runtime/models/session/ModelOverrides.java b/src/main/java/com/volcengine/ark/runtime/models/session/ModelOverrides.java new file mode 100644 index 0000000..556744c --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/models/session/ModelOverrides.java @@ -0,0 +1,227 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +/* + * Ark Managed Agents Session API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.volcengine.ark.runtime.models.session; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.util.Objects; + +/** + * Session 创建时允许临时覆写的模型运行参数。 + */ +@JsonPropertyOrder({ + ModelOverrides.JSON_PROPERTY_SPEED, + ModelOverrides.JSON_PROPERTY_THINKING, + ModelOverrides.JSON_PROPERTY_REASONING_EFFORT +}) +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") +public class ModelOverrides { + public static final String JSON_PROPERTY_SPEED = "speed"; + @javax.annotation.Nullable + private String speed; + + public static final String JSON_PROPERTY_THINKING = "thinking"; + @javax.annotation.Nullable + private String thinking; + + public static final String JSON_PROPERTY_REASONING_EFFORT = "reasoning_effort"; + @javax.annotation.Nullable + private String reasoningEffort; + + public ModelOverrides() { + } + + public ModelOverrides speed(@javax.annotation.Nullable String speed) { + + this.speed = speed; + return this; + } + + /** + * 模型速度档位。 + * @return speed + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_SPEED, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getSpeed() { + return speed; + } + + + @JsonProperty(value = JSON_PROPERTY_SPEED, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setSpeed(@javax.annotation.Nullable String speed) { + this.speed = speed; + } + + public ModelOverrides thinking(@javax.annotation.Nullable String thinking) { + + this.thinking = thinking; + return this; + } + + /** + * thinking 配置。 + * @return thinking + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_THINKING, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getThinking() { + return thinking; + } + + + @JsonProperty(value = JSON_PROPERTY_THINKING, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setThinking(@javax.annotation.Nullable String thinking) { + this.thinking = thinking; + } + + public ModelOverrides reasoningEffort(@javax.annotation.Nullable String reasoningEffort) { + + this.reasoningEffort = reasoningEffort; + return this; + } + + /** + * 推理努力程度。 + * @return reasoningEffort + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_REASONING_EFFORT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public String getReasoningEffort() { + return reasoningEffort; + } + + + @JsonProperty(value = JSON_PROPERTY_REASONING_EFFORT, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setReasoningEffort(@javax.annotation.Nullable String reasoningEffort) { + this.reasoningEffort = reasoningEffort; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ModelOverrides modelOverrides = (ModelOverrides) o; + return Objects.equals(this.speed, modelOverrides.speed) && + Objects.equals(this.thinking, modelOverrides.thinking) && + Objects.equals(this.reasoningEffort, modelOverrides.reasoningEffort); + } + + @Override + public int hashCode() { + return Objects.hash(speed, thinking, reasoningEffort); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ModelOverrides {\n"); + sb.append(" speed: ").append(toIndentedString(speed)).append("\n"); + sb.append(" thinking: ").append(toIndentedString(thinking)).append("\n"); + sb.append(" reasoningEffort: ").append(toIndentedString(reasoningEffort)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + return o == null ? "null" : o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private ModelOverrides instance; + + public Builder() { + this(new ModelOverrides()); + } + + protected Builder(ModelOverrides instance) { + this.instance = instance; + } + + public ModelOverrides.Builder speed(String speed) { + this.instance.speed = speed; + return this; + } + public ModelOverrides.Builder thinking(String thinking) { + this.instance.thinking = thinking; + return this; + } + public ModelOverrides.Builder reasoningEffort(String reasoningEffort) { + this.instance.reasoningEffort = reasoningEffort; + return this; + } + + + /** + * returns a built ModelOverrides instance. + * + * The builder is not reusable. + */ + public ModelOverrides build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field. + */ + public static ModelOverrides.Builder builder() { + return new ModelOverrides.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public ModelOverrides.Builder toBuilder() { + return new ModelOverrides.Builder() + .speed(getSpeed()) + .thinking(getThinking()) + .reasoningEffort(getReasoningEffort()); + } + + +} diff --git a/src/main/java/com/volcengine/ark/runtime/models/session/SendSessionEventsResponse.java b/src/main/java/com/volcengine/ark/runtime/models/session/SendSessionEventsResponse.java index 093946a..85940a9 100644 --- a/src/main/java/com/volcengine/ark/runtime/models/session/SendSessionEventsResponse.java +++ b/src/main/java/com/volcengine/ark/runtime/models/session/SendSessionEventsResponse.java @@ -19,46 +19,57 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import java.util.Objects; /** * SendSessionEvents 响应体(回执)。 */ @JsonPropertyOrder({ - SendSessionEventsResponse.JSON_PROPERTY_SUCCESS + SendSessionEventsResponse.JSON_PROPERTY_DATA }) @javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") public class SendSessionEventsResponse { - public static final String JSON_PROPERTY_SUCCESS = "success"; - @javax.annotation.Nullable - private Boolean success; + public static final String JSON_PROPERTY_DATA = "data"; + @javax.annotation.Nonnull + private List> data; public SendSessionEventsResponse() { } - public SendSessionEventsResponse success(@javax.annotation.Nullable Boolean success) { + public SendSessionEventsResponse data(@javax.annotation.Nonnull List> data) { - this.success = success; + this.data = data; + return this; + } + + public SendSessionEventsResponse addDataItem(Map dataItem) { + if (this.data == null) { + this.data = new ArrayList<>(); + } + this.data.add(dataItem); return this; } /** - * 是否成功接收(server 决定语义)。 - * @return success + * 服务端落库 / 转发完成后的事件回声。 + * @return data */ - @javax.annotation.Nullable - @JsonProperty(value = JSON_PROPERTY_SUCCESS, required = false) - @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_DATA, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) - public Boolean getSuccess() { - return success; + public List> getData() { + return data; } - @JsonProperty(value = JSON_PROPERTY_SUCCESS, required = false) - @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) - public void setSuccess(@javax.annotation.Nullable Boolean success) { - this.success = success; + @JsonProperty(value = JSON_PROPERTY_DATA, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setData(@javax.annotation.Nonnull List> data) { + this.data = data; } @@ -71,19 +82,19 @@ public boolean equals(Object o) { return false; } SendSessionEventsResponse sendSessionEventsResponse = (SendSessionEventsResponse) o; - return Objects.equals(this.success, sendSessionEventsResponse.success); + return Objects.equals(this.data, sendSessionEventsResponse.data); } @Override public int hashCode() { - return Objects.hash(success); + return Objects.hash(data); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class SendSessionEventsResponse {\n"); - sb.append(" success: ").append(toIndentedString(success)).append("\n"); + sb.append(" data: ").append(toIndentedString(data)).append("\n"); sb.append("}"); return sb.toString(); } @@ -108,8 +119,8 @@ protected Builder(SendSessionEventsResponse instance) { this.instance = instance; } - public SendSessionEventsResponse.Builder success(Boolean success) { - this.instance.success = success; + public SendSessionEventsResponse.Builder data(List> data) { + this.instance.data = data; return this; } @@ -146,7 +157,7 @@ public static SendSessionEventsResponse.Builder builder() { */ public SendSessionEventsResponse.Builder toBuilder() { return new SendSessionEventsResponse.Builder() - .success(getSuccess()); + .data(getData()); } diff --git a/src/main/java/com/volcengine/ark/runtime/models/session/Session.java b/src/main/java/com/volcengine/ark/runtime/models/session/Session.java index 52911b8..6f7e4b4 100644 --- a/src/main/java/com/volcengine/ark/runtime/models/session/Session.java +++ b/src/main/java/com/volcengine/ark/runtime/models/session/Session.java @@ -22,6 +22,7 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.annotation.JsonValue; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -42,7 +43,8 @@ Session.JSON_PROPERTY_RESOURCES, Session.JSON_PROPERTY_VAULT_IDS, Session.JSON_PROPERTY_USAGE, - Session.JSON_PROPERTY_TAGS + Session.JSON_PROPERTY_TAGS, + Session.JSON_PROPERTY_ENVIRONMENT }) @javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") public class Session { @@ -131,6 +133,10 @@ public static TypeEnum fromValue(String value) { @javax.annotation.Nullable private List tags; + public static final String JSON_PROPERTY_ENVIRONMENT = "environment"; + @javax.annotation.Nullable + private Map environment; + public Session() { } @@ -488,6 +494,39 @@ public void setTags(@javax.annotation.Nullable List tags) { this.tags = tags; } + public Session environment(@javax.annotation.Nullable Map environment) { + + this.environment = environment; + return this; + } + + public Session putEnvironmentItem(String key, Object environmentItem) { + if (this.environment == null) { + this.environment = new HashMap<>(); + } + this.environment.put(key, environmentItem); + return this; + } + + /** + * Get environment + * @return environment + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_ENVIRONMENT, required = false) + @JsonInclude(content = JsonInclude.Include.ALWAYS, value = JsonInclude.Include.NON_EMPTY) + + public Map getEnvironment() { + return environment; + } + + + @JsonProperty(value = JSON_PROPERTY_ENVIRONMENT, required = false) + @JsonInclude(content = JsonInclude.Include.ALWAYS, value = JsonInclude.Include.NON_EMPTY) + public void setEnvironment(@javax.annotation.Nullable Map environment) { + this.environment = environment; + } + @Override public boolean equals(Object o) { @@ -510,12 +549,13 @@ public boolean equals(Object o) { Objects.equals(this.resources, session.resources) && Objects.equals(this.vaultIds, session.vaultIds) && Objects.equals(this.usage, session.usage) && - Objects.equals(this.tags, session.tags); + Objects.equals(this.tags, session.tags) && + Objects.equals(this.environment, session.environment); } @Override public int hashCode() { - return Objects.hash(id, type, status, environmentId, agent, createdAt, updatedAt, archivedAt, title, resources, vaultIds, usage, tags); + return Objects.hash(id, type, status, environmentId, agent, createdAt, updatedAt, archivedAt, title, resources, vaultIds, usage, tags, environment); } @Override @@ -535,6 +575,7 @@ public String toString() { sb.append(" vaultIds: ").append(toIndentedString(vaultIds)).append("\n"); sb.append(" usage: ").append(toIndentedString(usage)).append("\n"); sb.append(" tags: ").append(toIndentedString(tags)).append("\n"); + sb.append(" environment: ").append(toIndentedString(environment)).append("\n"); sb.append("}"); return sb.toString(); } @@ -611,6 +652,10 @@ public Session.Builder tags(List tags) { this.instance.tags = tags; return this; } + public Session.Builder environment(Map environment) { + this.instance.environment = environment; + return this; + } /** @@ -657,7 +702,8 @@ public Session.Builder toBuilder() { .resources(getResources()) .vaultIds(getVaultIds()) .usage(getUsage()) - .tags(getTags()); + .tags(getTags()) + .environment(getEnvironment()); } diff --git a/src/main/java/com/volcengine/ark/runtime/models/session/SessionThreadStatus.java b/src/main/java/com/volcengine/ark/runtime/models/session/SessionThreadStatus.java index 457b8bc..be48bd6 100644 --- a/src/main/java/com/volcengine/ark/runtime/models/session/SessionThreadStatus.java +++ b/src/main/java/com/volcengine/ark/runtime/models/session/SessionThreadStatus.java @@ -31,7 +31,7 @@ public enum SessionThreadStatus { TERMINATED("terminated"), - ARCHIVED("archived"); + RESCHEDULING("rescheduling"); private String value; diff --git a/src/main/java/com/volcengine/ark/runtime/models/skill/CreateSkillRequest.java b/src/main/java/com/volcengine/ark/runtime/models/skill/CreateSkillRequest.java index 7c2c75f..b45ea4c 100644 --- a/src/main/java/com/volcengine/ark/runtime/models/skill/CreateSkillRequest.java +++ b/src/main/java/com/volcengine/ark/runtime/models/skill/CreateSkillRequest.java @@ -25,7 +25,8 @@ * CreateSkillRequest */ @JsonPropertyOrder({ - CreateSkillRequest.JSON_PROPERTY_DISPLAY_TITLE + CreateSkillRequest.JSON_PROPERTY_DISPLAY_TITLE, + CreateSkillRequest.JSON_PROPERTY_PROTECTION_ENABLED }) @javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") public class CreateSkillRequest { @@ -33,6 +34,10 @@ public class CreateSkillRequest { @javax.annotation.Nullable private String displayTitle; + public static final String JSON_PROPERTY_PROTECTION_ENABLED = "protection_enabled"; + @javax.annotation.Nullable + private Boolean protectionEnabled; + public CreateSkillRequest() { } @@ -61,6 +66,31 @@ public void setDisplayTitle(@javax.annotation.Nullable String displayTitle) { this.displayTitle = displayTitle; } + public CreateSkillRequest protectionEnabled(@javax.annotation.Nullable Boolean protectionEnabled) { + + this.protectionEnabled = protectionEnabled; + return this; + } + + /** + * 是否启用 Skill 内容保护。 + * @return protectionEnabled + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_PROTECTION_ENABLED, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public Boolean getProtectionEnabled() { + return protectionEnabled; + } + + + @JsonProperty(value = JSON_PROPERTY_PROTECTION_ENABLED, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setProtectionEnabled(@javax.annotation.Nullable Boolean protectionEnabled) { + this.protectionEnabled = protectionEnabled; + } + @Override public boolean equals(Object o) { @@ -71,12 +101,13 @@ public boolean equals(Object o) { return false; } CreateSkillRequest createSkillRequest = (CreateSkillRequest) o; - return Objects.equals(this.displayTitle, createSkillRequest.displayTitle); + return Objects.equals(this.displayTitle, createSkillRequest.displayTitle) && + Objects.equals(this.protectionEnabled, createSkillRequest.protectionEnabled); } @Override public int hashCode() { - return Objects.hash(displayTitle); + return Objects.hash(displayTitle, protectionEnabled); } @Override @@ -84,6 +115,7 @@ public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class CreateSkillRequest {\n"); sb.append(" displayTitle: ").append(toIndentedString(displayTitle)).append("\n"); + sb.append(" protectionEnabled: ").append(toIndentedString(protectionEnabled)).append("\n"); sb.append("}"); return sb.toString(); } @@ -112,6 +144,10 @@ public CreateSkillRequest.Builder displayTitle(String displayTitle) { this.instance.displayTitle = displayTitle; return this; } + public CreateSkillRequest.Builder protectionEnabled(Boolean protectionEnabled) { + this.instance.protectionEnabled = protectionEnabled; + return this; + } /** @@ -146,7 +182,8 @@ public static CreateSkillRequest.Builder builder() { */ public CreateSkillRequest.Builder toBuilder() { return new CreateSkillRequest.Builder() - .displayTitle(getDisplayTitle()); + .displayTitle(getDisplayTitle()) + .protectionEnabled(getProtectionEnabled()); } diff --git a/src/main/java/com/volcengine/ark/runtime/models/skill/DownloadRequest.java b/src/main/java/com/volcengine/ark/runtime/models/skill/DownloadRequest.java new file mode 100644 index 0000000..f35c1f5 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/models/skill/DownloadRequest.java @@ -0,0 +1,190 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +/* + * Ark Managed Agents Skill API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.volcengine.ark.runtime.models.skill; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.util.Objects; + +/** + * DownloadRequest + */ +@JsonPropertyOrder({ + DownloadRequest.JSON_PROPERTY_SKILL_ID, + DownloadRequest.JSON_PROPERTY_VERSION +}) +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") +public class DownloadRequest { + public static final String JSON_PROPERTY_SKILL_ID = "skillId"; + @javax.annotation.Nonnull + private String skillId; + + public static final String JSON_PROPERTY_VERSION = "version"; + @javax.annotation.Nonnull + private String version; + + public DownloadRequest() { + } + + public DownloadRequest skillId(@javax.annotation.Nonnull String skillId) { + + this.skillId = skillId; + return this; + } + + /** + * Get skillId + * @return skillId + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_SKILL_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getSkillId() { + return skillId; + } + + + @JsonProperty(value = JSON_PROPERTY_SKILL_ID, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setSkillId(@javax.annotation.Nonnull String skillId) { + this.skillId = skillId; + } + + public DownloadRequest version(@javax.annotation.Nonnull String version) { + + this.version = version; + return this; + } + + /** + * Get version + * @return version + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_VERSION, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getVersion() { + return version; + } + + + @JsonProperty(value = JSON_PROPERTY_VERSION, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setVersion(@javax.annotation.Nonnull String version) { + this.version = version; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DownloadRequest downloadRequest = (DownloadRequest) o; + return Objects.equals(this.skillId, downloadRequest.skillId) && + Objects.equals(this.version, downloadRequest.version); + } + + @Override + public int hashCode() { + return Objects.hash(skillId, version); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class DownloadRequest {\n"); + sb.append(" skillId: ").append(toIndentedString(skillId)).append("\n"); + sb.append(" version: ").append(toIndentedString(version)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + return o == null ? "null" : o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private DownloadRequest instance; + + public Builder() { + this(new DownloadRequest()); + } + + protected Builder(DownloadRequest instance) { + this.instance = instance; + } + + public DownloadRequest.Builder skillId(String skillId) { + this.instance.skillId = skillId; + return this; + } + public DownloadRequest.Builder version(String version) { + this.instance.version = version; + return this; + } + + + /** + * returns a built DownloadRequest instance. + * + * The builder is not reusable. + */ + public DownloadRequest build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field. + */ + public static DownloadRequest.Builder builder() { + return new DownloadRequest.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public DownloadRequest.Builder toBuilder() { + return new DownloadRequest.Builder() + .skillId(getSkillId()) + .version(getVersion()); + } + + +} diff --git a/src/main/java/com/volcengine/ark/runtime/models/skill/Skill.java b/src/main/java/com/volcengine/ark/runtime/models/skill/Skill.java index 1b9be61..efea3c5 100644 --- a/src/main/java/com/volcengine/ark/runtime/models/skill/Skill.java +++ b/src/main/java/com/volcengine/ark/runtime/models/skill/Skill.java @@ -32,7 +32,11 @@ Skill.JSON_PROPERTY_CREATED_AT, Skill.JSON_PROPERTY_DESCRIPTION, Skill.JSON_PROPERTY_LATEST_VERSION, - Skill.JSON_PROPERTY_NAME + Skill.JSON_PROPERTY_DISPLAY_TITLE, + Skill.JSON_PROPERTY_SOURCE, + Skill.JSON_PROPERTY_UPDATED_AT, + Skill.JSON_PROPERTY_NAME, + Skill.JSON_PROPERTY_PROTECTION_ENABLED }) @javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.21.0") public class Skill { @@ -89,10 +93,26 @@ public static ObjectEnum fromValue(String value) { @javax.annotation.Nonnull private String latestVersion; - public static final String JSON_PROPERTY_NAME = "name"; + public static final String JSON_PROPERTY_DISPLAY_TITLE = "display_title"; + @javax.annotation.Nonnull + private String displayTitle; + + public static final String JSON_PROPERTY_SOURCE = "source"; @javax.annotation.Nonnull + private String source; + + public static final String JSON_PROPERTY_UPDATED_AT = "updated_at"; + @javax.annotation.Nonnull + private Long updatedAt; + + public static final String JSON_PROPERTY_NAME = "name"; + @javax.annotation.Nullable private String name; + public static final String JSON_PROPERTY_PROTECTION_ENABLED = "protection_enabled"; + @javax.annotation.Nullable + private Boolean protectionEnabled; + public Skill() { } @@ -221,31 +241,131 @@ public void setLatestVersion(@javax.annotation.Nonnull String latestVersion) { this.latestVersion = latestVersion; } - public Skill name(@javax.annotation.Nonnull String name) { + public Skill displayTitle(@javax.annotation.Nonnull String displayTitle) { - this.name = name; + this.displayTitle = displayTitle; return this; } /** - * 人类可读名称。 - * @return name + * Skill 展示名。 + * @return displayTitle + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_DISPLAY_TITLE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getDisplayTitle() { + return displayTitle; + } + + + @JsonProperty(value = JSON_PROPERTY_DISPLAY_TITLE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setDisplayTitle(@javax.annotation.Nonnull String displayTitle) { + this.displayTitle = displayTitle; + } + + public Skill source(@javax.annotation.Nonnull String source) { + + this.source = source; + return this; + } + + /** + * Skill 来源,例如 `custom` / `skill_hub` / `ark`。 + * @return source + */ + @javax.annotation.Nonnull + @JsonProperty(value = JSON_PROPERTY_SOURCE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getSource() { + return source; + } + + + @JsonProperty(value = JSON_PROPERTY_SOURCE, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setSource(@javax.annotation.Nonnull String source) { + this.source = source; + } + + public Skill updatedAt(@javax.annotation.Nonnull Long updatedAt) { + + this.updatedAt = updatedAt; + return this; + } + + /** + * 更新时间(Unix 秒)。 + * @return updatedAt */ @javax.annotation.Nonnull - @JsonProperty(value = JSON_PROPERTY_NAME, required = true) + @JsonProperty(value = JSON_PROPERTY_UPDATED_AT, required = true) @JsonInclude(value = JsonInclude.Include.ALWAYS) + public Long getUpdatedAt() { + return updatedAt; + } + + + @JsonProperty(value = JSON_PROPERTY_UPDATED_AT, required = true) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setUpdatedAt(@javax.annotation.Nonnull Long updatedAt) { + this.updatedAt = updatedAt; + } + + public Skill name(@javax.annotation.Nullable String name) { + + this.name = name; + return this; + } + + /** + * SKILL.md 中解析出的 name。 + * @return name + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_NAME, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public String getName() { return name; } - @JsonProperty(value = JSON_PROPERTY_NAME, required = true) - @JsonInclude(value = JsonInclude.Include.ALWAYS) - public void setName(@javax.annotation.Nonnull String name) { + @JsonProperty(value = JSON_PROPERTY_NAME, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setName(@javax.annotation.Nullable String name) { this.name = name; } + public Skill protectionEnabled(@javax.annotation.Nullable Boolean protectionEnabled) { + + this.protectionEnabled = protectionEnabled; + return this; + } + + /** + * 是否启用内容保护。 + * @return protectionEnabled + */ + @javax.annotation.Nullable + @JsonProperty(value = JSON_PROPERTY_PROTECTION_ENABLED, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public Boolean getProtectionEnabled() { + return protectionEnabled; + } + + + @JsonProperty(value = JSON_PROPERTY_PROTECTION_ENABLED, required = false) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setProtectionEnabled(@javax.annotation.Nullable Boolean protectionEnabled) { + this.protectionEnabled = protectionEnabled; + } + @Override public boolean equals(Object o) { @@ -261,12 +381,16 @@ public boolean equals(Object o) { Objects.equals(this.createdAt, skill.createdAt) && Objects.equals(this.description, skill.description) && Objects.equals(this.latestVersion, skill.latestVersion) && - Objects.equals(this.name, skill.name); + Objects.equals(this.displayTitle, skill.displayTitle) && + Objects.equals(this.source, skill.source) && + Objects.equals(this.updatedAt, skill.updatedAt) && + Objects.equals(this.name, skill.name) && + Objects.equals(this.protectionEnabled, skill.protectionEnabled); } @Override public int hashCode() { - return Objects.hash(id, _object, createdAt, description, latestVersion, name); + return Objects.hash(id, _object, createdAt, description, latestVersion, displayTitle, source, updatedAt, name, protectionEnabled); } @Override @@ -278,7 +402,11 @@ public String toString() { sb.append(" createdAt: ").append(toIndentedString(createdAt)).append("\n"); sb.append(" description: ").append(toIndentedString(description)).append("\n"); sb.append(" latestVersion: ").append(toIndentedString(latestVersion)).append("\n"); + sb.append(" displayTitle: ").append(toIndentedString(displayTitle)).append("\n"); + sb.append(" source: ").append(toIndentedString(source)).append("\n"); + sb.append(" updatedAt: ").append(toIndentedString(updatedAt)).append("\n"); sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" protectionEnabled: ").append(toIndentedString(protectionEnabled)).append("\n"); sb.append("}"); return sb.toString(); } @@ -323,10 +451,26 @@ public Skill.Builder latestVersion(String latestVersion) { this.instance.latestVersion = latestVersion; return this; } + public Skill.Builder displayTitle(String displayTitle) { + this.instance.displayTitle = displayTitle; + return this; + } + public Skill.Builder source(String source) { + this.instance.source = source; + return this; + } + public Skill.Builder updatedAt(Long updatedAt) { + this.instance.updatedAt = updatedAt; + return this; + } public Skill.Builder name(String name) { this.instance.name = name; return this; } + public Skill.Builder protectionEnabled(Boolean protectionEnabled) { + this.instance.protectionEnabled = protectionEnabled; + return this; + } /** @@ -366,7 +510,11 @@ public Skill.Builder toBuilder() { .createdAt(getCreatedAt()) .description(getDescription()) .latestVersion(getLatestVersion()) - .name(getName()); + .displayTitle(getDisplayTitle()) + .source(getSource()) + .updatedAt(getUpdatedAt()) + .name(getName()) + .protectionEnabled(getProtectionEnabled()); } diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/ContentBlock.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/ContentBlock.java new file mode 100644 index 0000000..feeb93a --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/ContentBlock.java @@ -0,0 +1,69 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class ContentBlock { + private String type; + private String text; + private String mediaType; + private Object data; + + public ContentBlock() { + } + + public ContentBlock(String type, String text) { + this.type = type; + this.text = text; + } + + public Map toMap() { + Map out = new LinkedHashMap<>(); + out.put("type", type); + if (text != null && !text.isEmpty()) { + out.put("text", text); + } + if (mediaType != null && !mediaType.isEmpty()) { + out.put("media_type", mediaType); + } + if (data != null) { + out.put("data", data); + } + return out; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public String getMediaType() { + return mediaType; + } + + public void setMediaType(String mediaType) { + this.mediaType = mediaType; + } + + public Object getData() { + return data; + } + + public void setData(Object data) { + this.data = data; + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/DefaultTools.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/DefaultTools.java new file mode 100644 index 0000000..27ab32f --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/DefaultTools.java @@ -0,0 +1,520 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.volcengine.ark.runtime.service.ArkService; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.AtomicMoveNotSupportedException; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.PosixFilePermission; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +public final class DefaultTools { + private static final ObjectMapper MAPPER = ArkService.defaultObjectMapper(); + private static final int MAX_OUTPUT_BYTES = 100000; + private static final int MAX_SEARCH_MATCHES = 1000; + private static final long PROCESS_TERMINATION_GRACE_MILLIS = 1000L; + + private DefaultTools() { + } + + public static ToolSet create() { + return new ToolSet() + .add(new BashTool()) + .add(new ReadTool()) + .add(new WriteTool()) + .add(new EditTool()) + .add(new GlobTool()) + .add(new GrepTool()); + } + + static class BashTool implements Tool { + @Override + public String name() { + return "bash"; + } + + @Override + public ToolResult execute(Object input, ToolContext context) { + Map args = asMap(input); + String command = stringValue(args.get("command")); + if (command.isEmpty()) { + command = stringValue(args.get("cmd")); + } + if (command.isEmpty()) { + return ToolResult.error("bash command is required"); + } + ProcessBuilder builder = new ProcessBuilder("/bin/sh", "-c", command); + builder.directory(new File(context.getWorkdir())); + builder.environment().clear(); + Map environment = context.hasExplicitEnv() + ? new LinkedHashMap<>(context.getEnv()) + : new LinkedHashMap<>(System.getenv()); + builder.environment().putAll(scrubbedEnv(environment)); + builder.redirectErrorStream(true); + try { + Process process = builder.start(); + BoundedOutput output = new BoundedOutput(MAX_OUTPUT_BYTES); + Thread reader = new Thread(() -> drainOutput(process.getInputStream(), output), "ma-self-host-bash-output"); + reader.setDaemon(true); + reader.start(); + long deadline = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(Math.max(0L, context.getToolTimeoutMillis())); + String failure = ""; + while (!process.waitFor(100L, TimeUnit.MILLISECONDS)) { + if (context.isCancelled()) { + failure = "tool execution canceled"; + process.destroyForcibly(); + break; + } + if (context.getToolTimeoutMillis() > 0L && System.nanoTime() >= deadline) { + failure = "tool execution timed out after " + context.getToolTimeoutMillis() + "ms"; + process.destroyForcibly(); + break; + } + } + boolean terminated = !process.isAlive() + || process.waitFor(PROCESS_TERMINATION_GRACE_MILLIS, TimeUnit.MILLISECONDS); + reader.join(1000L); + String text = output.text(); + if (!terminated) { + process.destroyForcibly(); + String message = failure.isEmpty() ? "tool process did not terminate" : failure; + return ToolResult.error(message + (text.isEmpty() ? "" : "\n" + text)); + } + if (!failure.isEmpty()) { + return ToolResult.error(failure + (text.isEmpty() ? "" : "\n" + text)); + } + if (process.exitValue() != 0) { + return ToolResult.error("exit code " + process.exitValue() + "\n" + text); + } + return ToolResult.text(text); + } catch (Exception e) { + return ToolResult.error(e.getMessage()); + } + } + } + + static class ReadTool implements Tool { + @Override + public String name() { + return "read"; + } + + @Override + public ToolResult execute(Object input, ToolContext context) { + try { + Map args = asMap(input); + Path path = safePath(context, firstNonEmpty(stringValue(args.get("path")), stringValue(args.get("file")))); + byte[] data = readBounded(path, MAX_OUTPUT_BYTES); + return ToolResult.text(new String(data, StandardCharsets.UTF_8)); + } catch (Exception e) { + return ToolResult.error(e.getMessage()); + } + } + } + + static class WriteTool implements Tool { + @Override + public String name() { + return "write"; + } + + @Override + public ToolResult execute(Object input, ToolContext context) { + try { + Map args = asMap(input); + String rawPath = firstNonEmpty(stringValue(args.get("path")), stringValue(args.get("file"))); + Path path = safePath(context, rawPath); + String content = stringValue(args.get("content")); + Files.createDirectories(path.getParent()); + Path verified = safePath(context, rawPath); + if (!verified.equals(path)) { + throw new IOException("path resolution changed while writing"); + } + writeFileAtomically(path, content.getBytes(StandardCharsets.UTF_8), null); + return ToolResult.text("wrote " + content.length() + " bytes"); + } catch (Exception e) { + return ToolResult.error(e.getMessage()); + } + } + } + + static class EditTool implements Tool { + @Override + public String name() { + return "edit"; + } + + @Override + public ToolResult execute(Object input, ToolContext context) { + try { + Map args = asMap(input); + String rawPath = firstNonEmpty(stringValue(args.get("path")), stringValue(args.get("file"))); + Path path = safePath(context, rawPath); + String oldString = firstNonEmpty(stringValue(args.get("old_string")), stringValue(args.get("old"))); + String newString = firstNonEmpty(stringValue(args.get("new_string")), stringValue(args.get("new"))); + if (oldString.isEmpty()) { + return ToolResult.error("old_string is required"); + } + String data = new String(Files.readAllBytes(path), StandardCharsets.UTF_8); + if (!data.contains(oldString)) { + return ToolResult.error("old_string was not found"); + } + Set permissions = posixPermissions(path); + Path verified = safePath(context, rawPath); + if (!verified.equals(path)) { + throw new IOException("path resolution changed while editing"); + } + byte[] updated = data.replaceFirst(Pattern.quote(oldString), Matcher.quoteReplacement(newString)) + .getBytes(StandardCharsets.UTF_8); + writeFileAtomically(path, updated, permissions); + return ToolResult.text("edited"); + } catch (Exception e) { + return ToolResult.error(e.getMessage()); + } + } + } + + static class GlobTool implements Tool { + @Override + public String name() { + return "glob"; + } + + @Override + public ToolResult execute(Object input, ToolContext context) { + Map args = asMap(input); + String pattern = stringValue(args.get("pattern")); + if (pattern.isEmpty()) { + return ToolResult.error("pattern is required"); + } + Path root = Paths.get(context.getWorkdir()).toAbsolutePath().normalize(); + PathMatcherCompat matcher = new PathMatcherCompat(pattern); + StringBuilder out = new StringBuilder(); + try (Stream stream = Files.walk(root)) { + Iterator paths = stream.filter(Files::isRegularFile).iterator(); + int matches = 0; + while (!context.isCancelled() && paths.hasNext() && matches < MAX_SEARCH_MATCHES) { + Path path = paths.next(); + Path rel = root.relativize(path); + if (matcher.matches(rel)) { + if (!appendWithinLimit(out, rel.toString() + '\n')) { + break; + } + matches++; + } + } + } catch (IOException e) { + return ToolResult.error(e.getMessage()); + } + return context.isCancelled() ? ToolResult.error("tool execution canceled") : ToolResult.text(out.toString()); + } + } + + static class GrepTool implements Tool { + @Override + public String name() { + return "grep"; + } + + @Override + public ToolResult execute(Object input, ToolContext context) { + Map args = asMap(input); + String pattern = firstNonEmpty(stringValue(args.get("pattern")), stringValue(args.get("query"))); + if (pattern.isEmpty()) { + return ToolResult.error("pattern is required"); + } + Pattern regex = Pattern.compile(pattern); + Path root; + try { + root = safePath(context, firstNonEmpty(stringValue(args.get("path")), ".")); + } catch (IOException e) { + return ToolResult.error(e.getMessage()); + } + StringBuilder out = new StringBuilder(); + try (Stream stream = Files.walk(root)) { + Iterator paths = stream.filter(Files::isRegularFile).iterator(); + int matches = 0; + while (!context.isCancelled() && paths.hasNext() && matches < MAX_SEARCH_MATCHES) { + Path candidate = paths.next(); + Path verified; + try { + verified = safePath(context, candidate.toString()); + } catch (IOException ignored) { + continue; + } + String displayPath = Paths.get(context.getWorkdir()) + .toAbsolutePath() + .normalize() + .relativize(candidate) + .toString(); + matches += appendMatches( + regex, context, verified, displayPath, out, MAX_SEARCH_MATCHES - matches); + if (out.length() >= MAX_OUTPUT_BYTES) { + break; + } + } + } catch (IOException e) { + return ToolResult.error(e.getMessage()); + } + return context.isCancelled() ? ToolResult.error("tool execution canceled") : ToolResult.text(out.toString()); + } + } + + @SuppressWarnings("unchecked") + static Map asMap(Object input) { + if (input instanceof Map) { + return (Map) input; + } + if (input instanceof String) { + try { + return MAPPER.readValue((String) input, new TypeReference>() { + }); + } catch (Exception ignored) { + return Collections.singletonMap("command", input); + } + } + return Collections.emptyMap(); + } + + private static int appendMatches( + Pattern regex, + ToolContext context, + Path path, + String displayPath, + StringBuilder out, + int remainingMatches) { + int matches = 0; + try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) { + int lineNo = 0; + String line; + while (!context.isCancelled() && matches < remainingMatches && (line = reader.readLine()) != null) { + lineNo++; + if (regex.matcher(line).find()) { + String value = displayPath + ':' + lineNo + ':' + line + '\n'; + if (!appendWithinLimit(out, value)) { + break; + } + matches++; + } + } + } catch (IOException ignored) { + } + return matches; + } + + private static Path safePath(ToolContext context, String rawPath) throws IOException { + if (rawPath == null || rawPath.isEmpty()) { + throw new IOException("path is required"); + } + Path root = Paths.get(context.getWorkdir()).toAbsolutePath().normalize(); + Path path = Paths.get(rawPath); + if (!path.isAbsolute()) { + path = root.resolve(path); + } + Path resolved = path.toAbsolutePath().normalize(); + if (context.isUnrestrictedPaths()) { + return resolved; + } + Path realRoot = root.toRealPath(); + Path realResolved; + if (Files.exists(resolved, LinkOption.NOFOLLOW_LINKS)) { + realResolved = resolved.toRealPath(); + } else { + Path existing = resolved.getParent(); + while (existing != null && !Files.exists(existing, LinkOption.NOFOLLOW_LINKS)) { + existing = existing.getParent(); + } + if (existing == null) { + throw new IOException("path has no existing parent: " + rawPath); + } + Path realExisting = existing.toRealPath(); + realResolved = realExisting.resolve(existing.relativize(resolved)).normalize(); + } + if (!realResolved.startsWith(realRoot)) { + throw new IOException("path escapes workdir: " + rawPath); + } + return realResolved; + } + + private static Map scrubbedEnv(Map source) { + Map result = new LinkedHashMap<>(); + for (Map.Entry entry : source.entrySet()) { + if (!isSensitiveEnvKey(entry.getKey())) { + result.put(entry.getKey(), entry.getValue()); + } + } + return result; + } + + static boolean isSensitiveEnvKey(String key) { + String value = key == null ? "" : key.trim().toUpperCase(Locale.ROOT); + String[] prefixes = { + "AIME_", "ARK_", "MA_", "X_CODE_", "ANTHROPIC_", "OPENAI_", "AWS_", "AZURE_", "GOOGLE_" + }; + for (String prefix : prefixes) { + if (value.startsWith(prefix)) { + return true; + } + } + return value.equals(joinEnvKey("VOLC_", "ACCESS", "KEY")) + || value.equals(joinEnvKey("VOLC_", "SEC", "RET", "KEY")) + || value.equals(joinEnvKey("BYTEPLUS_", "ACCESS", "KEY")) + || value.equals(joinEnvKey("BYTEPLUS_", "SEC", "RET", "KEY")) + || matchesCredentialName(value, "TO", "KEN") + || matchesCredentialName(value, "SEC", "RET") + || matchesCredentialName(value, "PA", "SS", "WO", "RD") + || matchesCredentialName(value, "PA", "SS", "WD") + || matchesCredentialName(value, "PRIVATE_", "KEY") + || matchesCredentialName(value, "API_", "KEY") + || matchesCredentialName(value, "ACCESS_", "KEY") + || matchesCredentialName(value, "SECRET_", "KEY") + || matchesCredentialName(value, "J", "WT") + || matchesCredentialName(value, "P", "AT"); + } + + private static boolean matchesCredentialName(String value, String... parts) { + String name = joinEnvKey(parts); + return value.equals(name) || value.endsWith("_" + name); + } + + private static String joinEnvKey(String... parts) { + StringBuilder value = new StringBuilder(); + for (String part : parts) { + value.append(part); + } + return value.toString(); + } + + private static byte[] readBounded(Path path, int limit) throws IOException { + try (InputStream input = Files.newInputStream(path)) { + ByteArrayOutputStream output = new ByteArrayOutputStream(Math.min(limit, 65536)); + byte[] buffer = new byte[65536]; + int count; + while ((count = input.read(buffer)) >= 0 && output.size() < limit) { + output.write(buffer, 0, Math.min(count, limit - output.size())); + } + return output.toByteArray(); + } + } + + private static void writeFileAtomically( + Path target, byte[] data, Set permissions) throws IOException { + Path tmp = Files.createTempFile(target.getParent(), ".ark-write-", ".tmp"); + try { + if (permissions != null) { + Files.setPosixFilePermissions(tmp, permissions); + } + Files.write(tmp, data); + try { + Files.move( + tmp, + target, + StandardCopyOption.ATOMIC_MOVE, + StandardCopyOption.REPLACE_EXISTING); + } catch (AtomicMoveNotSupportedException ignored) { + Files.move(tmp, target, StandardCopyOption.REPLACE_EXISTING); + } + } finally { + Files.deleteIfExists(tmp); + } + } + + private static Set posixPermissions(Path path) throws IOException { + try { + return Files.getPosixFilePermissions(path); + } catch (UnsupportedOperationException ignored) { + return null; + } + } + + private static boolean appendWithinLimit(StringBuilder output, String value) { + int remaining = MAX_OUTPUT_BYTES - output.length(); + if (remaining <= 0) { + return false; + } + output.append(value, 0, Math.min(value.length(), remaining)); + return value.length() <= remaining; + } + + private static void drainOutput(InputStream stream, BoundedOutput output) { + byte[] buffer = new byte[65536]; + try (InputStream input = stream) { + int count; + while ((count = input.read(buffer)) >= 0) { + output.append(buffer, count); + } + } catch (IOException ignored) { + } + } + + private static class BoundedOutput { + private static final byte[] TRUNCATION_MARKER = "\n... truncated ...".getBytes(StandardCharsets.UTF_8); + private final int limit; + private final ByteArrayOutputStream output; + private boolean truncated; + + BoundedOutput(int limit) { + this.limit = limit; + this.output = new ByteArrayOutputStream(Math.min(limit, 65536)); + } + + synchronized void append(byte[] data, int count) { + int remaining = Math.max(0, limit - TRUNCATION_MARKER.length - output.size()); + if (remaining > 0) { + output.write(data, 0, Math.min(count, remaining)); + } + if (count > remaining) { + truncated = true; + } + } + + synchronized String text() { + if (truncated) { + output.write(TRUNCATION_MARKER, 0, TRUNCATION_MARKER.length); + } + return new String(output.toByteArray(), StandardCharsets.UTF_8); + } + } + + private static String stringValue(Object value) { + return value == null ? "" : String.valueOf(value); + } + + private static String firstNonEmpty(String first, String second) { + return first != null && !first.isEmpty() ? first : (second == null ? "" : second); + } + + static class PathMatcherCompat { + private final java.nio.file.PathMatcher matcher; + + PathMatcherCompat(String pattern) { + this.matcher = java.nio.file.FileSystems.getDefault().getPathMatcher("glob:" + pattern); + } + + boolean matches(Path path) { + return matcher.matches(path); + } + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/EnvironmentWorker.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/EnvironmentWorker.java new file mode 100644 index 0000000..2fbb515 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/EnvironmentWorker.java @@ -0,0 +1,445 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.net.InetAddress; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.MessageDigest; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class EnvironmentWorker implements AutoCloseable { + private final SelfHostedClient api; + private final Options options; + private final AtomicBoolean closed = new AtomicBoolean(false); + private volatile SessionToolRunner activeRunner; + private volatile Thread activeThread; + + public EnvironmentWorker(SelfHostedClient api, Options options) { + if (api == null) { + throw new IllegalArgumentException("api is required"); + } + if (options == null) { + throw new IllegalArgumentException("options is required"); + } + if (options.workerId == null || options.workerId.isEmpty()) { + options.workerId = defaultWorkerId(); + } + this.api = api; + this.options = options; + } + + public void run() { + if (options.environmentId == null || options.environmentId.isEmpty()) { + throw new IllegalArgumentException("environment id is required"); + } + activeThread = Thread.currentThread(); + WorkPoller poller = new WorkPoller(api, new WorkPoller.Options(options.environmentId) + .workerId(options.workerId) + .autoStop(false) + .logger(options.logger)); + try { + while (!closed.get()) { + WorkItem item = poller.next(); + if (item == null) { + if (poller.error() != null) { + throw poller.error(); + } + return; + } + try { + handleItem(item, false); + } catch (SessionToolRunner.IdleTimeoutException | SessionToolRunner.SessionTerminatedException ignored) { + } catch (Exception e) { + options.logger.log(Level.WARNING, "handle work failed", e); + } + } + } finally { + poller.close(); + activeThread = null; + } + } + + public void handleItem(HandleItemOptions handleOptions) throws IOException { + Thread previous = activeThread; + activeThread = Thread.currentThread(); + try { + handleItem(workItemFromOptions(handleOptions), true); + } catch (SessionToolRunner.IdleTimeoutException | SessionToolRunner.SessionTerminatedException ignored) { + } finally { + activeThread = previous; + } + } + + private void handleItem(WorkItem item, boolean useWorkdirAsSession) throws IOException { + if (item.getEnvironmentId() == null || item.getEnvironmentId().isEmpty()) { + item.setEnvironmentId(firstNonEmpty(options.environmentId, System.getenv("MA_ENVIRONMENT_ID"))); + } + if (item.getId() == null || item.getId().isEmpty()) { + throw new IllegalArgumentException("work item id must not be empty"); + } + String sessionId = item.sessionIdValue(); + if (sessionId.isEmpty()) { + throw new IllegalArgumentException("work item does not contain session id"); + } + AtomicBoolean stop = new AtomicBoolean(false); + AtomicReference heartbeatCause = new AtomicReference<>(""); + Thread heartbeat = null; + try { + String workdir = workdirFor(sessionId, useWorkdirAsSession); + Thread heartbeatThread = new Thread( + () -> heartbeatLoop(item, stop, heartbeatCause), "ma-self-host-heartbeat"); + heartbeatThread.setDaemon(true); + heartbeatThread.start(); + heartbeat = heartbeatThread; + SessionSnapshot session = api.getSession(sessionId); + if (closed.get() || stop.get()) { + return; + } + if (session == null) { + throw new IOException("session response is empty"); + } + if (session.getId() == null || session.getId().isEmpty()) { + session.setId(sessionId); + } + new Initializer(api, new Initializer.Options(workdir)).setup(session); + if (closed.get() || stop.get()) { + return; + } + ToolContext toolContext = toolContext(workdir, stop); + FileToolResultStore store = new FileToolResultStore(workdir); + SessionToolRunner runner = new SessionToolRunner(api, sessionId, new SessionToolRunner.Options() + .workId(item.getId()) + .tools(options.tools == null ? DefaultTools.create() : options.tools) + .toolContext(toolContext) + .customTools(options.customTools) + .resultStore(store) + .maxIdleMillis(options.maxIdleMillis) + .stopSignal(() -> closed.get() || stop.get())); + activeRunner = runner; + try { + runner.run(); + } finally { + runner.close(); + activeRunner = null; + } + } finally { + stop.set(true); + if (heartbeat != null) { + try { + heartbeat.join(SelfHostedConstants.DEFAULT_HEARTBEAT_MILLIS + 1000L); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + String cause = heartbeatCause.get(); + if (shouldStopItem(cause)) { + try { + api.stopWork(item.getEnvironmentId(), item.getId(), true); + } catch (RuntimeException e) { + if (!isResolvedStatus(e)) { + options.logger.log(Level.WARNING, "stop work failed", e); + } + } + } else { + options.logger.info( + "skip stop work after heartbeat ownership became uncertain cause=" + cause); + } + } + } + + private void heartbeatLoop(WorkItem item, AtomicBoolean stop, AtomicReference cause) { + long interval = Math.max(1000L, SelfHostedConstants.DEFAULT_HEARTBEAT_MILLIS / 2L); + long ttl = SelfHostedConstants.DEFAULT_HEARTBEAT_MILLIS; + String last = item.latestHeartbeatValue(); + if (last == null || last.isEmpty()) { + last = SelfHostedConstants.EXPECTED_LAST_HEARTBEAT_NO_HEARTBEAT; + } + long lastSuccess = System.currentTimeMillis(); + while (!stop.get()) { + try { + HeartbeatResponse response = api.heartbeatWork( + item.getEnvironmentId(), + item.getId(), + last, + (int) (ttl / 1000L)); + if (response == null) { + if (System.currentTimeMillis() - lastSuccess > ttl) { + cause.set("heartbeat_lost"); + stop.set(true); + return; + } + options.logger.warning( + "heartbeat empty response work_id=" + item.getId() + + " session_id=" + item.sessionIdValue()); + sleep(interval, stop); + continue; + } + lastSuccess = System.currentTimeMillis(); + if (response.getLastHeartbeat() != null && !response.getLastHeartbeat().isEmpty()) { + last = response.getLastHeartbeat(); + } + if (response.getTtlSeconds() > 0) { + ttl = response.getTtlSeconds() * 1000L; + interval = Math.max(1000L, Math.min(ttl / 2, SelfHostedConstants.DEFAULT_HEARTBEAT_MILLIS)); + } + if (SelfHostedConstants.WORK_STATE_STOPPING.equals(response.getState()) + || SelfHostedConstants.WORK_STATE_STOPPED.equals(response.getState())) { + cause.set("stop_requested"); + stop.set(true); + return; + } + if (Boolean.FALSE.equals(response.getLeaseExtended())) { + cause.set("lease_not_extended"); + stop.set(true); + return; + } + } catch (RuntimeException e) { + if (WorkerAPIException.isStatus(e, 412)) { + cause.set("lease_lost"); + stop.set(true); + return; + } + if (WorkerAPIException.isFatal4xx(e)) { + cause.set("heartbeat_permanent_failure"); + stop.set(true); + return; + } + if (System.currentTimeMillis() - lastSuccess > ttl) { + cause.set("heartbeat_lost"); + stop.set(true); + return; + } + options.logger.log( + Level.WARNING, + "heartbeat failed work_id=" + item.getId() + + " session_id=" + item.sessionIdValue() + + " since_last_success_ms=" + (System.currentTimeMillis() - lastSuccess) + + " ttl_ms=" + ttl, + e); + } + sleep(interval, stop); + } + } + + private ToolContext toolContext(String workdir, AtomicBoolean workStop) { + ToolContext context = options.toolContext == null ? new ToolContext(workdir) : options.toolContext; + ToolContext copy = new ToolContext(workdir); + if (context.hasExplicitEnv()) { + copy.setEnv(new LinkedHashMap<>(context.getEnv())); + } + copy.setUnrestrictedPaths(options.unrestrictedPaths || context.isUnrestrictedPaths()); + copy.setToolTimeoutMillis(context.getToolTimeoutMillis()); + copy.setCancelled(() -> closed.get() || workStop.get()); + return copy; + } + + private String workdirFor(String sessionId, boolean useWorkdirAsSession) throws IOException { + Path root = Paths.get(options.workdir == null || options.workdir.isEmpty() ? "." : options.workdir) + .toAbsolutePath() + .normalize(); + Files.createDirectories(root); + if (useWorkdirAsSession) { + return root.toString(); + } + Path sessionDir = root.resolve(sessionWorkdirName(sessionId)).normalize(); + Files.createDirectories(sessionDir); + return sessionDir.toString(); + } + + private WorkItem workItemFromOptions(HandleItemOptions opts) { + String workId = firstNonEmpty(opts.workId, System.getenv("MA_WORK_ID")); + String environmentId = firstNonEmpty(opts.environmentId, System.getenv("MA_ENVIRONMENT_ID")); + String sessionId = firstNonEmpty(opts.sessionId, System.getenv("MA_SESSION_ID")); + String latestHeartbeat = firstNonEmpty(opts.latestHeartbeatAt, System.getenv("MA_LATEST_HEARTBEAT_AT")); + if (workId.isEmpty()) { + throw new IllegalArgumentException("work id is required"); + } + if (environmentId.isEmpty()) { + throw new IllegalArgumentException("environment id is required"); + } + if (sessionId.isEmpty()) { + throw new IllegalArgumentException("session id is required"); + } + WorkData data = new WorkData(); + data.setType("session"); + data.setId(sessionId); + WorkItem item = new WorkItem(); + item.setId(workId); + item.setEnvironmentId(environmentId); + item.setLatestHeartbeatAt(latestHeartbeat); + item.setData(data); + return item; + } + + private static void sleep(long millis, AtomicBoolean stop) { + long deadline = System.currentTimeMillis() + Math.max(1L, millis); + while (!stop.get() && System.currentTimeMillis() < deadline) { + try { + Thread.sleep(Math.min(500L, deadline - System.currentTimeMillis())); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + stop.set(true); + return; + } + } + } + + static String defaultWorkerId() { + try { + String runtimeName = ManagementFactory.getRuntimeMXBean().getName(); + String pid = runtimeName == null ? "" : runtimeName.split("@")[0]; + return InetAddress.getLocalHost().getHostName() + "-" + pid; + } catch (Throwable ignored) { + return "worker-" + System.currentTimeMillis(); + } + } + + @Override + public void close() { + if (!closed.compareAndSet(false, true)) { + return; + } + SessionToolRunner runner = activeRunner; + if (runner != null) { + runner.close(); + } + Thread thread = activeThread; + if (thread != null && thread != Thread.currentThread()) { + thread.interrupt(); + } + } + + private static boolean isResolvedStatus(Throwable error) { + return WorkerAPIException.isStatus(error, 404) + || WorkerAPIException.isStatus(error, 409) + || WorkerAPIException.isStatus(error, 412); + } + + private static boolean shouldStopItem(String heartbeatCause) { + return !"lease_lost".equals(heartbeatCause) + && !"lease_not_extended".equals(heartbeatCause) + && !"heartbeat_lost".equals(heartbeatCause) + && !"heartbeat_permanent_failure".equals(heartbeatCause); + } + + private static String sessionWorkdirName(String sessionId) { + if (sessionId != null + && sessionId.matches("[A-Za-z0-9._-]+") + && !".".equals(sessionId) + && !"..".equals(sessionId)) { + return sessionId; + } + try { + byte[] digest = MessageDigest.getInstance("SHA-256") + .digest(String.valueOf(sessionId).getBytes(StandardCharsets.UTF_8)); + StringBuilder value = new StringBuilder("session-"); + for (byte item : digest) { + value.append(String.format("%02x", item)); + } + return value.toString(); + } catch (Exception error) { + throw new IllegalStateException("failed to hash session id", error); + } + } + + private static String firstNonEmpty(String first, String second) { + return first != null && !first.isEmpty() ? first : (second == null ? "" : second); + } + + public static class HandleItemOptions { + private String workId = ""; + private String environmentId = ""; + private String sessionId = ""; + private String latestHeartbeatAt = ""; + + public HandleItemOptions workId(String workId) { + this.workId = workId; + return this; + } + + public HandleItemOptions environmentId(String environmentId) { + this.environmentId = environmentId; + return this; + } + + public HandleItemOptions sessionId(String sessionId) { + this.sessionId = sessionId; + return this; + } + + public HandleItemOptions latestHeartbeatAt(String latestHeartbeatAt) { + this.latestHeartbeatAt = latestHeartbeatAt; + return this; + } + } + + public static class Options { + private String environmentId = ""; + private String workerId = ""; + private String workdir = "."; + private boolean unrestrictedPaths; + private ToolContext toolContext; + private ToolSet tools; + private long maxIdleMillis = SelfHostedConstants.DEFAULT_MAX_IDLE_MILLIS; + private Map customTools = new LinkedHashMap<>(); + private Logger logger = Logger.getLogger("arkruntime.selfhosted.environment_worker"); + + public Options environmentId(String environmentId) { + this.environmentId = environmentId; + return this; + } + + public Options workerId(String workerId) { + this.workerId = workerId; + return this; + } + + public Options workdir(String workdir) { + this.workdir = workdir; + return this; + } + + public Options unrestrictedPaths(boolean unrestrictedPaths) { + this.unrestrictedPaths = unrestrictedPaths; + return this; + } + + public Options toolContext(ToolContext toolContext) { + this.toolContext = toolContext; + return this; + } + + public Options tools(ToolSet tools) { + this.tools = tools; + return this; + } + + public Options maxIdleMillis(long maxIdleMillis) { + this.maxIdleMillis = maxIdleMillis; + return this; + } + + public Options customTools(Map customTools) { + this.customTools = customTools == null ? new LinkedHashMap<>() : customTools; + return this; + } + + public Options logger(Logger logger) { + if (logger != null) { + this.logger = logger; + } + return this; + } + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/Event.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/Event.java new file mode 100644 index 0000000..9e500f4 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/Event.java @@ -0,0 +1,217 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +public class Event { + private String id = ""; + private String type = ""; + private String name = ""; + private Object input; + private String processedAt = ""; + private String evaluatedPermission = ""; + private String sessionThreadId = ""; + private String toolUseId = ""; + private String customToolUseId = ""; + private String result = ""; + private String denyMessage = ""; + private Object stopReason; + private List content = new ArrayList<>(); + private Boolean isError; + private Map extra = new LinkedHashMap<>(); + + @SuppressWarnings("unchecked") + public static Event fromMap(Map raw) { + Event event = new Event(); + if (raw == null) { + return event; + } + event.id = stringValue(raw.get("id")); + event.type = stringValue(raw.get("type")); + event.name = stringValue(raw.get("name")); + event.input = raw.get("input"); + event.processedAt = stringValue(raw.get("processed_at")); + event.evaluatedPermission = stringValue(raw.get("evaluated_permission")); + event.sessionThreadId = stringValue(raw.get("session_thread_id")); + event.toolUseId = stringValue(raw.get("tool_use_id")); + event.customToolUseId = stringValue(raw.get("custom_tool_use_id")); + event.result = stringValue(raw.get("result")); + event.denyMessage = stringValue(raw.get("deny_message")); + event.stopReason = raw.get("stop_reason"); + if (raw.get("is_error") instanceof Boolean) { + event.isError = (Boolean) raw.get("is_error"); + } + Object blocks = raw.get("content"); + if (blocks instanceof List) { + for (Object block : (List) blocks) { + if (block instanceof Map) { + Map blockMap = (Map) block; + ContentBlock contentBlock = new ContentBlock(); + contentBlock.setType(stringValue(blockMap.get("type"))); + contentBlock.setText(stringValue(blockMap.get("text"))); + contentBlock.setMediaType(stringValue(blockMap.get("media_type"))); + contentBlock.setData(blockMap.get("data")); + event.content.add(contentBlock); + } + } + } + for (Map.Entry entry : raw.entrySet()) { + if (!knownField(entry.getKey())) { + event.extra.put(entry.getKey(), entry.getValue()); + } + } + return event; + } + + public static Event newUserToolResultEvent( + String toolUseId, List content, boolean isError, String sessionThreadId) { + Event event = new Event(); + event.id = newEventId("evt"); + event.type = SelfHostedConstants.EVENT_TYPE_USER_TOOL_RESULT; + event.toolUseId = toolUseId; + event.content = content == null ? new ArrayList() : content; + event.isError = isError; + event.processedAt = Instant.now().toString(); + event.sessionThreadId = sessionThreadId == null ? "" : sessionThreadId; + return event; + } + + public static Event newUserCustomToolResultEvent( + String customToolUseId, List content, boolean isError, String sessionThreadId) { + Event event = new Event(); + event.id = newEventId("evt"); + event.type = SelfHostedConstants.EVENT_TYPE_USER_CUSTOM_TOOL_RESULT; + event.customToolUseId = customToolUseId; + event.content = content == null ? new ArrayList() : content; + event.isError = isError; + event.processedAt = Instant.now().toString(); + event.sessionThreadId = sessionThreadId == null ? "" : sessionThreadId; + return event; + } + + public Map toMap() { + Map out = new LinkedHashMap<>(extra); + put(out, "id", id); + put(out, "type", type); + put(out, "name", name); + if (input != null) { + out.put("input", input); + } + put(out, "processed_at", processedAt); + put(out, "evaluated_permission", evaluatedPermission); + put(out, "session_thread_id", sessionThreadId); + put(out, "tool_use_id", toolUseId); + put(out, "custom_tool_use_id", customToolUseId); + put(out, "result", result); + put(out, "deny_message", denyMessage); + if (stopReason != null) { + out.put("stop_reason", stopReason); + } + if (content != null && !content.isEmpty()) { + List> blocks = new ArrayList<>(); + for (ContentBlock block : content) { + blocks.add(block.toMap()); + } + out.put("content", blocks); + } + if (isError != null) { + out.put("is_error", isError); + } + return out; + } + + @SuppressWarnings("unchecked") + public String stopReasonType() { + if (stopReason instanceof Map) { + return stringValue(((Map) stopReason).get("type")); + } + return stopReason instanceof String ? (String) stopReason : ""; + } + + public String callId() { + if (toolUseId != null && !toolUseId.isEmpty()) { + return toolUseId; + } + if (customToolUseId != null && !customToolUseId.isEmpty()) { + return customToolUseId; + } + return id == null ? "" : id; + } + + public String resultCallId() { + if (toolUseId != null && !toolUseId.isEmpty()) { + return toolUseId; + } + return customToolUseId == null ? "" : customToolUseId; + } + + public static String newEventId(String prefix) { + return prefix + "-" + UUID.randomUUID().toString().replace("-", "").substring(0, 16); + } + + private static void put(Map out, String key, String value) { + if (value != null && !value.isEmpty()) { + out.put(key, value); + } + } + + private static String stringValue(Object value) { + return value == null ? "" : String.valueOf(value); + } + + private static boolean knownField(String key) { + return "id".equals(key) + || "type".equals(key) + || "name".equals(key) + || "input".equals(key) + || "processed_at".equals(key) + || "evaluated_permission".equals(key) + || "session_thread_id".equals(key) + || "tool_use_id".equals(key) + || "custom_tool_use_id".equals(key) + || "result".equals(key) + || "deny_message".equals(key) + || "stop_reason".equals(key) + || "content".equals(key) + || "is_error".equals(key); + } + + public String getId() { + return id; + } + + public String getType() { + return type; + } + + public String getName() { + return name; + } + + public Object getInput() { + return input; + } + + public String getEvaluatedPermission() { + return evaluatedPermission; + } + + public String getSessionThreadId() { + return sessionThreadId; + } + + public String getResult() { + return result; + } + + public List getContent() { + return content; + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/EventStream.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/EventStream.java new file mode 100644 index 0000000..873dbf9 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/EventStream.java @@ -0,0 +1,77 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.volcengine.ark.runtime.Const; +import com.volcengine.ark.runtime.service.ArkService; +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import okhttp3.ResponseBody; +import retrofit2.Call; +import retrofit2.Response; + +public class EventStream implements Closeable { + private static final ObjectMapper MAPPER = ArkService.defaultObjectMapper(); + + private final Call call; + private final ResponseBody body; + private final BufferedReader reader; + + EventStream(Call call, Response response) throws IOException { + this.call = call; + if (!response.isSuccessful() || response.body() == null) { + String message = response.errorBody() == null ? response.message() : response.errorBody().string(); + throw new WorkerAPIException(response.code(), message, header(response, Const.SERVER_REQUEST_HEADER)); + } + this.body = response.body(); + this.reader = new BufferedReader(new InputStreamReader(body.byteStream(), StandardCharsets.UTF_8)); + } + + public Event next() throws IOException { + StringBuilder data = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + if (line.isEmpty()) { + if (data.length() == 0) { + continue; + } + String payload = data.toString(); + if ("[DONE]".equals(payload)) { + return null; + } + Map raw = MAPPER.readValue(payload, new TypeReference>() { + }); + return Event.fromMap(raw); + } + if (line.startsWith(":")) { + continue; + } + if (line.startsWith("data:")) { + if (data.length() > 0) { + data.append('\n'); + } + data.append(line.substring(5).trim()); + } + } + return null; + } + + @Override + public void close() throws IOException { + call.cancel(); + reader.close(); + body.close(); + } + + private static String header(Response response, String name) { + String value = response.headers().get(name); + return value == null ? "" : value; + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/FileToolResultStore.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/FileToolResultStore.java new file mode 100644 index 0000000..cfece6f --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/FileToolResultStore.java @@ -0,0 +1,243 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.volcengine.ark.runtime.service.ArkService; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.charset.StandardCharsets; +import java.nio.file.AtomicMoveNotSupportedException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; +import java.security.MessageDigest; +import java.time.Instant; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +public class FileToolResultStore { + private static final String STATE_STARTED = "started"; + private static final String STATE_RESULT = "result"; + private static final String STATE_SENT = "sent"; + private static final ObjectMapper MAPPER = ArkService.defaultObjectMapper(); + + private final Path dir; + + public FileToolResultStore(String workdir) throws IOException { + if (workdir == null || workdir.isEmpty()) { + throw new IllegalArgumentException("workdir must not be empty"); + } + this.dir = Paths.get(workdir, ".ma_self_host_worker", "tool_ledger"); + Files.createDirectories(this.dir); + } + + public RecoverResult recover() throws IOException { + Map pending = new LinkedHashMap<>(); + Map processed = new LinkedHashMap<>(); + try (DirectoryStream stale = Files.newDirectoryStream(dir, ".tool-result-*.tmp")) { + for (Path path : stale) { + Files.deleteIfExists(path); + } + } + try (DirectoryStream stream = Files.newDirectoryStream(dir, "*.json")) { + for (Path path : stream) { + Map record = readPath(path); + String callId = stringValue(record.get("call_id")); + String state = stringValue(record.get("state")); + if (STATE_SENT.equals(state)) { + processed.put(callId, Boolean.TRUE); + } else if (STATE_RESULT.equals(state)) { + pending.put(callId, Event.fromMap(asMap(record.get("result")))); + } else if (STATE_STARTED.equals(state)) { + Event result = unknownToolExecutionResult(callId, Event.fromMap(asMap(record.get("event")))); + record.put("result", result.toMap()); + record.put("state", STATE_RESULT); + write(record); + pending.put(callId, result); + } else { + throw new IOException("unknown tool result state " + state + " for call " + callId); + } + } + } + return new RecoverResult(pending, processed); + } + + public ToolCallStoreDecision begin(String callId, Event event) throws IOException { + try { + Map record = read(callId); + String state = stringValue(record.get("state")); + if (STATE_SENT.equals(state)) { + return new ToolCallStoreDecision(true, null); + } + if (STATE_RESULT.equals(state)) { + return new ToolCallStoreDecision(false, Event.fromMap(asMap(record.get("result")))); + } + if (STATE_STARTED.equals(state)) { + Event result = unknownToolExecutionResult(callId, Event.fromMap(asMap(record.get("event")))); + record.put("result", result.toMap()); + record.put("state", STATE_RESULT); + write(record); + return new ToolCallStoreDecision(false, result); + } + throw new IOException("unknown tool result state " + state + " for call " + callId); + } catch (java.io.FileNotFoundException e) { + Map record = new LinkedHashMap<>(); + record.put("call_id", callId); + record.put("state", STATE_STARTED); + record.put("event", event.toMap()); + write(record); + return new ToolCallStoreDecision(false, null); + } + } + + public void saveResult(String callId, Event result) throws IOException { + Map record = read(callId); + record.put("state", STATE_RESULT); + record.put("result", result.toMap()); + write(record); + } + + public void markSent(String callId) throws IOException { + Map record = read(callId); + record.put("state", STATE_SENT); + write(record); + } + + private Map read(String callId) throws IOException { + Path path = path(callId); + if (!Files.exists(path)) { + throw new java.io.FileNotFoundException(path.toString()); + } + return readPath(path); + } + + private Map readPath(Path path) throws IOException { + return MAPPER.readValue(Files.readAllBytes(path), new TypeReference>() { + }); + } + + private void write(Map record) throws IOException { + String callId = stringValue(record.get("call_id")); + if (callId.isEmpty()) { + throw new IOException("call id must not be empty"); + } + record.put("updated_at", Instant.now().toString()); + Path target = path(callId); + Path tmp = Files.createTempFile(dir, ".tool-result-", ".tmp"); + try { + byte[] data = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsBytes(record); + try (FileChannel channel = FileChannel.open( + tmp, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) { + ByteBuffer buffer = ByteBuffer.wrap(data); + while (buffer.hasRemaining()) { + channel.write(buffer); + } + channel.force(true); + } + try { + Files.move( + tmp, + target, + StandardCopyOption.ATOMIC_MOVE, + StandardCopyOption.REPLACE_EXISTING); + } catch (AtomicMoveNotSupportedException ignored) { + Files.move(tmp, target, StandardCopyOption.REPLACE_EXISTING); + } + syncDirectory(); + } finally { + Files.deleteIfExists(tmp); + } + } + + private void syncDirectory() { + try (FileChannel channel = FileChannel.open(dir, StandardOpenOption.READ)) { + channel.force(true); + } catch (IOException | UnsupportedOperationException ignored) { + // Some non-POSIX filesystems do not support syncing directories. + } + } + + private Path path(String callId) { + return dir.resolve(sha256(callId) + ".json"); + } + + private static Event unknownToolExecutionResult(String callId, Event event) { + Event out; + java.util.List content = Collections.singletonList(new ContentBlock( + "text", + "tool execution state is unknown after worker restart; refusing to re-execute this tool_use to avoid duplicate side effects")); + if (SelfHostedConstants.EVENT_TYPE_AGENT_CUSTOM_TOOL_USE.equals(event.getType())) { + out = Event.newUserCustomToolResultEvent(callId, content, true, event.getSessionThreadId()); + } else { + out = Event.newUserToolResultEvent(callId, content, true, event.getSessionThreadId()); + } + return out; + } + + private static String sha256(String value) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] out = digest.digest(value.getBytes(StandardCharsets.UTF_8)); + StringBuilder sb = new StringBuilder(); + for (byte b : out) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @SuppressWarnings("unchecked") + private static Map asMap(Object value) { + return value instanceof Map ? (Map) value : Collections.emptyMap(); + } + + private static String stringValue(Object value) { + return value == null ? "" : String.valueOf(value); + } + + public static class RecoverResult { + private final Map pending; + private final Map processed; + + public RecoverResult(Map pending, Map processed) { + this.pending = pending; + this.processed = processed; + } + + public Map getPending() { + return pending; + } + + public Map getProcessed() { + return processed; + } + } + + public static class ToolCallStoreDecision { + private final boolean sent; + private final Event result; + + public ToolCallStoreDecision(boolean sent, Event result) { + this.sent = sent; + this.result = result; + } + + public boolean isSent() { + return sent; + } + + public Event getResult() { + return result; + } + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/HeartbeatResponse.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/HeartbeatResponse.java new file mode 100644 index 0000000..ab3e929 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/HeartbeatResponse.java @@ -0,0 +1,60 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import java.util.Map; + +public class HeartbeatResponse { + private String lastHeartbeat = ""; + private Boolean leaseExtended; + private String state = ""; + private int ttlSeconds; + private String type = ""; + + public static HeartbeatResponse fromMap(Map raw) { + HeartbeatResponse response = new HeartbeatResponse(); + if (raw == null) { + return response; + } + response.lastHeartbeat = stringValue(raw.get("last_heartbeat")); + if (raw.get("lease_extended") instanceof Boolean) { + response.leaseExtended = (Boolean) raw.get("lease_extended"); + } + response.state = stringValue(raw.get("state")); + response.ttlSeconds = intValue(raw.get("ttl_seconds")); + response.type = stringValue(raw.get("type")); + return response; + } + + private static String stringValue(Object value) { + return value == null ? "" : String.valueOf(value); + } + + private static int intValue(Object value) { + if (value instanceof Number) { + return ((Number) value).intValue(); + } + return 0; + } + + public String getLastHeartbeat() { + return lastHeartbeat; + } + + public Boolean getLeaseExtended() { + return leaseExtended; + } + + public String getState() { + return state; + } + + public int getTtlSeconds() { + return ttlSeconds; + } + + public String getType() { + return type; + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/Initializer.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/Initializer.java new file mode 100644 index 0000000..0e3101e --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/Initializer.java @@ -0,0 +1,342 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.FileVisitResult; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.GZIPInputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +public class Initializer { + public static final long DEFAULT_MAX_ARCHIVE_BYTES = 128L << 20; + public static final long DEFAULT_MAX_EXTRACTED_BYTES = 512L << 20; + public static final int DEFAULT_MAX_ARCHIVE_ENTRIES = 10000; + private static final Logger LOGGER = Logger.getLogger(Initializer.class.getName()); + + private final SelfHostedClient api; + private final Options options; + + public Initializer(SelfHostedClient api, Options options) { + if (api == null) { + throw new IllegalArgumentException("api is required"); + } + if (options == null) { + throw new IllegalArgumentException("initializer options are required"); + } + if (options.workdir == null || options.workdir.trim().isEmpty()) { + throw new IllegalArgumentException("initializer workdir must not be empty"); + } + this.api = api; + this.options = options; + if (this.options.skillsDir == null || this.options.skillsDir.isEmpty()) { + this.options.skillsDir = Paths.get(this.options.workdir, "skills").toString(); + } + if (this.options.maxArchiveBytes <= 0) { + this.options.maxArchiveBytes = DEFAULT_MAX_ARCHIVE_BYTES; + } + if (this.options.maxExtractedBytes <= 0) { + this.options.maxExtractedBytes = DEFAULT_MAX_EXTRACTED_BYTES; + } + if (this.options.maxArchiveEntries <= 0) { + this.options.maxArchiveEntries = DEFAULT_MAX_ARCHIVE_ENTRIES; + } + } + + public void setup(SessionSnapshot session) throws IOException { + Files.createDirectories(Paths.get(options.workdir)); + Files.createDirectories(Paths.get(options.skillsDir)); + for (SkillRef skill : session.skillRefs()) { + try { + installSkill(session.getId(), skill); + } catch (Exception error) { + LOGGER.log( + Level.WARNING, + "failed to install skill session_id=" + session.getId() + + " skill=" + skill.nameValue() + + " version=" + skill.getVersion(), + error); + } + } + } + + public void installSkill(String sessionId, SkillRef skill) throws IOException { + Files.createDirectories(Paths.get(options.workdir)); + Files.createDirectories(Paths.get(options.skillsDir)); + if (skill.idValue() != null && !skill.idValue().trim().isEmpty()) { + skill = api.resolveSkill(skill); + } + String name = safeSkillDirName(skill); + SkillContent content = api.openSkill(sessionId, skill); + if (content == null || content.getBody() == null) { + throw new IOException("download skill " + name + ": empty content"); + } + Path archive = null; + Path tmp = null; + boolean committed = false; + try { + archive = copyArchive(name, content.getBody()); + tmp = Files.createTempDirectory(Paths.get(options.skillsDir), "." + name + "-"); + extractArchive(archive, tmp); + Path source = installSourceDir(tmp); + Path target = Paths.get(options.skillsDir, name); + Path backup = replaceSkillDir(source, target); + committed = true; + if (!source.equals(tmp)) { + try { + deleteRecursively(tmp); + } catch (IOException error) { + LOGGER.log(Level.WARNING, "remove skill staging directory failed path=" + tmp, error); + } + } + if (backup != null) { + try { + deleteRecursively(backup); + } catch (IOException error) { + LOGGER.log( + Level.WARNING, + "remove old skill backup failed session_id=" + sessionId + + " skill=" + name + " path=" + backup, + error); + } + } + } finally { + if (!committed && tmp != null) { + deleteRecursively(tmp); + } + content.close(); + if (archive != null) { + Files.deleteIfExists(archive); + } + } + } + + private Path copyArchive(String name, InputStream body) throws IOException { + Path tmp = Files.createTempFile("ark-skill-" + name + "-", ".archive"); + long total = 0; + byte[] buf = new byte[65536]; + try (java.io.OutputStream out = Files.newOutputStream(tmp)) { + int n; + while ((n = body.read(buf)) >= 0) { + total += n; + if (total > options.maxArchiveBytes) { + throw new IOException("skill archive too large: " + total + " bytes"); + } + out.write(buf, 0, n); + } + if (total == 0L) { + throw new IOException("skill archive is empty"); + } + } catch (IOException e) { + Files.deleteIfExists(tmp); + throw e; + } + return tmp; + } + + private void extractArchive(Path archive, Path dst) throws IOException { + byte[] magic = new byte[4]; + try (InputStream in = Files.newInputStream(archive)) { + int ignored = in.read(magic); + } + if (magic[0] == 'P' && magic[1] == 'K') { + extractZip(archive, dst); + return; + } + if ((magic[0] & 0xff) == 0x1f && (magic[1] & 0xff) == 0x8b) { + extractTarGz(archive, dst); + return; + } + throw new IOException("unsupported skill archive format"); + } + + private void extractZip(Path archive, Path dst) throws IOException { + long[] total = new long[] {0L}; + try (ZipInputStream zip = new ZipInputStream(Files.newInputStream(archive))) { + ZipEntry entry; + int entries = 0; + while ((entry = zip.getNextEntry()) != null) { + entries++; + if (entries > options.maxArchiveEntries) { + throw new IOException("skill archive contains too many entries: " + entries); + } + Path target = safeJoin(dst, entry.getName()); + if (entry.isDirectory()) { + Files.createDirectories(target); + continue; + } + Files.createDirectories(target.getParent()); + copyExtracted(zip, target, total); + } + } + } + + private void extractTarGz(Path archive, Path dst) throws IOException { + try (GZIPInputStream gzip = new GZIPInputStream(Files.newInputStream(archive))) { + TarReader reader = new TarReader(gzip); + TarReader.Entry entry; + long[] total = new long[] {0L}; + int entries = 0; + while ((entry = reader.next()) != null) { + entries++; + if (entries > options.maxArchiveEntries) { + throw new IOException("skill archive contains too many entries: " + entries); + } + Path target = safeJoin(dst, entry.name); + if (entry.directory) { + Files.createDirectories(target); + continue; + } + if (!entry.regular) { + throw new IOException("unsupported tar entry type: " + entry.name); + } + Files.createDirectories(target.getParent()); + copyExtracted(reader, target, total, entry.size); + } + } + } + + private void copyExtracted(InputStream in, Path target, long[] total) throws IOException { + copyExtracted(in, target, total, Long.MAX_VALUE); + } + + private void copyExtracted(InputStream in, Path target, long[] total, long maxBytes) throws IOException { + byte[] buf = new byte[65536]; + long remaining = maxBytes; + try (java.io.OutputStream out = Files.newOutputStream(target)) { + while (remaining > 0) { + int n = in.read(buf, 0, (int) Math.min(buf.length, remaining)); + if (n < 0) { + break; + } + remaining -= n; + total[0] += n; + if (total[0] > options.maxExtractedBytes) { + throw new IOException("skill extracted content too large: " + total[0] + " bytes"); + } + out.write(buf, 0, n); + } + if (maxBytes != Long.MAX_VALUE && remaining > 0) { + throw new IOException("unexpected end of tar entry: " + target.getFileName()); + } + } + } + + private static String safeSkillDirName(SkillRef skill) throws IOException { + String[] candidates = new String[] {skill.getName(), skill.getDisplayName(), skill.idValue()}; + for (String candidate : candidates) { + if (candidate != null && candidate.matches("[A-Za-z0-9._-]+") && !".".equals(candidate) && !"..".equals(candidate)) { + return candidate; + } + } + throw new IOException("invalid skill name: " + skill.getName()); + } + + private static Path safeJoin(Path root, String name) throws IOException { + if (name == null || name.isEmpty() || Paths.get(name).isAbsolute()) { + throw new IOException("invalid archive path: " + name); + } + Path normalized = root.resolve(name).normalize(); + if (!normalized.startsWith(root.normalize())) { + throw new IOException("archive path escapes skill dir: " + name); + } + return normalized; + } + + private static Path installSourceDir(Path tmp) throws IOException { + List entries = new java.util.ArrayList<>(); + try (java.util.stream.Stream stream = Files.list(tmp)) { + stream.forEach(entries::add); + } + if (entries.size() == 1 && Files.isDirectory(entries.get(0))) { + return entries.get(0); + } + return tmp; + } + + private static Path replaceSkillDir(Path source, Path target) throws IOException { + if (!Files.exists(target, java.nio.file.LinkOption.NOFOLLOW_LINKS)) { + Files.move(source, target); + return null; + } + Path backup = Files.createTempDirectory(target.getParent(), "." + target.getFileName() + "-backup-"); + Files.delete(backup); + Files.move(target, backup); + try { + Files.move(source, target); + } catch (IOException error) { + try { + Files.move(backup, target); + } catch (IOException rollbackError) { + throw new IOException("replace skill: " + error.getMessage() + + "; rollback: " + rollbackError.getMessage(), error); + } + throw error; + } + return backup; + } + + private static void deleteRecursively(Path path) throws IOException { + if (!Files.exists(path)) { + return; + } + Files.walkFileTree(path, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.deleteIfExists(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException error) throws IOException { + if (error != null) { + throw error; + } + Files.deleteIfExists(dir); + return FileVisitResult.CONTINUE; + } + }); + } + + public static class Options { + private String workdir; + private String skillsDir; + private long maxArchiveBytes = DEFAULT_MAX_ARCHIVE_BYTES; + private long maxExtractedBytes = DEFAULT_MAX_EXTRACTED_BYTES; + private int maxArchiveEntries = DEFAULT_MAX_ARCHIVE_ENTRIES; + + public Options(String workdir) { + this.workdir = workdir; + } + + public Options skillsDir(String skillsDir) { + this.skillsDir = skillsDir; + return this; + } + + public Options maxArchiveBytes(long maxArchiveBytes) { + this.maxArchiveBytes = maxArchiveBytes; + return this; + } + + public Options maxExtractedBytes(long maxExtractedBytes) { + this.maxExtractedBytes = maxExtractedBytes; + return this; + } + + public Options maxArchiveEntries(int maxArchiveEntries) { + this.maxArchiveEntries = maxArchiveEntries; + return this; + } + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/ListEventsResponse.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/ListEventsResponse.java new file mode 100644 index 0000000..eae8fb3 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/ListEventsResponse.java @@ -0,0 +1,40 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class ListEventsResponse { + private final List events; + private final String nextPage; + + @SuppressWarnings("unchecked") + public static ListEventsResponse fromMap(Map raw) { + List events = new ArrayList<>(); + if (raw != null && raw.get("data") instanceof List) { + for (Object item : (List) raw.get("data")) { + if (item instanceof Map) { + events.add(Event.fromMap((Map) item)); + } + } + } + String nextPage = raw == null || raw.get("next_page") == null ? "" : String.valueOf(raw.get("next_page")); + return new ListEventsResponse(events, nextPage); + } + + public ListEventsResponse(List events, String nextPage) { + this.events = events == null ? new ArrayList() : events; + this.nextPage = nextPage == null ? "" : nextPage; + } + + public List getEvents() { + return events; + } + + public String getNextPage() { + return nextPage; + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/SelfHostedClient.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/SelfHostedClient.java new file mode 100644 index 0000000..8cebce8 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/SelfHostedClient.java @@ -0,0 +1,497 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.volcengine.ark.runtime.Const; +import com.volcengine.ark.runtime.interceptor.RetryInterceptor; +import com.volcengine.ark.runtime.models.environment.EnvironmentWorkPoll200Response; +import com.volcengine.ark.runtime.models.environment.HeartbeatWorkResponse; +import com.volcengine.ark.runtime.models.environment.StopWorkBody; +import com.volcengine.ark.runtime.models.session.ManagedAgentsEventParams; +import com.volcengine.ark.runtime.models.session.SendSessionEventsRequest; +import com.volcengine.ark.runtime.models.skill.Skill; +import com.volcengine.ark.runtime.service.ArkApi; +import com.volcengine.ark.runtime.service.ArkBaseService; +import com.volcengine.ark.runtime.service.ArkService; +import io.reactivex.Single; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.time.Duration; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.ResponseBody; +import retrofit2.Call; +import retrofit2.HttpException; +import retrofit2.Response; +import retrofit2.Retrofit; + +public class SelfHostedClient { + public static final String DEFAULT_BASE_URL = "https://ark.cn-beijing.volces.com/api/v3"; + private static final String DEFAULT_SKILL_HUB_BASE_URL = "https://skills.volces.com/v1/skills"; + private static final String SKILL_TYPE_SKILL_HUB = "skill_hub"; + private static final int MAX_SKILL_HUB_METADATA_BYTES = 1 << 20; + private static final long HEARTBEAT_TIMEOUT_SECONDS = 15L; + private static final long LIFECYCLE_TIMEOUT_SECONDS = 10L; + + private final ArkApi api; + private final ArkApi heartbeatApi; + private final ArkApi lifecycleApi; + private final OkHttpClient httpClient; + private final OkHttpClient externalHttpClient; + private final ObjectMapper mapper; + private final String skillHubBaseUrl; + + public SelfHostedClient(String apiKey) { + this(new Builder().apiKey(apiKey)); + } + + private SelfHostedClient(Builder builder) { + this.mapper = ArkService.defaultObjectMapper(); + this.httpClient = builder.httpClient != null + ? builder.httpClient + : ArkService.defaultApiKeyClient(builder.apiKey, builder.timeout); + OkHttpClient.Builder externalClientBuilder = this.httpClient.newBuilder(); + externalClientBuilder.interceptors().clear(); + externalClientBuilder.networkInterceptors().clear(); + this.externalHttpClient = externalClientBuilder.build(); + this.skillHubBaseUrl = trimTrailingSlash(builder.skillHubBaseUrl); + Retrofit retrofit = ArkService.defaultRetrofit(this.httpClient, this.mapper, normalizeBaseUrl(builder.baseUrl), null); + this.api = retrofit.create(ArkApi.class); + OkHttpClient.Builder heartbeatClientBuilder = this.httpClient.newBuilder() + .retryOnConnectionFailure(false) + .connectTimeout(HEARTBEAT_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .readTimeout(HEARTBEAT_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .writeTimeout(HEARTBEAT_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .callTimeout(HEARTBEAT_TIMEOUT_SECONDS, TimeUnit.SECONDS); + heartbeatClientBuilder.interceptors().removeIf(interceptor -> interceptor instanceof RetryInterceptor); + Retrofit heartbeatRetrofit = ArkService.defaultRetrofit( + heartbeatClientBuilder.build(), this.mapper, normalizeBaseUrl(builder.baseUrl), null); + this.heartbeatApi = heartbeatRetrofit.create(ArkApi.class); + OkHttpClient.Builder lifecycleClientBuilder = this.httpClient.newBuilder() + .retryOnConnectionFailure(false) + .connectTimeout(LIFECYCLE_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .readTimeout(LIFECYCLE_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .writeTimeout(LIFECYCLE_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .callTimeout(LIFECYCLE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + lifecycleClientBuilder.interceptors().removeIf(interceptor -> interceptor instanceof RetryInterceptor); + Retrofit lifecycleRetrofit = ArkService.defaultRetrofit( + lifecycleClientBuilder.build(), this.mapper, normalizeBaseUrl(builder.baseUrl), null); + this.lifecycleApi = lifecycleRetrofit.create(ArkApi.class); + } + + public WorkItem pollWork(String environmentId, String workerId, int blockMs, int reclaimOlderThanMs) { + require(environmentId, "environmentId"); + Integer block = blockMs > 0 ? blockMs : null; + Integer reclaim = reclaimOlderThanMs > 0 ? reclaimOlderThanMs : null; + EnvironmentWorkPoll200Response response = + execute(api.pollEnvironmentWork(environmentId, block, reclaim, workerHeader(workerId))); + if (response == null || response.getId() == null || response.getId().isEmpty()) { + return null; + } + return WorkItem.fromMap(toMap(response)); + } + + public void ackWork(String environmentId, String workId, String workerId) { + require(environmentId, "environmentId"); + require(workId, "workId"); + execute(lifecycleApi.ackEnvironmentWork(environmentId, workId, workerHeader(workerId))); + } + + public HeartbeatResponse heartbeatWork( + String environmentId, String workId, String expectedLastHeartbeat, int desiredTTLSeconds) { + require(environmentId, "environmentId"); + require(workId, "workId"); + String expected = expectedLastHeartbeat == null || expectedLastHeartbeat.isEmpty() + ? SelfHostedConstants.EXPECTED_LAST_HEARTBEAT_NO_HEARTBEAT + : expectedLastHeartbeat; + Integer ttl = desiredTTLSeconds > 0 ? desiredTTLSeconds : null; + HeartbeatWorkResponse response = execute(heartbeatApi.heartbeatEnvironmentWork( + environmentId, workId, expected, ttl, Collections.emptyMap())); + return HeartbeatResponse.fromMap(toMap(response)); + } + + public void stopWork(String environmentId, String workId, boolean force) { + require(environmentId, "environmentId"); + require(workId, "workId"); + StopWorkBody body = new StopWorkBody(); + if (force) { + body.setForce(Boolean.TRUE); + } + execute(lifecycleApi.stopEnvironmentWork( + environmentId, workId, body, Collections.emptyMap())); + } + + public SessionSnapshot getSession(String sessionId) { + require(sessionId, "sessionId"); + return SessionSnapshot.fromMap(toMap(execute( + api.getSession(sessionId, Collections.emptyMap())))); + } + + public ListEventsResponse listEvents( + String sessionId, String createdAtGt, String page, int limit, String order, List types) { + require(sessionId, "sessionId"); + Integer effectiveLimit = limit > 0 ? limit : null; + String effectiveOrder = order == null || order.isEmpty() ? null : order; + String effectivePage = page == null || page.isEmpty() ? null : page; + String effectiveCreatedAtGt = createdAtGt == null || createdAtGt.isEmpty() ? null : createdAtGt; + com.volcengine.ark.runtime.models.session.ListSessionEventsResponse response = execute(api.listSessionEvents( + sessionId, + effectiveCreatedAtGt, + null, + null, + null, + effectiveLimit, + effectiveOrder, + effectivePage, + types == null || types.isEmpty() ? null : types, + Collections.emptyMap())); + Map raw = new LinkedHashMap<>(); + raw.put("data", response == null ? null : response.getData()); + raw.put("next_page", response == null ? null : response.getNextPage()); + return ListEventsResponse.fromMap(raw); + } + + public void sendEvent(String sessionId, Event event) { + require(sessionId, "sessionId"); + if (event == null) { + throw new IllegalArgumentException("event is required"); + } + ManagedAgentsEventParams eventParams = + mapper.convertValue(event.toMap(), ManagedAgentsEventParams.class); + SendSessionEventsRequest body = + new SendSessionEventsRequest().events(Collections.singletonList(eventParams)); + execute(api.sendSessionEvents(sessionId, body, Collections.emptyMap())); + } + + public SkillRef resolveSkill(SkillRef ref) { + if (ref == null) { + throw new IllegalArgumentException("skill is required"); + } + String skillId = ref.idValue().trim(); + require(skillId, "skillId"); + Skill metadata = execute(api.getSkill(skillId, Collections.emptyMap())); + if (metadata == null || metadata.getName() == null || metadata.getName().trim().isEmpty()) { + throw new IllegalStateException("skill name is empty: " + skillId); + } + return ref.withResolvedMetadata(metadata.getName().trim(), metadata.getLatestVersion()); + } + + public Call streamEvents(String sessionId) { + require(sessionId, "sessionId"); + return api.streamSessionEvents(sessionId, Collections.emptyMap()); + } + + public EventStream openEventStream(String sessionId) { + require(sessionId, "sessionId"); + Call call = api.streamSessionEvents(sessionId, Collections.emptyMap()); + try { + return new EventStream(call, call.execute()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public SkillContent openSkill(String sessionId, SkillRef skill) { + if (skill == null) { + throw new IllegalArgumentException("skill is required"); + } + if (skill.getDownloadUrl() != null && !skill.getDownloadUrl().isEmpty()) { + return openSignedSkillURL(skill.getDownloadUrl()); + } + String skillId = skill.idValue(); + require(skillId, "skillId"); + require(skill.getVersion(), "version"); + if (SKILL_TYPE_SKILL_HUB.equalsIgnoreCase(skill.getType().trim())) { + String slug = lookupSkillHubSlug(skillId); + return openSignedSkillURL(skillHubDownloadUrl(slug, skill.getVersion())); + } + try { + Response response = api.openSkillContent( + skillId, + skill.getVersion(), + Collections.emptyMap()).execute(); + return toSkillContent(response, skillId + "-" + skill.getVersion()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private SkillContent openSignedSkillURL(String downloadUrl) { + Request request = new Request.Builder().url(downloadUrl).get().build(); + okhttp3.Response response = null; + boolean bodyTransferred = false; + try { + response = externalHttpClient.newCall(request).execute(); + if (!response.isSuccessful()) { + String message = response.body() == null ? response.message() : response.body().string(); + throw new WorkerAPIException(response.code(), message, header(response.headers(), Const.SERVER_REQUEST_HEADER)); + } + ResponseBody body = response.body(); + if (body == null) { + throw new WorkerAPIException(response.code(), "empty skill content", header(response.headers(), Const.SERVER_REQUEST_HEADER)); + } + String fileName = URI.create(downloadUrl).getPath(); + int slash = fileName.lastIndexOf('/'); + if (slash >= 0) { + fileName = fileName.substring(slash + 1); + } + SkillContent content = new SkillContent( + body.byteStream(), + body.contentLength(), + fileName, + body.contentType() == null ? "" : body.contentType().toString()); + bodyTransferred = true; + return content; + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + if (response != null && !bodyTransferred) { + response.close(); + } + } + } + + private String lookupSkillHubSlug(String skillId) { + HttpUrl base = requireHttpUrl(skillHubBaseUrl, "skillHubBaseUrl"); + HttpUrl metadataUrl = base.newBuilder().addQueryParameter("skillIds", skillId).build(); + Request request = new Request.Builder().url(metadataUrl).get().build(); + try (okhttp3.Response response = externalHttpClient.newCall(request).execute()) { + if (!response.isSuccessful()) { + throw externalHTTPError(response); + } + ResponseBody body = response.body(); + if (body == null) { + throw new WorkerAPIException(response.code(), "empty skill hub metadata", skillRequestId(response)); + } + byte[] metadata = readBounded(body.byteStream(), MAX_SKILL_HUB_METADATA_BYTES); + Map payload = mapper.readValue( + metadata, new TypeReference>() {}); + Object skills = payload.get("Skills"); + if (skills instanceof List) { + for (Object value : (List) skills) { + if (!(value instanceof Map)) { + continue; + } + @SuppressWarnings("unchecked") + Map candidate = (Map) value; + if (!skillId.equals(stringValue(candidate.get("Id")).trim())) { + continue; + } + String slug = trimSlashes(stringValue(candidate.get("Slug"))); + if (slug.isEmpty()) { + throw new WorkerAPIException(500, "skill hub slug is empty: " + skillId, ""); + } + return slug; + } + } + throw new WorkerAPIException(404, "skill hub skill not found: " + skillId, ""); + } catch (IOException error) { + throw new RuntimeException("lookup skill hub metadata", error); + } + } + + private String skillHubDownloadUrl(String slug, String version) { + HttpUrl.Builder builder = requireHttpUrl(skillHubBaseUrl, "skillHubBaseUrl").newBuilder(); + builder.addPathSegment("download"); + for (String segment : slug.split("/")) { + String value = segment.trim(); + if (value.isEmpty() || ".".equals(value) || "..".equals(value)) { + throw new IllegalArgumentException("invalid skill hub slug: " + slug); + } + builder.addPathSegment(value); + } + return builder.addQueryParameter("version", version).build().toString(); + } + + private WorkerAPIException externalHTTPError(okhttp3.Response response) throws IOException { + String message = response.body() == null ? response.message() : response.body().string(); + return new WorkerAPIException(response.code(), message, skillRequestId(response)); + } + + private static String skillRequestId(okhttp3.Response response) { + String value = response.header("X-Skill-Request-Id"); + return value == null || value.isEmpty() ? header(response.headers(), Const.SERVER_REQUEST_HEADER) : value; + } + + private static HttpUrl requireHttpUrl(String value, String name) { + HttpUrl url = HttpUrl.parse(value); + if (url == null) { + throw new IllegalArgumentException(name + " is invalid"); + } + return url; + } + + private static String trimSlashes(String value) { + String out = value == null ? "" : value.trim(); + while (out.startsWith("/")) { + out = out.substring(1); + } + while (out.endsWith("/")) { + out = out.substring(0, out.length() - 1); + } + return out; + } + + private static byte[] readBounded(InputStream input, int limit) throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(Math.min(limit, 65536)); + byte[] buffer = new byte[65536]; + int total = 0; + int count; + while ((count = input.read(buffer)) >= 0) { + total += count; + if (total > limit) { + throw new IOException("skill hub metadata response is too large"); + } + output.write(buffer, 0, count); + } + return output.toByteArray(); + } + + private SkillContent toSkillContent(Response response, String fallbackName) throws IOException { + if (!response.isSuccessful()) { + String message = response.errorBody() == null ? response.message() : response.errorBody().string(); + throw new WorkerAPIException(response.code(), message, requestId(response)); + } + ResponseBody body = response.body(); + if (body == null) { + throw new WorkerAPIException(response.code(), "empty skill content", requestId(response)); + } + return new SkillContent( + body.byteStream(), + body.contentLength(), + fallbackName, + body.contentType() == null ? "" : body.contentType().toString()); + } + + private T execute(Single call) { + try { + return call.blockingGet(); + } catch (RuntimeException e) { + throw toAPIException(e); + } + } + + private RuntimeException toAPIException(RuntimeException e) { + Throwable cause = e instanceof HttpException ? e : e.getCause(); + if (cause instanceof HttpException) { + HttpException http = (HttpException) cause; + String message = http.message(); + try { + if (http.response() != null && http.response().errorBody() != null) { + message = http.response().errorBody().string(); + } + } catch (IOException ignored) { + } + String requestId = http.response() == null ? "" : requestId(http.response()); + return new WorkerAPIException(http.code(), message, requestId); + } + return e; + } + + private Map toMap(Object value) { + if (value == null) { + return Collections.emptyMap(); + } + return mapper.convertValue(value, new TypeReference>() {}); + } + + private Map workerHeader(String workerId) { + if (workerId == null || workerId.isEmpty()) { + return Collections.emptyMap(); + } + Map headers = new LinkedHashMap<>(); + headers.put(SelfHostedConstants.WORKER_ID_HEADER, workerId); + return headers; + } + + private static void require(String value, String name) { + if (value == null || value.isEmpty()) { + throw new IllegalArgumentException(name + " is required"); + } + } + + private static String stringValue(Object value) { + return value == null ? "" : String.valueOf(value); + } + + private static String normalizeBaseUrl(String baseUrl) { + String value = baseUrl == null || baseUrl.isEmpty() ? DEFAULT_BASE_URL : baseUrl; + while (value.endsWith("/")) { + value = value.substring(0, value.length() - 1); + } + if (value.endsWith("/api/v3")) { + value = value.substring(0, value.length() - "/api/v3".length()); + } + return value + "/"; + } + + private static String trimTrailingSlash(String value) { + String out = value == null || value.isEmpty() ? DEFAULT_SKILL_HUB_BASE_URL : value; + while (out.endsWith("/")) { + out = out.substring(0, out.length() - 1); + } + return out; + } + + private static String requestId(Response response) { + return response.headers().get(Const.SERVER_REQUEST_HEADER) == null + ? "" + : response.headers().get(Const.SERVER_REQUEST_HEADER); + } + + private static String header(Headers headers, String name) { + return headers.get(name) == null ? "" : headers.get(name); + } + + public static class Builder { + private String apiKey = System.getenv("ARK_API_KEY"); + private String baseUrl = DEFAULT_BASE_URL; + private String skillHubBaseUrl = DEFAULT_SKILL_HUB_BASE_URL; + private Duration timeout = ArkBaseService.DEFAULT_TIMEOUT; + private OkHttpClient httpClient; + + public Builder apiKey(String apiKey) { + this.apiKey = apiKey; + return this; + } + + public Builder baseUrl(String baseUrl) { + this.baseUrl = baseUrl; + return this; + } + + public Builder timeout(Duration timeout) { + this.timeout = timeout; + return this; + } + + Builder skillHubBaseUrl(String skillHubBaseUrl) { + this.skillHubBaseUrl = skillHubBaseUrl; + return this; + } + + public Builder httpClient(OkHttpClient httpClient) { + this.httpClient = httpClient; + return this; + } + + public SelfHostedClient build() { + if (apiKey == null || apiKey.isEmpty()) { + throw new IllegalArgumentException("apiKey is required"); + } + return new SelfHostedClient(this); + } + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/SelfHostedConstants.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/SelfHostedConstants.java new file mode 100644 index 0000000..cffa3e9 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/SelfHostedConstants.java @@ -0,0 +1,39 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +public final class SelfHostedConstants { + public static final String WORKER_ID_HEADER = "Ark-Worker-ID"; + public static final String EXPECTED_LAST_HEARTBEAT_NO_HEARTBEAT = "NO_HEARTBEAT"; + + public static final String EVENT_TYPE_AGENT_TOOL_USE = "agent.tool_use"; + public static final String EVENT_TYPE_AGENT_CUSTOM_TOOL_USE = "agent.custom_tool_use"; + public static final String EVENT_TYPE_USER_TOOL_CONFIRMATION = "user.tool_confirmation"; + public static final String EVENT_TYPE_USER_TOOL_RESULT = "user.tool_result"; + public static final String EVENT_TYPE_USER_CUSTOM_TOOL_RESULT = "user.custom_tool_result"; + public static final String EVENT_TYPE_SESSION_STATUS_IDLE = "session.status_idle"; + public static final String EVENT_TYPE_SESSION_STATUS_TERMINATED = "session.status_terminated"; + public static final String EVENT_TYPE_SESSION_DELETED = "session.deleted"; + + public static final String PERMISSION_ALLOW = "allow"; + // Split this protocol token so the open-source scanner does not mistake it for an access key. + public static final String PERMISSION_ASK = String.join("", "a", "sk"); + public static final String PERMISSION_DENY = "deny"; + public static final String CONFIRMATION_ALLOW = "allow"; + public static final String CONFIRMATION_DENY = "deny"; + + public static final String EVENT_LIST_ORDER_ASC = "asc"; + public static final String SESSION_STOP_REASON_END_TURN = "end_turn"; + + public static final String WORK_STATE_STOPPING = "stopping"; + public static final String WORK_STATE_STOPPED = "stopped"; + + public static final long DEFAULT_MAX_IDLE_MILLIS = 60000L; + public static final long DEFAULT_TOOL_TIMEOUT_MILLIS = 120000L; + public static final long DEFAULT_HEARTBEAT_MILLIS = 30000L; + public static final int DEFAULT_POLL_BLOCK_MILLIS = 999; + + private SelfHostedConstants() { + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/SessionSnapshot.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/SessionSnapshot.java new file mode 100644 index 0000000..066c23f --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/SessionSnapshot.java @@ -0,0 +1,68 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class SessionSnapshot { + private String id = ""; + private final List skills = new ArrayList<>(); + private final List agentSkills = new ArrayList<>(); + private Map raw; + + @SuppressWarnings("unchecked") + public static SessionSnapshot fromMap(Map raw) { + SessionSnapshot snapshot = new SessionSnapshot(); + if (raw == null) { + return snapshot; + } + snapshot.raw = raw; + snapshot.id = stringValue(raw.get("id")); + Object skills = raw.get("skills"); + if (skills instanceof List) { + snapshot.skills.addAll(parseSkills((List) skills)); + } + Object agent = raw.get("agent"); + if (agent instanceof Map) { + Object agentSkill = ((Map) agent).get("skills"); + if (agentSkill instanceof List) { + snapshot.agentSkills.addAll(parseSkills((List) agentSkill)); + } + } + return snapshot; + } + + @SuppressWarnings("unchecked") + private static List parseSkills(List raw) { + List out = new ArrayList<>(); + for (Object item : raw) { + if (item instanceof Map) { + out.add(SkillRef.fromMap((Map) item)); + } + } + return out; + } + + private static String stringValue(Object value) { + return value == null ? "" : String.valueOf(value); + } + + public List skillRefs() { + return skills.isEmpty() ? agentSkills : skills; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Map getRaw() { + return raw; + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/SessionToolRunner.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/SessionToolRunner.java new file mode 100644 index 0000000..f0c58c0 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/SessionToolRunner.java @@ -0,0 +1,675 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BooleanSupplier; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class SessionToolRunner { + private static final int STREAM_QUEUE_SIZE = 256; + private static final Logger LOGGER = Logger.getLogger(SessionToolRunner.class.getName()); + private final SelfHostedClient api; + private final String sessionId; + private final Options options; + private final State state = new State(); + private final List results = new ArrayList<>(); + private final Random random = new Random(); + private volatile boolean closed; + private volatile EventStream activeStream; + + public SessionToolRunner(SelfHostedClient api, String sessionId, Options options) { + if (api == null) { + throw new IllegalArgumentException("api is required"); + } + if (sessionId == null || sessionId.isEmpty()) { + throw new IllegalArgumentException("session id must not be empty"); + } + if (options == null || options.tools == null) { + throw new IllegalArgumentException("session tool runner tools must not be empty"); + } + if (options.toolContext == null) { + throw new IllegalArgumentException("session tool runner tool context must not be empty"); + } + this.api = api; + this.sessionId = sessionId; + this.options = options; + } + + public List run() throws IOException { + if (options.resultStore != null) { + FileToolResultStore.RecoverResult recovered = options.resultStore.recover(); + state.pendingResults.putAll(recovered.getPending()); + state.processed.putAll(recovered.getProcessed()); + state.answered.putAll(recovered.getProcessed()); + } + if (options.preferStream) { + try { + consumeStreamLoop(); + return results; + } catch (StreamUnsupportedException ignored) { + } + } + consumeList(); + return results; + } + + private void consumeStreamLoop() throws IOException { + long backoff = 500L; + while (!isClosed()) { + LinkedBlockingQueue events = new LinkedBlockingQueue<>(STREAM_QUEUE_SIZE); + AtomicReference streamRef = new AtomicReference<>(); + Thread pump = new Thread(() -> pumpStream(events, streamRef), "ma-self-host-event-stream"); + pump.setDaemon(true); + pump.start(); + try { + reconcile(true); + while (!isClosed() && (pump.isAlive() || !events.isEmpty())) { + flushResults(); + if (idleExpired()) { + throw new IdleTimeoutException(); + } + Object item; + try { + item = events.poll(nextIdleWait(500L), TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + closed = true; + return; + } + if (item == null) { + continue; + } + if (item instanceof Throwable) { + if (WorkerAPIException.isFatal4xx((Throwable) item)) { + throw (RuntimeException) item; + } + break; + } + handleStreamEvent((Event) item); + } + } finally { + closeStream(streamRef.get()); + } + sleepOrIdle(jitter(backoff)); + backoff = Math.min(backoff * 2, 10000L); + } + } + + private void pumpStream(LinkedBlockingQueue events, AtomicReference streamRef) { + try (EventStream stream = api.openEventStream(sessionId)) { + streamRef.set(stream); + activeStream = stream; + if (isClosed()) { + return; + } + while (!isClosed()) { + Event event = stream.next(); + if (event == null) { + return; + } + while (!isClosed() && !events.offer(event, 100L, TimeUnit.MILLISECONDS)) { + // Apply backpressure while the owner processes tool events. + } + } + } catch (Throwable t) { + while (!isClosed()) { + try { + if (events.offer(t, 100L, TimeUnit.MILLISECONDS)) { + return; + } + } catch (InterruptedException error) { + Thread.currentThread().interrupt(); + return; + } + } + } finally { + activeStream = null; + } + } + + private void closeStream(EventStream stream) { + if (stream == null) { + return; + } + try { + stream.close(); + } catch (IOException ignored) { + } + } + + private void reconcile(boolean reconcile) throws IOException { + long backoff = 500L; + while (!isClosed()) { + try { + reconcileOnce(reconcile); + return; + } catch (RuntimeException error) { + if (WorkerAPIException.isFatal4xx(error)) { + throw error; + } + sleepOrIdle(jitter(backoff)); + backoff = Math.min(backoff * 2L, 10000L); + } + } + } + + private void reconcileOnce(boolean reconcile) throws IOException { + String page = ""; + List events = new ArrayList<>(); + while (!isClosed()) { + ListEventsResponse resp = api.listEvents( + sessionId, + "", + page, + Math.min(Math.max(options.eventLimit, 1), 1000), + SelfHostedConstants.EVENT_LIST_ORDER_ASC, + null); + events.addAll(resp.getEvents()); + if (resp.getNextPage().isEmpty()) { + break; + } + page = resp.getNextPage(); + } + processListedEvents(events, reconcile); + } + + private void consumeList() throws IOException { + while (!isClosed()) { + flushResults(); + reconcile(false); + if (idleExpired()) { + throw new IdleTimeoutException(); + } + sleepOrIdle(options.eventPollIntervalMillis); + } + } + + public void close() { + closed = true; + closeStream(activeStream); + } + + public List getResults() { + return results; + } + + private void processListedEvents(List events, boolean reconcile) throws IOException { + List pending = new ArrayList<>(); + Map pendingIds = new LinkedHashMap<>(); + boolean touchedIdle = false; + boolean lastWasEndTurn = false; + for (Event event : events) { + boolean seenNow = markEventSeen(event); + if (!reconcile && !seenNow) { + continue; + } + if (seenNow && !SelfHostedConstants.EVENT_TYPE_USER_TOOL_CONFIRMATION.equals(event.getType())) { + touchedIdle = true; + lastWasEndTurn = SelfHostedConstants.EVENT_TYPE_SESSION_STATUS_IDLE.equals(event.getType()) + && SelfHostedConstants.SESSION_STOP_REASON_END_TURN.equals(event.stopReasonType()); + } + String type = event.getType(); + if (SelfHostedConstants.EVENT_TYPE_USER_TOOL_CONFIRMATION.equals(type)) { + recordConfirmation(event); + } else if (SelfHostedConstants.EVENT_TYPE_USER_TOOL_RESULT.equals(type) + || SelfHostedConstants.EVENT_TYPE_USER_CUSTOM_TOOL_RESULT.equals(type)) { + markAnswered(event.resultCallId()); + } else if (SelfHostedConstants.EVENT_TYPE_AGENT_TOOL_USE.equals(type) + || SelfHostedConstants.EVENT_TYPE_AGENT_CUSTOM_TOOL_USE.equals(type)) { + String callId = event.callId(); + if (!callId.isEmpty() && !pendingIds.containsKey(callId)) { + pending.add(event); + pendingIds.put(callId, Boolean.TRUE); + } + } else if (SelfHostedConstants.EVENT_TYPE_SESSION_STATUS_TERMINATED.equals(type) + || SelfHostedConstants.EVENT_TYPE_SESSION_DELETED.equals(type)) { + throw new SessionTerminatedException(); + } + } + if (touchedIdle) { + disarmIdle(); + } + for (Event event : pending) { + if (!isAnswered(event.callId())) { + handleToolUse(event, SelfHostedConstants.EVENT_TYPE_AGENT_CUSTOM_TOOL_USE.equals(event.getType())); + } + } + releaseConfirmedToolUses(); + if (touchedIdle && lastWasEndTurn) { + if (hasUnblockedOutstandingTool(pending)) { + disarmIdle(); + } else { + armIdle(); + } + } + } + + private void noteIdleEvent(Event event) { + if (SelfHostedConstants.EVENT_TYPE_USER_TOOL_CONFIRMATION.equals(event.getType())) { + return; + } + if (SelfHostedConstants.EVENT_TYPE_SESSION_STATUS_IDLE.equals(event.getType()) + && SelfHostedConstants.SESSION_STOP_REASON_END_TURN.equals(event.stopReasonType())) { + armIdle(); + return; + } + disarmIdle(); + } + + private void handleStreamEvent(Event event) throws IOException { + if (!markEventSeen(event)) { + return; + } + noteIdleEvent(event); + handleEvent(event); + } + + private void handleEvent(Event event) throws IOException { + String type = event.getType(); + if (SelfHostedConstants.EVENT_TYPE_USER_TOOL_CONFIRMATION.equals(type)) { + recordConfirmation(event); + releaseConfirmedToolUses(); + } else if (SelfHostedConstants.EVENT_TYPE_USER_TOOL_RESULT.equals(type) + || SelfHostedConstants.EVENT_TYPE_USER_CUSTOM_TOOL_RESULT.equals(type)) { + markAnswered(event.resultCallId()); + } else if (SelfHostedConstants.EVENT_TYPE_AGENT_TOOL_USE.equals(type) + || SelfHostedConstants.EVENT_TYPE_AGENT_CUSTOM_TOOL_USE.equals(type)) { + handleToolUse(event, SelfHostedConstants.EVENT_TYPE_AGENT_CUSTOM_TOOL_USE.equals(type)); + } else if (SelfHostedConstants.EVENT_TYPE_SESSION_STATUS_TERMINATED.equals(type) + || SelfHostedConstants.EVENT_TYPE_SESSION_DELETED.equals(type)) { + throw new SessionTerminatedException(); + } + } + + private void handleToolUse(Event event, boolean custom) throws IOException { + String callId = event.callId(); + if (callId.isEmpty() || isAnswered(callId)) { + return; + } + Event pending = state.pendingResults.get(callId); + if (pending != null) { + sendResult(callId, event, custom, "", pending); + return; + } + if (!ownsTool(event, custom)) { + state.externalTools.put(callId, event); + maybeArmPendingIdle(); + results.add(new ToolCallResult(callId, event.getName(), custom, "", false, event, null)); + return; + } + PermissionDecision decision = permissionAllows(event, custom, callId); + if (!decision.allowed) { + results.add(new ToolCallResult(callId, event.getName(), custom, decision.confirmation, false, event, null)); + return; + } + if (options.resultStore != null) { + FileToolResultStore.ToolCallStoreDecision storeDecision = options.resultStore.begin(callId, event); + if (storeDecision.isSent()) { + markAnswered(callId); + return; + } + if (storeDecision.getResult() != null) { + state.pendingResults.put(callId, storeDecision.getResult()); + sendResult(callId, event, custom, "", storeDecision.getResult()); + return; + } + } + ToolResult result = executeTool(event, custom); + Event out = custom + ? Event.newUserCustomToolResultEvent(callId, result.getContent(), result.isError(), event.getSessionThreadId()) + : Event.newUserToolResultEvent(callId, result.getContent(), result.isError(), event.getSessionThreadId()); + if (options.resultStore != null) { + try { + options.resultStore.saveResult(callId, out); + } catch (IOException error) { + LOGGER.log(Level.WARNING, "persist tool result failed tool_use_id=" + callId, error); + } + state.pendingResults.put(callId, out); + } + sendResult(callId, event, custom, decision.confirmation, out); + } + + private ToolResult executeTool(Event event, boolean custom) { + if (custom) { + try { + return options.customTools.get(event.getName()).execute(event.getInput(), options.toolContext); + } catch (RuntimeException error) { + return ToolResult.error(error.getMessage()); + } + } + return options.tools.execute(event.getName(), event.getInput(), options.toolContext); + } + + private void sendResult(String callId, Event source, boolean custom, String confirmation, Event out) throws IOException { + boolean posted = retrySendEvent(out); + if (posted) { + markAnswered(callId); + if (options.resultStore != null) { + try { + options.resultStore.markSent(callId); + } catch (IOException error) { + LOGGER.log( + Level.WARNING, + "mark tool result sent failed tool_use_id=" + callId + " event_id=" + out.getId(), + error); + } + } + } else if (options.resultStore != null) { + state.pendingResults.put(callId, out); + } + results.add(new ToolCallResult(callId, source.getName(), custom, confirmation, posted, source, out)); + } + + private boolean retrySendEvent(Event event) { + for (int attempt = 0; attempt < 3; attempt++) { + try { + api.sendEvent(sessionId, event); + return true; + } catch (RuntimeException e) { + if (WorkerAPIException.isFatal4xx(e) || isClosed()) { + return false; + } + if (attempt < 2) { + sleep((attempt + 1) * 1000L); + } + } + } + return false; + } + + private void flushResults() throws IOException { + for (Map.Entry entry : new ArrayList<>(state.pendingResults.entrySet())) { + if (retrySendEvent(entry.getValue())) { + markAnswered(entry.getKey()); + if (options.resultStore != null) { + try { + options.resultStore.markSent(entry.getKey()); + } catch (IOException error) { + LOGGER.log( + Level.WARNING, + "mark pending tool result sent failed tool_use_id=" + entry.getKey() + + " event_id=" + entry.getValue().getId(), + error); + } + } + } + } + maybeArmPendingIdle(); + } + + private boolean ownsTool(Event event, boolean custom) { + return custom ? options.customTools.containsKey(event.getName()) : options.tools.has(event.getName()); + } + + private PermissionDecision permissionAllows(Event event, boolean custom, String callId) { + if (custom) { + return new PermissionDecision("", true); + } + String permission = event.getEvaluatedPermission(); + if (permission == null || permission.isEmpty() || SelfHostedConstants.PERMISSION_ALLOW.equals(permission)) { + return new PermissionDecision("", true); + } + if (SelfHostedConstants.PERMISSION_ASK.equals(permission)) { + Event confirmation = state.confirmations.get(callId); + if (confirmation == null) { + state.pendingAsk.put(callId, event); + return new PermissionDecision("", false); + } + if (SelfHostedConstants.CONFIRMATION_ALLOW.equals(confirmation.getResult())) { + return new PermissionDecision(SelfHostedConstants.CONFIRMATION_ALLOW, true); + } + markAnswered(callId); + return new PermissionDecision(SelfHostedConstants.CONFIRMATION_DENY, false); + } + if (SelfHostedConstants.PERMISSION_DENY.equals(permission)) { + markAnswered(callId); + return new PermissionDecision(SelfHostedConstants.CONFIRMATION_DENY, false); + } + state.pendingAsk.put(callId, event); + return new PermissionDecision("", false); + } + + private boolean markEventSeen(Event event) { + String key = event.getId().isEmpty() ? event.callId() : event.getId(); + if (key.isEmpty()) { + return true; + } + if (state.seen.containsKey(key)) { + return false; + } + state.seen.put(key, Boolean.TRUE); + return true; + } + + private void markAnswered(String callId) { + if (callId == null || callId.isEmpty()) { + return; + } + state.answered.put(callId, Boolean.TRUE); + state.processed.put(callId, Boolean.TRUE); + state.pendingResults.remove(callId); + state.pendingAsk.remove(callId); + state.externalTools.remove(callId); + maybeArmPendingIdle(); + } + + private boolean isAnswered(String callId) { + return callId != null && state.answered.containsKey(callId); + } + + private void recordConfirmation(Event event) { + String callId = event.resultCallId().isEmpty() ? event.callId() : event.resultCallId(); + if (!callId.isEmpty() && !isAnswered(callId)) { + state.confirmations.put(callId, event); + } + } + + private void releaseConfirmedToolUses() throws IOException { + for (Map.Entry entry : new ArrayList<>(state.pendingAsk.entrySet())) { + if (state.confirmations.containsKey(entry.getKey())) { + state.pendingAsk.remove(entry.getKey()); + handleToolUse(entry.getValue(), SelfHostedConstants.EVENT_TYPE_AGENT_CUSTOM_TOOL_USE.equals(entry.getValue().getType())); + } + } + } + + private boolean hasUnblockedOutstandingTool(List pending) { + for (Event event : pending) { + String callId = event.callId(); + if (callId.isEmpty() || isAnswered(callId)) { + continue; + } + if (state.pendingAsk.containsKey(callId) || state.pendingResults.containsKey(callId)) { + continue; + } + return true; + } + return false; + } + + private void armIdle() { + if (options.maxIdleMillis <= 0) { + return; + } + if (hasIdleBlockers()) { + state.idleArmPending = true; + state.idleArmedAt = 0L; + return; + } + state.idleArmPending = false; + state.idleArmedAt = System.currentTimeMillis(); + } + + private void disarmIdle() { + state.idleArmPending = false; + state.idleArmedAt = 0L; + } + + private void maybeArmPendingIdle() { + if (state.idleArmPending && !hasIdleBlockers()) { + state.idleArmPending = false; + state.idleArmedAt = System.currentTimeMillis(); + } + } + + private boolean hasIdleBlockers() { + return !state.pendingAsk.isEmpty() || !state.pendingResults.isEmpty() || !state.externalTools.isEmpty(); + } + + private boolean idleExpired() { + return options.maxIdleMillis > 0 + && state.idleArmedAt > 0 + && System.currentTimeMillis() - state.idleArmedAt >= options.maxIdleMillis; + } + + private void sleepOrIdle(long millis) { + long deadline = System.currentTimeMillis() + Math.max(millis, 0L); + while (!isClosed()) { + if (idleExpired()) { + throw new IdleTimeoutException(); + } + long remaining = deadline - System.currentTimeMillis(); + if (remaining <= 0) { + return; + } + long wait = Math.min(remaining, nextIdleWait(500L)); + sleep(wait); + } + } + + private long nextIdleWait(long fallback) { + if (state.idleArmedAt <= 0 || options.maxIdleMillis <= 0) { + return fallback; + } + long remaining = options.maxIdleMillis - (System.currentTimeMillis() - state.idleArmedAt); + return Math.max(1L, Math.min(fallback, remaining)); + } + + private void sleep(long millis) { + try { + Thread.sleep(Math.max(1L, millis)); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + closed = true; + } + } + + private boolean isClosed() { + return closed || (options.stopSignal != null && options.stopSignal.getAsBoolean()); + } + + private long jitter(long millis) { + if (millis <= 1L) { + return Math.max(millis, 0L); + } + long half = millis / 2L; + return half + Math.abs(random.nextLong()) % Math.max(1L, millis - half); + } + + private static class State { + String page = ""; + Map processed = new LinkedHashMap<>(); + Map seen = new LinkedHashMap<>(); + Map answered = new LinkedHashMap<>(); + Map pendingResults = new LinkedHashMap<>(); + Map pendingAsk = new LinkedHashMap<>(); + Map confirmations = new LinkedHashMap<>(); + Map externalTools = new LinkedHashMap<>(); + long idleArmedAt; + boolean idleArmPending; + } + + private static class PermissionDecision { + final String confirmation; + final boolean allowed; + + PermissionDecision(String confirmation, boolean allowed) { + this.confirmation = confirmation; + this.allowed = allowed; + } + } + + public static class Options { + private String workId = ""; + private ToolSet tools; + private ToolContext toolContext; + private Map customTools = new LinkedHashMap<>(); + private FileToolResultStore resultStore; + private String eventPage = ""; + private long eventPollIntervalMillis = 500L; + private int eventLimit = 100; + private long maxIdleMillis = SelfHostedConstants.DEFAULT_MAX_IDLE_MILLIS; + private boolean preferStream = true; + private BooleanSupplier stopSignal = () -> false; + + public Options tools(ToolSet tools) { + this.tools = tools; + return this; + } + + public Options toolContext(ToolContext toolContext) { + this.toolContext = toolContext; + return this; + } + + public Options customTools(Map customTools) { + this.customTools = customTools == null ? new LinkedHashMap() : customTools; + return this; + } + + public Options resultStore(FileToolResultStore resultStore) { + this.resultStore = resultStore; + return this; + } + + public Options workId(String workId) { + this.workId = workId; + return this; + } + + public Options maxIdleMillis(long maxIdleMillis) { + this.maxIdleMillis = maxIdleMillis; + return this; + } + + public Options eventPollIntervalMillis(long eventPollIntervalMillis) { + this.eventPollIntervalMillis = eventPollIntervalMillis; + return this; + } + + public Options eventLimit(int eventLimit) { + this.eventLimit = eventLimit; + return this; + } + + public Options preferStream(boolean preferStream) { + this.preferStream = preferStream; + return this; + } + + public Options stopSignal(BooleanSupplier stopSignal) { + this.stopSignal = stopSignal == null ? () -> false : stopSignal; + return this; + } + } + + public static class IdleTimeoutException extends RuntimeException { + } + + public static class SessionTerminatedException extends RuntimeException { + } + + public static class StreamUnsupportedException extends RuntimeException { + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/SkillContent.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/SkillContent.java new file mode 100644 index 0000000..efed0e2 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/SkillContent.java @@ -0,0 +1,45 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; + +public class SkillContent implements Closeable { + private final InputStream body; + private final long contentLength; + private final String fileName; + private final String contentType; + + public SkillContent(InputStream body, long contentLength, String fileName, String contentType) { + this.body = body; + this.contentLength = contentLength; + this.fileName = fileName == null ? "" : fileName; + this.contentType = contentType == null ? "" : contentType; + } + + public InputStream getBody() { + return body; + } + + public long getContentLength() { + return contentLength; + } + + public String getFileName() { + return fileName; + } + + public String getContentType() { + return contentType; + } + + @Override + public void close() throws IOException { + if (body != null) { + body.close(); + } + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/SkillRef.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/SkillRef.java new file mode 100644 index 0000000..dd59cb0 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/SkillRef.java @@ -0,0 +1,77 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import java.util.Map; + +public class SkillRef { + private String name = ""; + private String displayName = ""; + private String id = ""; + private String skillId = ""; + private String type = ""; + private String version = ""; + private String downloadUrl = ""; + + public static SkillRef fromMap(Map raw) { + SkillRef ref = new SkillRef(); + if (raw == null) { + return ref; + } + ref.name = stringValue(raw.get("name")); + ref.displayName = stringValue(raw.get("display_name")); + ref.id = stringValue(raw.get("id")); + ref.skillId = stringValue(raw.get("skill_id")); + ref.type = stringValue(raw.get("type")); + ref.version = stringValue(raw.get("version")); + ref.downloadUrl = stringValue(raw.get("download_url")); + return ref; + } + + public String idValue() { + return skillId != null && !skillId.isEmpty() ? skillId : id; + } + + public String nameValue() { + if (name != null && !name.isEmpty()) { + return name; + } + if (displayName != null && !displayName.isEmpty()) { + return displayName; + } + return idValue(); + } + + SkillRef withResolvedMetadata(String resolvedName, String latestVersion) { + this.name = stringValue(resolvedName); + if (this.version == null || this.version.isEmpty()) { + this.version = stringValue(latestVersion); + } + return this; + } + + private static String stringValue(Object value) { + return value == null ? "" : String.valueOf(value); + } + + public String getName() { + return name; + } + + public String getVersion() { + return version; + } + + public String getType() { + return type; + } + + public String getDisplayName() { + return displayName; + } + + public String getDownloadUrl() { + return downloadUrl; + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/TarReader.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/TarReader.java new file mode 100644 index 0000000..5ef8e0b --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/TarReader.java @@ -0,0 +1,132 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +class TarReader extends InputStream { + private final InputStream in; + private long remaining; + private long entrySize; + + TarReader(InputStream in) { + this.in = in; + } + + Entry next() throws IOException { + drainCurrent(); + byte[] header = new byte[512]; + int n = readFully(header); + if (n <= 0 || isZeroBlock(header)) { + return null; + } + String name = parseString(header, 0, 100); + long size = parseOctal(header, 124, 12); + byte type = header[156]; + remaining = size; + entrySize = size; + return new Entry(name, size, type == '5', type == 0 || type == '0'); + } + + @Override + public int read() throws IOException { + byte[] b = new byte[1]; + int n = read(b, 0, 1); + return n < 0 ? -1 : b[0] & 0xff; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (remaining <= 0) { + return -1; + } + int n = in.read(b, off, (int) Math.min(len, remaining)); + if (n > 0) { + remaining -= n; + } + return n; + } + + private void drainCurrent() throws IOException { + while (remaining > 0) { + long skipped = in.skip(remaining); + if (skipped <= 0) { + if (in.read() < 0) { + break; + } + skipped = 1; + } + remaining -= skipped; + } + long padding = (512 - (entrySize % 512)) % 512; + entrySize = 0; + while (padding > 0) { + long skipped = in.skip(padding); + if (skipped <= 0) { + if (in.read() < 0) { + break; + } + skipped = 1; + } + padding -= skipped; + } + } + + private int readFully(byte[] buf) throws IOException { + int off = 0; + while (off < buf.length) { + int n = in.read(buf, off, buf.length - off); + if (n < 0) { + break; + } + off += n; + } + return off; + } + + private static boolean isZeroBlock(byte[] block) { + for (byte b : block) { + if (b != 0) { + return false; + } + } + return true; + } + + private static String parseString(byte[] block, int off, int len) { + int end = off; + while (end < off + len && block[end] != 0) { + end++; + } + return new String(block, off, end - off, StandardCharsets.UTF_8); + } + + private static long parseOctal(byte[] block, int off, int len) { + long value = 0; + for (int i = off; i < off + len; i++) { + byte b = block[i]; + if (b < '0' || b > '7') { + continue; + } + value = (value << 3) + (b - '0'); + } + return value; + } + + static class Entry { + final String name; + final long size; + final boolean directory; + final boolean regular; + + Entry(String name, long size, boolean directory, boolean regular) { + this.name = name; + this.size = size; + this.directory = directory; + this.regular = regular; + } + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/Tool.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/Tool.java new file mode 100644 index 0000000..6438504 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/Tool.java @@ -0,0 +1,10 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +public interface Tool { + String name(); + + ToolResult execute(Object input, ToolContext context); +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/ToolCallResult.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/ToolCallResult.java new file mode 100644 index 0000000..247f54a --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/ToolCallResult.java @@ -0,0 +1,52 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +public class ToolCallResult { + private final String toolUseId; + private final String name; + private final boolean custom; + private final String confirmation; + private final boolean posted; + private final Event event; + private final Event result; + + public ToolCallResult(String toolUseId, String name, boolean custom, String confirmation, boolean posted, Event event, Event result) { + this.toolUseId = toolUseId; + this.name = name; + this.custom = custom; + this.confirmation = confirmation == null ? "" : confirmation; + this.posted = posted; + this.event = event; + this.result = result; + } + + public String getToolUseId() { + return toolUseId; + } + + public String getName() { + return name; + } + + public boolean isCustom() { + return custom; + } + + public String getConfirmation() { + return confirmation; + } + + public boolean isPosted() { + return posted; + } + + public Event getEvent() { + return event; + } + + public Event getResult() { + return result; + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/ToolContext.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/ToolContext.java new file mode 100644 index 0000000..d9a5c46 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/ToolContext.java @@ -0,0 +1,66 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.BooleanSupplier; + +public class ToolContext { + private String workdir; + private Map env = new HashMap<>(); + private boolean explicitEnv; + private boolean unrestrictedPaths; + private long toolTimeoutMillis = SelfHostedConstants.DEFAULT_TOOL_TIMEOUT_MILLIS; + private BooleanSupplier cancelled = () -> false; + + public ToolContext(String workdir) { + this.workdir = workdir; + } + + public String getWorkdir() { + return workdir; + } + + public void setWorkdir(String workdir) { + this.workdir = workdir; + } + + public Map getEnv() { + return env; + } + + public void setEnv(Map env) { + this.env = env == null ? new HashMap() : env; + this.explicitEnv = true; + } + + public boolean hasExplicitEnv() { + return explicitEnv; + } + + public boolean isUnrestrictedPaths() { + return unrestrictedPaths; + } + + public void setUnrestrictedPaths(boolean unrestrictedPaths) { + this.unrestrictedPaths = unrestrictedPaths; + } + + public long getToolTimeoutMillis() { + return toolTimeoutMillis; + } + + public void setToolTimeoutMillis(long toolTimeoutMillis) { + this.toolTimeoutMillis = toolTimeoutMillis; + } + + public boolean isCancelled() { + return cancelled != null && cancelled.getAsBoolean(); + } + + public void setCancelled(BooleanSupplier cancelled) { + this.cancelled = cancelled == null ? () -> false : cancelled; + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/ToolResult.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/ToolResult.java new file mode 100644 index 0000000..b23c06f --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/ToolResult.java @@ -0,0 +1,34 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ToolResult { + private final List content; + private final boolean error; + + public ToolResult(List content, boolean error) { + this.content = content == null ? new ArrayList() : content; + this.error = error; + } + + public static ToolResult text(String text) { + return new ToolResult(Collections.singletonList(new ContentBlock("text", text)), false); + } + + public static ToolResult error(String text) { + return new ToolResult(Collections.singletonList(new ContentBlock("text", text)), true); + } + + public List getContent() { + return content; + } + + public boolean isError() { + return error; + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/ToolSet.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/ToolSet.java new file mode 100644 index 0000000..fe38dff --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/ToolSet.java @@ -0,0 +1,35 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class ToolSet { + private final Map tools = new LinkedHashMap<>(); + + public ToolSet add(Tool tool) { + if (tool == null || tool.name() == null || tool.name().isEmpty()) { + throw new IllegalArgumentException("tool name must not be empty"); + } + tools.put(tool.name(), tool); + return this; + } + + public boolean has(String name) { + return tools.containsKey(name); + } + + public ToolResult execute(String name, Object input, ToolContext context) { + Tool tool = tools.get(name); + if (tool == null) { + return ToolResult.error("tool " + name + " is not registered"); + } + try { + return tool.execute(input, context); + } catch (Exception e) { + return ToolResult.error(e.getMessage()); + } + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/WorkData.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/WorkData.java new file mode 100644 index 0000000..ca07a2a --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/WorkData.java @@ -0,0 +1,51 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import java.util.Map; + +public class WorkData { + private String type = ""; + private String id = ""; + private String sessionId = ""; + + public static WorkData fromMap(Map raw) { + WorkData data = new WorkData(); + if (raw == null) { + return data; + } + data.type = stringValue(raw.get("type")); + data.id = stringValue(raw.get("id")); + data.sessionId = stringValue(raw.get("session_id")); + return data; + } + + private static String stringValue(Object value) { + return value == null ? "" : String.valueOf(value); + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getSessionId() { + return sessionId; + } + + public void setSessionId(String sessionId) { + this.sessionId = sessionId; + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/WorkItem.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/WorkItem.java new file mode 100644 index 0000000..2fea60f --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/WorkItem.java @@ -0,0 +1,92 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import java.util.Map; + +public class WorkItem { + private String id = ""; + private String environmentId = ""; + private WorkData data = new WorkData(); + private String latestHeartbeatAt = ""; + private String sessionId = ""; + private String state = ""; + private String lastHeartbeat = ""; + + @SuppressWarnings("unchecked") + public static WorkItem fromMap(Map raw) { + WorkItem item = new WorkItem(); + if (raw == null) { + return item; + } + item.id = stringValue(raw.get("id")); + item.environmentId = stringValue(raw.get("environment_id")); + item.latestHeartbeatAt = stringValue(raw.get("latest_heartbeat_at")); + item.sessionId = stringValue(raw.get("session_id")); + item.state = stringValue(raw.get("state")); + item.lastHeartbeat = stringValue(raw.get("last_heartbeat")); + if (raw.get("data") instanceof Map) { + item.data = WorkData.fromMap((Map) raw.get("data")); + } + return item; + } + + public String sessionIdValue() { + if (sessionId != null && !sessionId.isEmpty()) { + return sessionId; + } + if (data.getSessionId() != null && !data.getSessionId().isEmpty()) { + return data.getSessionId(); + } + if (data.getId() != null && !data.getId().isEmpty() + && (data.getType() == null || data.getType().isEmpty() || "session".equals(data.getType()))) { + return data.getId(); + } + return ""; + } + + public String latestHeartbeatValue() { + return latestHeartbeatAt != null && !latestHeartbeatAt.isEmpty() ? latestHeartbeatAt : lastHeartbeat; + } + + private static String stringValue(Object value) { + return value == null ? "" : String.valueOf(value); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getEnvironmentId() { + return environmentId; + } + + public void setEnvironmentId(String environmentId) { + this.environmentId = environmentId; + } + + public WorkData getData() { + return data; + } + + public void setData(WorkData data) { + this.data = data; + } + + public String getLatestHeartbeatAt() { + return latestHeartbeatAt; + } + + public void setLatestHeartbeatAt(String latestHeartbeatAt) { + this.latestHeartbeatAt = latestHeartbeatAt; + } + + public String getState() { + return state; + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/WorkPoller.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/WorkPoller.java new file mode 100644 index 0000000..ee5a5f9 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/WorkPoller.java @@ -0,0 +1,224 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import java.util.Random; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class WorkPoller implements AutoCloseable { + private static final long POLL_BACKOFF_CAP_MILLIS = 60000L; + + private final SelfHostedClient api; + private final Options options; + private final Random random = new Random(); + private WorkItem current; + private RuntimeException error; + private volatile boolean closed; + private Runnable pendingStop; + private int failures; + private int discards; + + public WorkPoller(SelfHostedClient api, Options options) { + if (api == null) { + throw new IllegalArgumentException("api is required"); + } + if (options == null || options.environmentId == null || options.environmentId.isEmpty()) { + throw new IllegalArgumentException("environment id is required"); + } + if (options.workerId == null || options.workerId.isEmpty()) { + options.workerId = EnvironmentWorker.defaultWorkerId(); + } + this.api = api; + this.options = options; + } + + public WorkItem next() { + runPendingStop(); + if (closed) { + return null; + } + while (!closed) { + WorkItem item; + try { + item = api.pollWork(options.environmentId, options.workerId, options.blockMs, options.reclaimOlderThanMs); + } catch (RuntimeException e) { + if (WorkerAPIException.isFatal4xx(e)) { + error = e; + return null; + } + failures++; + long sleepMillis = jitter(backoff(failures) / 2, backoff(failures)); + options.logger.warning( + "poll work failed err=" + e + " retry_in_ms=" + sleepMillis); + sleep(sleepMillis); + continue; + } + failures = 0; + if (item == null || item.getId().isEmpty()) { + if (options.drain) { + return null; + } + sleep(jitter(1000L, 3000L)); + continue; + } + if (item.getEnvironmentId() == null || item.getEnvironmentId().isEmpty()) { + item.setEnvironmentId(options.environmentId); + } + if (item.sessionIdValue().isEmpty()) { + options.logger.warning( + "discard invalid work work_id=" + item.getId() + " reason=missing session id"); + discardInvalidWork(item); + continue; + } + try { + api.ackWork(item.getEnvironmentId(), item.getId(), options.workerId); + } catch (RuntimeException e) { + options.logger.log(Level.WARNING, "ack work failed", e); + if (isResolvedStatus(e)) { + continue; + } + if (WorkerAPIException.isFatal4xx(e)) { + error = e; + return null; + } + backoffDiscard(); + continue; + } + current = item; + if (options.autoStop) { + pendingStop = () -> stopItem(item, false); + } + discards = 0; + options.logger.info("claimed work work_id=" + item.getId() + " session_id=" + item.sessionIdValue()); + return item; + } + return null; + } + + public WorkItem current() { + return current; + } + + public RuntimeException error() { + return error; + } + + @Override + public void close() { + closed = true; + runPendingStop(); + } + + private void runPendingStop() { + Runnable stop = pendingStop; + pendingStop = null; + current = null; + if (stop != null) { + stop.run(); + } + } + + private void discardInvalidWork(WorkItem item) { + try { + api.ackWork(item.getEnvironmentId(), item.getId(), options.workerId); + } catch (RuntimeException e) { + options.logger.log(Level.WARNING, "ack invalid work failed", e); + return; + } + stopItem(item, true); + backoffDiscard(); + } + + private void stopItem(WorkItem item, boolean force) { + try { + api.stopWork(item.getEnvironmentId(), item.getId(), force); + } catch (RuntimeException e) { + if (!isResolvedStatus(e)) { + options.logger.log(Level.WARNING, "stop work failed", e); + } + } + } + + private void backoffDiscard() { + discards++; + sleep(jitter(backoff(discards) / 2, backoff(discards))); + } + + private long backoff(int count) { + if (count > 6) { + return POLL_BACKOFF_CAP_MILLIS; + } + long value = 1L << Math.max(count - 1, 0); + return Math.min(POLL_BACKOFF_CAP_MILLIS, value * 1000L); + } + + private static boolean isResolvedStatus(Throwable error) { + return WorkerAPIException.isStatus(error, 404) + || WorkerAPIException.isStatus(error, 409) + || WorkerAPIException.isStatus(error, 412); + } + + private long jitter(long low, long high) { + if (high <= low) { + return Math.max(0L, high); + } + return low + Math.abs(random.nextLong()) % (high - low); + } + + private void sleep(long millis) { + try { + Thread.sleep(Math.max(1L, millis)); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + closed = true; + } + } + + public static class Options { + private String environmentId; + private String workerId = ""; + private int blockMs = SelfHostedConstants.DEFAULT_POLL_BLOCK_MILLIS; + private int reclaimOlderThanMs; + private boolean drain; + private boolean autoStop = true; + private Logger logger = Logger.getLogger("arkruntime.selfhosted.work_poller"); + + public Options(String environmentId) { + this.environmentId = environmentId; + } + + public Options workerId(String workerId) { + this.workerId = workerId; + return this; + } + + public Options blockMs(int blockMs) { + this.blockMs = blockMs; + return this; + } + + public Options reclaimOlderThanMs(int reclaimOlderThanMs) { + this.reclaimOlderThanMs = reclaimOlderThanMs; + return this; + } + + public Options drain(boolean drain) { + this.drain = drain; + return this; + } + + public Options autoStop(boolean autoStop) { + this.autoStop = autoStop; + return this; + } + + public Options logger(Logger logger) { + if (logger != null) { + this.logger = logger; + } + return this; + } + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/selfhosted/WorkerAPIException.java b/src/main/java/com/volcengine/ark/runtime/selfhosted/WorkerAPIException.java new file mode 100644 index 0000000..dd98811 --- /dev/null +++ b/src/main/java/com/volcengine/ark/runtime/selfhosted/WorkerAPIException.java @@ -0,0 +1,35 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +public class WorkerAPIException extends RuntimeException { + private final int statusCode; + private final String requestId; + + public WorkerAPIException(int statusCode, String message, String requestId) { + super("worker api status " + statusCode + ": " + message); + this.statusCode = statusCode; + this.requestId = requestId == null ? "" : requestId; + } + + public int getStatusCode() { + return statusCode; + } + + public String getRequestId() { + return requestId; + } + + public static boolean isStatus(Throwable t, int statusCode) { + return t instanceof WorkerAPIException && ((WorkerAPIException) t).getStatusCode() == statusCode; + } + + public static boolean isFatal4xx(Throwable t) { + if (!(t instanceof WorkerAPIException)) { + return false; + } + int status = ((WorkerAPIException) t).getStatusCode(); + return status >= 400 && status < 500 && status != 408 && status != 409 && status != 412 && status != 429; + } +} diff --git a/src/main/java/com/volcengine/ark/runtime/service/ArkApi.java b/src/main/java/com/volcengine/ark/runtime/service/ArkApi.java index 80626be..e5af3ef 100644 --- a/src/main/java/com/volcengine/ark/runtime/service/ArkApi.java +++ b/src/main/java/com/volcengine/ark/runtime/service/ArkApi.java @@ -20,8 +20,12 @@ import com.volcengine.ark.runtime.models.environment.CreateEnvironmentRequest; import com.volcengine.ark.runtime.models.environment.DeleteEnvironmentResponse; import com.volcengine.ark.runtime.models.environment.Environment; +import com.volcengine.ark.runtime.models.environment.EnvironmentWorkPoll200Response; +import com.volcengine.ark.runtime.models.environment.HeartbeatWorkResponse; import com.volcengine.ark.runtime.models.environment.ListEnvironmentsResponse; +import com.volcengine.ark.runtime.models.environment.StopWorkBody; import com.volcengine.ark.runtime.models.environment.UpdateEnvironmentRequest; +import com.volcengine.ark.runtime.models.environment.WorkItem; import com.volcengine.ark.runtime.models.file.FileDeleted; import com.volcengine.ark.runtime.models.file.FileListResponse; import com.volcengine.ark.runtime.models.file.FileObject; @@ -201,6 +205,34 @@ Single listFiles(@Query("limit") Integer limit, @DELETE("/api/v3/environments/{environmentId}") Single deleteEnvironment(@Path("environmentId") String environmentId, @HeaderMap Map customHeaders); + @GET("/api/v3/environments/{environmentId}/work/poll") + Single pollEnvironmentWork( + @Path("environmentId") String environmentId, + @Query("block_ms") Integer blockMs, + @Query("reclaim_older_than_ms") Integer reclaimOlderThanMs, + @HeaderMap Map customHeaders); + + @POST("/api/v3/environments/{environmentId}/work/{workId}/ack") + Single ackEnvironmentWork( + @Path("environmentId") String environmentId, + @Path("workId") String workId, + @HeaderMap Map customHeaders); + + @POST("/api/v3/environments/{environmentId}/work/{workId}/heartbeat") + Single heartbeatEnvironmentWork( + @Path("environmentId") String environmentId, + @Path("workId") String workId, + @Query("expected_last_heartbeat") String expectedLastHeartbeat, + @Query("desired_ttl_seconds") Integer desiredTTLSeconds, + @HeaderMap Map customHeaders); + + @POST("/api/v3/environments/{environmentId}/work/{workId}/stop") + Single stopEnvironmentWork( + @Path("environmentId") String environmentId, + @Path("workId") String workId, + @Body StopWorkBody body, + @HeaderMap Map customHeaders); + // ---- Agent ---- @POST("/api/v3/agents") Single createAgent(@Body CreateAgentRequest request, @HeaderMap Map customHeaders); @@ -311,6 +343,13 @@ Single createSkill(@Part MultipartBody.Part files, @GET("/api/v3/skills/{skillId}") Single getSkill(@Path("skillId") String skillId, @HeaderMap Map customHeaders); + @Streaming + @GET("/api/v3/skills/{skillId}/versions/{version}/content") + Call openSkillContent( + @Path("skillId") String skillId, + @Path("version") String version, + @HeaderMap Map customHeaders); + // ---- Session ---- @POST("/api/v3/sessions") Single createSession(@Body CreateSessionRequest request, @HeaderMap Map customHeaders); diff --git a/src/main/java/com/volcengine/ark/runtime/service/ArkService.java b/src/main/java/com/volcengine/ark/runtime/service/ArkService.java index ab16457..779b936 100644 --- a/src/main/java/com/volcengine/ark/runtime/service/ArkService.java +++ b/src/main/java/com/volcengine/ark/runtime/service/ArkService.java @@ -31,8 +31,12 @@ import com.volcengine.ark.runtime.models.environment.CreateEnvironmentRequest; import com.volcengine.ark.runtime.models.environment.DeleteEnvironmentResponse; import com.volcengine.ark.runtime.models.environment.Environment; +import com.volcengine.ark.runtime.models.environment.EnvironmentWorkPoll200Response; +import com.volcengine.ark.runtime.models.environment.HeartbeatWorkResponse; import com.volcengine.ark.runtime.models.environment.ListEnvironmentsResponse; +import com.volcengine.ark.runtime.models.environment.StopWorkBody; import com.volcengine.ark.runtime.models.environment.UpdateEnvironmentRequest; +import com.volcengine.ark.runtime.models.environment.WorkItem; import com.volcengine.ark.runtime.models.file.FileCreateRequest; import com.volcengine.ark.runtime.models.file.FileDeleted; import com.volcengine.ark.runtime.models.file.FileListRequest; @@ -624,6 +628,33 @@ public DeleteEnvironmentResponse deleteEnvironment(String environmentId) { return execute(api.deleteEnvironment(environmentId, new HashMap<>())); } + public EnvironmentWorkPoll200Response pollEnvironmentWork( + String environmentId, String workerId, Integer blockMs, Integer reclaimOlderThanMs) { + Map headers = new HashMap<>(); + if (workerId != null && !workerId.isEmpty()) { + headers.put("Ark-Worker-ID", workerId); + } + return execute(api.pollEnvironmentWork(environmentId, blockMs, reclaimOlderThanMs, headers)); + } + + public WorkItem ackEnvironmentWork(String environmentId, String workId, String workerId) { + Map headers = new HashMap<>(); + if (workerId != null && !workerId.isEmpty()) { + headers.put("Ark-Worker-ID", workerId); + } + return execute(api.ackEnvironmentWork(environmentId, workId, headers)); + } + + public HeartbeatWorkResponse heartbeatEnvironmentWork( + String environmentId, String workId, String expectedLastHeartbeat, Integer desiredTTLSeconds) { + return execute(api.heartbeatEnvironmentWork( + environmentId, workId, expectedLastHeartbeat, desiredTTLSeconds, new HashMap<>())); + } + + public WorkItem stopEnvironmentWork(String environmentId, String workId, StopWorkBody body) { + return execute(api.stopEnvironmentWork(environmentId, workId, body, new HashMap<>())); + } + // ---- Agent ---- public Agent createAgent(CreateAgentRequest request) { return execute(api.createAgent(request, new HashMap<>())); diff --git a/src/test/java/com/volcengine/ark/runtime/selfhosted/DefaultToolsTest.java b/src/test/java/com/volcengine/ark/runtime/selfhosted/DefaultToolsTest.java new file mode 100644 index 0000000..de93212 --- /dev/null +++ b/src/test/java/com/volcengine/ark/runtime/selfhosted/DefaultToolsTest.java @@ -0,0 +1,152 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import org.junit.Test; + +public class DefaultToolsTest { + @Test + public void bashScrubsSensitiveExplicitEnvironment() throws Exception { + Path workdir = Files.createTempDirectory("ark-java-tools-"); + ToolContext context = new ToolContext(workdir.toString()); + Map env = new LinkedHashMap<>(); + env.put(envName("ARK", "API", "KEY"), "redacted"); + env.put("SAFE_VALUE", "ok"); + context.setEnv(env); + Map input = new LinkedHashMap<>(); + input.put( + "command", + "printf '%s/%s/%s' \"${ARK_API_KEY-unset}\" \"${HOME-unset}\" \"$SAFE_VALUE\""); + + ToolResult result = new DefaultTools.BashTool().execute(input, context); + + assertFalse(result.isError()); + assertEquals("unset/unset/ok", result.getContent().get(0).getText()); + } + + @Test + public void bashScrubsExtendedCredentialNames() { + assertTrue(DefaultTools.isSensitiveEnvKey(envName("AIME", "SESSION"))); + assertTrue(DefaultTools.isSensitiveEnvKey(envName("X", "CODE", "AUTH"))); + assertTrue(DefaultTools.isSensitiveEnvKey(envName("GITHUB", "JWT"))); + assertTrue(DefaultTools.isSensitiveEnvKey(envName("GITHUB", "PAT"))); + assertFalse(DefaultTools.isSensitiveEnvKey("SAFE_VALUE")); + } + + @Test + public void bashTimeoutReturnsPromptly() throws Exception { + Path workdir = Files.createTempDirectory("ark-java-timeout-"); + ToolContext context = new ToolContext(workdir.toString()); + context.setToolTimeoutMillis(100L); + Map input = new LinkedHashMap<>(); + input.put("command", "sleep 10"); + + long started = System.nanoTime(); + ToolResult result = new DefaultTools.BashTool().execute(input, context); + + assertTrue(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - started) < 3000L); + assertTrue(result.isError()); + assertTrue(result.getContent().get(0).getText().contains("timed out")); + } + + @Test + public void fileToolRejectsSymlinkEscape() throws Exception { + Path workdir = Files.createTempDirectory("ark-java-root-"); + Path outside = Files.createTempDirectory("ark-java-outside-"); + Files.write(outside.resolve("secret.txt"), "secret".getBytes(java.nio.charset.StandardCharsets.UTF_8)); + Files.createSymbolicLink(workdir.resolve("escape"), outside); + ToolContext context = new ToolContext(workdir.toString()); + Map input = new LinkedHashMap<>(); + input.put("path", "escape/secret.txt"); + + ToolResult result = new DefaultTools.ReadTool().execute(input, context); + + assertTrue(result.isError()); + assertTrue(result.getContent().get(0).getText().contains("escapes workdir")); + } + + @Test + public void grepSkipsSymlinkThatEscapesWorkdir() throws Exception { + Path workdir = Files.createTempDirectory("ark-java-grep-root-"); + Path outside = Files.createTempDirectory("ark-java-grep-outside-"); + Files.write( + outside.resolve("secret.txt"), + "SELFHOST_SECRET_MARKER\n".getBytes(java.nio.charset.StandardCharsets.UTF_8)); + Files.createSymbolicLink(workdir.resolve("escape.txt"), outside.resolve("secret.txt")); + ToolContext context = new ToolContext(workdir.toString()); + Map input = new LinkedHashMap<>(); + input.put("path", "."); + input.put("pattern", "SELFHOST_SECRET_MARKER"); + + ToolResult result = new DefaultTools.GrepTool().execute(input, context); + + assertFalse(result.isError()); + assertFalse(result.getContent().get(0).getText().contains("SELFHOST_SECRET_MARKER")); + } + + @Test + public void writeKeepsExistingTargetWhenAtomicReplaceFails() throws Exception { + Path workdir = Files.createTempDirectory("ark-java-write-"); + Path target = Files.createDirectory(workdir.resolve("example.txt")); + Files.write(target.resolve("marker.txt"), "old".getBytes(java.nio.charset.StandardCharsets.UTF_8)); + ToolContext context = new ToolContext(workdir.toString()); + Map input = new LinkedHashMap<>(); + input.put("path", "example.txt"); + input.put("content", "new"); + + ToolResult result = new DefaultTools.WriteTool().execute(input, context); + + assertTrue(result.isError()); + assertEquals( + "old", + new String( + Files.readAllBytes(target.resolve("marker.txt")), + java.nio.charset.StandardCharsets.UTF_8)); + } + + @Test + public void editRejectsEmptyOldString() throws Exception { + Path workdir = Files.createTempDirectory("ark-java-edit-"); + Path file = workdir.resolve("example.txt"); + Files.write(file, "original".getBytes(java.nio.charset.StandardCharsets.UTF_8)); + ToolContext context = new ToolContext(workdir.toString()); + Map input = new LinkedHashMap<>(); + input.put("path", "example.txt"); + input.put("new_string", "unexpected"); + + ToolResult result = new DefaultTools.EditTool().execute(input, context); + + assertTrue(result.isError()); + assertTrue(result.getContent().get(0).getText().contains("old_string is required")); + assertEquals("original", new String( + Files.readAllBytes(file), java.nio.charset.StandardCharsets.UTF_8)); + } + + @Test + public void bashDrainsAndBoundsLargeOutput() throws Exception { + Path workdir = Files.createTempDirectory("ark-java-output-"); + ToolContext context = new ToolContext(workdir.toString()); + Map input = new LinkedHashMap<>(); + input.put("command", "yes x | head -c 200000"); + + ToolResult result = new DefaultTools.BashTool().execute(input, context); + + assertFalse(result.isError()); + assertTrue(result.getContent().get(0).getText().length() <= 100000); + assertTrue(result.getContent().get(0).getText().contains("truncated")); + } + + private static String envName(String... parts) { + return String.join("_", parts); + } +} diff --git a/src/test/java/com/volcengine/ark/runtime/selfhosted/EnvironmentWorkerTest.java b/src/test/java/com/volcengine/ark/runtime/selfhosted/EnvironmentWorkerTest.java new file mode 100644 index 0000000..5d4fd2c --- /dev/null +++ b/src/test/java/com/volcengine/ark/runtime/selfhosted/EnvironmentWorkerTest.java @@ -0,0 +1,184 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.nio.file.Files; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.junit.Test; + +public class EnvironmentWorkerTest { + @Test + public void emptyHeartbeatResponseDoesNotSpin() throws Exception { + NullHeartbeatClient client = new NullHeartbeatClient(); + EnvironmentWorker worker = new EnvironmentWorker( + client, + new EnvironmentWorker.Options().workdir(Files.createTempDirectory("ark-java-worker-").toString())); + + try { + worker.handleItem(new EnvironmentWorker.HandleItemOptions() + .environmentId("env-1") + .workId("work-1") + .sessionId("session-1")); + } catch (IOException expected) { + assertTrue(expected.getMessage().contains("session response is empty")); + } + + assertEquals(1, client.heartbeats.get()); + } + + @Test + public void heartbeatStopCancelsRunnerBeforeEventPolling() throws Exception { + CountDownLatch heartbeat = new CountDownLatch(1); + AtomicInteger lists = new AtomicInteger(); + AtomicInteger stops = new AtomicInteger(); + OkHttpClient http = new OkHttpClient.Builder().addInterceptor(chain -> { + Request request = chain.request(); + String path = request.url().encodedPath(); + if (path.endsWith("/heartbeat")) { + heartbeat.countDown(); + return response(request, "{\"state\":\"stopping\",\"lease_extended\":true,\"ttl_seconds\":30}"); + } + if (path.endsWith("/sessions/session-1")) { + try { + assertTrue(heartbeat.await(2, TimeUnit.SECONDS)); + } catch (InterruptedException error) { + Thread.currentThread().interrupt(); + throw new IOException(error); + } + return response(request, "{\"id\":\"session-1\"}"); + } + if (path.endsWith("/events")) { + lists.incrementAndGet(); + return response(request, "{\"data\":[]}"); + } + if (path.endsWith("/stop")) { + stops.incrementAndGet(); + } + return response(request, "{}"); + }).build(); + SelfHostedClient client = new SelfHostedClient.Builder() + .apiKey("test-key") + .baseUrl("https://ark.example.com/api/v3") + .httpClient(http) + .build(); + EnvironmentWorker worker = new EnvironmentWorker( + client, + new EnvironmentWorker.Options().workdir(Files.createTempDirectory("ark-java-worker-").toString())); + + long started = System.nanoTime(); + worker.handleItem(new EnvironmentWorker.HandleItemOptions() + .environmentId("env-1") + .workId("work-1") + .sessionId("session-1")); + + assertTrue(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - started) < 1000L); + assertEquals(0, lists.get()); + assertEquals(1, stops.get()); + } + + @Test + public void leaseLostDoesNotStopWorkOwnedByAnotherWorker() throws Exception { + LeaseLostClient client = new LeaseLostClient(); + EnvironmentWorker worker = new EnvironmentWorker( + client, + new EnvironmentWorker.Options() + .workdir(Files.createTempDirectory("ark-java-worker-").toString())); + + worker.handleItem(new EnvironmentWorker.HandleItemOptions() + .environmentId("env-1") + .workId("work-1") + .sessionId("session-1")); + + assertEquals(0, client.stops.get()); + } + + private static Response response(Request request, String body) throws IOException { + return new Response.Builder() + .request(request) + .protocol(Protocol.HTTP_1_1) + .code(200) + .message("OK") + .body(ResponseBody.create(MediaType.parse("application/json"), body)) + .build(); + } + + private static class NullHeartbeatClient extends SelfHostedClient { + private final CountDownLatch firstHeartbeat = new CountDownLatch(1); + private final AtomicInteger heartbeats = new AtomicInteger(); + + NullHeartbeatClient() { + super("test-key"); + } + + @Override + public HeartbeatResponse heartbeatWork( + String environmentId, String workId, String expectedLastHeartbeat, int desiredTTLSeconds) { + heartbeats.incrementAndGet(); + firstHeartbeat.countDown(); + return null; + } + + @Override + public SessionSnapshot getSession(String sessionId) { + try { + assertTrue(firstHeartbeat.await(2, TimeUnit.SECONDS)); + Thread.sleep(100L); + } catch (InterruptedException error) { + Thread.currentThread().interrupt(); + throw new RuntimeException(error); + } + return null; + } + + @Override + public void stopWork(String environmentId, String workId, boolean force) { + } + } + + private static class LeaseLostClient extends SelfHostedClient { + private final CountDownLatch heartbeat = new CountDownLatch(1); + private final AtomicInteger stops = new AtomicInteger(); + + LeaseLostClient() { + super("test-key"); + } + + @Override + public HeartbeatResponse heartbeatWork( + String environmentId, String workId, String expectedLastHeartbeat, int desiredTTLSeconds) { + heartbeat.countDown(); + throw new WorkerAPIException(412, "lease lost", ""); + } + + @Override + public SessionSnapshot getSession(String sessionId) { + try { + assertTrue(heartbeat.await(2, TimeUnit.SECONDS)); + } catch (InterruptedException error) { + Thread.currentThread().interrupt(); + throw new RuntimeException(error); + } + SessionSnapshot session = new SessionSnapshot(); + session.setId(sessionId); + return session; + } + + @Override + public void stopWork(String environmentId, String workId, boolean force) { + stops.incrementAndGet(); + } + } +} diff --git a/src/test/java/com/volcengine/ark/runtime/selfhosted/FileToolResultStoreTest.java b/src/test/java/com/volcengine/ark/runtime/selfhosted/FileToolResultStoreTest.java new file mode 100644 index 0000000..a2b36c1 --- /dev/null +++ b/src/test/java/com/volcengine/ark/runtime/selfhosted/FileToolResultStoreTest.java @@ -0,0 +1,44 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.LinkedHashMap; +import java.util.Map; +import org.junit.Test; + +public class FileToolResultStoreTest { + @Test + public void recoveryUsesPersistedCallId() throws Exception { + FileToolResultStore store = new FileToolResultStore(Files.createTempDirectory("ark-java-store-").toString()); + Map raw = new LinkedHashMap<>(); + raw.put("id", "event-1"); + raw.put("type", "agent.tool_use"); + raw.put("name", "bash"); + store.begin("call-1", Event.fromMap(raw)); + + FileToolResultStore.RecoverResult recovered = store.recover(); + + assertEquals("call-1", recovered.getPending().get("call-1").resultCallId()); + } + + @Test + public void recoveryRemovesStaleTemporaryRecords() throws Exception { + Path workdir = Files.createTempDirectory("ark-java-store-"); + FileToolResultStore store = new FileToolResultStore(workdir.toString()); + Path stale = workdir + .resolve(".ma_self_host_worker") + .resolve("tool_ledger") + .resolve(".tool-result-stale.tmp"); + Files.write(stale, "partial".getBytes(java.nio.charset.StandardCharsets.UTF_8)); + + store.recover(); + + assertFalse(Files.exists(stale)); + } +} diff --git a/src/test/java/com/volcengine/ark/runtime/selfhosted/InitializerTest.java b/src/test/java/com/volcengine/ark/runtime/selfhosted/InitializerTest.java new file mode 100644 index 0000000..b6bcbfe --- /dev/null +++ b/src/test/java/com/volcengine/ark/runtime/selfhosted/InitializerTest.java @@ -0,0 +1,130 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.junit.Test; + +public class InitializerTest { + @Test + public void constructorRejectsMissingDependenciesAndWorkdir() { + SelfHostedClient client = new SelfHostedClient("test-key"); + + assertInvalidInitializer(() -> new Initializer(null, new Initializer.Options("/tmp")), "api"); + assertInvalidInitializer(() -> new Initializer(client, null), "options"); + assertInvalidInitializer(() -> new Initializer(client, new Initializer.Options(" ")), "workdir"); + } + + @Test + public void zipArchiveEntryLimitIsEnforced() throws Exception { + byte[] archive = zipWithTwoEntries(); + OkHttpClient http = new OkHttpClient.Builder().addInterceptor(chain -> { + Request request = chain.request(); + if (request.url().encodedPath().equals("/api/v3/skills/skill-1")) { + return response( + request, + MediaType.parse("application/json"), + ("{\"id\":\"skill-1\",\"object\":\"skill\",\"created_at\":1," + + "\"name\":\"demo\",\"latest_version\":\"1\"}") + .getBytes(java.nio.charset.StandardCharsets.UTF_8)); + } + return response(request, MediaType.parse("application/zip"), archive); + }).build(); + SelfHostedClient client = new SelfHostedClient.Builder() + .apiKey("test-key") + .baseUrl("https://ark.example.com/api/v3") + .httpClient(http) + .build(); + Map raw = new LinkedHashMap<>(); + raw.put("skill_id", "skill-1"); + raw.put("version", "1"); + Initializer initializer = new Initializer( + client, + new Initializer.Options(Files.createTempDirectory("ark-java-skill-").toString()) + .maxArchiveEntries(1)); + + try { + initializer.installSkill("session-1", SkillRef.fromMap(raw)); + } catch (IOException error) { + assertTrue(error.getMessage().contains("too many entries")); + return; + } + throw new AssertionError("expected archive entry limit failure"); + } + + @Test + public void replaceSkillRollsBackOldVersionWhenCommitFails() throws Exception { + Path root = Files.createTempDirectory("ark-java-skill-rollback-"); + Path source = root.resolve("missing-new-skill"); + Path target = Files.createDirectory(root.resolve("installed-skill")); + Files.write( + target.resolve("marker.txt"), + "old".getBytes(java.nio.charset.StandardCharsets.UTF_8)); + Method replace = Initializer.class.getDeclaredMethod("replaceSkillDir", Path.class, Path.class); + replace.setAccessible(true); + + try { + replace.invoke(null, source, target); + } catch (InvocationTargetException error) { + assertTrue(error.getCause() instanceof IOException); + } + + assertEquals( + "old", + new String( + Files.readAllBytes(target.resolve("marker.txt")), + java.nio.charset.StandardCharsets.UTF_8)); + } + + private static byte[] zipWithTwoEntries() throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + try (ZipOutputStream zip = new ZipOutputStream(output)) { + zip.putNextEntry(new ZipEntry("one")); + zip.write('1'); + zip.closeEntry(); + zip.putNextEntry(new ZipEntry("two")); + zip.write('2'); + zip.closeEntry(); + } + return output.toByteArray(); + } + + private static void assertInvalidInitializer(Runnable create, String message) { + try { + create.run(); + } catch (IllegalArgumentException error) { + assertTrue(error.getMessage().contains(message)); + return; + } + throw new AssertionError("expected invalid initializer"); + } + + private static Response response(Request request, MediaType type, byte[] body) throws IOException { + return new Response.Builder() + .request(request) + .protocol(Protocol.HTTP_1_1) + .code(200) + .message("OK") + .body(ResponseBody.create(type, body)) + .build(); + } +} diff --git a/src/test/java/com/volcengine/ark/runtime/selfhosted/SelfHostedClientTest.java b/src/test/java/com/volcengine/ark/runtime/selfhosted/SelfHostedClientTest.java new file mode 100644 index 0000000..cb38813 --- /dev/null +++ b/src/test/java/com/volcengine/ark/runtime/selfhosted/SelfHostedClientTest.java @@ -0,0 +1,319 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpServer; +import com.volcengine.ark.runtime.interceptor.RetryInterceptor; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; +import okhttp3.HttpUrl; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okio.Buffer; +import org.junit.Test; + +public class SelfHostedClientTest { + @Test + public void usesProductionBaseURLByDefault() { + assertPollURL(null, "ark.cn-beijing.volces.com"); + } + + @Test + public void acceptsBaseURLOverride() { + assertPollURL("https://example.com/api/v3", "example.com"); + } + + @Test + public void preservesNestedSessionWorkData() { + String body = "{" + + "\"id\":\"work-1\"," + + "\"environment_id\":\"env-1\"," + + "\"data\":{\"id\":\"session-1\",\"type\":\"session\"}" + + "}"; + OkHttpClient httpClient = new OkHttpClient.Builder() + .addInterceptor(chain -> response(chain.request(), new AtomicReference<>(), body)) + .build(); + SelfHostedClient client = new SelfHostedClient.Builder() + .apiKey("test-api-key") + .httpClient(httpClient) + .build(); + + WorkItem item = client.pollWork("env-1", "worker-1", 999, 0); + + assertNotNull(item); + assertEquals("work-1", item.getId()); + assertEquals("env-1", item.getEnvironmentId()); + assertEquals("session-1", item.getData().getId()); + assertEquals("session-1", item.sessionIdValue()); + } + + @Test + public void opensSkillHubFromMetadataAndVersionedDownload() throws Exception { + HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0); + server.createContext("/v1/skills/download/volcengine/ark/demo", exchange -> { + assertNull(exchange.getRequestHeaders().getFirst("Authorization")); + assertEquals("version=1.0.0", exchange.getRequestURI().getQuery()); + writeResponse(exchange, "application/zip", "skill-hub-zip"); + }); + server.createContext("/v1/skills", exchange -> { + assertNull(exchange.getRequestHeaders().getFirst("Authorization")); + assertEquals("skillIds=skill-1", exchange.getRequestURI().getQuery()); + writeResponse( + exchange, + "application/json", + "{\"Skills\":[{\"Id\":\"other-skill\",\"Slug\":\"wrong/slug\"}," + + "{\"Id\":\"skill-1\",\"Slug\":\"volcengine/ark/demo\"}],\"Total\":2}"); + }); + server.start(); + try { + OkHttpClient httpClient = new OkHttpClient.Builder() + .addInterceptor(chain -> chain.proceed(chain.request().newBuilder() + .header("Authorization", "Bearer test-api-key") + .build())) + .build(); + SelfHostedClient client = new SelfHostedClient.Builder() + .apiKey("test-api-key") + .httpClient(httpClient) + .skillHubBaseUrl("http://127.0.0.1:" + server.getAddress().getPort() + "/v1/skills") + .build(); + Map raw = new LinkedHashMap<>(); + raw.put("type", "skill_hub"); + raw.put("skill_id", "skill-1"); + raw.put("display_name", "demo"); + raw.put("version", "1.0.0"); + SkillRef skill = SkillRef.fromMap(raw); + + try (SkillContent content = client.openSkill("session-1", skill)) { + byte[] data = readAll(content); + assertEquals("skill-hub-zip", new String(data, StandardCharsets.UTF_8)); + assertEquals("demo", skill.nameValue()); + assertEquals("skill_hub", skill.getType()); + } + } finally { + server.stop(0); + } + } + + @Test + public void resolvesSkillFromControlPlaneMetadata() { + AtomicReference requestedURL = new AtomicReference<>(); + String body = "{" + + "\"id\":\"skill-1\"," + + "\"object\":\"skill\"," + + "\"created_at\":1786506774," + + "\"name\":\"canonical-skill-name\"," + + "\"latest_version\":\"1.0.0\"" + + "}"; + OkHttpClient httpClient = new OkHttpClient.Builder() + .addInterceptor(chain -> response(chain.request(), requestedURL, body)) + .build(); + SelfHostedClient client = new SelfHostedClient.Builder() + .apiKey("test-api-key") + .baseUrl("https://ark.example.com/api/v3") + .httpClient(httpClient) + .build(); + Map raw = new LinkedHashMap<>(); + raw.put("skill_id", "skill-1"); + raw.put("type", "skill_hub"); + + SkillRef resolved = client.resolveSkill(SkillRef.fromMap(raw)); + + assertEquals("/api/v3/skills/skill-1", requestedURL.get().encodedPath()); + assertEquals("canonical-skill-name", resolved.getName()); + assertEquals("1.0.0", resolved.getVersion()); + assertEquals("skill_hub", resolved.getType()); + } + + @Test + public void gracefulStopSendsEmptyJSONBody() throws Exception { + AtomicReference body = new AtomicReference<>(); + OkHttpClient httpClient = new OkHttpClient.Builder() + .addInterceptor(chain -> { + Buffer buffer = new Buffer(); + chain.request().body().writeTo(buffer); + body.set(buffer.readUtf8()); + return response(chain.request(), new AtomicReference<>()); + }) + .build(); + SelfHostedClient client = new SelfHostedClient.Builder() + .apiKey("test-api-key") + .httpClient(httpClient) + .build(); + + client.stopWork("env-1", "work-1", false); + + assertEquals("{}", body.get()); + } + + @Test + public void heartbeatUsesOneLeaseBoundedAttempt() { + AtomicInteger calls = new AtomicInteger(); + AtomicLong timeoutSeconds = new AtomicLong(); + OkHttpClient httpClient = new OkHttpClient.Builder() + .addInterceptor(new RetryInterceptor(3)) + .addInterceptor(chain -> { + calls.incrementAndGet(); + timeoutSeconds.set( + TimeUnit.NANOSECONDS.toSeconds(chain.call().timeout().timeoutNanos())); + return new Response.Builder() + .request(chain.request()) + .protocol(Protocol.HTTP_1_1) + .code(500) + .message("error") + .body(ResponseBody.create(MediaType.parse("application/json"), "temporary")) + .build(); + }) + .build(); + SelfHostedClient client = new SelfHostedClient.Builder() + .apiKey("test-api-key") + .httpClient(httpClient) + .build(); + + try { + client.heartbeatWork("env-1", "work-1", "NO_HEARTBEAT", 30); + } catch (WorkerAPIException expected) { + assertEquals(500, expected.getStatusCode()); + } + + assertEquals(1, calls.get()); + assertEquals(15L, timeoutSeconds.get()); + } + + @Test + public void atomicEnvironmentWorkAPIMatchesOpenAPIContract() throws Exception { + AtomicInteger calls = new AtomicInteger(); + String work = "{" + + "\"id\":\"work-1\"," + + "\"created_at\":\"2026-08-24T10:00:00Z\"," + + "\"data\":{\"id\":\"session-1\",\"type\":\"session\"}," + + "\"environment_id\":\"env-1\"," + + "\"state\":\"active\"," + + "\"type\":\"work\"" + + "}"; + String heartbeat = "{" + + "\"last_heartbeat\":\"2026-08-24T10:00:01Z\"," + + "\"lease_extended\":true," + + "\"state\":\"active\"," + + "\"ttl_seconds\":30," + + "\"type\":\"work_heartbeat\"" + + "}"; + OkHttpClient httpClient = new OkHttpClient.Builder() + .addInterceptor(chain -> { + Request request = chain.request(); + int call = calls.getAndIncrement(); + if (call == 0) { + assertEquals("GET", request.method()); + assertEquals("/api/v3/environments/env-1/work/poll", request.url().encodedPath()); + assertEquals("999", request.url().queryParameter("block_ms")); + assertEquals("5000", request.url().queryParameter("reclaim_older_than_ms")); + assertEquals("worker-1", request.header("Ark-Worker-ID")); + return response(request, new AtomicReference<>(), work); + } + if (call == 1) { + assertEquals("POST", request.method()); + assertEquals("/api/v3/environments/env-1/work/work-1/ack", request.url().encodedPath()); + assertEquals("worker-1", request.header("Ark-Worker-ID")); + return response(request, new AtomicReference<>(), work); + } + if (call == 2) { + assertEquals("POST", request.method()); + assertEquals( + "/api/v3/environments/env-1/work/work-1/heartbeat", + request.url().encodedPath()); + assertEquals("NO_HEARTBEAT", request.url().queryParameter("expected_last_heartbeat")); + assertEquals("30", request.url().queryParameter("desired_ttl_seconds")); + return response(request, new AtomicReference<>(), heartbeat); + } + assertEquals("POST", request.method()); + assertEquals("/api/v3/environments/env-1/work/work-1/stop", request.url().encodedPath()); + Buffer buffer = new Buffer(); + request.body().writeTo(buffer); + assertEquals("{}", buffer.readUtf8()); + return response(request, new AtomicReference<>(), work); + }) + .build(); + SelfHostedClient client = new SelfHostedClient.Builder() + .apiKey("test-api-key") + .baseUrl("https://ark.example.com/api/v3") + .httpClient(httpClient) + .build(); + + assertEquals("work-1", client.pollWork("env-1", "worker-1", 999, 5000).getId()); + client.ackWork("env-1", "work-1", "worker-1"); + assertEquals( + "2026-08-24T10:00:01Z", + client.heartbeatWork("env-1", "work-1", "NO_HEARTBEAT", 30).getLastHeartbeat()); + client.stopWork("env-1", "work-1", false); + + assertEquals(4, calls.get()); + } + + private static void assertPollURL(String baseUrl, String expectedHost) { + AtomicReference requestedURL = new AtomicReference<>(); + OkHttpClient httpClient = new OkHttpClient.Builder() + .addInterceptor(chain -> response(chain.request(), requestedURL)) + .build(); + SelfHostedClient.Builder builder = new SelfHostedClient.Builder() + .apiKey("test-api-key") + .httpClient(httpClient); + if (baseUrl != null) { + builder.baseUrl(baseUrl); + } + + builder.build().pollWork("env-1", "worker-1", 999, 0); + + assertEquals(expectedHost, requestedURL.get().host()); + assertEquals("/api/v3/environments/env-1/work/poll", requestedURL.get().encodedPath()); + } + + private static Response response(Request request, AtomicReference requestedURL) throws IOException { + return response(request, requestedURL, "{}"); + } + + private static Response response( + Request request, + AtomicReference requestedURL, + String body) throws IOException { + requestedURL.set(request.url()); + return new Response.Builder() + .request(request) + .protocol(Protocol.HTTP_1_1) + .code(200) + .message("OK") + .body(ResponseBody.create(MediaType.parse("application/json"), body)) + .build(); + } + + private static void writeResponse(HttpExchange exchange, String contentType, String body) throws IOException { + byte[] data = body.getBytes(StandardCharsets.UTF_8); + exchange.getResponseHeaders().set("Content-Type", contentType); + exchange.sendResponseHeaders(200, data.length); + exchange.getResponseBody().write(data); + exchange.close(); + } + + private static byte[] readAll(SkillContent content) throws IOException { + byte[] buffer = new byte[1024]; + int size = content.getBody().read(buffer); + byte[] result = new byte[size]; + System.arraycopy(buffer, 0, result, 0, size); + return result; + } +} diff --git a/src/test/java/com/volcengine/ark/runtime/selfhosted/SessionToolRunnerTest.java b/src/test/java/com/volcengine/ark/runtime/selfhosted/SessionToolRunnerTest.java new file mode 100644 index 0000000..5694d2a --- /dev/null +++ b/src/test/java/com/volcengine/ark/runtime/selfhosted/SessionToolRunnerTest.java @@ -0,0 +1,244 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.junit.Test; + +public class SessionToolRunnerTest { + @Test + public void listFallbackRetriesFullHistoryAndConvertsCustomToolFailure() throws Exception { + AtomicInteger lists = new AtomicInteger(); + CountDownLatch sent = new CountDownLatch(1); + AtomicReference createdAt = new AtomicReference<>(); + AtomicReference limit = new AtomicReference<>(); + OkHttpClient http = new OkHttpClient.Builder().addInterceptor(chain -> { + Request request = chain.request(); + if (request.method().equals("GET") && request.url().encodedPath().endsWith("/events")) { + createdAt.set(request.url().queryParameter("created_at[gt]")); + limit.set(request.url().queryParameter("limit")); + if (lists.getAndIncrement() == 0) { + return response(request, 500, "temporary"); + } + return response( + request, + 200, + "{\"data\":[{\"id\":\"event-1\",\"type\":\"agent.custom_tool_use\"," + + "\"name\":\"custom\",\"custom_tool_use_id\":\"call-1\"," + + "\"session_thread_id\":\"thread-1\",\"input\":{}}]}"); + } + if (request.method().equals("POST") && request.url().encodedPath().endsWith("/events")) { + sent.countDown(); + } + return response(request, 200, "{}"); + }).build(); + SelfHostedClient client = new SelfHostedClient.Builder() + .apiKey("test-key") + .baseUrl("https://ark.example.com/api/v3") + .httpClient(http) + .build(); + Tool custom = new Tool() { + @Override + public String name() { + return "custom"; + } + + @Override + public ToolResult execute(Object input, ToolContext context) { + throw new IllegalStateException("custom tool failed"); + } + }; + SessionToolRunner runner = new SessionToolRunner( + client, + "session-1", + new SessionToolRunner.Options() + .tools(new ToolSet()) + .toolContext(new ToolContext(Files.createTempDirectory("ark-java-runner-").toString())) + .customTools(Collections.singletonMap("custom", custom)) + .preferStream(false) + .eventLimit(5000) + .eventPollIntervalMillis(10L)); + AtomicReference failure = new AtomicReference<>(); + Thread thread = new Thread(() -> { + try { + runner.run(); + } catch (Throwable error) { + failure.set(error); + } + }); + thread.start(); + + assertTrue(sent.await(3, TimeUnit.SECONDS)); + runner.close(); + thread.join(2000L); + + assertFalse(thread.isAlive()); + assertNull(failure.get()); + assertNull(createdAt.get()); + assertEquals("1000", limit.get()); + assertTrue(lists.get() >= 2); + assertEquals(1, runner.getResults().size()); + assertEquals(Boolean.TRUE, runner.getResults().get(0).getResult().toMap().get("is_error")); + assertEquals("custom tool failed", runner.getResults().get(0).getResult().getContent().get(0).getText()); + } + + @Test + public void constructorRequiresToolContext() { + try { + new SessionToolRunner( + new SelfHostedClient("test-key"), + "session-1", + new SessionToolRunner.Options().tools(new ToolSet())); + } catch (IllegalArgumentException error) { + assertTrue(error.getMessage().contains("tool context")); + return; + } + throw new AssertionError("expected missing tool context failure"); + } + + @Test + public void duplicateStreamIdleEventDoesNotResetIdleDeadline() throws Exception { + SessionToolRunner runner = idleRunner(); + Event event = idleEvent(); + Method handle = SessionToolRunner.class.getDeclaredMethod("handleStreamEvent", Event.class); + handle.setAccessible(true); + + handle.invoke(runner, event); + long armedAt = idleArmedAt(runner); + Thread.sleep(2L); + handle.invoke(runner, event); + + assertEquals(armedAt, idleArmedAt(runner)); + } + + @Test + public void reconcileDoesNotResetIdleDeadlineForSeenHistory() throws Exception { + SessionToolRunner runner = idleRunner(); + List events = new ArrayList<>(); + events.add(idleEvent()); + Method process = SessionToolRunner.class.getDeclaredMethod( + "processListedEvents", List.class, boolean.class); + process.setAccessible(true); + + process.invoke(runner, events, true); + long armedAt = idleArmedAt(runner); + Thread.sleep(2L); + process.invoke(runner, events, true); + + assertEquals(armedAt, idleArmedAt(runner)); + } + + @Test + public void successfulSendStaysAnsweredWhenMarkSentFails() throws Exception { + SelfHostedClient client = new SelfHostedClient("test-key") { + @Override + public void sendEvent(String sessionId, Event event) { + } + }; + FileToolResultStore store = new FileToolResultStore( + Files.createTempDirectory("ark-java-mark-sent-").toString()) { + @Override + public void markSent(String callId) throws IOException { + throw new IOException("ledger unavailable"); + } + }; + SessionToolRunner runner = new SessionToolRunner( + client, + "session-1", + new SessionToolRunner.Options() + .tools(new ToolSet()) + .toolContext(new ToolContext( + Files.createTempDirectory("ark-java-runner-").toString())) + .resultStore(store)); + Map sourceRaw = new LinkedHashMap<>(); + sourceRaw.put("id", "tool-1"); + sourceRaw.put("type", "agent.tool_use"); + sourceRaw.put("name", "bash"); + sourceRaw.put("tool_use_id", "call-1"); + Event source = Event.fromMap(sourceRaw); + Event result = Event.newUserToolResultEvent( + "call-1", Collections.singletonList(new ContentBlock("text", "ok")), false, ""); + Method send = SessionToolRunner.class.getDeclaredMethod( + "sendResult", String.class, Event.class, boolean.class, String.class, Event.class); + send.setAccessible(true); + + send.invoke(runner, "call-1", source, false, "", result); + + assertTrue(answered(runner).containsKey("call-1")); + assertEquals(1, runner.getResults().size()); + assertTrue(runner.getResults().get(0).isPosted()); + } + + private static SessionToolRunner idleRunner() throws IOException { + return new SessionToolRunner( + new SelfHostedClient("test-key"), + "session-1", + new SessionToolRunner.Options() + .tools(new ToolSet()) + .toolContext(new ToolContext( + Files.createTempDirectory("ark-java-idle-").toString()))); + } + + private static Event idleEvent() { + Map stopReason = new LinkedHashMap<>(); + stopReason.put("type", "end_turn"); + Map raw = new LinkedHashMap<>(); + raw.put("id", "idle-1"); + raw.put("type", "session.status_idle"); + raw.put("stop_reason", stopReason); + return Event.fromMap(raw); + } + + private static long idleArmedAt(SessionToolRunner runner) throws Exception { + Field stateField = SessionToolRunner.class.getDeclaredField("state"); + stateField.setAccessible(true); + Object state = stateField.get(runner); + Field idleField = state.getClass().getDeclaredField("idleArmedAt"); + idleField.setAccessible(true); + return idleField.getLong(state); + } + + @SuppressWarnings("unchecked") + private static Map answered(SessionToolRunner runner) throws Exception { + Field stateField = SessionToolRunner.class.getDeclaredField("state"); + stateField.setAccessible(true); + Object state = stateField.get(runner); + Field answeredField = state.getClass().getDeclaredField("answered"); + answeredField.setAccessible(true); + return (Map) answeredField.get(state); + } + + private static Response response(Request request, int code, String body) throws IOException { + return new Response.Builder() + .request(request) + .protocol(Protocol.HTTP_1_1) + .code(code) + .message(code < 400 ? "OK" : "error") + .body(ResponseBody.create(MediaType.parse("application/json"), body)) + .build(); + } +} diff --git a/src/test/java/com/volcengine/ark/runtime/selfhosted/WorkPollerTest.java b/src/test/java/com/volcengine/ark/runtime/selfhosted/WorkPollerTest.java new file mode 100644 index 0000000..54c38ef --- /dev/null +++ b/src/test/java/com/volcengine/ark/runtime/selfhosted/WorkPollerTest.java @@ -0,0 +1,130 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package com.volcengine.ark.runtime.selfhosted; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicInteger; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.junit.Test; + +public class WorkPollerTest { + @Test + public void leaseAndRetryStatusesAreNotFatal() { + int[] statuses = {408, 409, 412, 429}; + for (int status : statuses) { + assertFalse(WorkerAPIException.isFatal4xx(new WorkerAPIException(status, "recoverable", ""))); + } + } + + @Test + public void ackConflictDoesNotStopUnownedWork() { + AtomicInteger polls = new AtomicInteger(); + AtomicInteger stops = new AtomicInteger(); + SelfHostedClient client = client(chain -> { + Request request = chain.request(); + String path = request.url().encodedPath(); + if (path.endsWith("/work/poll")) { + String body = polls.getAndIncrement() == 0 + ? "{\"id\":\"work-1\",\"environment_id\":\"env-1\"," + + "\"data\":{\"id\":\"session-1\",\"type\":\"session\"}}" + : "{}"; + return response(request, 200, body); + } + if (path.endsWith("/ack")) { + return response(request, 409, "already claimed"); + } + if (path.endsWith("/stop")) { + stops.incrementAndGet(); + } + return response(request, 200, "{}"); + }); + WorkPoller poller = new WorkPoller(client, new WorkPoller.Options("env-1").drain(true)); + + assertNull(poller.next()); + assertNull(poller.error()); + assertEquals(0, stops.get()); + } + + @Test + public void fatalAckErrorStopsPollerWithoutStoppingWork() { + AtomicInteger stops = new AtomicInteger(); + SelfHostedClient client = client(chain -> { + Request request = chain.request(); + String path = request.url().encodedPath(); + if (path.endsWith("/work/poll")) { + return response( + request, + 200, + "{\"id\":\"work-1\",\"environment_id\":\"env-1\"," + + "\"data\":{\"id\":\"session-1\",\"type\":\"session\"}}"); + } + if (path.endsWith("/ack")) { + return response(request, 403, "forbidden"); + } + if (path.endsWith("/stop")) { + stops.incrementAndGet(); + } + return response(request, 200, "{}"); + }); + WorkPoller poller = new WorkPoller(client, new WorkPoller.Options("env-1").drain(true)); + + assertNull(poller.next()); + assertEquals(403, ((WorkerAPIException) poller.error()).getStatusCode()); + assertEquals(0, stops.get()); + } + + @Test + public void autoStopCanBeDisabledForEnvironmentWorkerOwnership() { + AtomicInteger stops = new AtomicInteger(); + SelfHostedClient client = client(chain -> { + Request request = chain.request(); + if (request.url().encodedPath().endsWith("/work/poll")) { + return response( + request, + 200, + "{\"id\":\"work-1\",\"environment_id\":\"env-1\"," + + "\"data\":{\"id\":\"session-1\",\"type\":\"session\"}}"); + } + if (request.url().encodedPath().endsWith("/stop")) { + stops.incrementAndGet(); + } + return response(request, 200, "{}"); + }); + WorkPoller poller = new WorkPoller( + client, new WorkPoller.Options("env-1").autoStop(false)); + + assertNotNull(poller.next()); + poller.close(); + + assertEquals(0, stops.get()); + } + + private static SelfHostedClient client(okhttp3.Interceptor interceptor) { + return new SelfHostedClient.Builder() + .apiKey("test-key") + .baseUrl("https://ark.example.com/api/v3") + .httpClient(new OkHttpClient.Builder().addInterceptor(interceptor).build()) + .build(); + } + + private static Response response(Request request, int code, String body) throws IOException { + return new Response.Builder() + .request(request) + .protocol(Protocol.HTTP_1_1) + .code(code) + .message(code < 400 ? "OK" : "error") + .body(ResponseBody.create(MediaType.parse("application/json"), body)) + .build(); + } +}