Update OpenAIRealtimeLLMService to work with LLMContext and LLMContextAggregatorPair (cont'd).
Make `LLMUserAggregator` push the `LLMSetToolsFrame`s, in case a speech-to-speech service that needs to handle the frame itself—like `OpenAIRealtimeLLMService`—is downstream. As far as I can tell, pushing `LLMSetToolsFrame` should otherwise have no unwanted side effects.
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
|
import asyncio
|
||||||
import os
|
import os
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
@@ -14,7 +15,7 @@ from loguru import logger
|
|||||||
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||||
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.frames.frames import LLMRunFrame, TranscriptionMessage
|
from pipecat.frames.frames import LLMRunFrame, LLMSetToolsFrame, TranscriptionMessage
|
||||||
from pipecat.observers.loggers.transcription_log_observer import TranscriptionLogObserver
|
from pipecat.observers.loggers.transcription_log_observer import TranscriptionLogObserver
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
@@ -53,6 +54,18 @@ async def fetch_weather_from_api(params: FunctionCallParams):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_news(params: FunctionCallParams):
|
||||||
|
await params.result_callback(
|
||||||
|
{
|
||||||
|
"news": [
|
||||||
|
"Massive UFO currently hovering above New York City",
|
||||||
|
"Stock markets reach all-time highs",
|
||||||
|
"Living dinosaur species discovered in the Amazon rainforest",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def fetch_restaurant_recommendation(params: FunctionCallParams):
|
async def fetch_restaurant_recommendation(params: FunctionCallParams):
|
||||||
await params.result_callback({"name": "The Golden Dragon"})
|
await params.result_callback({"name": "The Golden Dragon"})
|
||||||
|
|
||||||
@@ -74,6 +87,13 @@ weather_function = FunctionSchema(
|
|||||||
required=["location", "format"],
|
required=["location", "format"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
get_news_function = FunctionSchema(
|
||||||
|
name="get_news",
|
||||||
|
description="Get the current news.",
|
||||||
|
properties={},
|
||||||
|
required=[],
|
||||||
|
)
|
||||||
|
|
||||||
restaurant_function = FunctionSchema(
|
restaurant_function = FunctionSchema(
|
||||||
name="get_restaurant_recommendation",
|
name="get_restaurant_recommendation",
|
||||||
description="Get a restaurant recommendation",
|
description="Get a restaurant recommendation",
|
||||||
@@ -141,10 +161,6 @@ even if you're asked about them.
|
|||||||
You are participating in a voice conversation. Keep your responses concise, short, and to the point
|
You are participating in a voice conversation. Keep your responses concise, short, and to the point
|
||||||
unless specifically asked to elaborate on a topic.
|
unless specifically asked to elaborate on a topic.
|
||||||
|
|
||||||
You have access to the following tools:
|
|
||||||
- get_current_weather: Get the current weather for a given location.
|
|
||||||
- get_restaurant_recommendation: Get a restaurant recommendation for a given location.
|
|
||||||
|
|
||||||
Remember, your responses should be short. Just one or two sentences, usually. Respond in English.""",
|
Remember, your responses should be short. Just one or two sentences, usually. Respond in English.""",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -158,6 +174,7 @@ Remember, your responses should be short. Just one or two sentences, usually. Re
|
|||||||
# llm.register_function(None, fetch_weather_from_api)
|
# llm.register_function(None, fetch_weather_from_api)
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
llm.register_function("get_restaurant_recommendation", fetch_restaurant_recommendation)
|
llm.register_function("get_restaurant_recommendation", fetch_restaurant_recommendation)
|
||||||
|
llm.register_function("get_news", get_news)
|
||||||
|
|
||||||
transcript = TranscriptProcessor()
|
transcript = TranscriptProcessor()
|
||||||
|
|
||||||
@@ -199,6 +216,16 @@ Remember, your responses should be short. Just one or two sentences, usually. Re
|
|||||||
# Kick off the conversation.
|
# Kick off the conversation.
|
||||||
await task.queue_frames([LLMRunFrame()])
|
await task.queue_frames([LLMRunFrame()])
|
||||||
|
|
||||||
|
async def set_tools_after_delay():
|
||||||
|
await asyncio.sleep(15)
|
||||||
|
new_tools = ToolsSchema(
|
||||||
|
standard_tools=[weather_function, restaurant_function, get_news_function]
|
||||||
|
)
|
||||||
|
logger.info("Registering new tool with LLMSetToolsFrame")
|
||||||
|
await task.queue_frames([LLMSetToolsFrame(tools=new_tools)])
|
||||||
|
|
||||||
|
asyncio.create_task(set_tools_after_delay())
|
||||||
|
|
||||||
@transport.event_handler("on_client_disconnected")
|
@transport.event_handler("on_client_disconnected")
|
||||||
async def on_client_disconnected(transport, client):
|
async def on_client_disconnected(transport, client):
|
||||||
logger.info(f"Client disconnected")
|
logger.info(f"Client disconnected")
|
||||||
|
|||||||
@@ -290,6 +290,12 @@ class LLMUserAggregator(LLMContextAggregator):
|
|||||||
await self._handle_llm_messages_update(frame)
|
await self._handle_llm_messages_update(frame)
|
||||||
elif isinstance(frame, LLMSetToolsFrame):
|
elif isinstance(frame, LLMSetToolsFrame):
|
||||||
self.set_tools(frame.tools)
|
self.set_tools(frame.tools)
|
||||||
|
# Push the LLMSetToolsFrame as well, since speech-to-speech LLM
|
||||||
|
# services (like OpenAI Realtime) may need to know about tool
|
||||||
|
# changes; unlike text-based LLM services they won't just "pick up
|
||||||
|
# the change" on the next LLM run, as the LLM is continuously
|
||||||
|
# running.
|
||||||
|
await self.push_frame(frame, direction)
|
||||||
elif isinstance(frame, LLMSetToolChoiceFrame):
|
elif isinstance(frame, LLMSetToolChoiceFrame):
|
||||||
self.set_tool_choice(frame.tool_choice)
|
self.set_tool_choice(frame.tool_choice)
|
||||||
elif isinstance(frame, SpeechControlParamsFrame):
|
elif isinstance(frame, SpeechControlParamsFrame):
|
||||||
|
|||||||
Reference in New Issue
Block a user