typellm 0.2.6__tar.gz → 0.3.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.
- {typellm-0.2.6 → typellm-0.3.0}/PKG-INFO +1 -1
- {typellm-0.2.6 → typellm-0.3.0}/pyproject.toml +1 -1
- {typellm-0.2.6 → typellm-0.3.0}/tests/test_batching.py +11 -10
- {typellm-0.2.6 → typellm-0.3.0}/tests/test_concurrency.py +9 -9
- {typellm-0.2.6 → typellm-0.3.0}/tests/test_grammar_fields.py +6 -6
- {typellm-0.2.6 → typellm-0.3.0}/tests/test_images.py +8 -8
- {typellm-0.2.6 → typellm-0.3.0}/tests/test_nullable.py +4 -4
- {typellm-0.2.6 → typellm-0.3.0}/tests/test_permutations.py +6 -6
- {typellm-0.2.6 → typellm-0.3.0}/tests/test_prefill.py +8 -6
- {typellm-0.2.6 → typellm-0.3.0}/tests/test_probabilities.py +1 -1
- {typellm-0.2.6 → typellm-0.3.0}/tests/test_prompts.py +1 -1
- {typellm-0.2.6 → typellm-0.3.0}/tests/test_text.py +3 -3
- {typellm-0.2.6 → typellm-0.3.0}/tests/test_typellm.py +256 -20
- {typellm-0.2.6 → typellm-0.3.0}/typellm/runtime.py +163 -17
- {typellm-0.2.6 → typellm-0.3.0}/typellm/sglang.py +27 -5
- {typellm-0.2.6 → typellm-0.3.0}/typellm.egg-info/PKG-INFO +1 -1
- {typellm-0.2.6 → typellm-0.3.0}/LICENSE +0 -0
- {typellm-0.2.6 → typellm-0.3.0}/README.md +0 -0
- {typellm-0.2.6 → typellm-0.3.0}/setup.cfg +0 -0
- {typellm-0.2.6 → typellm-0.3.0}/tests/test_dependencies.py +0 -0
- {typellm-0.2.6 → typellm-0.3.0}/tests/test_protocols.py +0 -0
- {typellm-0.2.6 → typellm-0.3.0}/typellm/__init__.py +0 -0
- {typellm-0.2.6 → typellm-0.3.0}/typellm/__main__.py +0 -0
- {typellm-0.2.6 → typellm-0.3.0}/typellm/benchmark.py +0 -0
- {typellm-0.2.6 → typellm-0.3.0}/typellm/cli.py +0 -0
- {typellm-0.2.6 → typellm-0.3.0}/typellm/images.py +0 -0
- {typellm-0.2.6 → typellm-0.3.0}/typellm/protocol.py +0 -0
- {typellm-0.2.6 → typellm-0.3.0}/typellm/schema.py +0 -0
- {typellm-0.2.6 → typellm-0.3.0}/typellm.egg-info/SOURCES.txt +0 -0
- {typellm-0.2.6 → typellm-0.3.0}/typellm.egg-info/dependency_links.txt +0 -0
- {typellm-0.2.6 → typellm-0.3.0}/typellm.egg-info/entry_points.txt +0 -0
- {typellm-0.2.6 → typellm-0.3.0}/typellm.egg-info/requires.txt +0 -0
- {typellm-0.2.6 → typellm-0.3.0}/typellm.egg-info/top_level.txt +0 -0
|
@@ -94,7 +94,7 @@ class PrefixWarmupTests(unittest.TestCase):
|
|
|
94
94
|
def test_the_context_is_warmed_before_any_batch_that_shares_it(self):
|
|
95
95
|
for thinking in (False, True):
|
|
96
96
|
with self.subTest(thinking=thinking):
|
|
97
|
-
client = TypeLLMClient(model="fake")
|
|
97
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
98
98
|
client.sglang = FakeServer()
|
|
99
99
|
client.generate(context="Receipt", questions=thinks(self.MIXED) if thinking else self.MIXED).result
|
|
100
100
|
warm = client.sglang.requests("count")
|
|
@@ -105,7 +105,7 @@ class PrefixWarmupTests(unittest.TestCase):
|
|
|
105
105
|
self.assertNotIn('{"a":', warm[0]["text"])
|
|
106
106
|
|
|
107
107
|
def test_open_fields_alone_are_not_warmed(self):
|
|
108
|
-
client = TypeLLMClient(model="fake")
|
|
108
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
109
109
|
client.sglang = FakeServer()
|
|
110
110
|
client.generate(context="Receipt", questions={"a": {"type": "integer"}, "n": {"type": "string"}}).result
|
|
111
111
|
self.assertEqual(client.sglang.requests("count"), [])
|
|
@@ -117,7 +117,7 @@ class PerFieldThinkingTests(unittest.TestCase):
|
|
|
117
117
|
for t in ([p["text"]] if isinstance(p["text"], str) else p["text"])]
|
|
118
118
|
|
|
119
119
|
def test_only_fields_that_ask_for_it_think(self):
|
|
120
|
-
client = TypeLLMClient(model="fake")
|
|
120
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
121
121
|
client.sglang = FakeServer()
|
|
122
122
|
result = client.generate(context="Receipt", questions={
|
|
123
123
|
"hard": {"type": "boolean", "instructions": "Hard?", "thinking": True},
|
|
@@ -133,7 +133,7 @@ class PerFieldThinkingTests(unittest.TestCase):
|
|
|
133
133
|
self.assertTrue(any("Easy?" in t and "<think>" not in t for t in answers))
|
|
134
134
|
|
|
135
135
|
def test_thinking_false_is_the_same_as_leaving_it_out(self):
|
|
136
|
-
client = TypeLLMClient(model="fake")
|
|
136
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
137
137
|
client.sglang = FakeServer()
|
|
138
138
|
client.generate(context="Receipt", questions={
|
|
139
139
|
"a": {"type": "boolean", "instructions": "A?", "thinking": True},
|
|
@@ -146,7 +146,7 @@ class PerFieldThinkingTests(unittest.TestCase):
|
|
|
146
146
|
self.assertFalse(any("Skip?" in t for t in prompts))
|
|
147
147
|
|
|
148
148
|
def test_each_field_gets_its_own_budget(self):
|
|
149
|
-
client = TypeLLMClient(model="fake")
|
|
149
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
150
150
|
client.sglang = FakeServer()
|
|
151
151
|
client.sglang.thinking_budget = 500 # the client's budget, for fields without their own
|
|
152
152
|
client.generate(context="Receipt", questions={
|
|
@@ -170,7 +170,7 @@ class PerFieldThinkingTests(unittest.TestCase):
|
|
|
170
170
|
item["meta_info"]["completion_tokens"] = 40 if thinking else 1
|
|
171
171
|
return response
|
|
172
172
|
|
|
173
|
-
client = TypeLLMClient(model="fake")
|
|
173
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
174
174
|
client.sglang = Metered()
|
|
175
175
|
done = client.generate(context="Receipt", questions={
|
|
176
176
|
"hard": {"type": "boolean", "instructions": "Hard?", "thinking": True},
|
|
@@ -186,7 +186,8 @@ class PerFieldThinkingTests(unittest.TestCase):
|
|
|
186
186
|
for field in ({"type": "boolean", "thinking": "yes"}, {"type": "boolean", "thinking_budget": 0},
|
|
187
187
|
{"type": "boolean", "thinking_budget": 2.5}):
|
|
188
188
|
with self.subTest(field=field), self.assertRaises(SchemaError):
|
|
189
|
-
TypeLLMClient(model="fake").compile_schema(
|
|
189
|
+
TypeLLMClient("http://127.0.0.1:30000", model="fake").compile_schema(
|
|
190
|
+
{"type": "object", "properties": {"x": field}})
|
|
190
191
|
|
|
191
192
|
|
|
192
193
|
class BatchedThinkingTests(unittest.TestCase):
|
|
@@ -198,7 +199,7 @@ class BatchedThinkingTests(unittest.TestCase):
|
|
|
198
199
|
}
|
|
199
200
|
|
|
200
201
|
def test_one_thinking_request_covers_every_prompt_in_a_layer(self):
|
|
201
|
-
client = TypeLLMClient(model="fake")
|
|
202
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
202
203
|
client.sglang = FakeServer()
|
|
203
204
|
result = client.generate(context="Receipt", questions=thinks(self.QUESTIONS)).result
|
|
204
205
|
self.assertEqual(result, {"flag": True, "count": 7, "name": "blue", "pick": "x"})
|
|
@@ -211,7 +212,7 @@ class BatchedThinkingTests(unittest.TestCase):
|
|
|
211
212
|
self.assertTrue(all("Reasoned." + THINK_STOP in t for t in texts))
|
|
212
213
|
|
|
213
214
|
def test_dag_thinks_once_per_layer(self):
|
|
214
|
-
client = TypeLLMClient(model="fake")
|
|
215
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
215
216
|
client.sglang = FakeServer()
|
|
216
217
|
client.generate(context="Receipt", questions=thinks({
|
|
217
218
|
"a": {"type": "boolean"}, "b": {"type": "integer"},
|
|
@@ -221,7 +222,7 @@ class BatchedThinkingTests(unittest.TestCase):
|
|
|
221
222
|
self.assertEqual([width(p) for p in client.sglang.requests("think")], [2, 2])
|
|
222
223
|
|
|
223
224
|
def test_images_count_prompt_tokens_in_one_batch(self):
|
|
224
|
-
client = TypeLLMClient(model="fake")
|
|
225
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
225
226
|
client.sglang = FakeServer()
|
|
226
227
|
client.generate(context="Receipt", images=[PNG],
|
|
227
228
|
questions=thinks({"a": {"type": "boolean"}, "b": {"type": "boolean"}})).result
|
|
@@ -35,7 +35,7 @@ class SlowServer(FakeServer):
|
|
|
35
35
|
|
|
36
36
|
class SharedClientTests(unittest.TestCase):
|
|
37
37
|
def test_concurrent_calls_keep_their_own_prompts(self):
|
|
38
|
-
client = TypeLLMClient(model="fake")
|
|
38
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
39
39
|
client.sglang = SlowServer()
|
|
40
40
|
finished = threading.Barrier(8)
|
|
41
41
|
|
|
@@ -53,7 +53,7 @@ class SharedClientTests(unittest.TestCase):
|
|
|
53
53
|
self.assertIn(f"Receipt {n}", prompt)
|
|
54
54
|
|
|
55
55
|
def test_a_seeded_call_is_reproducible_and_leaves_the_shared_stream(self):
|
|
56
|
-
client = TypeLLMClient(model="fake", seed=1)
|
|
56
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake", seed=1)
|
|
57
57
|
client.sglang = FakeServer()
|
|
58
58
|
state = client.rng.getstate()
|
|
59
59
|
client.generate(context="Roll", questions=PICK, seed=5).result
|
|
@@ -65,7 +65,7 @@ class SharedClientTests(unittest.TestCase):
|
|
|
65
65
|
self.assertNotEqual(client.rng.getstate(), state)
|
|
66
66
|
|
|
67
67
|
def test_clients_pickle_after_a_call(self):
|
|
68
|
-
client = TypeLLMClient(model="fake")
|
|
68
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
69
69
|
client.sglang = FakeServer()
|
|
70
70
|
client.generate(context="Receipt", questions={"b": {"type": "boolean"}}).result
|
|
71
71
|
copy = pickle.loads(pickle.dumps(client))
|
|
@@ -100,7 +100,7 @@ class UsageTests(unittest.TestCase):
|
|
|
100
100
|
QUESTIONS = {"a": {"type": "integer"}, "b": {"type": "boolean"}, "c": {"type": "string"}}
|
|
101
101
|
|
|
102
102
|
def test_usage_sums_every_generate_request_of_the_call(self):
|
|
103
|
-
client = TypeLLMClient(model="fake")
|
|
103
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
104
104
|
client.sglang = MeteredServer()
|
|
105
105
|
usage = client.generate(context="Receipt", questions=self.QUESTIONS).usage
|
|
106
106
|
n = prompts_sent(client.sglang)
|
|
@@ -121,7 +121,7 @@ class UsageTests(unittest.TestCase):
|
|
|
121
121
|
"Usage(input_tokens=5, thinking_tokens=0, requests=2, prompt_tokens=30)")
|
|
122
122
|
|
|
123
123
|
def test_concurrent_calls_count_only_their_own_requests(self):
|
|
124
|
-
client = TypeLLMClient(model="fake")
|
|
124
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
125
125
|
client.sglang = MeteredServer()
|
|
126
126
|
finished = threading.Barrier(4)
|
|
127
127
|
|
|
@@ -139,7 +139,7 @@ class UsageTests(unittest.TestCase):
|
|
|
139
139
|
self.assertEqual(sum(counts), len(client.sglang.payloads))
|
|
140
140
|
|
|
141
141
|
def test_a_failed_call_still_reports_the_requests_it_made(self):
|
|
142
|
-
client = TypeLLMClient(model="fake")
|
|
142
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
143
143
|
client.sglang = MeteredServer(fail_after=2)
|
|
144
144
|
with self.assertRaisesRegex(SGLangError, "went away") as caught:
|
|
145
145
|
client.generate(context="Receipt", questions=self.QUESTIONS)
|
|
@@ -160,7 +160,7 @@ class DeadlineTests(unittest.TestCase):
|
|
|
160
160
|
time.sleep(0.03)
|
|
161
161
|
return super()._request(path, payload, allow_text=allow_text)
|
|
162
162
|
|
|
163
|
-
client = TypeLLMClient(model="fake")
|
|
163
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
164
164
|
client.sglang = Slower()
|
|
165
165
|
client.generate(context="Receipt", questions=self.QUESTIONS).result
|
|
166
166
|
needed = len(client.sglang.payloads)
|
|
@@ -180,7 +180,7 @@ class DeadlineTests(unittest.TestCase):
|
|
|
180
180
|
cancel.set()
|
|
181
181
|
return response
|
|
182
182
|
|
|
183
|
-
client = TypeLLMClient(model="fake")
|
|
183
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
184
184
|
client.sglang = Cancelling()
|
|
185
185
|
with self.assertRaises(GenerationCancelled):
|
|
186
186
|
client.generate(context="Receipt", questions=self.QUESTIONS, cancel=cancel).result
|
|
@@ -254,7 +254,7 @@ class DeadlineTests(unittest.TestCase):
|
|
|
254
254
|
self.paths.append(path)
|
|
255
255
|
return super()._request(path, payload, allow_text=allow_text)
|
|
256
256
|
|
|
257
|
-
client = TypeLLMClient(model="fake")
|
|
257
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
258
258
|
client.sglang = Recording()
|
|
259
259
|
client.sglang.paths = []
|
|
260
260
|
for kwargs in ({"timeout": 0}, {"timeout": -1}, {"timeout": True}, {"timeout": "5"},
|
|
@@ -61,7 +61,7 @@ class GrammarNumberTests(unittest.TestCase):
|
|
|
61
61
|
QUESTIONS = {"a": {"type": "integer"}, "b": {"type": "number"}, "c": {"type": "integer"}}
|
|
62
62
|
|
|
63
63
|
def test_every_number_of_a_layer_takes_one_request(self):
|
|
64
|
-
client = TypeLLMClient(model="fake")
|
|
64
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
65
65
|
client.sglang = FakeServer()
|
|
66
66
|
result = client.generate(context="Receipt", questions=self.QUESTIONS).result
|
|
67
67
|
self.assertEqual(result, {"a": 7, "b": 7, "c": 7})
|
|
@@ -73,7 +73,7 @@ class GrammarNumberTests(unittest.TestCase):
|
|
|
73
73
|
self.assertIn('{"a": 7}', client._last_prompts.get()[0])
|
|
74
74
|
|
|
75
75
|
def test_numbers_and_strings_of_a_layer_share_one_request(self):
|
|
76
|
-
client = TypeLLMClient(model="fake")
|
|
76
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
77
77
|
client.sglang = FakeServer()
|
|
78
78
|
result = client.generate(context="Receipt", questions={
|
|
79
79
|
"a": {"type": "integer"}, "n": {"type": "string"},
|
|
@@ -87,14 +87,14 @@ class GrammarNumberTests(unittest.TestCase):
|
|
|
87
87
|
self.assertIn("null", patterns[3])
|
|
88
88
|
|
|
89
89
|
def test_sampling_passes_the_temperature_and_no_truncation(self):
|
|
90
|
-
client = TypeLLMClient(model="fake", temperature=0.7, seed=1)
|
|
90
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake", temperature=0.7, seed=1)
|
|
91
91
|
client.sglang = FakeServer()
|
|
92
92
|
client.generate(context="Receipt", questions={"a": {"type": "integer"}}).result
|
|
93
93
|
[params] = regex_requests(client.sglang)[0]["sampling_params"]
|
|
94
94
|
self.assertEqual((params["temperature"], params["top_p"], params["top_k"]), (0.7, 1.0, -1))
|
|
95
95
|
|
|
96
96
|
def test_nullable_numbers_can_be_null_in_the_same_request(self):
|
|
97
|
-
client = TypeLLMClient(model="fake")
|
|
97
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
98
98
|
client.sglang = NullServer()
|
|
99
99
|
result = client.generate(context="Receipt", questions={"tip": {"type": ["number", "null"]}}).result
|
|
100
100
|
self.assertEqual(result, {"tip": None})
|
|
@@ -104,7 +104,7 @@ class GrammarNumberTests(unittest.TestCase):
|
|
|
104
104
|
def test_an_incomplete_or_cut_off_number_is_an_error(self):
|
|
105
105
|
for reply, finish, error in ((" 3.}", "stop", ValueError), (" 12", "length", SGLangError)):
|
|
106
106
|
with self.subTest(reply=reply):
|
|
107
|
-
client = TypeLLMClient(model="fake")
|
|
107
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
108
108
|
client.sglang = ReplyServer(reply, finish)
|
|
109
109
|
with self.assertRaises(error):
|
|
110
110
|
client.generate(context="Receipt", questions={"a": {"type": "number"}}).result
|
|
@@ -112,7 +112,7 @@ class GrammarNumberTests(unittest.TestCase):
|
|
|
112
112
|
def test_negative_and_spaceless_numbers_parse(self):
|
|
113
113
|
for reply, value in ((" -3.5}", -3.5), ("42}", 42.0), (" 0", 0.0)):
|
|
114
114
|
with self.subTest(reply=reply):
|
|
115
|
-
client = TypeLLMClient(model="fake")
|
|
115
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
116
116
|
client.sglang = ReplyServer(reply)
|
|
117
117
|
self.assertEqual(client.generate(context="R", questions={"a": {"type": "number"}}).result,
|
|
118
118
|
{"a": value})
|
|
@@ -161,7 +161,7 @@ class ImageEncodingTests(unittest.TestCase):
|
|
|
161
161
|
|
|
162
162
|
class PicklingTests(unittest.TestCase):
|
|
163
163
|
def test_clients_pickle_and_copy_without_sharing_images(self):
|
|
164
|
-
client = TypeLLMClient(model="m")
|
|
164
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="m")
|
|
165
165
|
for copy in (pickle.loads(pickle.dumps(client)), deepcopy(client)):
|
|
166
166
|
self.assertEqual(copy.sglang.model, "m")
|
|
167
167
|
token = client.sglang._active_images.set(("a",))
|
|
@@ -177,7 +177,7 @@ class PlaceholderTests(unittest.TestCase):
|
|
|
177
177
|
self.assertEqual(FakeServerClient().image_placeholder(), VISION)
|
|
178
178
|
|
|
179
179
|
def test_text_only_template_is_rejected_before_any_request(self):
|
|
180
|
-
client = TypeLLMClient(model="fake")
|
|
180
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
181
181
|
client.sglang = FakeServerClient(VisionTokenizer(renders_images=False))
|
|
182
182
|
with self.assertRaisesRegex(SGLangError, "does not render image content"):
|
|
183
183
|
client.generate(context="Receipt", images=[PNG],
|
|
@@ -185,7 +185,7 @@ class PlaceholderTests(unittest.TestCase):
|
|
|
185
185
|
self.assertEqual(client.sglang.generate_payloads, [])
|
|
186
186
|
|
|
187
187
|
def test_context_containing_image_tokens_is_rejected(self):
|
|
188
|
-
client = TypeLLMClient(model="fake")
|
|
188
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
189
189
|
client.sglang = FakeServerClient()
|
|
190
190
|
with self.assertRaisesRegex(SGLangError, "2 image placeholders for 1 images"):
|
|
191
191
|
client.generate(context=VISION, images=[PNG],
|
|
@@ -199,7 +199,7 @@ class ImageRequestTests(unittest.TestCase):
|
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
def run_generate(self, images, **kwargs):
|
|
202
|
-
client = TypeLLMClient(model="fake")
|
|
202
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
203
203
|
client.sglang = FakeServerClient()
|
|
204
204
|
client.generate(context="Receipt", images=images, **kwargs).result
|
|
205
205
|
return client.sglang.generate_payloads
|
|
@@ -239,14 +239,14 @@ class ImageRequestTests(unittest.TestCase):
|
|
|
239
239
|
self.assert_images_attached(self.run_generate(images, questions=dag), images)
|
|
240
240
|
|
|
241
241
|
def test_numeric_decoding_carries_the_images(self):
|
|
242
|
-
client = TypeLLMClient(model="fake")
|
|
242
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
243
243
|
client.sglang = FakeServerClient()
|
|
244
244
|
client.generate(context="Receipt", images=[PNG],
|
|
245
245
|
questions={"n": {"type": "integer", "instructions": "Count?"}}).result
|
|
246
246
|
self.assert_images_attached(client.sglang.generate_payloads, [encode_image(PNG)])
|
|
247
247
|
|
|
248
248
|
def test_images_are_scoped_to_one_call(self):
|
|
249
|
-
client = TypeLLMClient(model="fake")
|
|
249
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
250
250
|
client.sglang = FakeServerClient()
|
|
251
251
|
client.generate(context="Receipt", images=[PNG], questions=self.QUESTIONS).result
|
|
252
252
|
client.sglang.generate_payloads.clear()
|
|
@@ -273,7 +273,7 @@ class ImageRequestTests(unittest.TestCase):
|
|
|
273
273
|
|
|
274
274
|
class ImageInputTokenTests(unittest.TestCase):
|
|
275
275
|
def test_images_count_as_the_server_expands_them(self):
|
|
276
|
-
client = TypeLLMClient(model="fake-vl")
|
|
276
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake-vl")
|
|
277
277
|
client.sglang = FakeServerClient()
|
|
278
278
|
questions = {"a": {"type": "boolean"}}
|
|
279
279
|
usage = client.generate(context="Receipt", images=[PNG], questions=questions).usage
|
|
@@ -302,7 +302,7 @@ class RuntimeContentTests(unittest.TestCase):
|
|
|
302
302
|
self.messages.append(messages)
|
|
303
303
|
return "x"
|
|
304
304
|
|
|
305
|
-
client = TypeLLMClient(model="fake")
|
|
305
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
306
306
|
client.sglang = Recording()
|
|
307
307
|
client.generate(context="Receipt", images=[PNG, PNG],
|
|
308
308
|
questions={"paid": {"type": "boolean", "instructions": "Paid?"}}).result
|
|
@@ -47,7 +47,7 @@ class CompileTests(unittest.TestCase):
|
|
|
47
47
|
|
|
48
48
|
class RuntimeTests(unittest.TestCase):
|
|
49
49
|
def test_nullable_number_can_answer_null(self):
|
|
50
|
-
client = TypeLLMClient(model="fake")
|
|
50
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
51
51
|
client.sglang = NullServer()
|
|
52
52
|
result = client.generate(context="Receipt", questions={
|
|
53
53
|
"tip": {"type": ["number", "null"]}, "count": {"type": "integer"},
|
|
@@ -56,7 +56,7 @@ class RuntimeTests(unittest.TestCase):
|
|
|
56
56
|
self.assertIn("Return null only if there is no value.", client._last_prompts.get()[0])
|
|
57
57
|
|
|
58
58
|
def test_nullable_string_writes_null_in_its_text_request(self):
|
|
59
|
-
client = TypeLLMClient(model="fake")
|
|
59
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
60
60
|
client.sglang = NullServer()
|
|
61
61
|
result = client.generate(context="Receipt", questions={
|
|
62
62
|
"note": {"type": ["string", "null"]}, "name": {"type": "string"},
|
|
@@ -74,7 +74,7 @@ class RuntimeTests(unittest.TestCase):
|
|
|
74
74
|
self.assertIn('{"note": null}', client._last_prompts.get()[0])
|
|
75
75
|
|
|
76
76
|
def test_nullable_boolean_scores_null_as_a_choice(self):
|
|
77
|
-
client = TypeLLMClient(model="fake")
|
|
77
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
78
78
|
client.sglang = FakeSGLang(selected_ids=[ord("C")])
|
|
79
79
|
result = client.generate(context="Receipt", questions={
|
|
80
80
|
"paid": {"type": ["boolean", "null"], "return_probabilities": True},
|
|
@@ -83,7 +83,7 @@ class RuntimeTests(unittest.TestCase):
|
|
|
83
83
|
self.assertEqual(set(result["paid"]["probabilities"]), {True, False, None})
|
|
84
84
|
|
|
85
85
|
def test_dependents_see_null(self):
|
|
86
|
-
client = TypeLLMClient(model="fake")
|
|
86
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
87
87
|
client.sglang = NullServer()
|
|
88
88
|
client.generate(context="Receipt", questions={
|
|
89
89
|
"tip": {"type": ["number", "null"]},
|
|
@@ -12,7 +12,7 @@ from tests.test_dependencies import DependencyFake
|
|
|
12
12
|
|
|
13
13
|
class PermutationTests(unittest.TestCase):
|
|
14
14
|
def run_case(self, dependent, values, **options):
|
|
15
|
-
client = TypeLLMClient(seed=42, **options)
|
|
15
|
+
client = TypeLLMClient("http://127.0.0.1:30000", seed=42, **options)
|
|
16
16
|
client.sglang = DependencyFake([65] * 100)
|
|
17
17
|
fields = {
|
|
18
18
|
'roll': {'type': 'string' if isinstance(values[0], str) else 'integer',
|
|
@@ -61,7 +61,7 @@ class PermutationTests(unittest.TestCase):
|
|
|
61
61
|
] + [{'type': 'string', 'enum': ['a', 'b'], 'permutations': v}
|
|
62
62
|
for v in (0, -1, True, False, 2.0, None, '8', 'AUTO', {}, [])]
|
|
63
63
|
invalid.append({'type': 'integer', 'enum': list(range(7)), 'permutations': 'all'})
|
|
64
|
-
client = TypeLLMClient()
|
|
64
|
+
client = TypeLLMClient("http://127.0.0.1:30000")
|
|
65
65
|
with patch.object(client.sglang, 'single_token') as tokens:
|
|
66
66
|
for field in invalid:
|
|
67
67
|
with self.subTest(field=field), self.assertRaises(SchemaError):
|
|
@@ -69,7 +69,7 @@ class PermutationTests(unittest.TestCase):
|
|
|
69
69
|
tokens.assert_not_called()
|
|
70
70
|
|
|
71
71
|
def test_plain_output_boolean_enum_and_clamped_budget(self):
|
|
72
|
-
client = TypeLLMClient()
|
|
72
|
+
client = TypeLLMClient("http://127.0.0.1:30000")
|
|
73
73
|
client.sglang = DependencyFake([65] * 3)
|
|
74
74
|
result = client.generate(context='x', questions={
|
|
75
75
|
'x': {'type': 'boolean', 'enum': [False, True], 'permutations': 100},
|
|
@@ -80,7 +80,7 @@ class PermutationTests(unittest.TestCase):
|
|
|
80
80
|
self.assertEqual(len(client.sglang.batch_prompts[0]), 3)
|
|
81
81
|
|
|
82
82
|
def test_average_probabilities_not_logits(self):
|
|
83
|
-
client = TypeLLMClient()
|
|
83
|
+
client = TypeLLMClient("http://127.0.0.1:30000")
|
|
84
84
|
fake = DependencyFake()
|
|
85
85
|
client.sglang = fake
|
|
86
86
|
with patch.object(fake, 'score_candidates_batch', return_value=([
|
|
@@ -114,7 +114,7 @@ class PermutationTests(unittest.TestCase):
|
|
|
114
114
|
self.assertEqual(len(value_orders(['a', 'b', 'c', 'd', 'e', 'f'])), 6)
|
|
115
115
|
|
|
116
116
|
def test_auto_cancels_a_pure_position_bias(self):
|
|
117
|
-
client = TypeLLMClient()
|
|
117
|
+
client = TypeLLMClient("http://127.0.0.1:30000")
|
|
118
118
|
client.sglang = DependencyFake()
|
|
119
119
|
biased = {65: math.log(.5), 66: math.log(.2), 67: math.log(.15), 68: math.log(.15)}
|
|
120
120
|
with patch.object(client.sglang, 'score_candidates_batch',
|
|
@@ -129,7 +129,7 @@ class PermutationTests(unittest.TestCase):
|
|
|
129
129
|
def test_one_matches_default(self):
|
|
130
130
|
outputs = []
|
|
131
131
|
for setting in ({}, {'permutations': 1}):
|
|
132
|
-
client = TypeLLMClient()
|
|
132
|
+
client = TypeLLMClient("http://127.0.0.1:30000")
|
|
133
133
|
client.sglang = DependencyFake([66])
|
|
134
134
|
outputs.append(client.generate(context='x', questions={'x': {
|
|
135
135
|
'type': 'string', 'enum': ['a', 'b'], 'return_probabilities': True, **setting}}).result)
|
|
@@ -13,7 +13,7 @@ class PrefillTests(unittest.TestCase):
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
def run_generate(self, questions, **kwargs):
|
|
16
|
-
client = TypeLLMClient(model="fake")
|
|
16
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
17
17
|
client.sglang = FakeServer()
|
|
18
18
|
result = client.generate(context="Receipt", questions=questions, **kwargs).result
|
|
19
19
|
return client, result
|
|
@@ -74,12 +74,14 @@ class StringConstraintTests(unittest.TestCase):
|
|
|
74
74
|
client._request = text_reply(' "say \\"hi\\""}')
|
|
75
75
|
self.assertEqual(client.generate_texts(['{"a":'], after_key=True), ['say "hi"'])
|
|
76
76
|
|
|
77
|
-
def
|
|
78
|
-
|
|
77
|
+
def test_running_out_of_tokens_cuts_the_string_off(self):
|
|
78
|
+
# A string stops at text_max_tokens: the text written by then is its value.
|
|
79
79
|
client = FakeServer()
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
for text, value in [(' "Edamame and', "Edamame and"),
|
|
81
|
+
(' "tab\\', "tab"), # cut inside an escape
|
|
82
|
+
(' "smile \\ud83d\\ude', "smile ")]: # cut inside a surrogate pair
|
|
83
|
+
client._request = text_reply(text, finish="length")
|
|
84
|
+
self.assertEqual(client.generate_texts(['{"a":'], after_key=True), [value])
|
|
83
85
|
|
|
84
86
|
|
|
85
87
|
if __name__ == "__main__":
|
|
@@ -36,7 +36,7 @@ class FieldProbabilityTests(unittest.TestCase):
|
|
|
36
36
|
{'type': 'boolean', 'return_probabilities': value}
|
|
37
37
|
for value in (None, 0, 1, 'true')
|
|
38
38
|
]
|
|
39
|
-
client = TypeLLMClient()
|
|
39
|
+
client = TypeLLMClient("http://127.0.0.1:30000")
|
|
40
40
|
with patch.object(client.sglang, 'single_token') as tokens:
|
|
41
41
|
for field in fields:
|
|
42
42
|
with self.subTest(field=field), self.assertRaises(SchemaError):
|
|
@@ -4,7 +4,7 @@ from typellm import TypeLLMClient
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def prompts(fields):
|
|
7
|
-
client = TypeLLMClient(model="fake")
|
|
7
|
+
client = TypeLLMClient("http://127.0.0.1:30000", model="fake")
|
|
8
8
|
client.sglang.single_token = lambda label: (ord(label), label)
|
|
9
9
|
return {c.name: c.opening_text() for c in client.compile_schema({"type": "object", "properties": fields})}
|
|
10
10
|
|
|
@@ -29,7 +29,7 @@ class TextTests(unittest.TestCase):
|
|
|
29
29
|
self.assertEqual(client.generate_texts(['p']),[''])
|
|
30
30
|
|
|
31
31
|
def test_mixed_fields_run_together(self):
|
|
32
|
-
client=TypeLLMClient()
|
|
32
|
+
client=TypeLLMClient("http://127.0.0.1:30000")
|
|
33
33
|
fake=FakeSGLang([ord('7'),3,ord('A')])
|
|
34
34
|
calls=[]
|
|
35
35
|
def generate(prefixes, **kwargs):
|
|
@@ -57,10 +57,10 @@ class TextTests(unittest.TestCase):
|
|
|
57
57
|
self.assertEqual(constructor.call_args.kwargs['text_max_tokens'],24)
|
|
58
58
|
for budget in (0,-1,True):
|
|
59
59
|
with self.assertRaises(ValueError):
|
|
60
|
-
TypeLLMClient(text_max_tokens=budget)
|
|
60
|
+
TypeLLMClient("http://127.0.0.1:30000", text_max_tokens=budget)
|
|
61
61
|
|
|
62
62
|
def test_thinking_then_constrained_text(self):
|
|
63
|
-
client=TypeLLMClient()
|
|
63
|
+
client=TypeLLMClient("http://127.0.0.1:30000")
|
|
64
64
|
client.sglang._context_length_cache=8192
|
|
65
65
|
client.sglang._chat_tokenizer=FakeChatTokenizer()
|
|
66
66
|
client.sglang._chat_tokenizer.apply_chat_template=lambda *args,**kw: "assistant\n<think>\n" if kw["enable_thinking"] else "completed"
|