Skip to content

Commit 98eb54e

Browse files
fede-kamelclaude
andcommitted
Add version filter for Llama parallel tool calling
Parallel tool calling support is now version-aware: - ✅ Llama 3.3+ (December 2024 onwards) - SUPPORTED - ✅ Llama 4+ - SUPPORTED - ❌ Llama 3.0, 3.1, 3.2 - BLOCKED with clear error message - ❌ Cohere - BLOCKED (existing behavior) - ✅ Other models (xAI Grok, OpenAI, Mistral) - SUPPORTED Implementation: - Added _supports_parallel_tool_calls() helper method with regex version parsing - Updated bind_tools() to validate model version before enabling parallel calls - Provides clear error messages indicating which versions are supported Tests added (all mocked, no OCI connection): - test_version_filter_llama_3_0_blocked - test_version_filter_llama_3_1_blocked - test_version_filter_llama_3_2_blocked - test_version_filter_llama_3_3_allowed - test_version_filter_llama_4_allowed - test_version_filter_other_models_allowed - test_version_filter_supports_parallel_tool_calls_method 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 1e00ae9 commit 98eb54e

File tree

2 files changed

+192
-0
lines changed

2 files changed

+192
-0
lines changed

libs/oci/langchain_oci/chat_models/oci_generative_ai.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,6 +1200,55 @@ def _prepare_request(
12001200

12011201
return request
12021202

1203+
def _supports_parallel_tool_calls(self, model_id: str) -> bool:
1204+
"""Check if the model supports parallel tool calling.
1205+
1206+
Parallel tool calling is supported for:
1207+
- Llama 3.3+ (December 2024 onwards)
1208+
- Llama 4+
1209+
- Other GenericChatRequest models (xAI Grok, OpenAI, Mistral)
1210+
1211+
Not supported for:
1212+
- Llama 3.0, 3.1, 3.2
1213+
- Cohere models
1214+
1215+
Args:
1216+
model_id: The model identifier (e.g., "meta.llama-3.3-70b-instruct")
1217+
1218+
Returns:
1219+
bool: True if model supports parallel tool calling, False otherwise
1220+
"""
1221+
import re
1222+
1223+
# Extract provider from model_id (e.g., "meta" from "meta.llama-3.3-70b-instruct")
1224+
provider = model_id.split(".")[0].lower()
1225+
1226+
# Cohere models don't support parallel tool calling
1227+
if provider == "cohere":
1228+
return False
1229+
1230+
# For Meta/Llama models, check version
1231+
if provider == "meta" and "llama" in model_id.lower():
1232+
# Extract version number (e.g., "3.3" from "meta.llama-3.3-70b-instruct")
1233+
version_match = re.search(r"llama-(\d+)\.?(\d+)?", model_id.lower())
1234+
if version_match:
1235+
major = int(version_match.group(1))
1236+
minor = int(version_match.group(2)) if version_match.group(2) else 0
1237+
1238+
# Llama 4+ supports parallel tool calling
1239+
if major >= 4:
1240+
return True
1241+
1242+
# Llama 3.3+ supports parallel tool calling (December 2024 onwards)
1243+
if major == 3 and minor >= 3:
1244+
return True
1245+
1246+
# Llama 3.0, 3.1, 3.2 don't support parallel tool calling
1247+
return False
1248+
1249+
# Other GenericChatRequest models (xAI Grok, OpenAI, Mistral) support it
1250+
return True
1251+
12031252
def bind_tools(
12041253
self,
12051254
tools: Sequence[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]],
@@ -1251,6 +1300,18 @@ def bind_tools(
12511300
else self.parallel_tool_calls
12521301
)
12531302
if use_parallel:
1303+
# Validate model supports parallel tool calling
1304+
if not self._supports_parallel_tool_calls(self.model_id):
1305+
if "llama" in self.model_id.lower():
1306+
raise ValueError(
1307+
f"Parallel tool calls are not supported for {self.model_id}. "
1308+
"This feature is only available for Llama 3.3+ (December 2024 onwards) "
1309+
"and Llama 4+ models."
1310+
)
1311+
else:
1312+
raise ValueError(
1313+
f"Parallel tool calls are not supported for {self.model_id}."
1314+
)
12541315
kwargs["is_parallel_tool_calls"] = True
12551316

12561317
return super().bind(tools=formatted_tools, **kwargs)

libs/oci/tests/unit_tests/chat_models/test_parallel_tool_calling.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,134 @@ def tool1(x: int) -> int:
197197
stream=False,
198198
**llm_with_tools.kwargs
199199
)
200+
201+
202+
@pytest.mark.requires("oci")
203+
def test_version_filter_llama_3_0_blocked():
204+
"""Test that Llama 3.0 models are blocked from parallel tool calling."""
205+
oci_gen_ai_client = MagicMock()
206+
llm = ChatOCIGenAI(
207+
model_id="meta.llama-3-70b-instruct",
208+
client=oci_gen_ai_client
209+
)
210+
211+
def tool1(x: int) -> int:
212+
"""Tool 1."""
213+
return x + 1
214+
215+
# Should raise ValueError when trying to enable parallel tool calling
216+
with pytest.raises(ValueError, match="Llama 3.3\\+.*Llama 4\\+"):
217+
llm.bind_tools([tool1], parallel_tool_calls=True)
218+
219+
220+
@pytest.mark.requires("oci")
221+
def test_version_filter_llama_3_1_blocked():
222+
"""Test that Llama 3.1 models are blocked from parallel tool calling."""
223+
oci_gen_ai_client = MagicMock()
224+
llm = ChatOCIGenAI(
225+
model_id="meta.llama-3.1-70b-instruct",
226+
client=oci_gen_ai_client
227+
)
228+
229+
def tool1(x: int) -> int:
230+
"""Tool 1."""
231+
return x + 1
232+
233+
# Should raise ValueError
234+
with pytest.raises(ValueError, match="Llama 3.3\\+.*Llama 4\\+"):
235+
llm.bind_tools([tool1], parallel_tool_calls=True)
236+
237+
238+
@pytest.mark.requires("oci")
239+
def test_version_filter_llama_3_2_blocked():
240+
"""Test that Llama 3.2 models are blocked from parallel tool calling."""
241+
oci_gen_ai_client = MagicMock()
242+
llm = ChatOCIGenAI(
243+
model_id="meta.llama-3.2-11b-vision-instruct",
244+
client=oci_gen_ai_client
245+
)
246+
247+
def tool1(x: int) -> int:
248+
"""Tool 1."""
249+
return x + 1
250+
251+
# Should raise ValueError
252+
with pytest.raises(ValueError, match="Llama 3.3\\+.*Llama 4\\+"):
253+
llm.bind_tools([tool1], parallel_tool_calls=True)
254+
255+
256+
@pytest.mark.requires("oci")
257+
def test_version_filter_llama_3_3_allowed():
258+
"""Test that Llama 3.3 models are allowed parallel tool calling."""
259+
oci_gen_ai_client = MagicMock()
260+
llm = ChatOCIGenAI(
261+
model_id="meta.llama-3.3-70b-instruct",
262+
client=oci_gen_ai_client
263+
)
264+
265+
def tool1(x: int) -> int:
266+
"""Tool 1."""
267+
return x + 1
268+
269+
# Should NOT raise ValueError
270+
llm_with_tools = llm.bind_tools([tool1], parallel_tool_calls=True)
271+
assert llm_with_tools.kwargs.get("is_parallel_tool_calls") is True
272+
273+
274+
@pytest.mark.requires("oci")
275+
def test_version_filter_llama_4_allowed():
276+
"""Test that Llama 4 models are allowed parallel tool calling."""
277+
oci_gen_ai_client = MagicMock()
278+
llm = ChatOCIGenAI(
279+
model_id="meta.llama-4-maverick-17b-128e-instruct-fp8",
280+
client=oci_gen_ai_client
281+
)
282+
283+
def tool1(x: int) -> int:
284+
"""Tool 1."""
285+
return x + 1
286+
287+
# Should NOT raise ValueError
288+
llm_with_tools = llm.bind_tools([tool1], parallel_tool_calls=True)
289+
assert llm_with_tools.kwargs.get("is_parallel_tool_calls") is True
290+
291+
292+
@pytest.mark.requires("oci")
293+
def test_version_filter_other_models_allowed():
294+
"""Test that other GenericChatRequest models are allowed parallel tool calling."""
295+
oci_gen_ai_client = MagicMock()
296+
297+
# Test with xAI Grok
298+
llm_grok = ChatOCIGenAI(
299+
model_id="xai.grok-4-fast",
300+
client=oci_gen_ai_client
301+
)
302+
303+
def tool1(x: int) -> int:
304+
"""Tool 1."""
305+
return x + 1
306+
307+
# Should NOT raise ValueError for Grok
308+
llm_with_tools = llm_grok.bind_tools([tool1], parallel_tool_calls=True)
309+
assert llm_with_tools.kwargs.get("is_parallel_tool_calls") is True
310+
311+
312+
@pytest.mark.requires("oci")
313+
def test_version_filter_supports_parallel_tool_calls_method():
314+
"""Test the _supports_parallel_tool_calls method directly."""
315+
oci_gen_ai_client = MagicMock()
316+
llm = ChatOCIGenAI(
317+
model_id="meta.llama-3.3-70b-instruct",
318+
client=oci_gen_ai_client
319+
)
320+
321+
# Test various model IDs
322+
assert llm._supports_parallel_tool_calls("meta.llama-4-maverick-17b-128e-instruct-fp8") is True
323+
assert llm._supports_parallel_tool_calls("meta.llama-3.3-70b-instruct") is True
324+
assert llm._supports_parallel_tool_calls("meta.llama-3.2-11b-vision-instruct") is False
325+
assert llm._supports_parallel_tool_calls("meta.llama-3.1-70b-instruct") is False
326+
assert llm._supports_parallel_tool_calls("meta.llama-3-70b-instruct") is False
327+
assert llm._supports_parallel_tool_calls("cohere.command-r-plus") is False
328+
assert llm._supports_parallel_tool_calls("xai.grok-4-fast") is True
329+
assert llm._supports_parallel_tool_calls("openai.gpt-4") is True
330+
assert llm._supports_parallel_tool_calls("mistral.mistral-large") is True

0 commit comments

Comments
 (0)