@@ -20,27 +20,13 @@ def test_forbid_configure_call_in_child_thread():
20
20
dspy .configure (lm = dspy .LM ("openai/gpt-4o" ), adapter = dspy .JSONAdapter (), callbacks = [lambda x : x ])
21
21
22
22
def worker ():
23
- with pytest .raises (RuntimeError , match = "Cannot call dspy.configure in a child thread " ):
23
+ with pytest .raises (RuntimeError , match = "Cannot call dspy.configure" ):
24
24
dspy .configure (lm = dspy .LM ("openai/gpt-4o-mini" ), callbacks = [])
25
25
26
26
with ThreadPoolExecutor (max_workers = 1 ) as executor :
27
27
executor .submit (worker )
28
28
29
29
30
- @pytest .mark .asyncio
31
- async def test_forbid_configure_call_in_async_function ():
32
- with pytest .raises (
33
- RuntimeError ,
34
- match = r"dspy.settings.configure\(\.\.\.\) cannot be called a second time from*" ,
35
- ):
36
- dspy .configure (lm = dspy .LM ("openai/gpt-4o" ), adapter = dspy .JSONAdapter (), callbacks = [lambda x : x ])
37
- dspy .configure (lm = dspy .LM ("openai/gpt-4o" ), adapter = dspy .JSONAdapter (), callbacks = [lambda x : x ])
38
-
39
- with dspy .context (lm = dspy .LM ("openai/gpt-4o-mini" ), callbacks = []):
40
- # context is allowed
41
- pass
42
-
43
-
44
30
def test_dspy_context ():
45
31
dspy .configure (lm = dspy .LM ("openai/gpt-4o" ), adapter = dspy .JSONAdapter (), callbacks = [lambda x : x ])
46
32
with dspy .context (lm = dspy .LM ("openai/gpt-4o-mini" ), callbacks = []):
@@ -165,3 +151,43 @@ async def aforward(self, question: str) -> str:
165
151
# The main thread is not affected by the context
166
152
assert dspy .settings .lm .model == "openai/gpt-4.1"
167
153
assert dspy .settings .trace == []
154
+
155
+
156
+ @pytest .mark .asyncio
157
+ async def test_dspy_configure_allowance_async ():
158
+ def bar1 ():
159
+ # `dspy.configure` is disallowed in different async tasks from the initial one.
160
+ # In this case, foo1 (async) calls bar1 (sync), and bar1 uses the async task from foo1.
161
+ with pytest .raises (RuntimeError ) as e :
162
+ dspy .configure (lm = dspy .LM ("openai/gpt-4o" ))
163
+ assert "dspy.settings.configure(...) can only be called from the same async" in str (e .value )
164
+
165
+ async def foo1 ():
166
+ bar1 ()
167
+ await asyncio .sleep (0.1 )
168
+
169
+ async def foo2 ():
170
+ # `dspy.configure` is disallowed in different async tasks from the initial one.
171
+ with pytest .raises (RuntimeError ) as e :
172
+ dspy .configure (lm = dspy .LM ("openai/gpt-4o" ))
173
+ assert "dspy.settings.configure(...) can only be called from the same async" in str (e .value )
174
+ await asyncio .sleep (0.1 )
175
+
176
+ async def foo3 ():
177
+ # `dspy.context` is allowed in different async tasks from the initial one.
178
+ with dspy .context (lm = dspy .LM ("openai/gpt-4o" )):
179
+ await asyncio .sleep (0.1 )
180
+
181
+ async def foo4 ():
182
+ # foo4 is directly invoked by the entry task, so it has the same async task as the entry task.
183
+ dspy .configure (lm = dspy .LM ("openai/gpt-4o" ))
184
+ await asyncio .sleep (0.1 )
185
+
186
+ # `dspy.configure` is allowed to be called multiple times in the same async task.
187
+ dspy .configure (lm = dspy .LM ("openai/gpt-4o-mini" ))
188
+ dspy .configure (lm = dspy .LM ("openai/gpt-4o" ))
189
+ dspy .configure (adapter = dspy .JSONAdapter ())
190
+
191
+ await asyncio .gather (foo1 (), foo2 (), foo3 ())
192
+
193
+ foo4 ()
0 commit comments