diff --git a/src/pipecat/services/anthropic/llm.py b/src/pipecat/services/anthropic/llm.py index 49b4f2f6a..236f269fa 100644 --- a/src/pipecat/services/anthropic/llm.py +++ b/src/pipecat/services/anthropic/llm.py @@ -45,7 +45,7 @@ from pipecat.processors.aggregators.openai_llm_context import ( OpenAILLMContextFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.llm_service import FunctionCallLLM, LLMService +from pipecat.services.llm_service import FunctionCallFromLLM, LLMService from pipecat.utils.tracing.service_decorators import traced_llm try: @@ -227,7 +227,7 @@ class AnthropicLLMService(LLMService): if tool_use_block: args = json.loads(json_accumulator) if json_accumulator else {} function_calls.append( - FunctionCallLLM( + FunctionCallFromLLM( context=context, tool_call_id=tool_use_block.id, function_name=tool_use_block.name, diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index 3096184d2..894e53285 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -52,7 +52,7 @@ from pipecat.processors.aggregators.openai_llm_context import ( OpenAILLMContextFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.llm_service import FunctionCallLLM, LLMService +from pipecat.services.llm_service import FunctionCallFromLLM, LLMService from pipecat.services.openai.llm import ( OpenAIAssistantContextAggregator, OpenAIUserContextAggregator, @@ -893,7 +893,7 @@ class GeminiMultimodalLiveLLMService(LLMService): logger.error("Function calls are not supported without a context object.") function_calls_llm = [ - FunctionCallLLM( + FunctionCallFromLLM( context=self._context, tool_call_id=f.id, function_name=f.name, diff --git a/src/pipecat/services/google/llm.py b/src/pipecat/services/google/llm.py index 68e6f2406..b63b3786c 100644 --- a/src/pipecat/services/google/llm.py +++ b/src/pipecat/services/google/llm.py @@ -42,7 +42,7 @@ from pipecat.processors.aggregators.openai_llm_context import ( ) from pipecat.processors.frame_processor import FrameDirection from pipecat.services.google.frames import LLMSearchResponseFrame -from pipecat.services.llm_service import FunctionCallLLM, LLMService +from pipecat.services.llm_service import FunctionCallFromLLM, LLMService from pipecat.services.openai.llm import ( OpenAIAssistantContextAggregator, OpenAIUserContextAggregator, @@ -578,7 +578,7 @@ class GoogleLLMService(LLMService): id = function_call.id or str(uuid.uuid4()) logger.debug(f"Function call: {function_call.name}:{id}") function_calls.append( - FunctionCallLLM( + FunctionCallFromLLM( context=context, tool_call_id=id, function_name=function_call.name, diff --git a/src/pipecat/services/google/llm_openai.py b/src/pipecat/services/google/llm_openai.py index db395705f..a497cb229 100644 --- a/src/pipecat/services/google/llm_openai.py +++ b/src/pipecat/services/google/llm_openai.py @@ -10,7 +10,7 @@ import os from openai import AsyncStream from openai.types.chat import ChatCompletionChunk -from pipecat.services.llm_service import FunctionCallLLM +from pipecat.services.llm_service import FunctionCallFromLLM # Suppress gRPC fork warnings os.environ["GRPC_ENABLE_FORK_SUPPORT"] = "false" @@ -127,7 +127,7 @@ class GoogleLLMOpenAIBetaService(OpenAILLMService): arguments = json.loads(arguments) function_calls.append( - FunctionCallLLM( + FunctionCallFromLLM( context=context, tool_call_id=tool_id, function_name=function_name, diff --git a/src/pipecat/services/llm_service.py b/src/pipecat/services/llm_service.py index 3f2eefc30..1ea55fb13 100644 --- a/src/pipecat/services/llm_service.py +++ b/src/pipecat/services/llm_service.py @@ -44,62 +44,6 @@ class FunctionCallResultCallback(Protocol): ) -> None: ... -@dataclass -class FunctionCallRegistryItem: - """Represents an entry of our function call registry. - - 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] - handler: FunctionCallHandler - cancel_on_interruption: bool - - -@dataclass -class FunctionCallLLM: - """Represents a function call returned by the LLM to be registered for execution. - - Attributes: - function_name (str): The name of the function. - tool_call_id (str): A unique identifier for the function call. - arguments (Mapping[str, Any]): The arguments for the function. - context (OpenAILLMContext): The LLM context. - - """ - - function_name: str - tool_call_id: str - arguments: Mapping[str, Any] - context: OpenAILLMContext - - -@dataclass -class FunctionCallRunner: - """Represents an internal function call entry to our function call - runner. The runner executes function calls in order. - - Attributes: - registry_name (Optional[str]): The function call name registration (could be None). - function_name (str): The name of the function. - tool_call_id (str): A unique identifier for the function call. - arguments (Mapping[str, Any]): The arguments for the function. - context (OpenAILLMContext): The LLM context. - - """ - - registry_item: FunctionCallRegistryItem - function_name: str - tool_call_id: str - arguments: Mapping[str, Any] - context: OpenAILLMContext - run_llm: Optional[bool] = None - - @dataclass class FunctionCallParams: """Parameters for a function call. @@ -122,6 +66,63 @@ class FunctionCallParams: result_callback: FunctionCallResultCallback +@dataclass +class FunctionCallFromLLM: + """Represents a function call returned by the LLM to be registered for execution. + + Attributes: + function_name (str): The name of the function. + tool_call_id (str): A unique identifier for the function call. + arguments (Mapping[str, Any]): The arguments for the function. + context (OpenAILLMContext): The LLM context. + + """ + + function_name: str + tool_call_id: str + arguments: Mapping[str, Any] + context: OpenAILLMContext + + +@dataclass +class FunctionCallRegistryItem: + """Represents an entry in our function call registry. This is what the user + registers. + + 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] + handler: FunctionCallHandler + cancel_on_interruption: bool + + +@dataclass +class FunctionCallRunnerItem: + """Represents an internal function call entry to our function call + runner. The runner executes function calls in order. + + Attributes: + registry_name (Optional[str]): The function call name registration (could be None). + function_name (str): The name of the function. + tool_call_id (str): A unique identifier for the function call. + arguments (Mapping[str, Any]): The arguments for the function. + context (OpenAILLMContext): The LLM context. + + """ + + registry_item: FunctionCallRegistryItem + function_name: str + tool_call_id: str + arguments: Mapping[str, Any] + context: OpenAILLMContext + run_llm: Optional[bool] = None + + class LLMService(AIService): """This class is a no-op but serves as a base class for LLM services.""" @@ -135,7 +136,7 @@ class LLMService(AIService): self._start_callbacks = {} self._adapter = self.adapter_class() self._functions: Dict[Optional[str], FunctionCallRegistryItem] = {} - self._function_call_tasks: Dict[asyncio.Task, FunctionCallRunner] = {} + self._function_call_tasks: Dict[asyncio.Task, FunctionCallRunnerItem] = {} self._sequential_runner_task: Optional[asyncio.Task] = None self._register_event_handler("on_completion_timeout") @@ -217,7 +218,7 @@ class LLMService(AIService): return True return function_name in self._functions.keys() - async def run_function_calls(self, function_calls: Sequence[FunctionCallLLM]): + async def run_function_calls(self, function_calls: Sequence[FunctionCallFromLLM]): total_function_calls = len(function_calls) for index, function_call in enumerate(function_calls): if function_call.function_name in self._functions.keys(): @@ -237,7 +238,7 @@ class LLMService(AIService): if not self._run_in_parallel: run_llm = index == total_function_calls - 1 - runner_item = FunctionCallRunner( + runner_item = FunctionCallRunnerItem( registry_item=item, function_name=function_call.function_name, tool_call_id=function_call.tool_call_id, @@ -299,7 +300,7 @@ class LLMService(AIService): await self.wait_for_task(task) del self._function_call_tasks[task] - async def _run_function_call(self, runner_item: FunctionCallRunner): + async def _run_function_call(self, runner_item: FunctionCallRunnerItem): if runner_item.function_name in self._functions.keys(): item = self._functions[runner_item.function_name] elif None in self._functions.keys(): diff --git a/src/pipecat/services/openai/base_llm.py b/src/pipecat/services/openai/base_llm.py index 7f3303a2e..2badfed96 100644 --- a/src/pipecat/services/openai/base_llm.py +++ b/src/pipecat/services/openai/base_llm.py @@ -34,7 +34,7 @@ from pipecat.processors.aggregators.openai_llm_context import ( OpenAILLMContextFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.llm_service import FunctionCallLLM, LLMService +from pipecat.services.llm_service import FunctionCallFromLLM, LLMService from pipecat.utils.tracing.service_decorators import traced_llm @@ -263,7 +263,7 @@ class BaseOpenAILLMService(LLMService): ): arguments = json.loads(arguments) function_calls.append( - FunctionCallLLM( + FunctionCallFromLLM( context=context, tool_call_id=tool_id, function_name=function_name, diff --git a/src/pipecat/services/openai_realtime_beta/openai.py b/src/pipecat/services/openai_realtime_beta/openai.py index 343c61415..4ea5843bf 100644 --- a/src/pipecat/services/openai_realtime_beta/openai.py +++ b/src/pipecat/services/openai_realtime_beta/openai.py @@ -48,7 +48,7 @@ from pipecat.processors.aggregators.openai_llm_context import ( OpenAILLMContextFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.llm_service import FunctionCallLLM, LLMService +from pipecat.services.llm_service import FunctionCallFromLLM, LLMService from pipecat.services.openai.llm import OpenAIContextAggregatorPair from pipecat.transcriptions.language import Language from pipecat.utils.time import time_now_iso8601 @@ -587,7 +587,7 @@ class OpenAIRealtimeBetaLLMService(LLMService): for item in items: args = json.loads(item.arguments) function_calls.append( - FunctionCallLLM( + FunctionCallFromLLM( context=self._context, tool_call_id=item.call_id, function_name=item.name,