Skip to content

Implement code execution engine API in ServicesHttpClient#51

Merged
orpiske merged 1 commit intowanaku-ai:mainfrom
orpiske:ci-issue-46
Jan 24, 2026
Merged

Implement code execution engine API in ServicesHttpClient#51
orpiske merged 1 commit intowanaku-ai:mainfrom
orpiske:ci-issue-46

Conversation

@orpiske
Copy link
Copy Markdown
Contributor

@orpiske orpiske commented Jan 23, 2026

Summary

Enhanced ServicesHttpClient with methods to interact with the new code execution engine API (/api/v2/code-execution-engine), enabling submission of code for execution and real-time monitoring via SSE streams.

Changes

  • executeCode(): Submit code execution requests to Wanaku

    • POST to /api/v2/code-execution-engine/{engineType}/{language}
    • Validates request before submission
    • Returns task ID and stream URL
  • streamCodeExecutionEvents(): Monitor execution via Server-Sent Events

    • GET from /api/v2/code-execution-engine/{engineType}/{language}/{taskId}
    • Real-time event streaming with Consumer callback pattern
    • Automatic termination on completion events
    • Robust SSE parsing with error handling
  • Added jackson-databind dependency for JSON serialization

  • Added imports for CodeExecutionEvent, CodeExecutionRequest, CodeExecutionResponse

Test Plan

  • Code compiles successfully
  • Manual testing with live Wanaku instance
  • Verify SSE stream parsing with different event types
  • Test error handling scenarios
  • Integration testing with LangChain4j module

Closes #46

Summary by Sourcery

Add client support in ServicesHttpClient for submitting code to the code execution engine and streaming execution events via SSE.

New Features:

  • Expose executeCode API to submit code execution requests to the remote code execution engine and receive task metadata.
  • Expose streamCodeExecutionEvents API to consume real-time code execution events for a given task via Server-Sent Events.

Enhancements:

  • Add internal SSE parsing and terminal event detection utilities to support robust event streaming in the client.

Build:

  • Add jackson-databind dependency to enable JSON deserialization of streamed code execution events.

- Added executeCode() method to submit code for execution
  - POST to /api/v2/code-execution-engine/{engineType}/{language}
  - Validates CodeExecutionRequest before submission
  - Returns WanakuResponse<CodeExecutionResponse> with task ID and stream URL

- Added streamCodeExecutionEvents() method to consume SSE stream
  - GET from /api/v2/code-execution-engine/{engineType}/{language}/{taskId}
  - Connects to Server-Sent Events (SSE) endpoint
  - Parses SSE format (data: {json}\n\n)
  - Deserializes JSON to CodeExecutionEvent objects
  - Uses Consumer<CodeExecutionEvent> callback pattern
  - Automatically stops on terminal events (COMPLETED, FAILED, TIMEOUT, CANCELLED)
  - Handles errors gracefully with logging

- Added jackson-databind dependency for JSON handling
- Added imports for CodeExecutionEvent, CodeExecutionRequest, CodeExecutionResponse

The new methods enable the SDK to submit code execution requests to Wanaku
and monitor execution progress in real-time via SSE streams.

Resolves wanaku-ai#46
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Jan 23, 2026

Reviewer's Guide

Extends ServicesHttpClient with a client-side API for the new /api/v2/code-execution-engine endpoints, including request submission and SSE-based event streaming, plus supporting JSON parsing via Jackson.

Sequence diagram for executeCode and SSE streaming with streamCodeExecutionEvents

sequenceDiagram
    actor Client
    participant ServicesHttpClient
    participant HttpClient
    participant WanakuCodeExecutionEngine as CodeExecutionEngineAPI
    participant ObjectMapper

    Client->>ServicesHttpClient: executeCode(engineType, language, codeExecutionRequest)
    ServicesHttpClient->>CodeExecutionRequest: validate()
    CodeExecutionRequest-->>ServicesHttpClient: validationOk
    ServicesHttpClient->>ServicesHttpClient: build path /api/v2/code-execution-engine/{engineType}/{language}
    ServicesHttpClient->>WanakuCodeExecutionEngine: HTTP POST path with CodeExecutionRequest
    WanakuCodeExecutionEngine-->>ServicesHttpClient: HTTP 2xx with CodeExecutionResponse JSON
    ServicesHttpClient->>ObjectMapper: deserialize CodeExecutionResponse
    ObjectMapper-->>ServicesHttpClient: CodeExecutionResponse
    ServicesHttpClient-->>Client: WanakuResponse<CodeExecutionResponse>

    Client->>ServicesHttpClient: streamCodeExecutionEvents(engineType, language, taskId, eventConsumer)
    ServicesHttpClient->>ServicesHttpClient: build path /api/v2/code-execution-engine/{engineType}/{language}/{taskId}
    ServicesHttpClient->>HttpClient: send(HttpRequest text/event-stream)
    HttpClient->>WanakuCodeExecutionEngine: HTTP GET SSE
    WanakuCodeExecutionEngine-->>HttpClient: HTTP 2xx with SSE InputStream
    HttpClient-->>ServicesHttpClient: HttpResponse<InputStream>
    ServicesHttpClient->>ServicesHttpClient: parseSSEStream(inputStream, eventConsumer)

    loop For each SSE event
        ServicesHttpClient->>ServicesHttpClient: read lines and assemble data
        ServicesHttpClient->>ObjectMapper: readValue(jsonData, CodeExecutionEvent)
        ObjectMapper-->>ServicesHttpClient: CodeExecutionEvent
        ServicesHttpClient->>Client: eventConsumer.accept(CodeExecutionEvent)
        ServicesHttpClient->>ServicesHttpClient: isTerminalEvent(CodeExecutionEvent)
        alt Terminal event
            ServicesHttpClient-->>ServicesHttpClient: break stream loop
        else Non-terminal event
            ServicesHttpClient-->>ServicesHttpClient: continue streaming
        end
    end

    alt HTTP error status
        WanakuCodeExecutionEngine-->>ServicesHttpClient: HTTP non-2xx
        ServicesHttpClient-->>Client: throw WanakuWebException
    end

    alt IO or InterruptedException
        ServicesHttpClient-->>Client: throw WanakuException
    end
Loading

Updated class diagram for ServicesHttpClient code execution engine methods

classDiagram
    class ServicesHttpClient {
        - String baseUrl
        - HttpClient httpClient
        - ObjectMapper objectMapper
        - ServiceAuthenticator serviceAuthenticator
        + WanakuResponse~CodeExecutionResponse~ executeCode(String engineType, String language, CodeExecutionRequest request)
        + void streamCodeExecutionEvents(String engineType, String language, String taskId, Consumer~CodeExecutionEvent~ eventConsumer)
        - void parseSSEStream(InputStream inputStream, Consumer~CodeExecutionEvent~ eventConsumer)
        - boolean isTerminalEvent(CodeExecutionEvent event)
        + void removeDataStore(String id)
        + void removeDataStoresByName(String name)
        + WanakuResponse executePost(String path, Object body, TypeReference typeReference)
    }

    class CodeExecutionRequest {
        - String code
        - Map~String, Object~ parameters
        + void validate()
    }

    class CodeExecutionResponse {
        + String taskId()
        + String streamUrl()
    }

    class CodeExecutionEvent {
        + CodeExecutionEventType getEventType()
        + String getOutput()
        + String getError()
        + Integer getExitCode()
        + String getMessage()
    }

    class CodeExecutionEventType {
        <<enumeration>>
        STARTED
        OUTPUT
        ERROR
        COMPLETED
        FAILED
        TIMEOUT
        CANCELLED
    }

    class WanakuResponse~T~ {
        + T getData()
        + int getStatusCode()
        + String getMessage()
    }

    class WanakuException {
        + WanakuException(String message)
        + WanakuException(String message, Throwable cause)
    }

    class WanakuWebException {
        + WanakuWebException(String message, int statusCode)
    }

    class ServiceAuthenticator {
        + String toHeaderValue()
    }

    class HttpClient {
        + HttpResponse send(HttpRequest request, HttpResponse.BodyHandler bodyHandler)
    }

    class ObjectMapper {
        + <T> T readValue(String content, Class clazz)
    }

    class Consumer~T~ {
        + void accept(T value)
    }

    class InputStream
    class HttpRequest
    class HttpResponse~T~ {
        + int statusCode()
        + T body()
    }

    ServicesHttpClient --> CodeExecutionRequest
    ServicesHttpClient --> CodeExecutionResponse
    ServicesHttpClient --> CodeExecutionEvent
    ServicesHttpClient --> WanakuResponse
    ServicesHttpClient --> WanakuException
    ServicesHttpClient --> WanakuWebException
    ServicesHttpClient --> ServiceAuthenticator
    ServicesHttpClient --> HttpClient
    ServicesHttpClient --> ObjectMapper
    ServicesHttpClient --> Consumer

    CodeExecutionEvent --> CodeExecutionEventType
    WanakuWebException --|> WanakuException
    WanakuException --|> RuntimeException
    HttpResponse --> InputStream
    Consumer <|.. eventConsumer
    WanakuResponse "1" --> "1" CodeExecutionResponse : wraps
Loading

File-Level Changes

Change Details Files
Add executeCode() to submit code execution jobs to the v2 code execution engine API.
  • Introduce executeCode(engineType, language, CodeExecutionRequest) on ServicesHttpClient.
  • Validate CodeExecutionRequest via request.validate() prior to dispatch.
  • Construct POST path /api/v2/code-execution-engine/{engineType}/{language}.
  • Invoke existing executePost helper with a TypeReference<WanakuResponse> for JSON deserialization.
capabilities-services-client/src/main/java/ai/wanaku/capabilities/sdk/services/ServicesHttpClient.java
Add SSE-based streaming support for code execution events, including parsing and termination logic.
  • Implement streamCodeExecutionEvents(engineType, language, taskId, Consumer) to open an HTTP GET SSE connection with appropriate headers and auth.
  • Handle non-2xx responses by throwing WanakuWebException with status code details.
  • Implement parseSSEStream(InputStream, Consumer) to read SSE lines, accumulate data: payloads into JSON strings, and deserialize each into CodeExecutionEvent via objectMapper.
  • On each parsed event, invoke the provided Consumer and stop reading when a terminal event is detected.
  • Implement isTerminalEvent(CodeExecutionEvent) to classify COMPLETED, FAILED, TIMEOUT, and CANCELLED as terminal types, ignoring null types and continuing on others.
  • Log JSON parsing failures for individual events but keep the stream processing running.
capabilities-services-client/src/main/java/ai/wanaku/capabilities/sdk/services/ServicesHttpClient.java
Add Jackson databind dependency to support JSON (de)serialization for SSE events and responses.
  • Declare com.fasterxml.jackson.core:jackson-databind dependency in the module POM so that objectMapper.readValue can be used for CodeExecutionEvent payloads.
capabilities-services-client/pom.xml

Assessment against linked issues

Issue Objective Addressed Explanation
#46 Add client support in ServicesHttpClient to submit code execution requests to the /api/v2/code-execution-engine endpoint, including JSON serialization/deserialization, authentication headers, error handling, and alignment with existing SDK patterns.
#46 Add client support in ServicesHttpClient to monitor code execution results (via GET/SSE) from the /api/v2/code-execution-engine endpoint, including response parsing, authentication, and error handling.
#46 Update dependencies (pom.xml) minimally as needed to support JSON handling for the new code execution engine REST interactions.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • The SSE parsing logic currently concatenates multiple data: lines without any delimiter, which can corrupt multi-line JSON payloads; consider appending a newline or space between data: segments to preserve valid JSON.
  • The SSE parser only looks at data: lines and ignores other valid SSE fields (e.g., event, id, comments), so if the backend ever uses those you may want to extend parseSSEStream to handle them explicitly or at least skip them robustly.
  • For streamCodeExecutionEvents, consider validating engineType, language, and taskId (e.g., non-null/non-blank) before building the URI to fail fast on invalid input rather than issuing malformed HTTP requests.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The SSE parsing logic currently concatenates multiple `data:` lines without any delimiter, which can corrupt multi-line JSON payloads; consider appending a newline or space between `data:` segments to preserve valid JSON.
- The SSE parser only looks at `data:` lines and ignores other valid SSE fields (e.g., `event`, `id`, comments), so if the backend ever uses those you may want to extend `parseSSEStream` to handle them explicitly or at least skip them robustly.
- For `streamCodeExecutionEvents`, consider validating `engineType`, `language`, and `taskId` (e.g., non-null/non-blank) before building the URI to fail fast on invalid input rather than issuing malformed HTTP requests.

## Individual Comments

### Comment 1
<location> `capabilities-services-client/src/main/java/ai/wanaku/capabilities/sdk/services/ServicesHttpClient.java:577-468` </location>
<code_context>
+            while ((line = reader.readLine()) != null) {
</code_context>

<issue_to_address>
**issue:** Handle a final SSE event that is not terminated by an empty line at end-of-stream.

Because events are only emitted on an empty line, an SSE stream that ends immediately after event data (without a trailing blank line) will drop the final event in `dataBuilder`. After the `while` loop, consider checking `dataBuilder.length() > 0` and emitting one last event to handle this case.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@@ -458,4 +466,155 @@ public void removeDataStore(String id) {
public void removeDataStoresByName(String name) {
executeDelete("/api/v1/data-store/remove?name=" + name);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Handle a final SSE event that is not terminated by an empty line at end-of-stream.

Because events are only emitted on an empty line, an SSE stream that ends immediately after event data (without a trailing blank line) will drop the final event in dataBuilder. After the while loop, consider checking dataBuilder.length() > 0 and emitting one last event to handle this case.

@orpiske orpiske merged commit f047634 into wanaku-ai:main Jan 24, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement REST Client in ServicesHttpClient

1 participant