Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding tool_choice to ModelSettings #825

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open

Conversation

webcoderz
Copy link

building upon : #785

i added the tool_choice to ModelSettings

@iabgcb
Copy link

iabgcb commented Feb 3, 2025

pls pls see to it urgently that linting errors are solved.

@webcoderz
Copy link
Author

pls pls see to it urgently that linting errors are solved.

yea i dont think those failures are on what i added, i think therye elsewhere :)

pyproject.toml Outdated Show resolved Hide resolved
After researching; it seems every single one of the major providers supports tool_choice
@github-actions github-actions bot temporarily deployed to deploy-preview February 4, 2025 02:41 Inactive
@webcoderz
Copy link
Author

can we get this landed?

@silkspace
Copy link

This is a nice to have, and unblocks core functionality. Which runway will this land on?

@webcoderz
Copy link
Author

since it's supported by the others will add to the other clients as well

@webcoderz
Copy link
Author

looks like mostof them already supprt / have it integrated, im not sure how it should look for gemini/cohere, cc @sydney-runkle

@kernelzeroday
Copy link

+1 request for review, are additional changes needed to get this functionality merged? Thank you!

@webcoderz
Copy link
Author

confused on what more is needed here ?

Copy link
Member

@sydney-runkle sydney-runkle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution - this still needs some more support across other model files. Happy to help get this across the line, but we need impl for all supported models.

pydantic_ai_slim/pydantic_ai/models/openai.py Outdated Show resolved Hide resolved
pydantic_ai_slim/pydantic_ai/models/openai.py Outdated Show resolved Hide resolved
Comment on lines 138 to 143
* Gemini
* Anthropic
* OpenAI
* Groq
* Cohere
* Mistral
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indication that it's supported by all of these models needs to be backed up with code changes - in all of the corresponding model files, we need to check model_settings.tool_choice like you've done for groq and openai

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could also use more documentation - we'll want to document what each of these specifically means.

Additionally, I'm concerned about having this on the top ModelSettings level - anthropic supports "auto", "any", or a specific tool name, which is different than the above. Thus, I think we should implement tool_choice on individual model settings (like AnthropicModelSettings, OpenAIModelSettings) with the appropriate options.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea I agree, will rebase onto each individual one since they all seem to to something slightly different

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like mistral already supports the tool_choice where when is creating the chat completion, it has a method self._get_tool_choice() at L251 that does the conditional check i added inline in groq and openai already, should i refactor those to match this ? seems to be a cleaner pattern

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also mistral has tool_choice set already in MistralToolChoiceEnum which was generated by speakeasy , not sure i like this pattern , i feel your rationale makes the most sense to have it inside each specific providers model settings, this seems out of place in its current state

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also seems like anthropic already has it supported via ToolChoiceParam which was also generated by speakeasy, not sure how to proceed on that as its already on that and mistral insofar

@webcoderz
Copy link
Author

Thanks @sydney-runkle , will try to get these changes in tommorow; I don't have a cohere , groq, or anthropic sub so unsure how to test those ; I only have access to llama based models and openAI so I might need assist on those; anthropic looks pretty straightforward I can prob get that one in also, and groq is llama so can prob swing that one as well, never touched cohere before though

@sydney-runkle
Copy link
Member

@webcoderz, you can look at their API/sdk docs to figure out what values are accepted + supported.

Given the demand for this, I'm also happy to pick this up tomorrow and get this into our next release.

…l model settings

most of them are the same but some providers require slightly different choices, eg.  anthropic supports "auto", "any", or a specific tool name instead
seems mistral and anthropic have their own types for their tool_choices, not sure what to do there, and with vertext it seems it supports it using the openai client but not sure its supported without it
… and anthropic , and gemini only mentions its supported when usng openai client, backing out for now
@webcoderz
Copy link
Author

webcoderz commented Feb 12, 2025

alright, so it seems mistral and anthropic are already using their own system for tool_choice already thats outside of their respective ModelSettings classes ( anthropic: L207 of anthropic.py) & (mistral: L247 of mistral.py) , gemini and vertex the only docs i find that mention tool_choice are when theyre using the openai client, so since its already there i dont think we need to add there? cc @sydney-runkle

@sydney-runkle
Copy link
Member

Thanks for the updates - I'll pick this up tomorrow to get things across the line.

@webcoderz
Copy link
Author

Awesome lmk; I was also unsure where to add (if even necessary?) in the docs

@TensorTemplar
Copy link

I will test this with sglang and vllm oai apis instead for llama models, since i am getting the same issues. @sydney-runkle are there plans to add integration tests that do real api calls (behind flag) for these cases? I could add an sglang/vllm/ollama container and tiny llama for such tests, if needed

@webcoderz
Copy link
Author

I will test this with sglang and vllm oai apis instead for llama models, since i am getting the same issues. @sydney-runkle are there plans to add integration tests that do real api calls (behind flag) for these cases? I could add an sglang/vllm/ollama container and tiny llama for such tests, if needed

Wouldn’t be a bad idea; im using the OpenAIModel with vllm already but unsure how the others perform

@sydney-runkle
Copy link
Member

In my queue for tomorrow - sorry, spent most of my time on FallbackModel today.

@webcoderz
Copy link
Author

Hahaha it happens! I also found myself down a rabbit hole:)

@TensorTemplar
Copy link

TensorTemplar commented Feb 14, 2025

I will test this with sglang and vllm oai apis instead for llama models, since i am getting the same issues. @sydney-runkle are there plans to add integration tests that do real api calls (behind flag) for these cases? I could add an sglang/vllm/ollama container and tiny llama for such tests, if needed

Wouldn’t be a bad idea; im using the OpenAIModel with vllm already but unsure how the others perform

Can confirm this works with sglang and llama3.x models, apologies for the copy paste formatting:

class InformationIsNeeded(BaseModel):
    """Indicates that more information is needed to start the research."""
    justification: str

class InformationIsSufficient(BaseModel):
    """Indicates that the information is sufficient to start the research."""
    justification: str

IntentResponse = Union[InformationIsNeeded, InformationIsSufficient]

llama_32_11b_vlm = OpenAIModel(
    "meta-llama/Llama-3.2-11B-Vision-Instruct",
    base_url=os.getenv("PIPELINE_OPENAI_COMPATIBLE_PROXY_URL"),  # e.g. the sglang instance
    api_key=os.getenv("PIPELINE_PROXY_API_KEY"),
)

query_intent_agent = Agent(
    llama_32_11b_vlm,
    result_type=IntentResponse,  # type: ignore
    retries=1,
    model_settings=ModelSettings(tool_choice="auto"),  # with this i am getting a tool call now
)

result = await query_intent_agent.run(
                user_prompt="Decide if the following user resquest is sufficiently clear or if more info is required: What is 3+3?",
                result_type=IntentResponse,  # type: ignore
)
ModelResponse(parts=[ToolCallPart(tool_name='final_result_InformationIsSufficient', args='{"justification": "The user request is clear and can be answered directly without needing more information."}', tool_call_id='1', part_kind='tool-call')], model_name='Llama-3.2-11B-Vision-Instruct', timestamp='2025-02-14T13:10:50+00:00', kind='response')

According to sglang docs you need to add --tool-call-parser

docker run -p 30000:30000 docker.io/lmsysorg/sglang:v0.4.3-cu124-srt \
    python3 -m sglang.launch_server \
    --model-path meta-llama/Llama-3.2-11B-Vision-Instruct \
    --trust-remote-code \
    --host 0.0.0.0 \
    --port 30000 \
    --mem-fraction-static 0.85 \
    --tool-call-parser llama3

@sydney-runkle
Copy link
Member

sydney-runkle commented Feb 14, 2025

Ok, my understanding of tool_choice specs:

Anthropic:

# basically, auto, any, or a specific tool
ToolChoiceParam: TypeAlias = Union[ToolChoiceAutoParam, ToolChoiceAnyParam, ToolChoiceToolParam]

Cohere:

V2ChatRequestToolChoice = typing.Union[typing.Literal["REQUIRED", "NONE"], typing.Any]

Gemini:

# reference: https://ai.google.dev/gemini-api/docs/function-calling
FunctionCallConfigMode = Literal["ANY", "NONE", "AUTO"]

Groq:

# ChatCompletionNamedToolChoiceParam indicates a specific tool
ChatCompletionToolChoiceOptionParam: TypeAlias = Union[
    Literal["none", "auto", "required"], ChatCompletionNamedToolChoiceParam
]

Mistral:

ChatCompletionStreamRequestToolChoice = Union[ToolChoice, ToolChoiceEnum]
ToolChoiceEnum = Literal["auto", "none", "any", "required"]

OpenAI:

ChatCompletionToolChoiceOptionParam: TypeAlias = Union[
    Literal["none", "auto", "required"], ChatCompletionNamedToolChoiceParam
]

Looks like we can unify these into the following options:

  • 'none': the model will not call any tool
  • 'auto': the model chooses to call tool(s), or not
  • 'required': the model will call at least one tool
  • named function: the model will call said tool - can structure this as a typed dict, similar to OpenAI

We should implement support for this across all models.

@webcoderz, given this analysis, are you ok with continuing here? We can store this tool_choice setting in ModelSettings.

@webcoderz
Copy link
Author

Ok, my understanding of tool_choice specs:

Anthropic:

# basically, auto, any, or a specific tool
ToolChoiceParam: TypeAlias = Union[ToolChoiceAutoParam, ToolChoiceAnyParam, ToolChoiceToolParam]

Cohere:

V2ChatRequestToolChoice = typing.Union[typing.Literal["REQUIRED", "NONE"], typing.Any]

Gemini:

# reference: https://ai.google.dev/gemini-api/docs/function-calling
FunctionCallConfigMode = Literal["ANY", "NONE", "AUTO"]

Groq:

# ChatCompletionNamedToolChoiceParam indicates a specific tool
ChatCompletionToolChoiceOptionParam: TypeAlias = Union[
    Literal["none", "auto", "required"], ChatCompletionNamedToolChoiceParam
]

Mistral:

ChatCompletionStreamRequestToolChoice = Union[ToolChoice, ToolChoiceEnum]
ToolChoiceEnum = Literal["auto", "none", "any", "required"]

OpenAI:

ChatCompletionToolChoiceOptionParam: TypeAlias = Union[
    Literal["none", "auto", "required"], ChatCompletionNamedToolChoiceParam
]

Looks like we can unify these into the following options:

  • 'none': the model will not call any tool
  • 'auto': the model chooses to call tool(s), or not
  • 'required': the model will call at least one tool
  • named function: the model will call said tool - can structure this as a typed dict, similar to OpenAI

We should implement support for this across all models.

@webcoderz, given this analysis, are you ok with continuing here? We can store this tool_choice setting in ModelSettings.

yea no prob can take care of it this weekend
Cool ok going back into Modelsettings? 👍

@webcoderz
Copy link
Author

So @sydney-runkle , a little confused here, should I pmuch just revert everything back to my PR where I did have in ModelSettings originally?

@sydney-runkle
Copy link
Member

Yep, but you'll have to implement logic for all of the models (not just groq + openai)

@webcoderz
Copy link
Author

gotcha, so a mixture of adding to to top level, and then pmuch your research from above at the modelsettings of the. specific model level

Yep, but you'll have to implement logic for all of the models (not just groq + openai)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants