langfun 0.1.2.dev202509150805__py3-none-any.whl → 0.1.2.dev202509170804__py3-none-any.whl

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.

Potentially problematic release.


This version of langfun might be problematic. Click here for more details.

@@ -0,0 +1,43 @@
1
+ # Copyright 2025 The Langfun Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import unittest
15
+ from langfun.env import interface
16
+
17
+
18
+ class IdTest(unittest.TestCase):
19
+
20
+ def test_environment_id(self):
21
+ env_id = interface.EnvironmentId('env@1/a b:c#def')
22
+ self.assertEqual(str(env_id), 'env@1/a b:c#def')
23
+ self.assertEqual(
24
+ env_id.working_dir(root_dir='/tmp'),
25
+ '/tmp/env_1/ab_c_def'
26
+ )
27
+ self.assertIsNone(env_id.working_dir(root_dir=None))
28
+
29
+ def test_sandbox_id(self):
30
+ sandbox_id = interface.SandboxId(
31
+ environment_id=interface.EnvironmentId('env'),
32
+ sandbox_id='sandbox'
33
+ )
34
+ self.assertEqual(str(sandbox_id), 'env/sandbox')
35
+ self.assertEqual(
36
+ sandbox_id.working_dir(root_dir='/tmp'),
37
+ '/tmp/env/sandbox'
38
+ )
39
+ self.assertIsNone(sandbox_id.working_dir(root_dir=None))
40
+
41
+
42
+ if __name__ == '__main__':
43
+ unittest.main()
@@ -0,0 +1,59 @@
1
+ # Copyright 2025 The Langfun Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Load balancers for environments."""
15
+
16
+ import abc
17
+ import threading
18
+
19
+ from langfun.env import interface
20
+ import pyglove as pg
21
+
22
+
23
+ class LoadBalancer(pg.Object):
24
+ """Base class for load balancers."""
25
+
26
+ @abc.abstractmethod
27
+ def acquire(self, sandbox_pool: list[interface.Sandbox]) -> interface.Sandbox:
28
+ """Acquires a free sandbox from a pool of sandboxes.
29
+
30
+ The load balancer will pick a sandbox from the pool and mark it as pending.
31
+
32
+ Args:
33
+ sandbox_pool: The pool of sandboxes to pick from.
34
+
35
+ Raises:
36
+ IndexError: If all sandboxes in the pool are either busy or dead.
37
+ """
38
+
39
+
40
+ class RoundRobin(LoadBalancer):
41
+ """Round robin load balancer."""
42
+
43
+ def _on_bound(self):
44
+ super()._on_bound()
45
+ self._counter = 0
46
+ self._acquire_lock = threading.Lock()
47
+
48
+ def acquire(self, sandbox_pool: list[interface.Sandbox]) -> interface.Sandbox:
49
+ """Returns a free sandbox from the pool."""
50
+ with self._acquire_lock:
51
+ for _ in range(len(sandbox_pool)):
52
+ sandbox = sandbox_pool[self._counter % len(sandbox_pool)]
53
+ self._counter = self._counter + 1
54
+ if sandbox.is_alive and not sandbox.is_busy and not sandbox.is_pending:
55
+ # Mark the sandbox as pending so that it will not be acquired by other
56
+ # threads.
57
+ sandbox.set_pending()
58
+ return sandbox
59
+ raise IndexError('No free sandbox in the pool.')
@@ -0,0 +1,157 @@
1
+ # Copyright 2025 The Langfun Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import concurrent.futures
15
+ import time
16
+ import unittest
17
+
18
+ from langfun.env import interface
19
+ from langfun.env import load_balancers
20
+
21
+
22
+ class TestingSandbox(interface.Sandbox):
23
+ sandbox_id: str
24
+ is_alive: bool = True
25
+ is_pending: bool = False
26
+ is_busy: bool = False
27
+
28
+ def _on_bound(self) -> None:
29
+ super()._on_bound()
30
+ self._session_id = None
31
+
32
+ @property
33
+ def id(self) -> interface.SandboxId:
34
+ return interface.SandboxId(
35
+ environment_id=interface.EnvironmentId('testing-env'),
36
+ sandbox_id=self.sandbox_id
37
+ )
38
+
39
+ @property
40
+ def environment(self) -> interface.Environment:
41
+ raise NotImplementedError()
42
+
43
+ @property
44
+ def features(self) -> dict[str, interface.Feature]:
45
+ raise NotImplementedError()
46
+
47
+ def set_pending(self, pending: bool = True) -> None:
48
+ self.rebind(
49
+ is_pending=pending, skip_notification=True, raise_on_no_change=False
50
+ )
51
+
52
+ def set_busy(self, busy: bool = True) -> None:
53
+ self.rebind(
54
+ is_busy=busy, skip_notification=True, raise_on_no_change=False
55
+ )
56
+
57
+ def set_alive(self, alive: bool = True) -> None:
58
+ self.rebind(
59
+ is_alive=alive, skip_notification=True, raise_on_no_change=False
60
+ )
61
+
62
+ def start(self) -> None:
63
+ self.set_alive()
64
+
65
+ def shutdown(self) -> None:
66
+ self.set_alive(False)
67
+
68
+ def ping(self) -> None:
69
+ pass
70
+
71
+ def start_session(self, session_id: str) -> None:
72
+ self._session_id = session_id
73
+
74
+ def end_session(self, session_id: str) -> None:
75
+ self._session_id = None
76
+
77
+ @property
78
+ def session_id(self) -> str | None:
79
+ return self._session_id
80
+
81
+
82
+ class RoundRobinTest(unittest.TestCase):
83
+
84
+ def test_basic(self):
85
+ sandbox_pool = [
86
+ TestingSandbox(
87
+ '0',
88
+ is_alive=False,
89
+ is_pending=False,
90
+ is_busy=False,
91
+ ),
92
+ TestingSandbox(
93
+ '1',
94
+ is_alive=True,
95
+ is_pending=True,
96
+ is_busy=False,
97
+ ),
98
+ TestingSandbox(
99
+ '2',
100
+ is_alive=True,
101
+ is_pending=False,
102
+ is_busy=True,
103
+ ),
104
+ TestingSandbox(
105
+ '3',
106
+ is_alive=True,
107
+ is_pending=False,
108
+ is_busy=False,
109
+ ),
110
+ TestingSandbox(
111
+ '4',
112
+ is_alive=True,
113
+ is_pending=False,
114
+ is_busy=False,
115
+ ),
116
+ ]
117
+ lb = load_balancers.RoundRobin()
118
+ sandbox = lb.acquire(sandbox_pool)
119
+ self.assertIs(sandbox, sandbox_pool[3])
120
+ self.assertTrue(sandbox.is_pending)
121
+
122
+ sandbox = lb.acquire(sandbox_pool)
123
+ self.assertIs(sandbox, sandbox_pool[4])
124
+ self.assertTrue(sandbox.is_pending)
125
+
126
+ sandbox_pool[0].set_alive()
127
+ sandbox = lb.acquire(sandbox_pool)
128
+ self.assertIs(sandbox, sandbox_pool[0])
129
+ self.assertTrue(sandbox.is_pending)
130
+
131
+ with self.assertRaisesRegex(IndexError, 'No free sandbox in the pool.'):
132
+ lb.acquire(sandbox_pool)
133
+
134
+ def test_thread_safety(self):
135
+ sandbox_pool = [TestingSandbox(str(i)) for i in range(64)]
136
+
137
+ lb = load_balancers.RoundRobin()
138
+
139
+ def _thread_func(i):
140
+ sandbox = lb.acquire(sandbox_pool)
141
+ time.sleep(0.1)
142
+ sandbox.set_busy()
143
+ sandbox.set_pending(False)
144
+ time.sleep(0.1)
145
+ sandbox.set_busy(False)
146
+ sandbox.set_alive(False)
147
+ time.sleep(0.1)
148
+ sandbox.set_alive()
149
+ return i
150
+
151
+ with concurrent.futures.ThreadPoolExecutor(max_workers=64) as executor:
152
+ for i, o in enumerate(executor.map(_thread_func, range(1024))):
153
+ self.assertEqual(o, i)
154
+
155
+
156
+ if __name__ == '__main__':
157
+ unittest.main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langfun
3
- Version: 0.1.2.dev202509150805
3
+ Version: 0.1.2.dev202509170804
4
4
  Summary: Langfun: Language as Functions.
5
5
  Home-page: https://github.com/google/langfun
6
6
  Author: Langfun Authors
@@ -165,8 +165,17 @@ langfun/core/templates/demonstration.py,sha256=vCrgYubdZM5Umqcgp8NUVGXgr4P_c-fik
165
165
  langfun/core/templates/demonstration_test.py,sha256=SafcDQ0WgI7pw05EmPI2S4v1t3ABKzup8jReCljHeK4,2162
166
166
  langfun/core/templates/selfplay.py,sha256=yhgrJbiYwq47TgzThmHrDQTF4nDrTI09CWGhuQPNv-s,2273
167
167
  langfun/core/templates/selfplay_test.py,sha256=Ot__1P1M8oJfoTp-M9-PQ6HUXqZKyMwvZ5f7yQ3yfyM,2326
168
- langfun-0.1.2.dev202509150805.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
169
- langfun-0.1.2.dev202509150805.dist-info/METADATA,sha256=WGTqnnkjfg9kGoytP4lQ57CpSlQAHLULz2jkcppH3rk,7380
170
- langfun-0.1.2.dev202509150805.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
171
- langfun-0.1.2.dev202509150805.dist-info/top_level.txt,sha256=RhlEkHxs1qtzmmtWSwYoLVJAc1YrbPtxQ52uh8Z9VvY,8
172
- langfun-0.1.2.dev202509150805.dist-info/RECORD,,
168
+ langfun/env/__init__.py,sha256=zgiONH9pIpNTEL623jRmW2mMI-GhThzEegZoys_znrQ,1554
169
+ langfun/env/base_environment.py,sha256=BMdSpsk0uMCPklrZ7XQPpJ03IL3ZEzCIs3RlUfG8yNo,14215
170
+ langfun/env/base_feature.py,sha256=SVvgoA68vafZZct5ZuHjPHblrCVv2Es4bLg5wgVTMww,4653
171
+ langfun/env/base_sandbox.py,sha256=oSuUSd3zVgBxH14r9uX9SelyoXHzQQWzGHEiain8eKo,13690
172
+ langfun/env/base_test.py,sha256=FuytEQJeKnxX_VEW0axdEoCg1A1d2rIVQss0qxoPPAQ,26739
173
+ langfun/env/interface.py,sha256=4Q6ewOtj8XJyy8OEQqSYwogjzfvivFOljGS5txhlx_U,21534
174
+ langfun/env/interface_test.py,sha256=nrqFLtpHd0vnOjoIX9cjV5WD1c-7J3WW29aogNu_94A,1395
175
+ langfun/env/load_balancers.py,sha256=jbT3pB7QpPVovbXeJK1PNohUcrYrJNtWU8KaWzxO9nI,1963
176
+ langfun/env/load_balancers_test.py,sha256=bAjC89zxmROpkl56BqvMU_yKOJ03Gtu8x8j56HO5HNc,4124
177
+ langfun-0.1.2.dev202509170804.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
178
+ langfun-0.1.2.dev202509170804.dist-info/METADATA,sha256=Ps_Bme20ro1JnbT4nQj2Cs-euv4K-DpwcTg4BQr37u4,7380
179
+ langfun-0.1.2.dev202509170804.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
180
+ langfun-0.1.2.dev202509170804.dist-info/top_level.txt,sha256=RhlEkHxs1qtzmmtWSwYoLVJAc1YrbPtxQ52uh8Z9VvY,8
181
+ langfun-0.1.2.dev202509170804.dist-info/RECORD,,