Skip to content

Commit 9b62574

Browse files
authored
Merge pull request #341 from enoch3712/327-bug-litellm-max_tokens-no-supported
[BUG] Litellm: max_tokens no supported
2 parents 44f78ea + f3587ee commit 9b62574

6 files changed

Lines changed: 156 additions & 148 deletions

File tree

extract_thinker/concatenation_handler.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ def __init__(self, llm):
1313

1414
def _is_valid_json_continuation(self, response: str) -> bool:
1515
"""Check if the response is a valid JSON continuation."""
16+
if not response:
17+
return False
18+
1619
cleaned_response = response.strip()
1720

1821
# Check if response contains JSON markers

extract_thinker/extractor.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,9 @@ def extract(
215215
Returns:
216216
The parsed result from the LLM as validated by response_model.
217217
"""
218+
if isinstance(source, dict) and self.document_loader is None:
219+
self.document_loader = DocumentLoaderData()
220+
218221
self._validate_dependencies(response_model, vision)
219222
self.extra_content = content
220223
self.completion_strategy = completion_strategy

extract_thinker/global_models.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
11
def get_lite_model():
22
"""Return the lite model for cost efficiency."""
33
#return "vertex_ai/gemini-2.0-flash"
4-
return "gemini/gemini-2.0-flash"
4+
return "gemini/gemini-2.5-flash-preview-05-20"
55

66
def get_big_model():
77
"""Return the big model for high performance."""
88
#return "vertex_ai/gemini-2.0-flash"
9-
return "gemini/gemini-2.0-flash"
9+
return "gemini/gemini-2.5-flash-preview-05-20"
10+
11+
def get_gemini_flash_model():
12+
"""Return the Gemini 2.5 Flash model."""
13+
return "gemini/gemini-2.5-flash-preview-05-20"
14+
15+
def get_gpt_mini_model():
16+
"""Return the GPT-4.1 Mini model."""
17+
return "gpt-4.1-mini"
18+
19+
def get_gpt_o4_model():
20+
"""Return the GPT-4o model."""
21+
return "gpt-4o"

extract_thinker/llm.py

Lines changed: 80 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class LLM:
3232
MAX_TOKEN_LIMIT = 120000 # Maximum token limit (for Claude 3.7 Sonnet)
3333
MAX_THINKING_BUDGET = 64000 # Maximum thinking budget
3434
MIN_THINKING_BUDGET = 1200 # Minimum thinking budget
35+
DEFAULT_OUTPUT_TOKENS = 32000
3536

3637
def __init__(
3738
self,
@@ -55,6 +56,7 @@ def __init__(
5556
self.is_thinking = False # Initialize is_thinking flag
5657
self.page_count = None # Initialize page count
5758
self.thinking_budget = self.THINKING_BUDGET_TOKENS # Default thinking budget
59+
self.thinking_token_limit: Optional[int] = None
5860

5961
if self.backend == LLMEngine.DEFAULT:
6062
self.client = instructor.from_litellm(
@@ -67,7 +69,6 @@ def __init__(
6769
from pydantic_ai import Agent
6870
from pydantic_ai.models import KnownModelName
6971
from typing import cast
70-
import asyncio
7172

7273
self.client = None
7374
self.agent = Agent(
@@ -158,7 +159,7 @@ def set_page_count(self, page_count: int) -> None:
158159
thinking_tokens = min(thinking_tokens, self.MAX_THINKING_BUDGET)
159160

160161
# Update token limit and thinking budget
161-
self.token_limit = content_tokens
162+
self.thinking_token_limit = content_tokens
162163
self.thinking_budget = thinking_tokens
163164

164165
def request(
@@ -171,14 +172,7 @@ def request(
171172
# Combine messages into a single prompt
172173
combined_prompt = " ".join([m["content"] for m in messages])
173174
try:
174-
# Create event loop if it doesn't exist
175-
try:
176-
loop = asyncio.get_event_loop()
177-
except RuntimeError:
178-
loop = asyncio.new_event_loop()
179-
asyncio.set_event_loop(loop)
180-
181-
result = loop.run_until_complete(
175+
result = asyncio.run(
182176
self.agent.run(
183177
combined_prompt,
184178
result_type=response_model if response_model else str
@@ -225,139 +219,108 @@ def request(
225219

226220
def _request_with_router(self, messages: List[Dict[str, str]], response_model: Optional[str]) -> Any:
227221
"""Handle request using router with or without thinking parameter"""
222+
max_tokens = self.DEFAULT_OUTPUT_TOKENS
223+
if self.token_limit is not None:
224+
max_tokens = self.token_limit
225+
elif self.is_thinking:
226+
max_tokens = self.thinking_token_limit
227+
228+
params = {
229+
"model": self.model,
230+
"messages": messages,
231+
"response_model": response_model,
232+
"temperature": self.temperature,
233+
"timeout": self.TIMEOUT,
234+
"max_completion_tokens": max_tokens,
235+
}
228236
if self.is_thinking:
229-
# Add thinking parameter for supported models
230-
thinking_param = {
231-
"type": "enabled",
232-
"budget_tokens": self.thinking_budget
233-
}
234-
try:
235-
return self.router.completion(
236-
model=self.model,
237-
messages=messages,
238-
response_model=response_model,
239-
temperature=self.temperature,
240-
timeout=self.TIMEOUT,
241-
thinking=thinking_param,
242-
)
243-
except Exception as e:
244-
# If thinking parameter causes an error, try without it
245-
if "property 'thinking' is unsupported" in str(e):
246-
print(f"Warning: Model {self.model} doesn't support thinking parameter, proceeding without it.")
247-
return self.router.completion(
248-
model=self.model,
249-
messages=messages,
250-
response_model=response_model,
251-
temperature=self.temperature,
252-
timeout=self.TIMEOUT,
253-
)
254-
else:
255-
raise e
256-
else:
257-
# Normal request without thinking parameter
258-
return self.router.completion(
259-
model=self.model,
260-
messages=messages,
261-
response_model=response_model,
262-
temperature=self.temperature,
263-
timeout=self.TIMEOUT,
264-
)
237+
if litellm.supports_reasoning(self.model):
238+
# Add thinking parameter for supported models
239+
thinking_param = {
240+
"type": "enabled",
241+
"budget_tokens": self.thinking_budget
242+
}
243+
params["thinking"] = thinking_param
244+
else:
245+
print(f"Warning: Model {self.model} doesn't support thinking parameter, proceeding without it.")
246+
247+
return self.router.completion(**params)
265248

266249
def _request_direct(self, messages: List[Dict[str, str]], response_model: Optional[str]) -> Any:
267250
"""Handle direct request with or without thinking parameter"""
251+
max_tokens = self.DEFAULT_OUTPUT_TOKENS
252+
if self.token_limit is not None:
253+
max_tokens = self.token_limit
254+
elif self.is_thinking:
255+
max_tokens = self.thinking_token_limit
256+
268257
base_params = {
269258
"model": self.model,
270259
"messages": messages,
271260
"temperature": self.temperature,
272261
"response_model": response_model,
273262
"max_retries": 1,
274-
"max_tokens": self.token_limit,
263+
"max_completion_tokens": max_tokens,
275264
"timeout": self.TIMEOUT,
276265
}
277266

278267
if self.is_thinking:
279-
# Try with thinking parameter
280-
thinking_param = {
281-
"type": "enabled",
282-
"budget_tokens": self.thinking_budget
283-
}
284-
try:
285-
return self.client.chat.completions.create(
286-
**base_params,
287-
thinking=thinking_param,
288-
)
289-
except Exception as e:
290-
# If thinking parameter causes an error, try without it
291-
if "property 'thinking' is unsupported" in str(e):
292-
print(f"Warning: Model {self.model} doesn't support thinking parameter, proceeding without it.")
293-
return self.client.chat.completions.create(**base_params)
294-
else:
295-
raise e
296-
else:
297-
# Normal request without thinking parameter
298-
return self.client.chat.completions.create(**base_params)
299-
300-
def raw_completion(self, messages: List[Dict[str, str]]) -> str:
301-
"""Make raw completion request without response model."""
302-
if self.router:
303-
if self.is_thinking:
304-
# Add thinking parameter for supported models
268+
if litellm.supports_reasoning(self.model):
269+
# Try with thinking parameter
305270
thinking_param = {
306271
"type": "enabled",
307272
"budget_tokens": self.thinking_budget
308273
}
309-
try:
310-
raw_response = self.router.completion(
311-
model=self.model,
312-
messages=messages,
313-
thinking=thinking_param,
314-
)
315-
except Exception as e:
316-
# If thinking parameter causes an error, try without it
317-
if "property 'thinking' is unsupported" in str(e):
318-
print(f"Warning: Model {self.model} doesn't support thinking parameter, proceeding without it.")
319-
raw_response = self.router.completion(
320-
model=self.model,
321-
messages=messages,
322-
)
323-
else:
324-
raise e
274+
base_params["thinking"] = thinking_param
325275
else:
326-
raw_response = self.router.completion(
327-
model=self.model,
328-
messages=messages,
276+
print(f"Warning: Model {self.model} doesn't support thinking parameter, proceeding without it.")
277+
278+
return self.client.chat.completions.create(**base_params)
279+
280+
def raw_completion(self, messages: List[Dict[str, str]]) -> str:
281+
"""Make raw completion request without response model."""
282+
if self.backend == LLMEngine.PYDANTIC_AI:
283+
# Combine messages into a single prompt
284+
combined_prompt = " ".join([m["content"] for m in messages])
285+
try:
286+
result = asyncio.run(
287+
self.agent.run(
288+
combined_prompt,
289+
result_type=str
290+
)
329291
)
330-
else:
331-
if self.is_thinking:
292+
return result.data
293+
except Exception as e:
294+
raise ValueError(f"Failed to extract from source: {str(e)}")
295+
296+
max_tokens = self.DEFAULT_OUTPUT_TOKENS
297+
if self.token_limit is not None:
298+
max_tokens = self.token_limit
299+
elif self.is_thinking:
300+
max_tokens = self.thinking_token_limit
301+
302+
params = {
303+
"model": self.model,
304+
"messages": messages,
305+
"max_completion_tokens": max_tokens,
306+
}
307+
308+
if self.is_thinking:
309+
if litellm.supports_reasoning(self.model):
332310
# Add thinking parameter for supported models
333311
thinking_param = {
334312
"type": "enabled",
335313
"budget_tokens": self.thinking_budget
336314
}
337-
try:
338-
raw_response = litellm.completion(
339-
model=self.model,
340-
messages=messages,
341-
max_tokens=self.token_limit,
342-
thinking=thinking_param,
343-
)
344-
except Exception as e:
345-
# If thinking parameter causes an error, try without it
346-
if "property 'thinking' is unsupported" in str(e):
347-
print(f"Warning: Model {self.model} doesn't support thinking parameter, proceeding without it.")
348-
raw_response = litellm.completion(
349-
model=self.model,
350-
messages=messages,
351-
max_tokens=self.token_limit,
352-
)
353-
else:
354-
raise e
315+
params["thinking"] = thinking_param
355316
else:
356-
raw_response = litellm.completion(
357-
model=self.model,
358-
messages=messages,
359-
max_tokens=self.token_limit,
360-
)
317+
print(f"Warning: Model {self.model} doesn't support thinking parameter, proceeding without it.")
318+
319+
if self.router:
320+
raw_response = self.router.completion(**params)
321+
else:
322+
raw_response = litellm.completion(**params)
323+
361324
return raw_response.choices[0].message.content
362325

363326
def set_timeout(self, timeout_ms: int) -> None:

0 commit comments

Comments
 (0)