-
Notifications
You must be signed in to change notification settings - Fork 42
Fixed naming from workers to threads, preparing production release 0.0.1 #261
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,7 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import asyncio | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import os | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import multiprocessing | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Confirm intent for new multiprocessing import (currently unused). The new import isn’t referenced in this module. If you don’t plan to introduce multiprocessing-based code in this PR, please remove it to avoid confusion. If it’s a placeholder for imminent work, ignore this note. 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from asyncio import Queue, sleep | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from typing import List, Dict | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -34,7 +36,7 @@ class Runtime: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| key (str | None, optional): API key for authentication. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| If not provided, will use the EXOSPHERE_API_KEY environment variable. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| batch_size (int, optional): Number of states to fetch per poll. Defaults to 16. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workers (int, optional): Number of concurrent worker tasks. Defaults to 4. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| thread_count (int, optional): Number of concurrent worker threads. Defaults to 4. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| state_manage_version (str, optional): State manager API version. Defaults to "v0". | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| poll_interval (int, optional): Seconds between polling for new states. Defaults to 1. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -47,13 +49,13 @@ class Runtime: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| runtime.start() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def __init__(self, namespace: str, name: str, nodes: List[type[BaseNode]], state_manager_uri: str | None = None, key: str | None = None, batch_size: int = 16, workers: int = 4, state_manage_version: str = "v0", poll_interval: int = 1): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def __init__(self, namespace: str, name: str, nodes: List[type[BaseNode]], state_manager_uri: str | None = None, key: str | None = None, batch_size: int = 16, thread_count: int = 4, state_manage_version: str = "v0", poll_interval: int = 1): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Renaming
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainAPI misnomer: “thread_count” suggests OS threads, but implementation uses asyncio tasks. Change before publishing 0.0.1. Exposing “thread_count” as a public constructor param (and naming the worker as “_worker_thread”) is misleading and will be hard to change post-release. The runtime spawns asyncio tasks (cooperative coroutines) via asyncio.create_task, not threads. Recommend renaming to worker_count (or task_count) and updating validation, docs, and call sites accordingly. Proposed minimal API/name fixes: -def __init__(self, namespace: str, name: str, nodes: List[type[BaseNode]], state_manager_uri: str | None = None, key: str | None = None, batch_size: int = 16, thread_count: int = 4, state_manage_version: str = "v0", poll_interval: int = 1):
+def __init__(self, namespace: str, name: str, nodes: List[type[BaseNode]], state_manager_uri: str | None = None, key: str | None = None, batch_size: int = 16, worker_count: int = 4, state_manage_version: str = "v0", poll_interval: int = 1):
@@
- self._thread_count = thread_count
+ self._worker_count = worker_count
@@
- if self._thread_count < 1:
- raise ValueError("Thread count should be at least 1")
+ if self._worker_count < 1:
+ raise ValueError("Worker count should be at least 1")
@@
- worker_tasks = [asyncio.create_task(self._worker_thread()) for _ in range(self._thread_count)]
+ worker_tasks = [asyncio.create_task(self._worker_task()) for _ in range(self._worker_count)]Also update docstrings to reflect the terminology and avoid implying OS threads: - workers (int, optional): Number of concurrent worker tasks. Defaults to 4.
+ worker_count (int, optional): Number of concurrent worker tasks (asyncio tasks). Defaults to 4.
@@
- ValueError: If configuration is invalid (e.g., missing URI or key, batch_size/workers < 1).
+ ValueError: If configuration is invalid (e.g., missing URI or key, batch_size/worker_count < 1).If you truly intend real threads, we’d need a different design (e.g., threading or ThreadPoolExecutor, and careful event loop handoff), which is a larger change. Run this to find lingering references that need renaming and stale “workers” mentions in code/docs: Also applies to: 58-58, 91-92, 351-351 🏁 Script executed: #!/bin/bash
set -euo pipefail
rg -nP -C2 '(?<!_)workers?\b|\bthread_count\b|\b_thread_count\b|\b_worker_thread\b'Length of output: 7529 Rename The constructor parameter and internal naming currently suggests OS threads, but the runtime spawns asyncio tasks. To avoid a breaking API change post-release, please rename all Locations to update:
Proposed diffs: --- a/python-sdk/exospherehost/runtime.py
@@ -52,7 +52,7 @@ class Runtime:
- def __init__(self, namespace: str, name: str, nodes: List[type[BaseNode]], state_manager_uri: str | None = None, key: str | None = None, batch_size: int = 16, thread_count: int = 4, state_manage_version: str = "v0", poll_interval: int = 1):
+ def __init__(self, namespace: str, name: str, nodes: List[type[BaseNode]], state_manager_uri: str | None = None, key: str | None = None, batch_size: int = 16, worker_count: int = 4, state_manage_version: str = "v0", poll_interval: int = 1):
@@ -58,7 +58,7 @@ class Runtime:
- self._thread_count = thread_count
+ self._worker_count = worker_count
@@ -91,5 +91,5 @@ class Runtime:
- if self._thread_count < 1:
- raise ValueError("Thread count should be at least 1")
+ if self._worker_count < 1:
+ raise ValueError("Worker count should be at least 1")
@@ -311,4 +311,4 @@ class Runtime:
- async def _worker_thread(self):
+ async def _worker_task(self):
@@ -351,4 +351,4 @@ class Runtime:
- worker_tasks = [asyncio.create_task(self._worker_thread()) for _ in range(self._thread_count)]
+ worker_tasks = [asyncio.create_task(self._worker_task()) for _ in range(self._worker_count)]--- a/python-sdk/tests/test_runtime_validation.py
@@ -63,7 +63,7 @@ def test_batch_size_validation():
Runtime(namespace="ns", name="rt", nodes=[GoodNode], batch_size=0)
with pytest.raises(ValueError):
- Runtime(namespace="ns", name="rt", nodes=[GoodNode], workers=0)
+ Runtime(namespace="ns", name="rt", nodes=[GoodNode], worker_count=0)Please apply these changes across docstrings and any other call sites to ensure consistency. Let me know if you need help updating remaining references. 📝 Committable suggestion
Suggested change
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._name = name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._namespace = namespace | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._key = key | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._batch_size = batch_size | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._state_queue = Queue(maxsize=2*batch_size) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._workers = workers | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._thread_count = thread_count | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._nodes = nodes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._node_names = [node.__name__ for node in nodes] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._state_manager_uri = state_manager_uri | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -81,13 +83,13 @@ def _validate_runtime(self): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Validate runtime configuration. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Raises: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ValueError: If batch_size or workers is less than 1, or if required | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ValueError: If batch_size or thread_count is less than 1, or if required | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| configuration (state_manager_uri, key) is not provided. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if self._batch_size < 1: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ValueError("Batch size should be at least 1") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if self._workers < 1: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ValueError("Workers should be at least 1") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if self._thread_count < 1: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ValueError("Thread count should be at least 1") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if self._state_manager_uri is None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ValueError("State manager URI is not set") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if self._key is None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -306,9 +308,9 @@ def _validate_nodes(self): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if len(errors) > 0: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ValueError("Following errors while validating nodes: " + "\n".join(errors)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def _worker(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def _worker_thread(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Worker task that processes states from the queue. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Worker thread that processes states from the queue. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Continuously fetches states from the queue, executes the corresponding node, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| and notifies the state manager of the result. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -319,7 +321,7 @@ async def _worker(self): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| node = self._node_mapping[state["node_name"]] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| secrets = await self._get_secrets(state["state_id"]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| outputs = await node()._execute(node.Inputs(**state["inputs"]), node.Secrets(**secrets["secrets"])) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| outputs = await node()._execute(node.Inputs(**state["inputs"]), node.Secrets(**secrets["secrets"])) # type: ignore | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Secrets shape mismatch and unnecessary type: ignore. Return a flat mapping from _get_secrets and pass it directly. Currently, _get_secrets() is annotated as Dict[str, str] but the worker indexes secrets["secrets"], forcing a type ignore. Fix by returning the inner map from _get_secrets and using it directly: Change the call site (adjust names if you apply the worker refactor above): -outputs = await node()._execute(node.Inputs(**state["inputs"]), node.Secrets(**secrets["secrets"])) # type: ignore
+outputs = await node()._execute(node.Inputs(**state["inputs"]), node.Secrets(**secrets))And update _get_secrets to return the flat dict: - async def _get_secrets(self, state_id: str) -> Dict[str, str]:
+ async def _get_secrets(self, state_id: str) -> Dict[str, str]:
@@
- async with session.get(endpoint, headers=headers) as response: # type: ignore
+ async with session.get(endpoint, headers=headers) as response: # type: ignore
res = await response.json()
@@
- return res
+ # Expecting server payload like: {"secrets": {"KEY": "VALUE", ...}}
+ return res.get("secrets", {})This removes the type suppression and aligns annotation with actual return. 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if outputs is None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| outputs = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -338,17 +340,17 @@ async def _start(self): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Start the runtime event loop. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Registers nodes, starts the polling and worker tasks, and runs until stopped. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Registers nodes, starts the polling and worker threads, and runs until stopped. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Raises: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RuntimeError: If the runtime is not connected (no nodes registered). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await self._register() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| poller = asyncio.create_task(self._enqueue()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| worker_tasks = [asyncio.create_task(self._worker()) for _ in range(self._workers)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| worker_threads = [asyncio.create_task(self._worker_thread()) for _ in range(self._thread_count)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await asyncio.gather(poller, *worker_tasks) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await asyncio.gather(poller, *worker_threads) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def start(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.