1
1
import base64
2
2
import os
3
3
import tempfile
4
- from typing import Dict , List
4
+ from typing import Optional
5
5
6
6
import pytest
7
7
from PIL import Image as PILImage
8
8
from io import BytesIO
9
9
from pydantic import BaseModel , Field
10
10
11
- import outlines
12
11
from outlines .inputs import Image
13
12
from outlines .templates import (
14
13
Template ,
@@ -77,8 +76,8 @@ def test_vision_invalid_image_format():
77
76
Vision (prompt = "Test prompt" , image = image )
78
77
79
78
80
- def render (content : str , ** kwargs ):
81
- template = build_template_from_string (content )
79
+ def render (content : str , filters : Optional [ dict ] = None , ** kwargs ):
80
+ template = build_template_from_string (content , filters or {} )
82
81
return template .render (kwargs )
83
82
84
83
@@ -180,251 +179,58 @@ def test_render_jinja():
180
179
assert render (tpl , is_true = False ) == "final"
181
180
182
181
183
- def test_prompt_basic ():
184
- with pytest .deprecated_call ():
185
-
186
- @outlines .prompt
187
- def test_tpl (variable ):
188
- """{{variable}} test"""
189
-
190
- assert list (test_tpl .signature .parameters ) == ["variable" ]
191
-
192
- with pytest .raises (TypeError ):
193
- test_tpl (v = "test" )
194
-
195
- p = test_tpl ("test" )
196
- assert p == "test test"
197
-
198
- p = test_tpl (variable = "test" )
199
- assert p == "test test"
200
-
201
- @outlines .prompt
202
- def test_single_quote_tpl (variable ):
203
- "${variable} test"
204
-
205
- assert list (test_single_quote_tpl .signature .parameters ) == ["variable" ]
206
-
207
- p = test_tpl ("test" )
208
- assert p == "test test"
209
-
210
-
211
- def test_prompt_kwargs ():
212
- with pytest .deprecated_call ():
213
-
214
- @outlines .prompt
215
- def test_kwarg_tpl (var , other_var = "other" ):
216
- """{{var}} and {{other_var}}"""
217
-
218
- assert list (test_kwarg_tpl .signature .parameters ) == ["var" , "other_var" ]
219
-
220
- p = test_kwarg_tpl ("test" )
221
- assert p == "test and other"
222
-
223
- p = test_kwarg_tpl ("test" , other_var = "kwarg" )
224
- assert p == "test and kwarg"
225
-
226
- p = test_kwarg_tpl ("test" , "test" )
227
- assert p == "test and test"
228
-
229
-
230
- def test_no_prompt ():
231
- with pytest .deprecated_call ():
232
- with pytest .raises (TypeError , match = "template" ):
233
-
234
- @outlines .prompt
235
- def test_empty (variable ):
236
- pass
237
-
238
- with pytest .raises (TypeError , match = "template" ):
239
-
240
- @outlines .prompt
241
- def test_only_code (variable ):
242
- return variable
243
-
244
-
245
- def test_prompt_function ():
246
- def empty_fn ():
247
- pass
248
-
249
- def with_description ():
250
- """A description.
251
-
252
- But this is ignored.
253
- """
254
- pass
255
-
256
- with pytest .deprecated_call ():
257
-
258
- @outlines .prompt
259
- def name_description_ppt (fn ):
260
- """
261
- {{fn|name}}: {{fn|description}}
262
- """
263
-
264
- rendered = name_description_ppt (empty_fn )
265
- assert rendered == "empty_fn: "
266
-
267
- rendered = name_description_ppt (with_description )
268
- assert rendered == "with_description: A description."
269
-
270
- def with_signature (one : int , two : List [str ], three : float = 1.0 ):
271
- pass
272
-
273
- with pytest .deprecated_call ():
274
-
275
- @outlines .prompt
276
- def name_signature_ppt (fn ):
277
- """
278
- {{fn|name}}: {{fn|signature}}
279
- """
280
-
281
- rendered = name_signature_ppt (with_signature )
282
- assert (
283
- rendered == "with_signature: one: int, two: List[str], three: float = 1.0"
284
- )
285
-
286
- def test_function_call (one , two = 2 ):
287
- return one + two
288
-
289
- with pytest .deprecated_call ():
290
-
291
- @outlines .prompt
292
- def source_ppt (fn ):
293
- """
294
- {{fn|source}}
295
- """
296
-
297
- rendered = source_ppt (test_function_call )
298
- assert rendered == "def test_function_call(one, two=2):\n return one + two\n "
299
-
300
-
301
- def test_prompt_pydantic_response ():
302
- class SimpleResponse (BaseModel ):
303
- one : str = Field (description = "a description" )
304
- two : str
182
+ def test_render_filters ():
183
+ def foo (bar : str ) -> str :
184
+ """This is a sample function."""
185
+ return bar
305
186
306
- with pytest .deprecated_call ():
307
-
308
- @outlines .prompt
309
- def source_ppt (model ):
310
- "{{model | schema }}"
311
-
312
- prompt = source_ppt (SimpleResponse )
313
- assert prompt == '{\n "one": "a description",\n "two": "<two>"\n }'
314
-
315
- class NestedResponse (BaseModel ):
316
- answer : str
317
- thought : SimpleResponse
318
-
319
- prompt = source_ppt (NestedResponse )
320
- assert (
321
- prompt
322
- == '{\n "answer": "<answer>",\n "thought": {\n "one": "a description",\n "two": "<two>"\n }\n }'
323
- )
324
-
325
- class ConvolutedResponse (BaseModel ):
326
- part_one : NestedResponse
327
- part_two : SimpleResponse
328
-
329
- prompt = source_ppt (ConvolutedResponse )
330
- assert (
331
- prompt
332
- == '{\n "part_one": {\n "answer": "<answer>",\n "thought": {\n "one": "a description",\n "two": "<two>"\n }\n },\n "part_two": {\n "one": "a description",\n "two": "<two>"\n }\n }'
333
- )
334
-
335
-
336
- def test_prompt_dict_response ():
337
- response = {"one" : "a description" , "two" : "" }
338
-
339
- with pytest .deprecated_call ():
340
-
341
- @outlines .prompt
342
- def source_ppt (model ):
343
- "{{model | schema }}"
344
-
345
- prompt = source_ppt (response )
346
- assert prompt == '{\n "one": "a description",\n "two": ""\n }'
347
-
348
-
349
- def test_prompt_args ():
350
- def no_args ():
351
- pass
352
-
353
- def with_args (x , y , z ):
354
- pass
355
-
356
- def with_annotations (x : bool , y : str , z : Dict [int , List [str ]]):
357
- pass
358
-
359
- def with_defaults (x = True , y = "Hi" , z = {4 : ["I" , "love" , "outlines" ]}):
360
- pass
361
-
362
- def with_annotations_and_defaults (
363
- x : bool = True ,
364
- y : str = "Hi" ,
365
- z : Dict [int , List [str ]] = {4 : ["I" , "love" , "outlines" ]},
366
- ):
367
- pass
368
-
369
- def with_all (
370
- x1 ,
371
- y1 ,
372
- z1 ,
373
- x2 : bool ,
374
- y2 : str ,
375
- z2 : Dict [int , List [str ]],
376
- x3 = True ,
377
- y3 = "Hi" ,
378
- z3 = {4 : ["I" , "love" , "outlines" ]},
379
- x4 : bool = True ,
380
- y4 : str = "Hi" ,
381
- z4 : Dict [int , List [str ]] = {4 : ["I" , "love" , "outlines" ]},
382
- ):
383
- pass
187
+ class PydanticClass (BaseModel ):
188
+ foo : str = Field (description = "bar" )
384
189
385
- with pytest .deprecated_call ():
386
-
387
- @outlines .prompt
388
- def args_prompt (fn ):
389
- """args: {{ fn | args }}"""
390
-
391
- assert args_prompt (no_args ) == "args: "
392
- assert args_prompt (with_args ) == "args: x, y, z"
393
- assert (
394
- args_prompt (with_annotations )
395
- == "args: x: bool, y: str, z: Dict[int, List[str]]"
396
- )
397
- assert (
398
- args_prompt (with_defaults )
399
- == "args: x=True, y='Hi', z={4: ['I', 'love', 'outlines']}"
400
- )
401
- assert (
402
- args_prompt (with_annotations_and_defaults )
403
- == "args: x: bool = True, y: str = 'Hi', z: Dict[int, List[str]] = {4: ['I', 'love', 'outlines']}"
404
- )
405
- assert (
406
- args_prompt (with_all )
407
- == "args: x1, y1, z1, x2: bool, y2: str, z2: Dict[int, List[str]], x3=True, y3='Hi', z3={4: ['I', 'love', 'outlines']}, x4: bool = True, y4: str = 'Hi', z4: Dict[int, List[str]] = {4: ['I', 'love', 'outlines']}"
408
- )
190
+ def custom_filter (x : str ) -> str :
191
+ return x .upper ()
409
192
193
+ # name filter
194
+ tpl = """
195
+ {{ func | name }}
196
+ """
197
+ assert render (tpl , func = foo ) == "foo"
410
198
411
- def test_prompt_with_additional_filters ():
412
- def reverse (s : str ) -> str :
413
- return s [::- 1 ]
199
+ # description filter
200
+ tpl = """
201
+ {{ func | description }}
202
+ """
203
+ assert render (tpl , func = foo ) == "This is a sample function."
414
204
415
- with pytest .deprecated_call ():
205
+ # source filter
206
+ tpl = """
207
+ {{ func | source }}
208
+ """
209
+ assert render (tpl , func = foo ) == 'def foo(bar: str) -> str:\n """This is a sample function."""\n return bar\n '
416
210
417
- @outlines .prompt (filters = dict (reverse = reverse ))
418
- def test_tpl (variable ):
419
- """{{ variable | reverse }} test"""
211
+ # signature filter
212
+ tpl = """
213
+ {{ func | signature }}
214
+ """
215
+ assert render (tpl , func = foo ) == "bar: str"
420
216
421
- assert list (test_tpl .signature .parameters ) == ["variable" ]
217
+ # args filter
218
+ tpl = """
219
+ {{ func | args }}
220
+ """
221
+ assert render (tpl , func = foo ) == "bar: str"
422
222
423
- p = test_tpl ("test" )
424
- assert p == "tset test"
223
+ # schema filter
224
+ tpl = """
225
+ {{ schema | schema }}
226
+ """
227
+ assert render (tpl , schema = PydanticClass ) == '{\n "foo": "bar"\n }'
425
228
426
- p = test_tpl (variable = "example" )
427
- assert p == "elpmaxe test"
229
+ # custom filters
230
+ tpl = """
231
+ {{ name | custom_filter }}
232
+ """
233
+ assert render (tpl , {"custom_filter" : custom_filter }, name = "John" ) == "JOHN"
428
234
429
235
430
236
@pytest .fixture
@@ -470,7 +276,6 @@ def temp_prompt_file():
470
276
471
277
def test_prompt_from_file (temp_prompt_file ):
472
278
prompt = Template .from_file (temp_prompt_file )
473
- assert prompt .signature is None
474
279
examples = [
475
280
{"question" : "What is the capital of France?" , "answer" : "Paris" },
476
281
{"question" : "What is 2 + 2?" , "answer" : "4" },
@@ -497,7 +302,6 @@ def test_prompt_from_str():
497
302
Hello, {{ name }}!
498
303
"""
499
304
prompt = Template .from_string (content )
500
- assert prompt .signature is None
501
305
assert prompt (name = "World" ) == "Hello, World!"
502
306
503
307
0 commit comments