langfun 0.1.2.dev202501300804__py3-none-any.whl → 0.1.2.dev202502010803__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.
- langfun/core/coding/python/__init__.py +4 -0
- langfun/core/coding/python/sandboxing.py +152 -0
- langfun/core/coding/python/sandboxing_test.py +62 -0
- langfun/core/llms/__init__.py +2 -0
- langfun/core/llms/deepseek.py +17 -0
- langfun/core/llms/openai.py +22 -0
- {langfun-0.1.2.dev202501300804.dist-info → langfun-0.1.2.dev202502010803.dist-info}/METADATA +1 -1
- {langfun-0.1.2.dev202501300804.dist-info → langfun-0.1.2.dev202502010803.dist-info}/RECORD +11 -9
- {langfun-0.1.2.dev202501300804.dist-info → langfun-0.1.2.dev202502010803.dist-info}/LICENSE +0 -0
- {langfun-0.1.2.dev202501300804.dist-info → langfun-0.1.2.dev202502010803.dist-info}/WHEEL +0 -0
- {langfun-0.1.2.dev202501300804.dist-info → langfun-0.1.2.dev202502010803.dist-info}/top_level.txt +0 -0
@@ -32,5 +32,9 @@ from langfun.core.coding.python.correction import correct
|
|
32
32
|
from langfun.core.coding.python.correction import run_with_correction
|
33
33
|
from langfun.core.coding.python.correction import CodeWithError
|
34
34
|
|
35
|
+
from langfun.core.coding.python.sandboxing import Sandbox
|
36
|
+
from langfun.core.coding.python.sandboxing import SandboxOutput
|
37
|
+
from langfun.core.coding.python.sandboxing import MultiProcessingSandbox
|
38
|
+
|
35
39
|
# pylint: enable=g-importing-member
|
36
40
|
# pylint: enable=g-bad-import-order
|
@@ -0,0 +1,152 @@
|
|
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
|
+
"""Python sandboxing."""
|
15
|
+
|
16
|
+
import abc
|
17
|
+
import os
|
18
|
+
import tempfile
|
19
|
+
from typing import Annotated
|
20
|
+
|
21
|
+
from langfun.core.coding.python import parsing
|
22
|
+
import pyglove as pg
|
23
|
+
|
24
|
+
|
25
|
+
class SandboxOutput(pg.Object):
|
26
|
+
"""Sandbox output."""
|
27
|
+
|
28
|
+
stdout: Annotated[
|
29
|
+
str,
|
30
|
+
'The stdout of the sandbox execution.'
|
31
|
+
] = ''
|
32
|
+
|
33
|
+
stderr: Annotated[
|
34
|
+
str,
|
35
|
+
'The stderr of the sandbox execution.'
|
36
|
+
] = ''
|
37
|
+
|
38
|
+
|
39
|
+
class Sandbox(pg.Object):
|
40
|
+
"""Interface for Python sandbox."""
|
41
|
+
|
42
|
+
def _on_bound(self):
|
43
|
+
super()._on_bound()
|
44
|
+
self._uploaded_files: dict[str, str] = {}
|
45
|
+
|
46
|
+
def run(
|
47
|
+
self,
|
48
|
+
code: str,
|
49
|
+
*,
|
50
|
+
timeout: int | None = 30,
|
51
|
+
**kwargs
|
52
|
+
) -> SandboxOutput:
|
53
|
+
"""Runs code in the sandbox. Raises pg.coding.CodeError if failed."""
|
54
|
+
return self._run(self.normalize_code(code), timeout=timeout, **kwargs)
|
55
|
+
|
56
|
+
def normalize_code(self, code: str) -> str:
|
57
|
+
"""Returns normalized code runnable in the sandbox."""
|
58
|
+
for original_path, uploaded_path in self._uploaded_files.items():
|
59
|
+
code = code.replace(original_path, uploaded_path)
|
60
|
+
return parsing.clean(code)
|
61
|
+
|
62
|
+
@abc.abstractmethod
|
63
|
+
def _run(
|
64
|
+
self,
|
65
|
+
code: str,
|
66
|
+
*,
|
67
|
+
timeout: int | None = 30,
|
68
|
+
**kwargs
|
69
|
+
) -> SandboxOutput:
|
70
|
+
"""Runs code in the sandbox. Raises pg.coding.CodeError if failed."""
|
71
|
+
|
72
|
+
def upload(self, path: str) -> str:
|
73
|
+
"""Uploads a file to the sandbox. Returns the uploaded path."""
|
74
|
+
uploaded_path = self._upload(path)
|
75
|
+
self._uploaded_files[path] = uploaded_path
|
76
|
+
return uploaded_path
|
77
|
+
|
78
|
+
@abc.abstractmethod
|
79
|
+
def _upload(self, path: str) -> str:
|
80
|
+
"""Uploads a file to the sandbox."""
|
81
|
+
|
82
|
+
def setup(self) -> None:
|
83
|
+
"""Sets up the sandbox."""
|
84
|
+
self._uploaded_files = {}
|
85
|
+
self._setup()
|
86
|
+
|
87
|
+
def _setup(self) -> None:
|
88
|
+
"""Sets up the sandbox."""
|
89
|
+
|
90
|
+
def cleanup(self) -> None:
|
91
|
+
"""Cleans up the sandbox."""
|
92
|
+
self._uploaded_files = {}
|
93
|
+
self._cleanup()
|
94
|
+
|
95
|
+
def _cleanup(self) -> None:
|
96
|
+
"""Cleans up the sandbox."""
|
97
|
+
|
98
|
+
def __enter__(self):
|
99
|
+
"""Enters the sandbox."""
|
100
|
+
self.setup()
|
101
|
+
return self
|
102
|
+
|
103
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
104
|
+
"""Exits the sandbox."""
|
105
|
+
self.cleanup()
|
106
|
+
|
107
|
+
|
108
|
+
class MultiProcessingSandbox(Sandbox):
|
109
|
+
"""Sandbox using multiprocessing."""
|
110
|
+
|
111
|
+
def _on_bound(self):
|
112
|
+
super()._on_bound()
|
113
|
+
self._working_dir = None
|
114
|
+
|
115
|
+
@property
|
116
|
+
def working_dir(self) -> str | None:
|
117
|
+
"""Returns the directory of the sandbox."""
|
118
|
+
return self._working_dir
|
119
|
+
|
120
|
+
def _setup(self) -> None:
|
121
|
+
"""Sets up the sandbox."""
|
122
|
+
self._working_dir = tempfile.TemporaryDirectory()
|
123
|
+
|
124
|
+
def _cleanup(self) -> None:
|
125
|
+
"""Cleans up the sandbox."""
|
126
|
+
assert self._working_dir is not None
|
127
|
+
self._working_dir.cleanup()
|
128
|
+
|
129
|
+
def _run(
|
130
|
+
self,
|
131
|
+
code: str,
|
132
|
+
*,
|
133
|
+
timeout: int | None = 30,
|
134
|
+
**kwargs
|
135
|
+
) -> SandboxOutput:
|
136
|
+
"""Runs code in the sandbox."""
|
137
|
+
stdout = pg.coding.run(
|
138
|
+
code, returns_stdout=True, sandbox=True, timeout=timeout
|
139
|
+
)
|
140
|
+
return SandboxOutput(stdout=stdout)
|
141
|
+
|
142
|
+
def _upload(self, path: str) -> str:
|
143
|
+
"""Uploads a file to the sandbox."""
|
144
|
+
if self._working_dir is None:
|
145
|
+
raise ValueError('Sandbox is not set up.')
|
146
|
+
|
147
|
+
# Upload the file to the sandbox directory.
|
148
|
+
uploaded_path = os.path.join(self._working_dir.name, os.path.basename(path))
|
149
|
+
with pg.io.open(path, 'r') as r:
|
150
|
+
with pg.io.open(uploaded_path, 'w') as w:
|
151
|
+
w.write(r.read())
|
152
|
+
return uploaded_path
|
@@ -0,0 +1,62 @@
|
|
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 inspect
|
15
|
+
import os
|
16
|
+
import tempfile
|
17
|
+
import unittest
|
18
|
+
|
19
|
+
from langfun.core.coding.python import sandboxing
|
20
|
+
import pyglove as pg
|
21
|
+
|
22
|
+
|
23
|
+
class MultiProcessingSandboxTest(unittest.TestCase):
|
24
|
+
"""Tests for MultiProcessingSandbox."""
|
25
|
+
|
26
|
+
def test_basics(self):
|
27
|
+
with tempfile.TemporaryDirectory() as dir1:
|
28
|
+
input_file = os.path.join(dir1, 'test.json')
|
29
|
+
pg.save(pg.Dict(x=1, y=2), input_file)
|
30
|
+
with sandboxing.MultiProcessingSandbox() as sandbox:
|
31
|
+
self.assertIsNotNone(sandbox.working_dir)
|
32
|
+
sandbox_input_file = sandbox.upload(input_file)
|
33
|
+
code = (
|
34
|
+
f"""
|
35
|
+
import pyglove as pg
|
36
|
+
print(pg.load({input_file!r}).x)
|
37
|
+
"""
|
38
|
+
)
|
39
|
+
self.assertEqual(
|
40
|
+
sandbox.normalize_code(code),
|
41
|
+
inspect.cleandoc(
|
42
|
+
f"""
|
43
|
+
import pyglove as pg
|
44
|
+
print(pg.load({sandbox_input_file!r}).x)
|
45
|
+
"""
|
46
|
+
),
|
47
|
+
)
|
48
|
+
self.assertEqual(sandbox.run(code).stdout, '1\n')
|
49
|
+
|
50
|
+
def test_bad_code(self):
|
51
|
+
with sandboxing.MultiProcessingSandbox() as sandbox:
|
52
|
+
with self.assertRaisesRegex(pg.coding.CodeError, '.* is not defined'):
|
53
|
+
sandbox.run('print(x)')
|
54
|
+
|
55
|
+
def test_bad_usages(self):
|
56
|
+
sandbox = sandboxing.MultiProcessingSandbox()
|
57
|
+
with self.assertRaisesRegex(ValueError, 'Sandbox is not set up.'):
|
58
|
+
sandbox.upload('abc.txt')
|
59
|
+
|
60
|
+
|
61
|
+
if __name__ == '__main__':
|
62
|
+
unittest.main()
|
langfun/core/llms/__init__.py
CHANGED
@@ -67,6 +67,7 @@ from langfun.core.llms.vertexai import VertexAIGeminiPro1
|
|
67
67
|
# OpenAI models.
|
68
68
|
from langfun.core.llms.openai import OpenAI
|
69
69
|
|
70
|
+
from langfun.core.llms.openai import GptO3Mini
|
70
71
|
from langfun.core.llms.openai import GptO1
|
71
72
|
from langfun.core.llms.openai import GptO1Preview
|
72
73
|
from langfun.core.llms.openai import GptO1Preview_20240912
|
@@ -167,6 +168,7 @@ from langfun.core.llms.groq import GroqMistral_8x7B
|
|
167
168
|
# DeepSeek models.
|
168
169
|
from langfun.core.llms.deepseek import DeepSeek
|
169
170
|
from langfun.core.llms.deepseek import DeepSeekChat
|
171
|
+
from langfun.core.llms.deepseek import DeepSeekReasoner
|
170
172
|
|
171
173
|
# Whisper models.
|
172
174
|
from langfun.core.llms.groq import GroqWhisper_Large_v3
|
langfun/core/llms/deepseek.py
CHANGED
@@ -25,6 +25,13 @@ SUPPORTED_MODELS_AND_SETTINGS = {
|
|
25
25
|
# TODO(yifenglu): The RPM and TPM are arbitrary numbers. Update them once DeepSeek provides concrete guidelines.
|
26
26
|
# DeepSeek doesn't control the rate limit at the moment: https://api-docs.deepseek.com/quick_start/rate_limit
|
27
27
|
# The cost is based on: https://api-docs.deepseek.com/quick_start/pricing
|
28
|
+
'deepseek-reasoner': pg.Dict(
|
29
|
+
in_service=True,
|
30
|
+
rpm=100,
|
31
|
+
tpm=1000000,
|
32
|
+
cost_per_1k_input_tokens=0.00055,
|
33
|
+
cost_per_1k_output_tokens=0.00219,
|
34
|
+
),
|
28
35
|
'deepseek-chat': pg.Dict(
|
29
36
|
in_service=True,
|
30
37
|
rpm=100,
|
@@ -107,6 +114,16 @@ class DeepSeek(openai_compatible.OpenAICompatible):
|
|
107
114
|
return [k for k, v in SUPPORTED_MODELS_AND_SETTINGS.items() if v.in_service]
|
108
115
|
|
109
116
|
|
117
|
+
class DeepSeekReasoner(DeepSeek):
|
118
|
+
"""DeepSeek Reasoner model.
|
119
|
+
|
120
|
+
Currently it is powered by DeepSeek-R1 model, 64k input context, 8k max
|
121
|
+
output, 32k max CoT output.
|
122
|
+
"""
|
123
|
+
|
124
|
+
model = 'deepseek-reasoner'
|
125
|
+
|
126
|
+
|
110
127
|
class DeepSeekChat(DeepSeek):
|
111
128
|
"""DeepSeek Chat model.
|
112
129
|
|
langfun/core/llms/openai.py
CHANGED
@@ -31,6 +31,20 @@ SUPPORTED_MODELS_AND_SETTINGS = {
|
|
31
31
|
# o1 (preview) models.
|
32
32
|
# Pricing in US dollars, from https://openai.com/api/pricing/
|
33
33
|
# as of 2024-10-10.
|
34
|
+
'o3-mini-2025-01-31': pg.Dict(
|
35
|
+
in_service=True,
|
36
|
+
rpm=10000,
|
37
|
+
tpm=5000000,
|
38
|
+
cost_per_1k_input_tokens=0.0011,
|
39
|
+
cost_per_1k_output_tokens=0.0044,
|
40
|
+
),
|
41
|
+
'o3-mini': pg.Dict(
|
42
|
+
in_service=True,
|
43
|
+
rpm=10000,
|
44
|
+
tpm=5000000,
|
45
|
+
cost_per_1k_input_tokens=0.0011,
|
46
|
+
cost_per_1k_output_tokens=0.0044,
|
47
|
+
),
|
34
48
|
'o1': pg.Dict(
|
35
49
|
in_service=True,
|
36
50
|
rpm=10000,
|
@@ -410,6 +424,14 @@ class OpenAI(openai_compatible.OpenAICompatible):
|
|
410
424
|
return super()._request_args(options)
|
411
425
|
|
412
426
|
|
427
|
+
class GptO3Mini(OpenAI):
|
428
|
+
"""GPT-O3-mini."""
|
429
|
+
|
430
|
+
model = 'o3-mini'
|
431
|
+
multimodal = True
|
432
|
+
timeout = None
|
433
|
+
|
434
|
+
|
413
435
|
class GptO1(OpenAI):
|
414
436
|
"""GPT-O1."""
|
415
437
|
|
@@ -31,7 +31,7 @@ langfun/core/agentic/action_eval.py,sha256=ZtjTh34S7XPIUqandQ0YwAtzw-S7ofuZ7rRXn
|
|
31
31
|
langfun/core/agentic/action_eval_test.py,sha256=tRUkWmOE9p0rpNOq19xAY2oDEnYsEEykjg6sUpAwJk0,2832
|
32
32
|
langfun/core/agentic/action_test.py,sha256=Gu7P5XQvzqbKawn2jjyTpWaARzzhzO04KkC1TuBnUnw,4612
|
33
33
|
langfun/core/coding/__init__.py,sha256=5utju_fwEsImaiftx4oXKl9FAM8p281k8-Esdh_-m1w,835
|
34
|
-
langfun/core/coding/python/__init__.py,sha256=
|
34
|
+
langfun/core/coding/python/__init__.py,sha256=4ByknuoNU-mOIHwHKnTtmo6oD64oMFtlqPlYWmA5Wic,1736
|
35
35
|
langfun/core/coding/python/correction.py,sha256=7zBedlhQKMPA4cfchUMxAOFl6Zl5RqCyllRHGWys40s,7092
|
36
36
|
langfun/core/coding/python/correction_test.py,sha256=sie88lAbsV15bvkRcYC88pgToybZYXI32Xmg_ym5V1A,4175
|
37
37
|
langfun/core/coding/python/execution.py,sha256=tsXnJ-11RqNDro0C-6LwbHkqPuNVJ_cLxAq8-C5Wz20,4442
|
@@ -40,6 +40,8 @@ langfun/core/coding/python/generation.py,sha256=sA6t97qBflO3WtuL9axknEVQPgqXHeqa
|
|
40
40
|
langfun/core/coding/python/generation_test.py,sha256=dcF5ef1UApzRfTpvICiChpynkzZ1mDsE0DvH0iMpTvg,2743
|
41
41
|
langfun/core/coding/python/parsing.py,sha256=jvGDIwoaY3mdGXeFhjP27w0ukO0TtdCC7G4ODVNp8S4,4554
|
42
42
|
langfun/core/coding/python/parsing_test.py,sha256=PIexYpSEhgNaSd4T6QYWzWHzm3sL4VhQJ4dhdvJAQk8,5005
|
43
|
+
langfun/core/coding/python/sandboxing.py,sha256=yeEdydMkfHk3Hj3-5ykeROpYyLbRfZ4BwGWJYvFBmSI,4001
|
44
|
+
langfun/core/coding/python/sandboxing_test.py,sha256=H_0_pd-_uS-ci5yYhmDTR6-hyzosAFkExziAHndfdDo,2023
|
43
45
|
langfun/core/eval/__init__.py,sha256=OEXr1ZRuvLuhJJfuQ1ZWQ-SvYzjyrtiAAEogYaB7E6o,1933
|
44
46
|
langfun/core/eval/base.py,sha256=XXerMVkK4wREo7K1_aCyay6vDjw3mfs389XThAdzv50,75768
|
45
47
|
langfun/core/eval/base_test.py,sha256=-LsIV9DXlDal0EnOlaWpibJvfef0NbxtZAm0OH_abAE,27189
|
@@ -71,12 +73,12 @@ langfun/core/eval/v2/reporting.py,sha256=QOp5jX761Esvi5w_UIRLDqPY_XRO6ru02-DOrdq
|
|
71
73
|
langfun/core/eval/v2/reporting_test.py,sha256=UmYSAQvD3AIXsSyWQ-WD2uLtEISYpmBeoKY5u5Qwc8E,5696
|
72
74
|
langfun/core/eval/v2/runners.py,sha256=DKEmSlGXjOXKWFdBhTpLy7tMsBHZHd1Brl3hWIngsSQ,15931
|
73
75
|
langfun/core/eval/v2/runners_test.py,sha256=A37fKK2MvAVTiShsg_laluJzJ9AuAQn52k7HPbfD0Ks,11666
|
74
|
-
langfun/core/llms/__init__.py,sha256=
|
76
|
+
langfun/core/llms/__init__.py,sha256=PKkEVyGBSMc1CZOh8vVN-NCxBPDv3Sctt0fsN7NU1EU,7770
|
75
77
|
langfun/core/llms/anthropic.py,sha256=z_DWDpR1VKNzv6wq-9CXLzWdqCDXRKuVFacJNpgBqAs,10826
|
76
78
|
langfun/core/llms/anthropic_test.py,sha256=zZ2eSP8hhVv-RDSWxT7wX-NS5DfGfQmCjS9P0pusAHM,6556
|
77
79
|
langfun/core/llms/compositional.py,sha256=csW_FLlgL-tpeyCOTVvfUQkMa_zCN5Y2I-YbSNuK27U,2872
|
78
80
|
langfun/core/llms/compositional_test.py,sha256=4eTnOer-DncRKGaIJW2ZQQMLnt5r2R0UIx_DYOvGAQo,2027
|
79
|
-
langfun/core/llms/deepseek.py,sha256=
|
81
|
+
langfun/core/llms/deepseek.py,sha256=9EbuNrngd5BwpsvsEfkW2XQdq3K23S3-jlI8RbMNZF4,4141
|
80
82
|
langfun/core/llms/deepseek_test.py,sha256=dS72i52bwMpCN4dJDvpJI59AnNChpwxS5eYYFrhGh90,1843
|
81
83
|
langfun/core/llms/fake.py,sha256=gCHBYBLvBCsC78HI1hpoqXCS-p1FMTgY1P1qh_sGBPk,3070
|
82
84
|
langfun/core/llms/fake_test.py,sha256=2h13qkwEz_JR0mtUDPxdAhQo7MueXaFSwsD2DIRDW9g,7653
|
@@ -88,7 +90,7 @@ langfun/core/llms/groq.py,sha256=oGxyyCi5TtMVu2POdO8pXS7pK4Es56FtqjghZIGYopc,757
|
|
88
90
|
langfun/core/llms/groq_test.py,sha256=9aOD3nO4dEoH57B-5iOr5DQjG0vyv1jzFtAe7HfoiNg,1963
|
89
91
|
langfun/core/llms/llama_cpp.py,sha256=Z7P3gc4xeIjc2bX0Ey1y5EUYJVMnMa2Q67PZ9iye9sE,1409
|
90
92
|
langfun/core/llms/llama_cpp_test.py,sha256=wfTO7nmUwL65U2kK9P9fcMt92JjNDuVia4G1E7znf_4,1086
|
91
|
-
langfun/core/llms/openai.py,sha256=
|
93
|
+
langfun/core/llms/openai.py,sha256=bIJae2UDO_uwoZGZRYP58GM4FdP4w9qjQhBIYJyGH34,17089
|
92
94
|
langfun/core/llms/openai_compatible.py,sha256=MF9JCc0uTPkIK95uvQTMIBXk4z_xKQMZqQHFaVmXwcA,5898
|
93
95
|
langfun/core/llms/openai_compatible_test.py,sha256=0uFYhCiuHo2Wrlgj16-GRG6rW8P6EaHCUguL3jS0QJ8,16708
|
94
96
|
langfun/core/llms/openai_test.py,sha256=m85YjGCvWvV5ZYagjC0FqI0FcqyCEVCbUUs8Wm3iUrc,2475
|
@@ -146,8 +148,8 @@ langfun/core/templates/demonstration.py,sha256=vCrgYubdZM5Umqcgp8NUVGXgr4P_c-fik
|
|
146
148
|
langfun/core/templates/demonstration_test.py,sha256=SafcDQ0WgI7pw05EmPI2S4v1t3ABKzup8jReCljHeK4,2162
|
147
149
|
langfun/core/templates/selfplay.py,sha256=yhgrJbiYwq47TgzThmHrDQTF4nDrTI09CWGhuQPNv-s,2273
|
148
150
|
langfun/core/templates/selfplay_test.py,sha256=Ot__1P1M8oJfoTp-M9-PQ6HUXqZKyMwvZ5f7yQ3yfyM,2326
|
149
|
-
langfun-0.1.2.
|
150
|
-
langfun-0.1.2.
|
151
|
-
langfun-0.1.2.
|
152
|
-
langfun-0.1.2.
|
153
|
-
langfun-0.1.2.
|
151
|
+
langfun-0.1.2.dev202502010803.dist-info/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
152
|
+
langfun-0.1.2.dev202502010803.dist-info/METADATA,sha256=sDqvjFmjKbCvYCNHOc4te_JsKdrhnzWgmPx5wwSI_GM,8172
|
153
|
+
langfun-0.1.2.dev202502010803.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
154
|
+
langfun-0.1.2.dev202502010803.dist-info/top_level.txt,sha256=RhlEkHxs1qtzmmtWSwYoLVJAc1YrbPtxQ52uh8Z9VvY,8
|
155
|
+
langfun-0.1.2.dev202502010803.dist-info/RECORD,,
|
File without changes
|
File without changes
|
{langfun-0.1.2.dev202501300804.dist-info → langfun-0.1.2.dev202502010803.dist-info}/top_level.txt
RENAMED
File without changes
|