Files
wehub-resource-sync 6b7e6b44f1
Python Build and Type Check / python-ci (ubuntu-latest, 3.11) (push) Has been cancelled
Python Build and Type Check / python-ci (ubuntu-latest, 3.13) (push) Has been cancelled
Python Build and Type Check / python-ci (windows-latest, 3.11) (push) Has been cancelled
Python Build and Type Check / python-ci (windows-latest, 3.13) (push) Has been cancelled
Python Integration Tests / python-ci (ubuntu-latest, 3.13) (push) Has been cancelled
Python Integration Tests / python-ci (windows-latest, 3.13) (push) Has been cancelled
Python Notebook Tests / python-ci (ubuntu-latest, 3.13) (push) Has been cancelled
Python Notebook Tests / python-ci (windows-latest, 3.13) (push) Has been cancelled
Python Smoke Tests / python-ci (ubuntu-latest, 3.13) (push) Has been cancelled
Python Smoke Tests / python-ci (windows-latest, 3.13) (push) Has been cancelled
Python Unit Tests / python-ci (ubuntu-latest, 3.13) (push) Has been cancelled
Python Unit Tests / python-ci (windows-latest, 3.13) (push) Has been cancelled
gh-pages / build (push) Has been cancelled
Python Publish (pypi) / Upload release to PyPI (push) Has been cancelled
Spellcheck / spellcheck (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:37:31 +08:00

388 lines
13 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"id": "a56845b0",
"metadata": {},
"source": [
"# Function Tool Calling\n",
"\n",
"In order to use function tools, the completion endpoint needs a json schema of the function(s). This notebook uses `pydantic` to describe a function and its parameters and the `OpenAI` built-in `pydantic_function_tool` to create the necessary json schema. Other techniques may be used to create a definition for your functions.\n"
]
},
{
"cell_type": "markdown",
"id": "daf62482",
"metadata": {},
"source": [
"## Manual Function Tool Calling\n",
"\n",
"This example demonstrates function tool calling by manually using `pydantic` and `pydantic_function_tool`. See the next example for a simplified approach.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "53437ac4",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Adding 5 and 7 gives you 12.\n"
]
}
],
"source": [
"# Copyright (c) 2024 Microsoft Corporation.\n",
"# Licensed under the MIT License\n",
"\n",
"import json\n",
"import os\n",
"\n",
"from dotenv import load_dotenv\n",
"from graphrag_llm.completion import LLMCompletion, create_completion\n",
"from graphrag_llm.config import AuthMethod, ModelConfig\n",
"from graphrag_llm.types import LLMCompletionResponse\n",
"from graphrag_llm.utils import (\n",
" CompletionMessagesBuilder,\n",
")\n",
"from openai import pydantic_function_tool\n",
"from pydantic import BaseModel, ConfigDict, Field\n",
"\n",
"load_dotenv()\n",
"\n",
"api_key = os.getenv(\"GRAPHRAG_API_KEY\")\n",
"model_config = ModelConfig(\n",
" model_provider=\"azure\",\n",
" model=os.getenv(\"GRAPHRAG_MODEL\", \"gpt-4o\"),\n",
" azure_deployment_name=os.getenv(\"GRAPHRAG_MODEL\", \"gpt-4o\"),\n",
" api_base=os.getenv(\"GRAPHRAG_API_BASE\"),\n",
" api_version=os.getenv(\"GRAPHRAG_API_VERSION\", \"2025-04-01-preview\"),\n",
" api_key=api_key,\n",
" auth_method=AuthMethod.AzureManagedIdentity if not api_key else AuthMethod.ApiKey,\n",
")\n",
"llm_completion: LLMCompletion = create_completion(model_config)\n",
"\n",
"\n",
"class AddTwoNumbers(BaseModel):\n",
" \"\"\"Input Argument for add two numbers.\"\"\"\n",
"\n",
" model_config = ConfigDict(\n",
" extra=\"forbid\",\n",
" )\n",
"\n",
" a: int = Field(description=\"The first number to add.\")\n",
" b: int = Field(description=\"The second number to add.\")\n",
"\n",
"\n",
"# The actual function\n",
"def add_two_numbers(options: AddTwoNumbers) -> int:\n",
" \"\"\"Add two numbers.\"\"\"\n",
" return options.a + options.b\n",
"\n",
"\n",
"add_definition = pydantic_function_tool(\n",
" AddTwoNumbers,\n",
" # Function name and description\n",
" name=\"my_add_two_numbers_function\",\n",
" description=\"Add two numbers.\",\n",
")\n",
"\n",
"# Mapping of available functions\n",
"available_functions = {\n",
" \"my_add_two_numbers_function\": {\n",
" \"function\": add_two_numbers,\n",
" \"input_model\": AddTwoNumbers,\n",
" },\n",
"}\n",
"\n",
"messages_builder = CompletionMessagesBuilder().add_user_message(\n",
" \"Add 5 and 7 using a function call.\"\n",
")\n",
"\n",
"response: LLMCompletionResponse = llm_completion.completion(\n",
" messages=messages_builder.build(),\n",
" tools=[add_definition],\n",
") # type: ignore\n",
"\n",
"if not response.choices[0].message.tool_calls:\n",
" msg = \"No function call found in response.\"\n",
" raise ValueError(msg)\n",
"\n",
"# Add the assistant message with the function call to the message history\n",
"messages_builder.add_assistant_message(\n",
" message=response.choices[0].message,\n",
")\n",
"\n",
"for tool_call in response.choices[0].message.tool_calls:\n",
" tool_id = tool_call.id\n",
" if tool_call.type != \"function\":\n",
" continue\n",
" function_name = tool_call.function.name\n",
" function_args = tool_call.function.arguments\n",
"\n",
" args_dict = json.loads(function_args)\n",
"\n",
" InputModel = available_functions[function_name][\"input_model\"]\n",
" function = available_functions[function_name][\"function\"]\n",
" input_options = InputModel(**args_dict)\n",
"\n",
" result = function(input_options)\n",
"\n",
" messages_builder.add_tool_message(\n",
" content=str(result),\n",
" tool_call_id=tool_id,\n",
" )\n",
"\n",
"final_response: LLMCompletionResponse = llm_completion.completion(\n",
" messages=messages_builder.build(),\n",
") # type: ignore\n",
"print(final_response.content)"
]
},
{
"cell_type": "markdown",
"id": "b31c7a9c",
"metadata": {},
"source": [
"### Function Tool Definition\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "eb6950e8",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{\n",
" \"type\": \"function\",\n",
" \"function\": {\n",
" \"name\": \"my_add_two_numbers_function\",\n",
" \"strict\": true,\n",
" \"parameters\": {\n",
" \"additionalProperties\": false,\n",
" \"description\": \"Input Argument for add two numbers.\",\n",
" \"properties\": {\n",
" \"a\": {\n",
" \"description\": \"The first number to add.\",\n",
" \"title\": \"A\",\n",
" \"type\": \"integer\"\n",
" },\n",
" \"b\": {\n",
" \"description\": \"The second number to add.\",\n",
" \"title\": \"B\",\n",
" \"type\": \"integer\"\n",
" }\n",
" },\n",
" \"required\": [\n",
" \"a\",\n",
" \"b\"\n",
" ],\n",
" \"title\": \"AddTwoNumbers\",\n",
" \"type\": \"object\"\n",
" },\n",
" \"description\": \"Add two numbers.\"\n",
" }\n",
"}\n"
]
}
],
"source": [
"# View the output schema\n",
"# This is what is passed to the completion tools param\n",
"# Created using pydantic and pydantic_function_tool\n",
"# but may be created manually as well\n",
"print(json.dumps(add_definition, indent=2))"
]
},
{
"cell_type": "markdown",
"id": "660de4c9",
"metadata": {},
"source": [
"## Tool Calling with FunctionToolManager\n",
"\n",
"If using `pydantic` to describe function arguments, you can use the `FunctionToolManager` to register functions, produce defintions, and call functions in response to the LLM. This helps automate some of the above work.\n",
"\n",
"The following example demonstrates calling multiple functions in one LLM call.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4fae701e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Adding numbers: 3 8\n",
"Multiplying numbers: 9 5\n",
"Reversing text: GraphRAG\n",
"3 + 8 is 11, 9 * 5 is 45, and the reversed string 'GraphRAG' is 'GARhparG'.\n"
]
}
],
"source": [
"# Copyright (c) 2024 Microsoft Corporation.\n",
"# Licensed under the MIT License\n",
"\n",
"import os\n",
"\n",
"from dotenv import load_dotenv\n",
"from graphrag_llm.completion import LLMCompletion, create_completion\n",
"from graphrag_llm.config import AuthMethod, ModelConfig\n",
"from graphrag_llm.types import LLMCompletionResponse\n",
"from graphrag_llm.utils import (\n",
" CompletionMessagesBuilder,\n",
" FunctionToolManager,\n",
")\n",
"from pydantic import BaseModel, ConfigDict, Field\n",
"\n",
"load_dotenv()\n",
"\n",
"api_key = os.getenv(\"GRAPHRAG_API_KEY\")\n",
"model_config = ModelConfig(\n",
" model_provider=\"azure\",\n",
" model=os.getenv(\"GRAPHRAG_MODEL\", \"gpt-4o\"),\n",
" azure_deployment_name=os.getenv(\"GRAPHRAG_MODEL\", \"gpt-4o\"),\n",
" api_base=os.getenv(\"GRAPHRAG_API_BASE\"),\n",
" api_version=os.getenv(\"GRAPHRAG_API_VERSION\", \"2025-04-01-preview\"),\n",
" api_key=api_key,\n",
" auth_method=AuthMethod.AzureManagedIdentity if not api_key else AuthMethod.ApiKey,\n",
")\n",
"llm_completion: LLMCompletion = create_completion(model_config)\n",
"\n",
"\n",
"class NumbersInput(BaseModel):\n",
" \"\"\"Numbers input.\"\"\"\n",
"\n",
" model_config = ConfigDict(\n",
" extra=\"forbid\",\n",
" )\n",
"\n",
" a: int = Field(description=\"The first number.\")\n",
" b: int = Field(description=\"The second number.\")\n",
"\n",
"\n",
"def add(options: NumbersInput) -> str:\n",
" \"\"\"Add two numbers.\"\"\"\n",
" # Print something to ensure function is called for verification\n",
" print(\"Adding numbers:\", options.a, options.b)\n",
" return str(options.a + options.b)\n",
"\n",
"\n",
"def multiply(options: NumbersInput) -> str:\n",
" \"\"\"Multiply two numbers.\"\"\"\n",
" # Print something to ensure function is called for verification\n",
" print(\"Multiplying numbers:\", options.a, options.b)\n",
" return str(options.a * options.b)\n",
"\n",
"\n",
"class TextInput(BaseModel):\n",
" \"\"\"Text input.\"\"\"\n",
"\n",
" model_config = ConfigDict(\n",
" extra=\"forbid\",\n",
" )\n",
"\n",
" test: str = Field(description=\"The string to reverse.\")\n",
"\n",
"\n",
"def reverse_text(options: TextInput) -> str:\n",
" \"\"\"Reverse a string.\"\"\"\n",
" # Print something to ensure function is called for verification\n",
" print(\"Reversing text:\", options.test)\n",
" return options.test[::-1]\n",
"\n",
"\n",
"function_tool_manager = FunctionToolManager()\n",
"\n",
"function_tool_manager.register_function_tool(\n",
" name=\"add\",\n",
" description=\"Add two numbers.\",\n",
" function=add,\n",
" input_model=NumbersInput,\n",
")\n",
"function_tool_manager.register_function_tool(\n",
" name=\"multiply\",\n",
" description=\"Multiply two numbers.\",\n",
" function=multiply,\n",
" input_model=NumbersInput,\n",
")\n",
"function_tool_manager.register_function_tool(\n",
" name=\"reverse_text\",\n",
" description=\"Reverse a string.\",\n",
" function=reverse_text,\n",
" input_model=TextInput,\n",
")\n",
"\n",
"\n",
"messages_builder = CompletionMessagesBuilder().add_user_message(\n",
" \"What is 3 + 8 and 9 * 5? Also, reverse the string 'GraphRAG'.\"\n",
")\n",
"\n",
"# Multiple tool calls in parallel\n",
"response: LLMCompletionResponse = llm_completion.completion(\n",
" messages=messages_builder.build(),\n",
" tools=function_tool_manager.definitions(),\n",
" parallel_tool_calls=True,\n",
") # type: ignore\n",
"\n",
"# Add the assistant message with the function call to the message history\n",
"messages_builder.add_assistant_message(\n",
" message=response.choices[0].message,\n",
")\n",
"\n",
"tool_results = function_tool_manager.call_functions(response)\n",
"\n",
"for tool_message in tool_results:\n",
" messages_builder.add_tool_message(**tool_message)\n",
"\n",
"final_response: LLMCompletionResponse = llm_completion.completion(\n",
" messages=messages_builder.build(),\n",
") # type: ignore\n",
"print(final_response.content)"
]
},
{
"cell_type": "markdown",
"id": "b2d36f7a",
"metadata": {},
"source": [
"## MCP Tools\n",
"\n",
"**Not currently supported**. `graphrag_llm` currently only implements the `completion` endpoints which do not support MCP tools.\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.9"
}
},
"nbformat": 4,
"nbformat_minor": 5
}