LLMService: use a single FunctionCallParams parameter for function calls
This commit is contained in:
@@ -25,6 +25,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
- Function calls now receive a single parameter `FunctionCallParams` instead of
|
||||||
|
`(function_name, tool_call_id, args, llm, context, result_callback)` which is
|
||||||
|
now deprecated.
|
||||||
|
|
||||||
- Changed the user aggregator timeout for late transcriptions from 1.0s to 0.5s
|
- Changed the user aggregator timeout for late transcriptions from 1.0s to 0.5s
|
||||||
(`LLMUserAggregatorParams.aggregation_timeout`). Sometimes, the STT services
|
(`LLMUserAggregatorParams.aggregation_timeout`). Sometimes, the STT services
|
||||||
might give us more than one transcription which could come after the user
|
might give us more than one transcription which could come after the user
|
||||||
@@ -52,6 +56,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
|
|
||||||
|
- Function calls with parameters `(function_name, tool_call_id, args, llm,
|
||||||
|
context, result_callback)` are deprectated, use a single `FunctionCallParams`
|
||||||
|
parameter instead.
|
||||||
|
|
||||||
- `TransportParams.camera_*` parameters are now deprecated, use
|
- `TransportParams.camera_*` parameters are now deprecated, use
|
||||||
`TransportParams.video_*` instead.
|
`TransportParams.video_*` instead.
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,9 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import inspect
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any, Optional, Set, Tuple, Type
|
from typing import Any, Awaitable, Callable, Mapping, Optional, Protocol, Set, Tuple, Type
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
@@ -17,6 +18,7 @@ from pipecat.frames.frames import (
|
|||||||
FunctionCallCancelFrame,
|
FunctionCallCancelFrame,
|
||||||
FunctionCallInProgressFrame,
|
FunctionCallInProgressFrame,
|
||||||
FunctionCallResultFrame,
|
FunctionCallResultFrame,
|
||||||
|
FunctionCallResultProperties,
|
||||||
StartInterruptionFrame,
|
StartInterruptionFrame,
|
||||||
UserImageRequestFrame,
|
UserImageRequestFrame,
|
||||||
)
|
)
|
||||||
@@ -28,14 +30,55 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
|||||||
from pipecat.processors.frame_processor import FrameDirection
|
from pipecat.processors.frame_processor import FrameDirection
|
||||||
from pipecat.services.ai_service import AIService
|
from pipecat.services.ai_service import AIService
|
||||||
|
|
||||||
|
# Type alias for a callable that handles LLM function calls.
|
||||||
|
FunctionCallHandler = Callable[["FunctionCallParams"], Awaitable[None]]
|
||||||
|
|
||||||
|
|
||||||
|
# Type alias for a callback function that handles the result of an LLM function call.
|
||||||
|
class FunctionCallResultCallback(Protocol):
|
||||||
|
async def __call__(
|
||||||
|
self, result: Any, *, properties: Optional[FunctionCallResultProperties] = None
|
||||||
|
) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class FunctionEntry:
|
class FunctionCallEntry:
|
||||||
|
"""Represents an internal entry for a function call.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
function_name (Optional[str]): The name of the function.
|
||||||
|
handler (FunctionCallHandler): The handler for processing function call parameters.
|
||||||
|
cancel_on_interruption (bool): Flag indicating whether to cancel the call on interruption.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
function_name: Optional[str]
|
function_name: Optional[str]
|
||||||
callback: Any # TODO(aleix): add proper typing.
|
handler: FunctionCallHandler
|
||||||
cancel_on_interruption: bool
|
cancel_on_interruption: bool
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FunctionCallParams:
|
||||||
|
"""Parameters for a function call.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
function_name (str): The name of the function being called.
|
||||||
|
arguments (Mapping[str, Any]): The arguments for the function.
|
||||||
|
tool_call_id (str): A unique identifier for the function call.
|
||||||
|
llm (LLMService): The LLMService instance being used.
|
||||||
|
context (OpenAILLMContext): The LLM context.
|
||||||
|
result_callback (FunctionCallResultCallback): Callback to handle the result of the function call.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
function_name: str
|
||||||
|
tool_call_id: str
|
||||||
|
arguments: Mapping[str, Any]
|
||||||
|
llm: "LLMService"
|
||||||
|
context: OpenAILLMContext
|
||||||
|
result_callback: FunctionCallResultCallback
|
||||||
|
|
||||||
|
|
||||||
class LLMService(AIService):
|
class LLMService(AIService):
|
||||||
"""This class is a no-op but serves as a base class for LLM services."""
|
"""This class is a no-op but serves as a base class for LLM services."""
|
||||||
|
|
||||||
@@ -78,16 +121,16 @@ class LLMService(AIService):
|
|||||||
def register_function(
|
def register_function(
|
||||||
self,
|
self,
|
||||||
function_name: Optional[str],
|
function_name: Optional[str],
|
||||||
callback: Any,
|
handler: Any,
|
||||||
start_callback=None,
|
start_callback=None,
|
||||||
*,
|
*,
|
||||||
cancel_on_interruption: bool = False,
|
cancel_on_interruption: bool = False,
|
||||||
):
|
):
|
||||||
# Registering a function with the function_name set to None will run that callback
|
# Registering a function with the function_name set to None will run
|
||||||
# for all functions
|
# that handler for all functions
|
||||||
self._functions[function_name] = FunctionEntry(
|
self._functions[function_name] = FunctionCallEntry(
|
||||||
function_name=function_name,
|
function_name=function_name,
|
||||||
callback=callback,
|
handler=handler,
|
||||||
cancel_on_interruption=cancel_on_interruption,
|
cancel_on_interruption=cancel_on_interruption,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -120,7 +163,7 @@ class LLMService(AIService):
|
|||||||
context: OpenAILLMContext,
|
context: OpenAILLMContext,
|
||||||
tool_call_id: str,
|
tool_call_id: str,
|
||||||
function_name: str,
|
function_name: str,
|
||||||
arguments: str,
|
arguments: Mapping[str, Any],
|
||||||
run_llm: bool = True,
|
run_llm: bool = True,
|
||||||
):
|
):
|
||||||
if not function_name in self._functions.keys() and not None in self._functions.keys():
|
if not function_name in self._functions.keys() and not None in self._functions.keys():
|
||||||
@@ -163,7 +206,7 @@ class LLMService(AIService):
|
|||||||
context: OpenAILLMContext,
|
context: OpenAILLMContext,
|
||||||
tool_call_id: str,
|
tool_call_id: str,
|
||||||
function_name: str,
|
function_name: str,
|
||||||
arguments: str,
|
arguments: Mapping[str, Any],
|
||||||
run_llm: bool = True,
|
run_llm: bool = True,
|
||||||
):
|
):
|
||||||
if function_name in self._functions.keys():
|
if function_name in self._functions.keys():
|
||||||
@@ -202,7 +245,9 @@ class LLMService(AIService):
|
|||||||
await self.push_frame(progress_frame_upstream, FrameDirection.UPSTREAM)
|
await self.push_frame(progress_frame_upstream, FrameDirection.UPSTREAM)
|
||||||
|
|
||||||
# Define a callback function that pushes a FunctionCallResultFrame upstream & downstream.
|
# Define a callback function that pushes a FunctionCallResultFrame upstream & downstream.
|
||||||
async def function_call_result_callback(result, *, properties=None):
|
async def function_call_result_callback(
|
||||||
|
result: Any, *, properties: Optional[FunctionCallResultProperties] = None
|
||||||
|
):
|
||||||
result_frame_downstream = FunctionCallResultFrame(
|
result_frame_downstream = FunctionCallResultFrame(
|
||||||
function_name=function_name,
|
function_name=function_name,
|
||||||
tool_call_id=tool_call_id,
|
tool_call_id=tool_call_id,
|
||||||
@@ -221,9 +266,30 @@ class LLMService(AIService):
|
|||||||
await self.push_frame(result_frame_downstream, FrameDirection.DOWNSTREAM)
|
await self.push_frame(result_frame_downstream, FrameDirection.DOWNSTREAM)
|
||||||
await self.push_frame(result_frame_upstream, FrameDirection.UPSTREAM)
|
await self.push_frame(result_frame_upstream, FrameDirection.UPSTREAM)
|
||||||
|
|
||||||
await entry.callback(
|
signature = inspect.signature(entry.handler)
|
||||||
function_name, tool_call_id, arguments, self, context, function_call_result_callback
|
if len(signature.parameters) > 1:
|
||||||
)
|
import warnings
|
||||||
|
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("always")
|
||||||
|
warnings.warn(
|
||||||
|
"Function calls with parameters `(function_name, tool_call_id, arguments, llm, context, result_callback)` are deprecated, use a single `FunctionCallParams` parameter instead.",
|
||||||
|
DeprecationWarning,
|
||||||
|
)
|
||||||
|
|
||||||
|
await entry.handler(
|
||||||
|
function_name, tool_call_id, arguments, self, context, function_call_result_callback
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
params = FunctionCallParams(
|
||||||
|
function_name=function_name,
|
||||||
|
tool_call_id=tool_call_id,
|
||||||
|
arguments=arguments,
|
||||||
|
llm=self,
|
||||||
|
context=context,
|
||||||
|
result_callback=function_call_result_callback,
|
||||||
|
)
|
||||||
|
await entry.handler(params)
|
||||||
|
|
||||||
async def _cancel_function_call(self, function_name: str):
|
async def _cancel_function_call(self, function_name: str):
|
||||||
cancelled_tasks = set()
|
cancelled_tasks = set()
|
||||||
|
|||||||
Reference in New Issue
Block a user