chore: import upstream snapshot with attribution
SDK Tests / changes (push) Successful in 2m29s
Real E2E Tests / changes (push) Successful in 2m29s
Deploy Docs Pages / build (push) Has been cancelled
Deploy Docs Pages / deploy (push) Has been cancelled
Real E2E Tests / JavaScript E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Python E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Java E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / C# E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Go E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Real E2E CI (push) Has been cancelled
SDK Tests / SDK CI (push) Has been cancelled
SDK Tests / CLI Tests (push) Has been cancelled
SDK Tests / Python SDK Quality (code-interpreter) (push) Has been cancelled
SDK Tests / Python SDK Quality (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (sandbox) (push) Has been cancelled
SDK Tests / CLI Quality (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Go SDK Quality And Tests (push) Has been cancelled
SDK Tests / changes (push) Successful in 2m29s
Real E2E Tests / changes (push) Successful in 2m29s
Deploy Docs Pages / build (push) Has been cancelled
Deploy Docs Pages / deploy (push) Has been cancelled
Real E2E Tests / JavaScript E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Python E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Java E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / C# E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Go E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Real E2E CI (push) Has been cancelled
SDK Tests / SDK CI (push) Has been cancelled
SDK Tests / CLI Tests (push) Has been cancelled
SDK Tests / Python SDK Quality (code-interpreter) (push) Has been cancelled
SDK Tests / Python SDK Quality (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (sandbox) (push) Has been cancelled
SDK Tests / CLI Quality (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Go SDK Quality And Tests (push) Has been cancelled
This commit is contained in:
+230
@@ -0,0 +1,230 @@
|
||||
// Copyright 2026 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using OpenSandbox.Adapters;
|
||||
using OpenSandbox.CodeInterpreter.Models;
|
||||
using OpenSandbox.CodeInterpreter.Services;
|
||||
using OpenSandbox.Core;
|
||||
using OpenSandbox.Internal;
|
||||
using OpenSandbox.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace OpenSandbox.CodeInterpreter.Adapters;
|
||||
|
||||
/// <summary>
|
||||
/// Adapter implementation for the codes service.
|
||||
/// </summary>
|
||||
internal sealed class CodesAdapter : ICodes
|
||||
{
|
||||
private readonly HttpClientWrapper _client;
|
||||
private readonly HttpClient _sseHttpClient;
|
||||
private readonly string _baseUrl;
|
||||
private readonly IReadOnlyDictionary<string, string> _headers;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
PropertyNameCaseInsensitive = true,
|
||||
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
|
||||
};
|
||||
|
||||
public CodesAdapter(
|
||||
HttpClientWrapper client,
|
||||
HttpClient sseHttpClient,
|
||||
string baseUrl,
|
||||
IReadOnlyDictionary<string, string> headers,
|
||||
ILogger logger)
|
||||
{
|
||||
_client = client ?? throw new ArgumentNullException(nameof(client));
|
||||
_sseHttpClient = sseHttpClient ?? throw new ArgumentNullException(nameof(sseHttpClient));
|
||||
_baseUrl = baseUrl?.TrimEnd('/') ?? throw new ArgumentNullException(nameof(baseUrl));
|
||||
_headers = headers ?? new Dictionary<string, string>();
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public async Task<CodeContext> CreateContextAsync(string language, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(language))
|
||||
{
|
||||
throw new InvalidArgumentException("Language cannot be empty");
|
||||
}
|
||||
|
||||
var request = new CreateContextRequest { Language = language };
|
||||
_logger.LogDebug("Creating code context (language={Language})", language);
|
||||
var response = await _client.PostAsync<CodeContext>("/code/context", request, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (response == null || string.IsNullOrEmpty(response.Language))
|
||||
{
|
||||
throw new SandboxApiException(
|
||||
message: "Create code context failed: unexpected response shape",
|
||||
error: new SandboxError(SandboxErrorCodes.UnexpectedResponse, "Create code context failed: unexpected response shape"));
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public async Task<CodeContext> GetContextAsync(string contextId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(contextId))
|
||||
{
|
||||
throw new InvalidArgumentException("contextId cannot be empty");
|
||||
}
|
||||
|
||||
_logger.LogDebug("Fetching code context: {ContextId}", contextId);
|
||||
var response = await _client.GetAsync<CodeContext>($"/code/contexts/{Uri.EscapeDataString(contextId)}", cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (response == null || string.IsNullOrEmpty(response.Language))
|
||||
{
|
||||
throw new SandboxApiException(
|
||||
message: "Get code context failed: unexpected response shape",
|
||||
error: new SandboxError(SandboxErrorCodes.UnexpectedResponse, "Get code context failed: unexpected response shape"));
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<CodeContext>> ListContextsAsync(string language, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(language))
|
||||
{
|
||||
throw new InvalidArgumentException("Language cannot be empty");
|
||||
}
|
||||
|
||||
_logger.LogDebug("Listing code contexts (language={Language})", language);
|
||||
var queryParams = new Dictionary<string, string?> { ["language"] = language };
|
||||
|
||||
var response = await _client.GetAsync<List<CodeContext>>("/code/contexts", queryParams, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (response == null)
|
||||
{
|
||||
throw new SandboxApiException(
|
||||
message: "List code contexts failed: unexpected response shape",
|
||||
error: new SandboxError(SandboxErrorCodes.UnexpectedResponse, "List code contexts failed: unexpected response shape"));
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public async Task DeleteContextAsync(string contextId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(contextId))
|
||||
{
|
||||
throw new InvalidArgumentException("contextId cannot be empty");
|
||||
}
|
||||
|
||||
_logger.LogInformation("Deleting code context: {ContextId}", contextId);
|
||||
await _client.DeleteAsync($"/code/contexts/{Uri.EscapeDataString(contextId)}", cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task DeleteContextsAsync(string language, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(language))
|
||||
{
|
||||
throw new InvalidArgumentException("Language cannot be empty");
|
||||
}
|
||||
|
||||
_logger.LogInformation("Deleting code contexts (language={Language})", language);
|
||||
var queryParams = new Dictionary<string, string?> { ["language"] = language };
|
||||
await _client.DeleteAsync("/code/contexts", queryParams, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task InterruptAsync(string executionId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(executionId))
|
||||
{
|
||||
throw new InvalidArgumentException("executionId cannot be empty");
|
||||
}
|
||||
|
||||
_logger.LogInformation("Interrupting code execution: {ExecutionId}", executionId);
|
||||
var queryParams = new Dictionary<string, string?> { ["id"] = executionId };
|
||||
await _client.DeleteAsync("/code", queryParams, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<ServerStreamEvent> RunStreamAsync(
|
||||
RunCodeRequest request,
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
throw new InvalidArgumentException("request cannot be null");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.Code))
|
||||
{
|
||||
throw new InvalidArgumentException("Code cannot be empty");
|
||||
}
|
||||
|
||||
var url = $"{_baseUrl}/code";
|
||||
_logger.LogDebug("Running code stream (codeLength={CodeLength})", request.Code.Length);
|
||||
var json = JsonSerializer.Serialize(request, JsonOptions);
|
||||
|
||||
using var httpRequest = new HttpRequestMessage(HttpMethod.Post, url)
|
||||
{
|
||||
Content = new StringContent(json, Encoding.UTF8, "application/json")
|
||||
};
|
||||
|
||||
httpRequest.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("text/event-stream"));
|
||||
|
||||
foreach (var header in _headers)
|
||||
{
|
||||
httpRequest.Headers.TryAddWithoutValidation(header.Key, header.Value);
|
||||
}
|
||||
|
||||
using var response = await _sseHttpClient.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
await foreach (var ev in SseParser.ParseJsonEventStreamAsync<ServerStreamEvent>(response, "Run code failed", cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
yield return ev;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Execution> RunAsync(string code, RunCodeOptions? options = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(code))
|
||||
{
|
||||
throw new InvalidArgumentException("Code cannot be empty");
|
||||
}
|
||||
|
||||
if (options?.Context != null && options.Language != null)
|
||||
{
|
||||
throw new InvalidArgumentException("Provide either options.Context or options.Language, not both");
|
||||
}
|
||||
|
||||
var context = options?.Context
|
||||
?? (options?.Language != null
|
||||
? new CodeContext { Language = options.Language }
|
||||
: new CodeContext { Language = SupportedLanguage.Python });
|
||||
|
||||
var request = new RunCodeRequest
|
||||
{
|
||||
Code = code,
|
||||
Context = context
|
||||
};
|
||||
|
||||
var execution = new Execution();
|
||||
_logger.LogDebug("Running code (codeLength={CodeLength})", code.Length);
|
||||
var dispatcher = new ExecutionEventDispatcher(execution, options?.Handlers);
|
||||
|
||||
await foreach (var ev in RunStreamAsync(request, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
await dispatcher.DispatchAsync(ev).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return execution;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
// Copyright 2026 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using OpenSandbox.CodeInterpreter.Factory;
|
||||
using OpenSandbox.CodeInterpreter.Services;
|
||||
using OpenSandbox.Config;
|
||||
using OpenSandbox.Core;
|
||||
using OpenSandbox.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
|
||||
namespace OpenSandbox.CodeInterpreter;
|
||||
|
||||
/// <summary>
|
||||
/// Options for creating a code interpreter.
|
||||
/// </summary>
|
||||
public class CodeInterpreterCreateOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the adapter factory. If not provided, a default factory is used.
|
||||
/// </summary>
|
||||
public ICodeInterpreterAdapterFactory? AdapterFactory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets diagnostics options such as logging.
|
||||
/// </summary>
|
||||
public SdkDiagnosticsOptions? Diagnostics { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Code interpreter facade for executing code in multiple languages.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class wraps an existing <see cref="Sandbox"/> and provides a high-level API for code execution.
|
||||
/// Use <see cref="Codes"/> to create contexts and run code.
|
||||
/// <see cref="Files"/>, <see cref="Commands"/>, and <see cref="Metrics"/> are exposed for convenience
|
||||
/// and are the same instances as on the underlying <see cref="Sandbox"/>.
|
||||
/// This type does not own the remote sandbox lifecycle. Call <see cref="Sandbox.KillAsync"/> when you want to terminate
|
||||
/// the remote instance. Dispose the wrapped <see cref="Sandbox"/> to release local SDK resources.
|
||||
/// </remarks>
|
||||
public sealed class CodeInterpreter
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the underlying sandbox instance.
|
||||
/// </summary>
|
||||
public Sandbox Sandbox { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the codes service for code execution operations.
|
||||
/// </summary>
|
||||
public ICodes Codes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sandbox ID.
|
||||
/// </summary>
|
||||
public string Id => Sandbox.Id;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the filesystem service.
|
||||
/// </summary>
|
||||
public ISandboxFiles Files => Sandbox.Files;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the command execution service.
|
||||
/// </summary>
|
||||
public IExecdCommands Commands => Sandbox.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the metrics service.
|
||||
/// </summary>
|
||||
public IExecdMetrics Metrics => Sandbox.Metrics;
|
||||
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private CodeInterpreter(Sandbox sandbox, ICodes codes, ILogger logger)
|
||||
{
|
||||
Sandbox = sandbox ?? throw new ArgumentNullException(nameof(sandbox));
|
||||
Codes = codes ?? throw new ArgumentNullException(nameof(codes));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_logger.LogDebug("Code interpreter initialized for sandbox: {SandboxId}", sandbox.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new code interpreter from an existing sandbox.
|
||||
/// </summary>
|
||||
/// <param name="sandbox">The sandbox to wrap.</param>
|
||||
/// <param name="options">Optional creation options.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A new code interpreter instance.</returns>
|
||||
/// <exception cref="InvalidArgumentException">Thrown when <paramref name="sandbox"/> is null.</exception>
|
||||
/// <exception cref="SandboxException">Thrown when endpoint discovery or adapter initialization fails.</exception>
|
||||
public static async Task<CodeInterpreter> CreateAsync(
|
||||
Sandbox sandbox,
|
||||
CodeInterpreterCreateOptions? options = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (sandbox == null)
|
||||
{
|
||||
throw new InvalidArgumentException("sandbox cannot be null");
|
||||
}
|
||||
|
||||
var loggerFactory = options?.Diagnostics?.LoggerFactory ?? sandbox.SharedLoggerFactory ?? NullLoggerFactory.Instance;
|
||||
var logger = loggerFactory.CreateLogger("OpenSandbox.CodeInterpreter.CodeInterpreter");
|
||||
var endpoint = await sandbox.GetEndpointAsync(Constants.DefaultExecdPort, cancellationToken).ConfigureAwait(false);
|
||||
logger.LogInformation("Creating code interpreter for sandbox: {SandboxId}", sandbox.Id);
|
||||
var protocol = sandbox.ConnectionConfig.Protocol == ConnectionProtocol.Https ? "https" : "http";
|
||||
var execdBaseUrl = $"{protocol}://{endpoint.EndpointAddress}";
|
||||
var execdHeaders = MergeHeaders(sandbox.ConnectionConfig.Headers, endpoint.Headers);
|
||||
var adapterFactory = options?.AdapterFactory ?? DefaultCodeInterpreterAdapterFactory.Create();
|
||||
|
||||
var codes = adapterFactory.CreateCodes(new CreateCodesStackOptions
|
||||
{
|
||||
ConnectionConfig = sandbox.ConnectionConfig,
|
||||
ExecdBaseUrl = execdBaseUrl,
|
||||
ExecdHeaders = execdHeaders,
|
||||
HttpClientProvider = sandbox.SharedHttpClientProvider,
|
||||
LoggerFactory = loggerFactory
|
||||
});
|
||||
|
||||
return new CodeInterpreter(sandbox, codes, logger);
|
||||
}
|
||||
|
||||
private static IReadOnlyDictionary<string, string> MergeHeaders(
|
||||
IReadOnlyDictionary<string, string> baseHeaders,
|
||||
IReadOnlyDictionary<string, string>? overrideHeaders)
|
||||
{
|
||||
var merged = baseHeaders.ToDictionary(header => header.Key, header => header.Value);
|
||||
if (overrideHeaders != null)
|
||||
{
|
||||
foreach (var header in overrideHeaders)
|
||||
{
|
||||
merged[header.Key] = header.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return merged;
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
// Copyright 2026 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using OpenSandbox.CodeInterpreter.Adapters;
|
||||
using OpenSandbox.CodeInterpreter.Services;
|
||||
using OpenSandbox.Core;
|
||||
using OpenSandbox.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace OpenSandbox.CodeInterpreter.Factory;
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of the code interpreter adapter factory.
|
||||
/// </summary>
|
||||
public class DefaultCodeInterpreterAdapterFactory : ICodeInterpreterAdapterFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the default adapter factory.
|
||||
/// </summary>
|
||||
/// <returns>A new factory instance.</returns>
|
||||
public static DefaultCodeInterpreterAdapterFactory Create() => new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public ICodes CreateCodes(CreateCodesStackOptions options)
|
||||
{
|
||||
if (options == null)
|
||||
{
|
||||
throw new InvalidArgumentException("options cannot be null");
|
||||
}
|
||||
|
||||
if (options.ConnectionConfig == null)
|
||||
{
|
||||
throw new InvalidArgumentException("options.ConnectionConfig cannot be null");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(options.ExecdBaseUrl))
|
||||
{
|
||||
throw new InvalidArgumentException("options.ExecdBaseUrl cannot be null or empty");
|
||||
}
|
||||
|
||||
if (options.ExecdHeaders == null)
|
||||
{
|
||||
throw new InvalidArgumentException("options.ExecdHeaders cannot be null");
|
||||
}
|
||||
|
||||
if (options.HttpClientProvider == null)
|
||||
{
|
||||
throw new InvalidArgumentException("options.HttpClientProvider cannot be null");
|
||||
}
|
||||
|
||||
if (options.LoggerFactory == null)
|
||||
{
|
||||
throw new InvalidArgumentException("options.LoggerFactory cannot be null");
|
||||
}
|
||||
|
||||
var client = new HttpClientWrapper(
|
||||
options.HttpClientProvider.HttpClient,
|
||||
options.ExecdBaseUrl,
|
||||
options.ExecdHeaders,
|
||||
options.LoggerFactory.CreateLogger("OpenSandbox.HttpClientWrapper"));
|
||||
|
||||
return new CodesAdapter(
|
||||
client,
|
||||
options.HttpClientProvider.SseHttpClient,
|
||||
options.ExecdBaseUrl,
|
||||
options.ExecdHeaders,
|
||||
options.LoggerFactory.CreateLogger("OpenSandbox.CodeInterpreter.CodesAdapter"));
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
// Copyright 2026 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using OpenSandbox.CodeInterpreter.Services;
|
||||
using OpenSandbox.Config;
|
||||
using OpenSandbox;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace OpenSandbox.CodeInterpreter.Factory;
|
||||
|
||||
/// <summary>
|
||||
/// Options for creating a codes service stack.
|
||||
/// </summary>
|
||||
public class CreateCodesStackOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the connection configuration.
|
||||
/// </summary>
|
||||
public required ConnectionConfig ConnectionConfig { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the execd API base URL.
|
||||
/// </summary>
|
||||
public required string ExecdBaseUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets headers to apply to execd requests.
|
||||
/// </summary>
|
||||
public required IReadOnlyDictionary<string, string> ExecdHeaders { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the HTTP client provider for this SDK instance.
|
||||
/// </summary>
|
||||
public required HttpClientProvider HttpClientProvider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the logger factory for this SDK instance.
|
||||
/// </summary>
|
||||
public required ILoggerFactory LoggerFactory { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Factory interface for creating code interpreter adapters.
|
||||
/// </summary>
|
||||
public interface ICodeInterpreterAdapterFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a codes service instance.
|
||||
/// </summary>
|
||||
/// <param name="options">The creation options.</param>
|
||||
/// <returns>The codes service.</returns>
|
||||
ICodes CreateCodes(CreateCodesStackOptions options);
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
// Copyright 2026 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace OpenSandbox.CodeInterpreter.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Supported programming languages for code execution.
|
||||
/// </summary>
|
||||
public static class SupportedLanguage
|
||||
{
|
||||
/// <summary>
|
||||
/// Python language.
|
||||
/// </summary>
|
||||
public const string Python = "python";
|
||||
|
||||
/// <summary>
|
||||
/// Java language.
|
||||
/// </summary>
|
||||
public const string Java = "java";
|
||||
|
||||
/// <summary>
|
||||
/// Go language.
|
||||
/// </summary>
|
||||
public const string Go = "go";
|
||||
|
||||
/// <summary>
|
||||
/// TypeScript language.
|
||||
/// </summary>
|
||||
public const string TypeScript = "typescript";
|
||||
|
||||
/// <summary>
|
||||
/// JavaScript language.
|
||||
/// </summary>
|
||||
public const string JavaScript = "javascript";
|
||||
|
||||
/// <summary>
|
||||
/// Bash shell.
|
||||
/// </summary>
|
||||
public const string Bash = "bash";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a code execution context.
|
||||
/// </summary>
|
||||
public class CodeContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the context ID.
|
||||
/// </summary>
|
||||
[JsonPropertyName("id")]
|
||||
public string? Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the programming language.
|
||||
/// </summary>
|
||||
[JsonPropertyName("language")]
|
||||
public required string Language { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request to run code.
|
||||
/// </summary>
|
||||
public class RunCodeRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the code to execute.
|
||||
/// </summary>
|
||||
[JsonPropertyName("code")]
|
||||
public required string Code { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the execution context.
|
||||
/// </summary>
|
||||
[JsonPropertyName("context")]
|
||||
public required CodeContext Context { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Options for running code.
|
||||
/// </summary>
|
||||
public class RunCodeOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the execution context. If provided, code runs in this context.
|
||||
/// </summary>
|
||||
public CodeContext? Context { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the language for a new ephemeral context.
|
||||
/// Cannot be used together with Context.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When only <see cref="Language"/> is provided and <see cref="Context"/> is null, execd creates or reuses
|
||||
/// a default session for that language, so state can persist across runs.
|
||||
/// </remarks>
|
||||
public string? Language { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the execution event handlers.
|
||||
/// </summary>
|
||||
public OpenSandbox.Models.ExecutionHandlers? Handlers { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request to create a code context.
|
||||
/// </summary>
|
||||
internal class CreateContextRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the programming language.
|
||||
/// </summary>
|
||||
[JsonPropertyName("language")]
|
||||
public required string Language { get; set; }
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>OpenSandbox.CodeInterpreter</RootNamespace>
|
||||
<AssemblyName>OpenSandbox.CodeInterpreter</AssemblyName>
|
||||
|
||||
<!-- Package Information -->
|
||||
<PackageId>Alibaba.OpenSandbox.CodeInterpreter</PackageId>
|
||||
<Version>$(OpenSandboxCodeInterpreterPackageVersion)</Version>
|
||||
<Authors>Alibaba Group</Authors>
|
||||
<Company>Alibaba Group Holding Ltd.</Company>
|
||||
<Product>OpenSandbox Code Interpreter SDK</Product>
|
||||
<Description>A C# SDK for code interpretation with OpenSandbox. Provides high-level APIs for executing code in multiple languages (Python, JavaScript, TypeScript, Go, Java, Bash) within secure sandbox environments.</Description>
|
||||
<Copyright>Copyright 2026 Alibaba Group Holding Ltd.</Copyright>
|
||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||
<PackageProjectUrl>https://open-sandbox.ai</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/opensandbox-group/OpenSandbox.git</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageTags>sandbox;code-interpreter;execution;opensandbox;alibaba;python;javascript</PackageTags>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
|
||||
<!-- Build Settings -->
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||
<TreatWarningsAsErrors Condition="'$(Configuration)' == 'Release'">true</TreatWarningsAsErrors>
|
||||
|
||||
<!--
|
||||
Default to local source reference for development.
|
||||
For release packing with NuGet version range, run:
|
||||
dotnet pack -c Release -p:UseLocalOpenSandboxProjectReference=false
|
||||
-->
|
||||
<UseLocalOpenSandboxProjectReference Condition="'$(UseLocalOpenSandboxProjectReference)' == ''">true</UseLocalOpenSandboxProjectReference>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Expose internals to test project -->
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="OpenSandbox.CodeInterpreter.Tests" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Use local project reference for day-to-day development -->
|
||||
<ItemGroup Condition="'$(UseLocalOpenSandboxProjectReference)' == 'true'">
|
||||
<ProjectReference Include="..\..\..\..\sandbox\csharp\src\OpenSandbox\OpenSandbox.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Use NuGet dependency range for release packaging -->
|
||||
<ItemGroup Condition="'$(UseLocalOpenSandboxProjectReference)' != 'true'">
|
||||
<PackageReference Include="Alibaba.OpenSandbox" Version="$(OpenSandboxDependencyVersionRange)" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Common Dependencies -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2" />
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.5" />
|
||||
<PackageReference Include="PolySharp" Version="1.14.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- .NET Standard 2.0 specific dependencies -->
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
|
||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Package files -->
|
||||
<ItemGroup>
|
||||
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,105 @@
|
||||
// Copyright 2026 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using OpenSandbox.CodeInterpreter.Models;
|
||||
using OpenSandbox.Core;
|
||||
using OpenSandbox.Models;
|
||||
|
||||
namespace OpenSandbox.CodeInterpreter.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service interface for code execution operations.
|
||||
/// </summary>
|
||||
public interface ICodes
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new code execution context for the specified language.
|
||||
/// </summary>
|
||||
/// <param name="language">The programming language (use <see cref="SupportedLanguage"/> constants).</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The created context.</returns>
|
||||
/// <exception cref="InvalidArgumentException">Thrown when <paramref name="language"/> is null or empty.</exception>
|
||||
/// <exception cref="SandboxException">Thrown when the sandbox service request fails.</exception>
|
||||
Task<CodeContext> CreateContextAsync(string language, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an existing context by ID.
|
||||
/// </summary>
|
||||
/// <param name="contextId">The context ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The context.</returns>
|
||||
/// <exception cref="InvalidArgumentException">Thrown when <paramref name="contextId"/> is null or empty.</exception>
|
||||
/// <exception cref="SandboxException">Thrown when the sandbox service request fails.</exception>
|
||||
Task<CodeContext> GetContextAsync(string contextId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Lists active contexts for the specified language.
|
||||
/// </summary>
|
||||
/// <param name="language">Required language filter.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>List of contexts.</returns>
|
||||
/// <exception cref="InvalidArgumentException">Thrown when <paramref name="language"/> is null or empty.</exception>
|
||||
/// <exception cref="SandboxException">Thrown when the sandbox service request fails.</exception>
|
||||
Task<IReadOnlyList<CodeContext>> ListContextsAsync(string language, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a context by ID.
|
||||
/// </summary>
|
||||
/// <param name="contextId">The context ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <exception cref="InvalidArgumentException">Thrown when <paramref name="contextId"/> is null or empty.</exception>
|
||||
/// <exception cref="SandboxException">Thrown when the sandbox service request fails.</exception>
|
||||
Task DeleteContextAsync(string contextId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all contexts for the specified language.
|
||||
/// </summary>
|
||||
/// <param name="language">The programming language.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <exception cref="InvalidArgumentException">Thrown when <paramref name="language"/> is null or empty.</exception>
|
||||
/// <exception cref="SandboxException">Thrown when the sandbox service request fails.</exception>
|
||||
Task DeleteContextsAsync(string language, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Runs code and returns the execution result.
|
||||
/// </summary>
|
||||
/// <param name="code">The code to execute.</param>
|
||||
/// <param name="options">Optional execution options.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The execution result.</returns>
|
||||
/// <exception cref="InvalidArgumentException">Thrown when required request fields are missing.</exception>
|
||||
/// <exception cref="SandboxException">Thrown when the sandbox service request fails.</exception>
|
||||
Task<Execution> RunAsync(string code, RunCodeOptions? options = null, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Runs code and streams execution events.
|
||||
/// </summary>
|
||||
/// <param name="request">The run code request.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>An async enumerable of server stream events.</returns>
|
||||
/// <exception cref="InvalidArgumentException">Thrown when the request is invalid.</exception>
|
||||
/// <exception cref="SandboxException">Thrown when the sandbox service request fails.</exception>
|
||||
IAsyncEnumerable<ServerStreamEvent> RunStreamAsync(RunCodeRequest request, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Interrupts a running code execution.
|
||||
/// </summary>
|
||||
/// <param name="executionId">
|
||||
/// The execution ID to interrupt, typically obtained from the run result or the <c>init</c> event.
|
||||
/// </param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <exception cref="InvalidArgumentException">Thrown when <paramref name="executionId"/> is null or empty.</exception>
|
||||
/// <exception cref="SandboxException">Thrown when the sandbox service request fails.</exception>
|
||||
Task InterruptAsync(string executionId, CancellationToken cancellationToken = default);
|
||||
}
|
||||
Reference in New Issue
Block a user