partners/ollama: fix tool calling with nested schemas (#28225)

## Description

This PR addresses the following:

**Fixes Issue #25343:**
- Adds additional logic to parse shallowly nested JSON-encoded strings
in tool call arguments, allowing for proper parsing of responses like
that of Llama3.1 and 3.2 with nested schemas.
 
**Adds Integration Test for Fix:**
- Adds a Ollama specific integration test to ensure the issue is
resolved and to prevent regressions in the future.

**Fixes Failing Integration Tests:**
- Fixes failing integration tests (even prior to changes) caused by
`llama3-groq-tool-use` model. Previously,
tests`test_structured_output_async` and
`test_structured_output_optional_param` failed due to the model not
issuing a tool call in the response. Resolved by switching to
`llama3.1`.

## Issue
Fixes #25343.

## Dependencies
No dependencies.

____

Done in collaboration with @ishaan-upadhyay @mirajismail @ZackSteine.
This commit is contained in:
TheDannyG
2024-11-27 10:32:02 -05:00
committed by GitHub
parent bb83abd037
commit 607c60a594
3 changed files with 112 additions and 3 deletions

View File

@@ -0,0 +1,41 @@
"""Ollama specific chat model integration tests"""
from typing import List, Optional
import pytest
from pydantic import BaseModel, Field
from langchain_ollama import ChatOllama
@pytest.mark.parametrize(("model"), [("llama3.1")])
def test_structured_output_deeply_nested(model: str) -> None:
"""Test to verify structured output with a nested objects."""
llm = ChatOllama(model=model, temperature=0)
class Person(BaseModel):
"""Information about a person."""
name: Optional[str] = Field(default=None, description="The name of the person")
hair_color: Optional[str] = Field(
default=None, description="The color of the person's hair if known"
)
height_in_meters: Optional[str] = Field(
default=None, description="Height measured in meters"
)
class Data(BaseModel):
"""Extracted data about people."""
people: List[Person]
chat = llm.with_structured_output(Data) # type: ignore[arg-type]
text = (
"Alan Smith is 6 feet tall and has blond hair."
"Alan Poe is 3 feet tall and has grey hair."
)
result = chat.invoke(text)
assert isinstance(result, Data)
for chunk in chat.stream(text):
assert isinstance(chunk, Data)

View File

@@ -1,4 +1,4 @@
"""Test chat model integration."""
"""Test chat model integration using standard integration tests."""
from typing import Type
@@ -16,7 +16,7 @@ class TestChatOllama(ChatModelIntegrationTests):
@property
def chat_model_params(self) -> dict:
return {"model": "llama3-groq-tool-use"}
return {"model": "llama3.1"}
@property
def supports_image_inputs(self) -> bool: