@@ -109,12 +109,14 @@ def generate_text(
109
109
max_tokens : int | None = None ,
110
110
timeout : float | None = None ,
111
111
predicted_output : str | None = None ,
112
+ reasoning_effort : str | None = None ,
112
113
):
113
114
message_dicts , tool_dicts = self ._prep_message_and_tools (
114
115
messages = messages ,
115
116
prompt = prompt ,
116
117
system_prompt = system_prompt ,
117
118
tools = tools ,
119
+ reasoning_effort = reasoning_effort ,
118
120
)
119
121
120
122
openai_client = self .get_client ()
@@ -138,6 +140,7 @@ def generate_text(
138
140
if predicted_output
139
141
else openai .NotGiven ()
140
142
),
143
+ reasoning_effort = reasoning_effort if reasoning_effort else openai .NotGiven (),
141
144
)
142
145
143
146
openai_message = completion .choices [0 ].message
@@ -183,12 +186,14 @@ def generate_structured(
183
186
response_format : Type [StructuredOutputType ],
184
187
max_tokens : int | None = None ,
185
188
timeout : float | None = None ,
189
+ reasoning_effort : str | None = None ,
186
190
) -> LlmGenerateStructuredResponse [StructuredOutputType ]:
187
191
message_dicts , tool_dicts = self ._prep_message_and_tools (
188
192
messages = messages ,
189
193
prompt = prompt ,
190
194
system_prompt = system_prompt ,
191
195
tools = tools ,
196
+ reasoning_effort = reasoning_effort ,
192
197
)
193
198
194
199
openai_client = self .get_client ()
@@ -205,6 +210,7 @@ def generate_structured(
205
210
response_format = response_format ,
206
211
max_tokens = max_tokens or openai .NotGiven (),
207
212
timeout = timeout or openai .NotGiven (),
213
+ reasoning_effort = reasoning_effort if reasoning_effort else openai .NotGiven (),
208
214
)
209
215
210
216
openai_message = completion .choices [0 ].message
@@ -244,6 +250,7 @@ def to_message_dict(message: Message) -> ChatCompletionMessageParam:
244
250
new_item ["type" ] = "function"
245
251
parsed_tool_calls .append (new_item )
246
252
message_dict ["tool_calls" ] = parsed_tool_calls
253
+ message_dict ["role" ] = "assistant"
247
254
248
255
if message .tool_call_id :
249
256
message_dict ["tool_call_id" ] = message .tool_call_id
@@ -284,11 +291,18 @@ def _prep_message_and_tools(
284
291
prompt : str | None = None ,
285
292
system_prompt : str | None = None ,
286
293
tools : list [FunctionTool ] | None = None ,
294
+ reasoning_effort : str | None = None ,
287
295
):
288
296
message_dicts = [cls .to_message_dict (message ) for message in messages ] if messages else []
289
297
if system_prompt :
290
298
message_dicts .insert (
291
- 0 , cls .to_message_dict (Message (role = "system" , content = system_prompt ))
299
+ 0 ,
300
+ cls .to_message_dict (
301
+ Message (
302
+ role = "system" if not reasoning_effort else "developer" ,
303
+ content = system_prompt ,
304
+ )
305
+ ),
292
306
)
293
307
if prompt :
294
308
message_dicts .append (cls .to_message_dict (Message (role = "user" , content = prompt )))
@@ -310,12 +324,14 @@ def generate_text_stream(
310
324
temperature : float | None = None ,
311
325
max_tokens : int | None = None ,
312
326
timeout : float | None = None ,
327
+ reasoning_effort : str | None = None ,
313
328
) -> Iterator [str | ToolCall | Usage ]:
314
329
message_dicts , tool_dicts = self ._prep_message_and_tools (
315
330
messages = messages ,
316
331
prompt = prompt ,
317
332
system_prompt = system_prompt ,
318
333
tools = tools ,
334
+ reasoning_effort = reasoning_effort ,
319
335
)
320
336
321
337
openai_client = self .get_client ()
@@ -333,6 +349,7 @@ def generate_text_stream(
333
349
timeout = timeout or openai .NotGiven (),
334
350
stream = True ,
335
351
stream_options = {"include_usage" : True },
352
+ reasoning_effort = reasoning_effort if reasoning_effort else openai .NotGiven (),
336
353
)
337
354
338
355
try :
@@ -515,7 +532,7 @@ def to_message_param(message: Message) -> MessageParam:
515
532
)
516
533
],
517
534
)
518
- elif message .role == "tool_use" :
535
+ elif message .role == "tool_use" or ( message . role == "assistant" and message . tool_calls ) :
519
536
if not message .tool_calls :
520
537
return MessageParam (role = "assistant" , content = [])
521
538
tool_call = message .tool_calls [0 ] # Assuming only one tool call per message
@@ -679,14 +696,6 @@ def construct_message_from_stream(
679
696
680
697
@dataclass
681
698
class GeminiProvider :
682
- # !!! NOTE THE FOLLOWING LIMITATIONS FOR GEMINI:
683
- # - super strict rate limits making it unusable for evals or prod
684
- # - no multi-turn tool use
685
- # - no nested Pydantic models for structured outputs
686
- # - no nullable fields for structured outputs
687
- # - no dynamic retrieval for google search
688
- # These will likely be changed as the SDK matures. Make sure to keep an eye on updates and update these notes/our implementation as needed.
689
-
690
699
model_name : str
691
700
provider_name = LlmProviderType .GEMINI
692
701
defaults : LlmProviderDefaults | None = None
@@ -985,7 +994,7 @@ def _prep_message_and_tools(
985
994
986
995
@staticmethod
987
996
def to_content (message : Message ) -> Content :
988
- if message .role == "tool_use" :
997
+ if message .role == "tool_use" or ( message . role == "assistant" and message . tool_calls ) :
989
998
if not message .tool_calls :
990
999
return Content (
991
1000
role = "model" ,
@@ -1117,6 +1126,7 @@ def generate_text(
1117
1126
run_name : str | None = None ,
1118
1127
timeout : float | None = None ,
1119
1128
predicted_output : str | None = None ,
1129
+ reasoning_effort : str | None = None ,
1120
1130
) -> LlmGenerateTextResponse :
1121
1131
try :
1122
1132
if run_name :
@@ -1140,6 +1150,7 @@ def generate_text(
1140
1150
tools = tools ,
1141
1151
timeout = timeout ,
1142
1152
predicted_output = predicted_output ,
1153
+ reasoning_effort = reasoning_effort ,
1143
1154
)
1144
1155
elif model .provider_name == LlmProviderType .ANTHROPIC :
1145
1156
model = cast (AnthropicProvider , model )
@@ -1182,6 +1193,7 @@ def generate_structured(
1182
1193
max_tokens : int | None = None ,
1183
1194
run_name : str | None = None ,
1184
1195
timeout : float | None = None ,
1196
+ reasoning_effort : str | None = None ,
1185
1197
) -> LlmGenerateStructuredResponse [StructuredOutputType ]:
1186
1198
try :
1187
1199
if run_name :
@@ -1203,6 +1215,7 @@ def generate_structured(
1203
1215
temperature = temperature ,
1204
1216
tools = tools ,
1205
1217
timeout = timeout ,
1218
+ reasoning_effort = reasoning_effort ,
1206
1219
)
1207
1220
elif model .provider_name == LlmProviderType .ANTHROPIC :
1208
1221
raise NotImplementedError ("Anthropic structured outputs are not yet supported" )
@@ -1236,6 +1249,7 @@ def generate_text_stream(
1236
1249
max_tokens : int | None = None ,
1237
1250
run_name : str | None = None ,
1238
1251
timeout : float | None = None ,
1252
+ reasoning_effort : str | None = None ,
1239
1253
) -> Iterator [str | ToolCall | Usage ]:
1240
1254
try :
1241
1255
if run_name :
@@ -1260,6 +1274,7 @@ def generate_text_stream(
1260
1274
temperature = temperature or default_temperature ,
1261
1275
tools = tools ,
1262
1276
timeout = timeout ,
1277
+ reasoning_effort = reasoning_effort ,
1263
1278
)
1264
1279
elif model .provider_name == LlmProviderType .ANTHROPIC :
1265
1280
model = cast (AnthropicProvider , model )
0 commit comments