Skip to content
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/code-interpreter/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ async def main() -> None:
if ts_exec.error:
print(f"[TypeScript error] {ts_exec.error.name}: {ts_exec.error.value}")

await interpreter.kill()
await sandbox.kill()


if __name__ == "__main__":
Expand Down
2 changes: 1 addition & 1 deletion sdks/code-interpreter/kotlin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public class QuickStart {

// 7. Cleanup
// Note: kill() terminates the remote instance; close() (auto-called) cleans up local resources
interpreter.kill();
sandbox.kill();
} catch (SandboxException e) {
// Handle Sandbox specific exceptions
System.err.println("Sandbox Error: [" + e.getError().getCode() + "] " + e.getError().getMessage());
Expand Down
2 changes: 1 addition & 1 deletion sdks/code-interpreter/kotlin/README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public class QuickStart {

// 7. 清理资源
// 注意: kill() 会立即终止远程沙箱实例;try-with-resources 会自动调用 close() 清理本地资源
interpreter.kill();
sandbox.kill();
} catch (SandboxException e) {
// 处理 Sandbox 特定异常
System.err.println("沙箱错误: [" + e.getError().getCode() + "] " + e.getError().getMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,7 @@ import com.alibaba.opensandbox.sandbox.domain.exceptions.InvalidArgumentExceptio
import com.alibaba.opensandbox.sandbox.domain.exceptions.SandboxException
import com.alibaba.opensandbox.sandbox.domain.exceptions.SandboxInternalException
import com.alibaba.opensandbox.sandbox.domain.models.execd.DEFAULT_EXECD_PORT
import com.alibaba.opensandbox.sandbox.domain.models.sandboxes.SandboxEndpoint
import com.alibaba.opensandbox.sandbox.domain.models.sandboxes.SandboxInfo
import com.alibaba.opensandbox.sandbox.domain.models.sandboxes.SandboxMetrics
import org.slf4j.LoggerFactory
import java.time.Duration
import java.time.OffsetDateTime
import java.util.UUID

/**
Expand Down Expand Up @@ -185,105 +180,6 @@ class CodeInterpreter internal constructor(
}
}

/**
* Gets a specific network endpoint for the underlying sandbox.
*
* This allows access to specific ports exposed by the sandbox, which can be
* useful for connecting to additional services or debugging interfaces.
*
* @param port The port number to get the endpoint for
* @return Endpoint information including host, port, and connection details
* @throws SandboxException if endpoint cannot be retrieved
*/
fun getEndpoint(port: Int): SandboxEndpoint {
return sandbox.getEndpoint(port)
}

/**
* Gets the current status of this sandbox.
*
* @return Current sandbox status including state and metadata
* @throws SandboxException if status cannot be retrieved
*/
fun getInfo(): SandboxInfo {
return sandbox.getInfo()
}

/**
* Gets the current resource usage metrics for the underlying sandbox.
*
* Provides real-time information about CPU usage, memory consumption,
* disk I/O, and other performance metrics.
*
* @return Current sandbox metrics including CPU, memory, and I/O statistics
* @throws SandboxException if metrics cannot be retrieved
*/
fun getMetrics(): SandboxMetrics {
return sandbox.getMetrics()
}

/**
* Renew the sandbox expiration time to delay automatic termination.
*
* The new expiration time will be set to the current time plus the provided duration.
*
* @param timeout Duration to add to the current time to set the new expiration
* @throws SandboxException if the operation fails
*/
fun renew(timeout: Duration) {
logger.info("Renew code interpreter {} timeout, estimated expiration to {}", id, OffsetDateTime.now().plus(timeout))
sandbox.renew(timeout)
}

/**
* Pauses the sandbox while preserving its state.
*
* The sandbox will transition to PAUSED state and can be resumed later.
* All running processes will be suspended.
*
* @throws SandboxException if pause operation fails
*/
fun pause() {
logger.info("Pausing code interpreter: {}", id)
sandbox.pause()
}

/**
* Resumes a previously paused code interpreter.
*
* The sandbox will transition from PAUSED to RUNNING state and all
* suspended processes will be resumed.
*
* @throws SandboxException if resume operation fails
*/
fun resume() {
logger.info("Resuming code interpreter: {}", id)
sandbox.resume()
}

/**
* This method sends a termination signal to the remote sandbox instance, causing it to stop immediately.
* This is an irreversible operation.
*
* Note: This method does NOT close the local `Sandbox` object resources (like connection pools).
* You should call `close()` or use a try-with-resources block to clean up local resources.
*
* @throws SandboxException if termination fails
*/
fun kill() {
logger.info("Killing code interpreter: {}", id)
sandbox.kill()
}

/**
* Checks if the code interpreter and its underlying sandbox are healthy and responsive.
*
* This performs health checks on both the sandbox infrastructure and code execution services.
*
* @return true if both sandbox and code execution services are healthy, false otherwise
*/
fun isHealthy(): Boolean = sandbox.isHealthy()

/**
* Builder for creating CodeInterpreter instances from existing Sandbox instances.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,27 @@ import com.alibaba.opensandbox.sandbox.domain.models.execd.executions.ExecutionH
* session persistence, and multi-language support.
*/
interface Codes {
/**
* Gets an existing execution context by id.
*
* A [CodeContext] represents a persistent execution session (kernel/runtime) that can be reused
* across multiple executions to preserve state (variables, imports, working directory, etc.).
*
* @param id Execution context id
* @return The existing [CodeContext]
*/
fun getContext(id: String): CodeContext

/**
* Lists active execution contexts for a given language/runtime.
*
* This is useful for debugging, monitoring, or cleaning up leaked contexts.
*
* @param language Execution runtime (e.g., "python", "bash", "java")
* @return List of [CodeContext] currently available for the given language
*/
fun listContexts(language: String): List<CodeContext>

/**
* Creates a new execution context for code interpretation.
*
Expand All @@ -36,6 +57,24 @@ interface Codes {
*/
fun createContext(language: String): CodeContext

/**
* Deletes an execution context (session) by id.
*
* This should terminate the underlying context thread/process and release resources.
*
* @param id Execution context id to delete
*/
fun deleteContext(id: String)

/**
* Deletes all execution contexts under a specific language/runtime.
*
* This is a bulk cleanup operation intended for context management.
*
* @param language Target execution runtime whose contexts should be deleted
*/
fun deleteContexts(language: String)

/**
* Executes code within the specified context.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@ class CodesAdapter(
private val api =
CodeInterpretingApi("${httpClientProvider.config.protocol}://${execdEndpoint.endpoint}", httpClientProvider.httpClient)

override fun getContext(id: String): CodeContext {
try {
val result = api.getContext(id)
return result.toCodeContext()
} catch (e: Exception) {
logger.error("Failed to get context", e)
throw e.toSandboxException()
}
}

override fun listContexts(language: String): List<CodeContext> {
try {
val list = api.listContexts(language)
return list.map { it.toCodeContext() }
} catch (e: Exception) {
logger.error("Failed to list contexts", e)
throw e.toSandboxException()
}
}

override fun createContext(language: String): CodeContext {
try {
val request = ApiCodeContextRequest(language = language)
Expand All @@ -63,6 +83,24 @@ class CodesAdapter(
}
}

override fun deleteContext(id: String) {
try {
api.deleteContext(id)
} catch (e: Exception) {
logger.error("Failed to delete context", e)
throw e.toSandboxException()
}
}

override fun deleteContexts(language: String) {
try {
deleteContexts(language)
} catch (e: Exception) {
logger.error("Failed to delete contexts", e)
throw e.toSandboxException()
}
}

override fun run(request: RunCodeRequest): Execution {
if (request.code.isEmpty()) {
throw InvalidArgumentException("Code cannot be empty")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,19 @@ package com.alibaba.opensandbox.codeinterpreter

import com.alibaba.opensandbox.codeinterpreter.domain.services.Codes
import com.alibaba.opensandbox.sandbox.Sandbox
import com.alibaba.opensandbox.sandbox.domain.models.sandboxes.SandboxEndpoint
import com.alibaba.opensandbox.sandbox.domain.models.sandboxes.SandboxInfo
import com.alibaba.opensandbox.sandbox.domain.models.sandboxes.SandboxMetrics
import com.alibaba.opensandbox.sandbox.domain.services.Commands
import com.alibaba.opensandbox.sandbox.domain.services.Filesystem
import com.alibaba.opensandbox.sandbox.domain.services.Metrics
import io.mockk.Runs
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.junit5.MockKExtension
import io.mockk.just
import io.mockk.mockk
import io.mockk.verify
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertSame
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import java.time.Duration
import java.util.UUID

@ExtendWith(MockKExtension::class)
Expand Down Expand Up @@ -98,77 +91,4 @@ class CodeInterpreterTest {
fun `codes should return code service`() {
assertSame(codeService, codeInterpreter.codes())
}

@Test
fun `getEndpoint should delegate to sandbox`() {
val port = 8888
val endpoint = mockk<SandboxEndpoint>()
every { sandbox.getEndpoint(port) } returns endpoint

assertSame(endpoint, codeInterpreter.getEndpoint(port))
verify { sandbox.getEndpoint(port) }
}

@Test
fun `getInfo should delegate to sandbox`() {
val info = mockk<SandboxInfo>()
every { sandbox.getInfo() } returns info

assertSame(info, codeInterpreter.getInfo())
verify { sandbox.getInfo() }
}

@Test
fun `getMetrics should delegate to sandbox`() {
val metrics = mockk<SandboxMetrics>()
every { sandbox.getMetrics() } returns metrics

assertSame(metrics, codeInterpreter.getMetrics())
verify { sandbox.getMetrics() }
}

@Test
fun `renew should delegate to sandbox`() {
val timeout = Duration.ofMinutes(10)
every { sandbox.renew(timeout) } just Runs

codeInterpreter.renew(timeout)

verify { sandbox.renew(timeout) }
}

@Test
fun `pause should delegate to sandbox`() {
every { sandbox.pause() } just Runs

codeInterpreter.pause()

verify { sandbox.pause() }
}

@Test
fun `resume should delegate to sandbox`() {
every { sandbox.resume() } just Runs

codeInterpreter.resume()

verify { sandbox.resume() }
}

@Test
fun `kill should delegate to sandbox`() {
every { sandbox.kill() } just Runs

codeInterpreter.kill()

verify { sandbox.kill() }
}

@Test
fun `isHealthy should delegate to sandbox`() {
every { sandbox.isHealthy() } returns true

assertTrue(codeInterpreter.isHealthy())
verify { sandbox.isHealthy() }
}
}
2 changes: 1 addition & 1 deletion sdks/code-interpreter/kotlin/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ org.gradle.parallel=true

# Project metadata
project.group=com.alibaba.opensandbox
project.version=1.0.0
project.version=1.0.1
project.description=A Kotlin SDK for Code Interpreter
2 changes: 1 addition & 1 deletion sdks/code-interpreter/kotlin/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ spotless = "6.23.3"
maven-publish = "0.35.0"
dokka = "1.9.10"
jackson = "2.18.2"
sandbox = "1.0.0"
sandbox = "1.0.1"
junit-platform = "1.13.4"

[libraries]
Expand Down
4 changes: 2 additions & 2 deletions sdks/code-interpreter/python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ async def main() -> None:
print(result.result[0].text)

# 8. Cleanup remote instance (optional but recommended)
await interpreter.kill()
await sandbox.kill()


if __name__ == "__main__":
Expand Down Expand Up @@ -119,7 +119,7 @@ with sandbox:
result = interpreter.codes.run("result = 2 + 2\nresult")
if result.result:
print(result.result[0].text)
interpreter.kill()
sandbox.kill()
```

## Runtime Configuration
Expand Down
Loading
Loading