turbolane-cli 2.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,206 @@
1
+ Metadata-Version: 2.4
2
+ Name: turbolane-cli
3
+ Version: 2.0.0
4
+ Summary: RL-optimized parallel TCP file transfer CLI (TurboLane)
5
+ Classifier: Development Status :: 3 - Alpha
6
+ Classifier: Intended Audience :: Science/Research
7
+ Classifier: Topic :: System :: Networking
8
+ Classifier: Programming Language :: Python :: 3.10
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+ Dynamic: classifier
12
+ Dynamic: description
13
+ Dynamic: description-content-type
14
+ Dynamic: requires-python
15
+ Dynamic: summary
16
+
17
+ # TurboLane — Phase 2: CLI File Transfer Server
18
+
19
+ RL-optimized parallel TCP file transfer system. Phase 2 builds a production-grade
20
+ CLI application on top of the Phase 1 TurboLane engine.
21
+
22
+ ---
23
+
24
+ ## Folder Structure
25
+
26
+ ```
27
+ .
28
+ ├── turbolane/ # Phase 1 — RL engine (UNCHANGED)
29
+ │ ├── __init__.py
30
+ │ ├── engine.py # TurboLaneEngine — only public import
31
+ │ ├── policies/
32
+ │ │ └── federated.py # FederatedPolicy (DCI / Q-learning)
33
+ │ └── rl/
34
+ │ ├── agent.py # RLAgent (Q-table, Bellman updates)
35
+ │ └── storage.py # QTableStorage (atomic JSON persistence)
36
+
37
+ ├── turbolane_server/ # Phase 2 — CLI application layer
38
+ │ ├── __init__.py
39
+ │ ├── protocol.py # Binary wire protocol (struct + CRC32)
40
+ │ ├── metrics.py # In-app RTT / throughput / loss metrics
41
+ │ ├── adapter.py # TurboLaneAdapter — engine ↔ server bridge
42
+ │ ├── transfer.py # TransferSession + StreamWorker (sender side)
43
+ │ ├── server.py # TurboLaneServer + FileAssembler (receiver)
44
+ │ ├── sender.py # TurboLaneSender — orchestrator
45
+ │ └── cli.py # argparse CLI: start / send / status
46
+
47
+ ├── models/
48
+ │ └── dci/ # Q-table persistence directory
49
+ ├── setup.py
50
+ └── README.md
51
+ ```
52
+
53
+ ---
54
+
55
+ ## Architecture
56
+
57
+ ```
58
+ SENDER SIDE RECEIVER SIDE
59
+ ─────────────────────────────── ────────────────────────────
60
+ turbolane-server send turbolane-server start
61
+ │ │
62
+ TurboLaneSender TurboLaneServer
63
+ │ │
64
+ ┌─────────────────┐ ┌────────────────────┐
65
+ │ TurboLaneAdapter│ │ accept loop │
66
+ │ (5s RL loop) │ │ (one thread/conn)│
67
+ │ │ └────────────────────┘
68
+ │ TurboLaneEngine │ │
69
+ │ (embedded DCI) │ ┌────────────────────┐
70
+ └────────┬────────┘ │ StreamHandler │
71
+ │ adjust_streams(n) │ HELLO/CHUNK/PING │
72
+ ▼ └────────────────────┘
73
+ TransferSession │
74
+ ┌─────────────────────────────┐ ┌────────────────────┐
75
+ │ ChunkQueue (thread-safe) │ │ FileAssembler │
76
+ │ StreamWorker × N │ │ (sparse write, │
77
+ │ (one thread per stream) │ │ out-of-order OK) │
78
+ └─────────────────────────────┘ └────────────────────┘
79
+ │ N×TCP connections
80
+ └──────────────────────────────────────────────┘
81
+
82
+ MetricsCollector (shared)
83
+ ├── per-stream StreamMetrics
84
+ ├── RTT: in-app PING/PONG round-trip timing
85
+ ├── Throughput: bytes_sent / elapsed per snapshot
86
+ └── Loss: chunk retransmit rate proxy
87
+ ```
88
+
89
+ ### Key design rules
90
+ - **TurboLane engine is completely decoupled** — only `adapter.py` imports from `turbolane.*`
91
+ - **No networking in the engine** — sockets live only in `transfer.py` and `server.py`
92
+ - **Single transfer lock** — server rejects new connections with `BUSY` during active transfer
93
+ - **RTT without root** — measured via application-layer PING/PONG timing per stream
94
+
95
+ ---
96
+
97
+ ## Wire Protocol
98
+
99
+ Binary struct header (34 bytes, big-endian) + payload:
100
+
101
+ | Field | Bytes | Description |
102
+ |--------------|-------|--------------------------------------|
103
+ | magic | 4 | `0x544C414E` ("TLAN") |
104
+ | msg_type | 1 | MessageType enum |
105
+ | stream_id | 1 | Parallel stream index (0-255) |
106
+ | chunk_idx | 4 | Chunk index within file |
107
+ | total_chunks | 4 | Total chunks in transfer |
108
+ | seq | 4 | Sequence number |
109
+ | file_offset | 8 | Byte offset in source file |
110
+ | data_len | 4 | Payload length (0 for control) |
111
+ | checksum | 4 | CRC32 of payload |
112
+ | **payload** | N | Raw file bytes / JSON metadata |
113
+
114
+ Message types: `HELLO`, `HELLO_ACK`, `CHUNK`, `CHUNK_ACK`, `PING`, `PONG`,
115
+ `TRANSFER_DONE`, `COMPLETE`, `ERROR`, `BUSY`, `STATUS_REQ`, `STATUS_RESP`
116
+
117
+ ---
118
+
119
+ ## Installation
120
+
121
+ ```bash
122
+ # From the project root (where setup.py lives)
123
+ pip install -e .
124
+ ```
125
+
126
+ ---
127
+
128
+ ## Usage
129
+
130
+ ### 1. Start the receiver server
131
+
132
+ ```bash
133
+ turbolane-server start --port 9000 --output-dir ./received
134
+ ```
135
+
136
+ Options:
137
+ ```
138
+ --host HOST Bind interface (default: 0.0.0.0)
139
+ --port PORT TCP port (default: 9000)
140
+ --output-dir DIR Where to save received files (default: ./received)
141
+ --verbose / -v Debug logging
142
+ ```
143
+
144
+ ### 2. Send a file
145
+
146
+ ```bash
147
+ turbolane-server send /data/large_dataset.tar \
148
+ --host 192.168.1.50 --port 9000 \
149
+ --streams 6 \
150
+ --min-streams 1 --max-streams 32 \
151
+ --model-dir models/dci \
152
+ --interval 5.0
153
+ ```
154
+
155
+ Options:
156
+ ```
157
+ FILE File to send (required positional)
158
+ --host HOST Receiver hostname/IP (required)
159
+ --port PORT Receiver port (default: 9000)
160
+ --streams N Initial parallel TCP streams (default: 4)
161
+ --min-streams N Minimum streams TurboLane may use (default: 1)
162
+ --max-streams N Maximum streams TurboLane may use (default: 32)
163
+ --model-dir DIR Q-table persistence directory (default: models/dci)
164
+ --interval SECS RL decision interval in seconds (default: 5.0)
165
+ --timeout SECS Max wait for completion (default: unlimited)
166
+ --verbose / -v Debug logging
167
+ ```
168
+
169
+ ### 3. Query server status
170
+
171
+ ```bash
172
+ turbolane-server status --host 192.168.1.50 --port 9000
173
+ ```
174
+
175
+ ---
176
+
177
+ ## How TurboLane integrates (5-second loop)
178
+
179
+ ```
180
+ Every 5 seconds (TurboLaneAdapter._tick):
181
+ 1. MetricsCollector.snapshot()
182
+ → throughput_mbps (sum of per-stream byte rates)
183
+ → rtt_ms (mean of PING/PONG RTT samples)
184
+ → loss_pct (chunk retransmit rate proxy)
185
+
186
+ 2. engine.learn(throughput, rtt, loss)
187
+ → Q-table Bellman update from previous decision's outcome
188
+
189
+ 3. engine.decide(throughput, rtt, loss)
190
+ → Q-learning ε-greedy action → new stream count
191
+
192
+ 4. session.adjust_streams(new_count)
193
+ → spawn / stop StreamWorker threads to match recommendation
194
+ ```
195
+
196
+ ---
197
+
198
+ ## Future upgrades (designed-in hooks)
199
+
200
+ | Capability | Where to add |
201
+ |----------------------|---------------------------|
202
+ | PPO algorithm | `turbolane/rl/` only |
203
+ | Multi-session | `server.py` busy logic |
204
+ | Shared policy learning | `adapter.py` FederatedPolicy |
205
+ | Resume / checkpointing | `ChunkQueue` + `FileAssembler` |
206
+ | TLS encryption | `StreamWorker` + `StreamHandler` |
@@ -0,0 +1,190 @@
1
+ # TurboLane — Phase 2: CLI File Transfer Server
2
+
3
+ RL-optimized parallel TCP file transfer system. Phase 2 builds a production-grade
4
+ CLI application on top of the Phase 1 TurboLane engine.
5
+
6
+ ---
7
+
8
+ ## Folder Structure
9
+
10
+ ```
11
+ .
12
+ ├── turbolane/ # Phase 1 — RL engine (UNCHANGED)
13
+ │ ├── __init__.py
14
+ │ ├── engine.py # TurboLaneEngine — only public import
15
+ │ ├── policies/
16
+ │ │ └── federated.py # FederatedPolicy (DCI / Q-learning)
17
+ │ └── rl/
18
+ │ ├── agent.py # RLAgent (Q-table, Bellman updates)
19
+ │ └── storage.py # QTableStorage (atomic JSON persistence)
20
+
21
+ ├── turbolane_server/ # Phase 2 — CLI application layer
22
+ │ ├── __init__.py
23
+ │ ├── protocol.py # Binary wire protocol (struct + CRC32)
24
+ │ ├── metrics.py # In-app RTT / throughput / loss metrics
25
+ │ ├── adapter.py # TurboLaneAdapter — engine ↔ server bridge
26
+ │ ├── transfer.py # TransferSession + StreamWorker (sender side)
27
+ │ ├── server.py # TurboLaneServer + FileAssembler (receiver)
28
+ │ ├── sender.py # TurboLaneSender — orchestrator
29
+ │ └── cli.py # argparse CLI: start / send / status
30
+
31
+ ├── models/
32
+ │ └── dci/ # Q-table persistence directory
33
+ ├── setup.py
34
+ └── README.md
35
+ ```
36
+
37
+ ---
38
+
39
+ ## Architecture
40
+
41
+ ```
42
+ SENDER SIDE RECEIVER SIDE
43
+ ─────────────────────────────── ────────────────────────────
44
+ turbolane-server send turbolane-server start
45
+ │ │
46
+ TurboLaneSender TurboLaneServer
47
+ │ │
48
+ ┌─────────────────┐ ┌────────────────────┐
49
+ │ TurboLaneAdapter│ │ accept loop │
50
+ │ (5s RL loop) │ │ (one thread/conn)│
51
+ │ │ └────────────────────┘
52
+ │ TurboLaneEngine │ │
53
+ │ (embedded DCI) │ ┌────────────────────┐
54
+ └────────┬────────┘ │ StreamHandler │
55
+ │ adjust_streams(n) │ HELLO/CHUNK/PING │
56
+ ▼ └────────────────────┘
57
+ TransferSession │
58
+ ┌─────────────────────────────┐ ┌────────────────────┐
59
+ │ ChunkQueue (thread-safe) │ │ FileAssembler │
60
+ │ StreamWorker × N │ │ (sparse write, │
61
+ │ (one thread per stream) │ │ out-of-order OK) │
62
+ └─────────────────────────────┘ └────────────────────┘
63
+ │ N×TCP connections
64
+ └──────────────────────────────────────────────┘
65
+
66
+ MetricsCollector (shared)
67
+ ├── per-stream StreamMetrics
68
+ ├── RTT: in-app PING/PONG round-trip timing
69
+ ├── Throughput: bytes_sent / elapsed per snapshot
70
+ └── Loss: chunk retransmit rate proxy
71
+ ```
72
+
73
+ ### Key design rules
74
+ - **TurboLane engine is completely decoupled** — only `adapter.py` imports from `turbolane.*`
75
+ - **No networking in the engine** — sockets live only in `transfer.py` and `server.py`
76
+ - **Single transfer lock** — server rejects new connections with `BUSY` during active transfer
77
+ - **RTT without root** — measured via application-layer PING/PONG timing per stream
78
+
79
+ ---
80
+
81
+ ## Wire Protocol
82
+
83
+ Binary struct header (34 bytes, big-endian) + payload:
84
+
85
+ | Field | Bytes | Description |
86
+ |--------------|-------|--------------------------------------|
87
+ | magic | 4 | `0x544C414E` ("TLAN") |
88
+ | msg_type | 1 | MessageType enum |
89
+ | stream_id | 1 | Parallel stream index (0-255) |
90
+ | chunk_idx | 4 | Chunk index within file |
91
+ | total_chunks | 4 | Total chunks in transfer |
92
+ | seq | 4 | Sequence number |
93
+ | file_offset | 8 | Byte offset in source file |
94
+ | data_len | 4 | Payload length (0 for control) |
95
+ | checksum | 4 | CRC32 of payload |
96
+ | **payload** | N | Raw file bytes / JSON metadata |
97
+
98
+ Message types: `HELLO`, `HELLO_ACK`, `CHUNK`, `CHUNK_ACK`, `PING`, `PONG`,
99
+ `TRANSFER_DONE`, `COMPLETE`, `ERROR`, `BUSY`, `STATUS_REQ`, `STATUS_RESP`
100
+
101
+ ---
102
+
103
+ ## Installation
104
+
105
+ ```bash
106
+ # From the project root (where setup.py lives)
107
+ pip install -e .
108
+ ```
109
+
110
+ ---
111
+
112
+ ## Usage
113
+
114
+ ### 1. Start the receiver server
115
+
116
+ ```bash
117
+ turbolane-server start --port 9000 --output-dir ./received
118
+ ```
119
+
120
+ Options:
121
+ ```
122
+ --host HOST Bind interface (default: 0.0.0.0)
123
+ --port PORT TCP port (default: 9000)
124
+ --output-dir DIR Where to save received files (default: ./received)
125
+ --verbose / -v Debug logging
126
+ ```
127
+
128
+ ### 2. Send a file
129
+
130
+ ```bash
131
+ turbolane-server send /data/large_dataset.tar \
132
+ --host 192.168.1.50 --port 9000 \
133
+ --streams 6 \
134
+ --min-streams 1 --max-streams 32 \
135
+ --model-dir models/dci \
136
+ --interval 5.0
137
+ ```
138
+
139
+ Options:
140
+ ```
141
+ FILE File to send (required positional)
142
+ --host HOST Receiver hostname/IP (required)
143
+ --port PORT Receiver port (default: 9000)
144
+ --streams N Initial parallel TCP streams (default: 4)
145
+ --min-streams N Minimum streams TurboLane may use (default: 1)
146
+ --max-streams N Maximum streams TurboLane may use (default: 32)
147
+ --model-dir DIR Q-table persistence directory (default: models/dci)
148
+ --interval SECS RL decision interval in seconds (default: 5.0)
149
+ --timeout SECS Max wait for completion (default: unlimited)
150
+ --verbose / -v Debug logging
151
+ ```
152
+
153
+ ### 3. Query server status
154
+
155
+ ```bash
156
+ turbolane-server status --host 192.168.1.50 --port 9000
157
+ ```
158
+
159
+ ---
160
+
161
+ ## How TurboLane integrates (5-second loop)
162
+
163
+ ```
164
+ Every 5 seconds (TurboLaneAdapter._tick):
165
+ 1. MetricsCollector.snapshot()
166
+ → throughput_mbps (sum of per-stream byte rates)
167
+ → rtt_ms (mean of PING/PONG RTT samples)
168
+ → loss_pct (chunk retransmit rate proxy)
169
+
170
+ 2. engine.learn(throughput, rtt, loss)
171
+ → Q-table Bellman update from previous decision's outcome
172
+
173
+ 3. engine.decide(throughput, rtt, loss)
174
+ → Q-learning ε-greedy action → new stream count
175
+
176
+ 4. session.adjust_streams(new_count)
177
+ → spawn / stop StreamWorker threads to match recommendation
178
+ ```
179
+
180
+ ---
181
+
182
+ ## Future upgrades (designed-in hooks)
183
+
184
+ | Capability | Where to add |
185
+ |----------------------|---------------------------|
186
+ | PPO algorithm | `turbolane/rl/` only |
187
+ | Multi-session | `server.py` busy logic |
188
+ | Shared policy learning | `adapter.py` FederatedPolicy |
189
+ | Resume / checkpointing | `ChunkQueue` + `FileAssembler` |
190
+ | TLS encryption | `StreamWorker` + `StreamHandler` |
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,27 @@
1
+ from pathlib import Path
2
+ from setuptools import setup, find_packages
3
+
4
+ this_dir = Path(__file__).parent
5
+ readme = (this_dir / "README.md").read_text(encoding="utf-8")
6
+
7
+ setup(
8
+ name="turbolane-cli",
9
+ version="2.0.0",
10
+ description="RL-optimized parallel TCP file transfer CLI (TurboLane)",
11
+ long_description=readme,
12
+ long_description_content_type="text/markdown",
13
+ packages=find_packages(exclude=["tests*"]),
14
+ python_requires=">=3.10",
15
+ install_requires=[],
16
+ entry_points={
17
+ "console_scripts": [
18
+ "turbolane-server=turbolane_server.cli:main",
19
+ ],
20
+ },
21
+ classifiers=[
22
+ "Development Status :: 3 - Alpha",
23
+ "Intended Audience :: Science/Research",
24
+ "Topic :: System :: Networking",
25
+ "Programming Language :: Python :: 3.10",
26
+ ],
27
+ )
@@ -0,0 +1,10 @@
1
+ """
2
+ TurboLane — RL-based network optimization engine.
3
+
4
+ Public API:
5
+ from turbolane import TurboLaneEngine
6
+ """
7
+ from turbolane.engine import TurboLaneEngine
8
+
9
+ __version__ = "1.0.0"
10
+ __all__ = ["TurboLaneEngine"]
@@ -0,0 +1,218 @@
1
+ """
2
+ turbolane/engine.py
3
+
4
+ TurboLaneEngine — the single public entry point for the TurboLane SDK.
5
+
6
+ This is the ONLY class that application code (or the adapter) should import.
7
+ All policy selection, agent wiring, and mode routing happens here.
8
+
9
+ Supported modes:
10
+ 'dci' → FederatedPolicy (data center / private network) ← current use
11
+ 'client' → EdgePolicy (public internet / edge) ← future
12
+
13
+ Supported algorithms:
14
+ 'qlearning' → RLAgent ← current use
15
+ 'ppo' → PPOAgent ← future (drop-in via FederatedPolicy)
16
+
17
+ Usage (DCI + Q-learning):
18
+ from turbolane.engine import TurboLaneEngine
19
+
20
+ engine = TurboLaneEngine(mode='dci', algorithm='qlearning')
21
+ streams = engine.decide(throughput_mbps, rtt_ms, loss_pct)
22
+ engine.learn(throughput_mbps, rtt_ms, loss_pct)
23
+ engine.save()
24
+ """
25
+
26
+ import logging
27
+
28
+ logger = logging.getLogger(__name__)
29
+
30
+ # Supported modes and their policy classes
31
+ _MODE_POLICY_MAP = {
32
+ "dci": "turbolane.policies.federated.FederatedPolicy",
33
+ "client": "turbolane.policies.edge.EdgePolicy", # future
34
+ }
35
+
36
+ _VALID_ALGORITHMS = {"qlearning", "ppo"}
37
+
38
+
39
+ class TurboLaneEngine:
40
+ """
41
+ Unified TurboLane control-plane engine.
42
+
43
+ Public interface (identical regardless of mode or algorithm):
44
+ decide(throughput_mbps, rtt_ms, loss_pct) → int
45
+ learn(throughput_mbps, rtt_ms, loss_pct)
46
+ save() → bool
47
+ get_stats() → dict
48
+ reset()
49
+
50
+ Convenience properties:
51
+ .current_connections → int
52
+ .mode → str
53
+ .algorithm → str
54
+ """
55
+
56
+ def __init__(
57
+ self,
58
+ mode: str = "dci",
59
+ algorithm: str = "qlearning",
60
+ **policy_kwargs,
61
+ ):
62
+ """
63
+ Initialize TurboLane engine.
64
+
65
+ Args:
66
+ mode: 'dci' or 'client'
67
+ algorithm: 'qlearning' or 'ppo'
68
+ **policy_kwargs: Passed directly to the policy constructor.
69
+ See FederatedPolicy.__init__ for valid keys.
70
+
71
+ Example:
72
+ TurboLaneEngine(
73
+ mode='dci',
74
+ algorithm='qlearning',
75
+ model_dir='models/dci',
76
+ min_connections=1,
77
+ max_connections=32,
78
+ default_connections=4,
79
+ monitoring_interval=5.0,
80
+ )
81
+ """
82
+ mode = mode.lower()
83
+ algorithm = algorithm.lower().replace("-", "").replace("_", "")
84
+
85
+ if mode not in _MODE_POLICY_MAP:
86
+ raise ValueError(
87
+ f"Unknown mode '{mode}'. Valid modes: {list(_MODE_POLICY_MAP)}"
88
+ )
89
+ if algorithm not in _VALID_ALGORITHMS:
90
+ raise ValueError(
91
+ f"Unknown algorithm '{algorithm}'. Valid: {list(_VALID_ALGORITHMS)}"
92
+ )
93
+
94
+ self.mode = mode
95
+ self.algorithm = algorithm
96
+
97
+ # Instantiate the appropriate policy
98
+ self._policy = self._build_policy(mode, algorithm, policy_kwargs)
99
+
100
+ logger.info(
101
+ "TurboLaneEngine ready: mode=%s algorithm=%s",
102
+ self.mode, self.algorithm,
103
+ )
104
+
105
+ # -----------------------------------------------------------------------
106
+ # Core interface — these are the ONLY methods the adapter calls
107
+ # -----------------------------------------------------------------------
108
+
109
+ def decide(
110
+ self,
111
+ throughput_mbps: float,
112
+ rtt_ms: float,
113
+ loss_pct: float,
114
+ ) -> int:
115
+ """
116
+ Make a stream count recommendation based on current network metrics.
117
+
118
+ Args:
119
+ throughput_mbps: Observed throughput in Mbps
120
+ rtt_ms: Observed round-trip time in milliseconds
121
+ loss_pct: Observed packet loss in percent (0–100)
122
+
123
+ Returns:
124
+ Recommended number of parallel TCP streams (int)
125
+ """
126
+ return self._policy.decide(throughput_mbps, rtt_ms, loss_pct)
127
+
128
+ def learn(
129
+ self,
130
+ throughput_mbps: float,
131
+ rtt_ms: float,
132
+ loss_pct: float,
133
+ ) -> None:
134
+ """
135
+ Update the policy from the outcome of the previous decision.
136
+
137
+ Call this once per monitoring cycle, AFTER decide(), with
138
+ the metrics observed after the previous action took effect.
139
+
140
+ Args:
141
+ throughput_mbps: Current throughput in Mbps
142
+ rtt_ms: Current RTT in milliseconds
143
+ loss_pct: Current packet loss in percent (0–100)
144
+ """
145
+ self._policy.learn(throughput_mbps, rtt_ms, loss_pct)
146
+
147
+ def save(self) -> bool:
148
+ """
149
+ Persist the policy to disk.
150
+
151
+ Returns:
152
+ True on success, False on failure.
153
+ """
154
+ return self._policy.save()
155
+
156
+ def get_stats(self) -> dict:
157
+ """Return a stats dict for logging, monitoring, and CLI display."""
158
+ stats = self._policy.get_stats()
159
+ stats["engine_mode"] = self.mode
160
+ stats["engine_algorithm"] = self.algorithm
161
+ return stats
162
+
163
+ def sync_current_connections(self, observed_connections: int) -> int:
164
+ """
165
+ Synchronize the policy with the real applied stream count observed by
166
+ the transfer layer.
167
+ """
168
+ sync_fn = getattr(self._policy, "sync_current_connections", None)
169
+ if sync_fn is None:
170
+ return self.current_connections
171
+ return sync_fn(observed_connections)
172
+
173
+ def reset(self) -> None:
174
+ """Clear the policy's learned state. Does not delete files on disk."""
175
+ self._policy.reset()
176
+
177
+ # -----------------------------------------------------------------------
178
+ # Convenience properties
179
+ # -----------------------------------------------------------------------
180
+
181
+ @property
182
+ def current_connections(self) -> int:
183
+ """Current recommended stream count."""
184
+ return self._policy.current_connections
185
+
186
+ # -----------------------------------------------------------------------
187
+ # Internal factory
188
+ # -----------------------------------------------------------------------
189
+
190
+ def _build_policy(self, mode: str, algorithm: str, kwargs: dict):
191
+ """
192
+ Instantiate the correct policy for the given mode.
193
+
194
+ DCI mode: always uses FederatedPolicy, passes algorithm as a hint
195
+ for future PPO support. Currently FederatedPolicy only supports
196
+ Q-learning; PPO support will be added to FederatedPolicy directly.
197
+ """
198
+ if mode == "dci":
199
+ from turbolane.policies.federated import FederatedPolicy
200
+ return FederatedPolicy(**kwargs)
201
+
202
+ elif mode == "client":
203
+ # Future: EdgePolicy
204
+ raise NotImplementedError(
205
+ "Client/Edge mode is not yet implemented. "
206
+ "Use mode='dci' for data center transfers."
207
+ )
208
+
209
+ else:
210
+ raise ValueError(f"Unknown mode: {mode}")
211
+
212
+ def __repr__(self) -> str:
213
+ return (
214
+ f"TurboLaneEngine("
215
+ f"mode={self.mode!r}, "
216
+ f"algorithm={self.algorithm!r}, "
217
+ f"connections={self.current_connections})"
218
+ )
@@ -0,0 +1,4 @@
1
+ """turbolane/policies — environment-specific policy wrappers."""
2
+ from turbolane.policies.federated import FederatedPolicy
3
+
4
+ __all__ = ["FederatedPolicy"]