Skip to content

Add gRPC command interface - #147

Open
GabyUnalaq wants to merge 9 commits into
mainfrom
grpc_interface
Open

GabyUnalaq wants to merge 9 commits into
mainfrom
grpc_interface

Conversation

@GabyUnalaq

@GabyUnalaq GabyUnalaq commented Jul 28, 2026 •

Copy link
Copy Markdown
Contributor

Issues: eclipse-ankaios/ank-sdk-rust#35 eclipse-ankaios/ankaios#764

Adds the possibility to connect to the Ankaios server directly, through the gRPC command interface.

Definition of Done

The PR shall be merged only if all items mentioned in CONTRIBUTING.md have been followed. In case an item is not applicable as described, please provide a short explanation in the description.

@GabyUnalaq GabyUnalaq self-assigned this Jul 28, 2026
@GabyUnalaq GabyUnalaq added enhancement New feature or request. Issue will appear in the change log "Features" ready for review Waiting for a response from contributor python Pull requests that update python code labels Jul 28, 2026
@GabyUnalaq GabyUnalaq changed the title Initial implementation of the gRPC command interface Add gRPC command interface Jul 28, 2026
@GabyUnalaq GabyUnalaq added this to the v1.1 milestone Aug 4, 2026
@GabyUnalaq GabyUnalaq added the blocked The issue is blocked by another issue label Aug 7, 2026
@GabyUnalaq

Copy link
Copy Markdown
Contributor Author

This PR is blocked until the proto fetching method accounts for the grpc_api proto file, needed for the command interface:
#149

@GabyUnalaq GabyUnalaq removed the blocked The issue is blocked by another issue label Aug 12, 2026

@inf17101 inf17101 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Intermediate review state.

Comment thread README.md Outdated
...
```

This requires the `grpc` extra:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why do we call the command interface feature grpc in the python sdk and command_interface in the Rust SDK?
The whole feature is called command_interface and grpc is just a protocol that is pluggable in Ankaios.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Renamed it to "command", and also updated the readme and the examples to exclude the protocol name. Also added some comments to make clear to the user that using the command interface means a grpc connection.

Comment thread .agents/ARCHITECTURE.md Outdated
Comment thread ankaios_sdk/_components/connection/__init__.py
Comment thread ankaios_sdk/_components/connection/command_interface.py
Comment thread ankaios_sdk/_components/connection/command_interface.py
Comment on lines +252 to +263
self._state = CommandInterfaceState.TERMINATED
if self._call is not None:
self._call.cancel()
if self._reader_thread is not None:
self._reader_thread.join(timeout=2)
if self._reader_thread.is_alive():
self._logger.error("Reader thread did not stop.")
self._reader_thread = None
if self._channel is not None:
self._channel.close()
self._channel = None
self._call = None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As self._state is a lock, I assume this class should be thread safe. This does not look thread safe. Assume connect and disconnect are called from two threads concurrently. First disconnect is executed until the state is set to TERMINATED. Then connect is executed. As the state is TERMINATED connect will not fail and _call, _read_thread and _channel will be set to the new values. Now disconnect is continued. The new _call, _read_thread and _channel will be terminated, while the original ones are untouched.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

There is indeed an issue here. The problem is that it is identical on the control interface side. My suggestion would be to leave it as is and create another issue with a Pr for fixing this everywhere, so that there are no race conditions regarding the state afterwards.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Then we should look at the control interface side and check if the problem is there as well. If it is first make a PR to fix it there and only then merge this PR. We should not introduce a new bug, only there is already a similar one somewhere else.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I have created a PR (#152) to fix them on the control interface side. Once it is merged to main, I will rebase this branch and fix them properly here as well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This should be resolved now.

Comment thread ankaios_sdk/_components/connection/command_interface.py Outdated
Comment on lines +290 to +302
def _grpc_target(self) -> str:
"""
Returns server_url as a bare host:port gRPC channel target.

:returns: The channel target.
:rtype: str
"""
for prefix in self._URL_SCHEME_PREFIXES:
if self._server_url.startswith(prefix):
return self._server_url[len(prefix):]
return self._server_url

def _build_channel(self) -> grpc.Channel:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Improve the ordering of method definitions in this file. The caller should be above the callee. The callees should be near the caller. The position of the setter/getters is OK. Also they are technically callees, they belong to the field definition in the top.

Comment thread ankaios_sdk/_components/connection/connection.py
response._response = None
response.content_type = ResponseType.CONTROL_INTERFACE_ACCEPTED
response.content = None
logger.debug(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why are we logging here about a received object? Nothing is received here. We only create an object.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is the moment when we know the request ID and the type of the response. The same case is in the other constructor-like methods.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What I do not like, is that message is received by control interface, but the logging is done in response. The logging is a side effect of creating a response object. This is just unexpected.

Comment thread ankaios_sdk/ankaios.py
Comment on lines +191 to +210
@_state.setter
def _state(self, value: CommandInterfaceState) -> None:
"""
Set the current state of the connection.

:param value: The new state to set.
:type value: CommandInterfaceState
"""
with self._state_lock:
self._state_value = value

@property
def connected(self) -> bool:
"""
Check if the gRPC connection is established.

:returns: True if connected, False otherwise.
:rtype: bool
"""
return self._state == CommandInterfaceState.CONNECTED

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I have no idea why at the moment a lock is used here at all. I would expect that assignment of self._state is atomic.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This issue is similar to one above, which can be tackled in a separate PR. Currently it is done in the same way as the control interface side.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Then we should create an PR for this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done (#152).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This should be resolved now.

Comment on lines +188 to +189
with self._state_lock:
return self._state_value

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reading of _state_value is also atomic. We do not need this lock either. I guess we also do not need this getter at all.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This should be resolved now.

@sonarqubecloud

sonarqubecloud Bot commented Oct 2, 2026

Copy link
Copy Markdown

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request. Issue will appear in the change log "Features" python Pull requests that update python code ready for review Waiting for a response from contributor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants