examples(foundational): associate image requests to function calls
This commit is contained in:
@@ -55,9 +55,15 @@ async def fetch_user_image(params: FunctionCallParams):
|
|||||||
logger.debug(f"Requesting image with user_id={user_id}, question={question}")
|
logger.debug(f"Requesting image with user_id={user_id}, question={question}")
|
||||||
|
|
||||||
# Request a user image frame and indicate that it should be added to the
|
# Request a user image frame and indicate that it should be added to the
|
||||||
# context.
|
# context. Also associate it to the function call.
|
||||||
await params.llm.push_frame(
|
await params.llm.push_frame(
|
||||||
UserImageRequestFrame(user_id=user_id, text=question, append_to_context=True),
|
UserImageRequestFrame(
|
||||||
|
user_id=user_id,
|
||||||
|
text=question,
|
||||||
|
append_to_context=True,
|
||||||
|
function_name=params.function_name,
|
||||||
|
tool_call_id=params.tool_call_id,
|
||||||
|
),
|
||||||
FrameDirection.UPSTREAM,
|
FrameDirection.UPSTREAM,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -55,9 +55,15 @@ async def fetch_user_image(params: FunctionCallParams):
|
|||||||
logger.debug(f"Requesting image with user_id={user_id}, question={question}")
|
logger.debug(f"Requesting image with user_id={user_id}, question={question}")
|
||||||
|
|
||||||
# Request a user image frame and indicate that it should be added to the
|
# Request a user image frame and indicate that it should be added to the
|
||||||
# context.
|
# context. Also associate it to the function call.
|
||||||
await params.llm.push_frame(
|
await params.llm.push_frame(
|
||||||
UserImageRequestFrame(user_id=user_id, text=question, append_to_context=True),
|
UserImageRequestFrame(
|
||||||
|
user_id=user_id,
|
||||||
|
text=question,
|
||||||
|
append_to_context=True,
|
||||||
|
function_name=params.function_name,
|
||||||
|
tool_call_id=params.tool_call_id,
|
||||||
|
),
|
||||||
FrameDirection.UPSTREAM,
|
FrameDirection.UPSTREAM,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -55,9 +55,15 @@ async def fetch_user_image(params: FunctionCallParams):
|
|||||||
logger.debug(f"Requesting image with user_id={user_id}, question={question}")
|
logger.debug(f"Requesting image with user_id={user_id}, question={question}")
|
||||||
|
|
||||||
# Request a user image frame and indicate that it should be added to the
|
# Request a user image frame and indicate that it should be added to the
|
||||||
# context.
|
# context. Also associate it to the function call.
|
||||||
await params.llm.push_frame(
|
await params.llm.push_frame(
|
||||||
UserImageRequestFrame(user_id=user_id, text=question, append_to_context=True),
|
UserImageRequestFrame(
|
||||||
|
user_id=user_id,
|
||||||
|
text=question,
|
||||||
|
append_to_context=True,
|
||||||
|
function_name=params.function_name,
|
||||||
|
tool_call_id=params.tool_call_id,
|
||||||
|
),
|
||||||
FrameDirection.UPSTREAM,
|
FrameDirection.UPSTREAM,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -64,9 +64,15 @@ async def fetch_user_image(params: FunctionCallParams):
|
|||||||
|
|
||||||
# Request a user image frame. In this case, we don't want the requested
|
# Request a user image frame. In this case, we don't want the requested
|
||||||
# image to be added to the context because we will process it with
|
# image to be added to the context because we will process it with
|
||||||
# Moondream.
|
# Moondream. Also associate it to the function call.
|
||||||
await params.llm.push_frame(
|
await params.llm.push_frame(
|
||||||
UserImageRequestFrame(user_id=user_id, text=question, append_to_context=False),
|
UserImageRequestFrame(
|
||||||
|
user_id=user_id,
|
||||||
|
text=question,
|
||||||
|
append_to_context=False,
|
||||||
|
function_name=params.function_name,
|
||||||
|
tool_call_id=params.tool_call_id,
|
||||||
|
),
|
||||||
FrameDirection.UPSTREAM,
|
FrameDirection.UPSTREAM,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -56,9 +56,15 @@ async def fetch_user_image(params: FunctionCallParams):
|
|||||||
logger.debug(f"Requesting image with user_id={user_id}, question={question}")
|
logger.debug(f"Requesting image with user_id={user_id}, question={question}")
|
||||||
|
|
||||||
# Request a user image frame and indicate that it should be added to the
|
# Request a user image frame and indicate that it should be added to the
|
||||||
# context.
|
# context. Also associate it to the function call.
|
||||||
await params.llm.push_frame(
|
await params.llm.push_frame(
|
||||||
UserImageRequestFrame(user_id=user_id, text=question, append_to_context=True),
|
UserImageRequestFrame(
|
||||||
|
user_id=user_id,
|
||||||
|
text=question,
|
||||||
|
append_to_context=True,
|
||||||
|
function_name=params.function_name,
|
||||||
|
tool_call_id=params.tool_call_id,
|
||||||
|
),
|
||||||
FrameDirection.UPSTREAM,
|
FrameDirection.UPSTREAM,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
@@ -16,7 +15,7 @@ from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
|||||||
from pipecat.audio.turn.smart_turn.local_smart_turn_v3 import LocalSmartTurnAnalyzerV3
|
from pipecat.audio.turn.smart_turn.local_smart_turn_v3 import LocalSmartTurnAnalyzerV3
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.audio.vad.vad_analyzer import VADParams
|
from pipecat.audio.vad.vad_analyzer import VADParams
|
||||||
from pipecat.frames.frames import LLMRunFrame, TTSSpeakFrame
|
from pipecat.frames.frames import LLMRunFrame, TTSSpeakFrame, UserImageRequestFrame
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
@@ -25,6 +24,7 @@ from pipecat.processors.aggregators.llm_response_universal import (
|
|||||||
LLMContextAggregatorPair,
|
LLMContextAggregatorPair,
|
||||||
LLMUserAggregatorParams,
|
LLMUserAggregatorParams,
|
||||||
)
|
)
|
||||||
|
from pipecat.processors.frame_processor import FrameDirection
|
||||||
from pipecat.runner.types import RunnerArguments
|
from pipecat.runner.types import RunnerArguments
|
||||||
from pipecat.runner.utils import (
|
from pipecat.runner.utils import (
|
||||||
create_transport,
|
create_transport,
|
||||||
@@ -43,10 +43,6 @@ from pipecat.turns.user_turn_strategies import UserTurnStrategies
|
|||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
|
|
||||||
# Global variable to store the client ID
|
|
||||||
client_id = ""
|
|
||||||
|
|
||||||
|
|
||||||
async def get_weather(params: FunctionCallParams):
|
async def get_weather(params: FunctionCallParams):
|
||||||
location = params.arguments["location"]
|
location = params.arguments["location"]
|
||||||
await params.result_callback(f"The weather in {location} is currently 72 degrees and sunny.")
|
await params.result_callback(f"The weather in {location} is currently 72 degrees and sunny.")
|
||||||
@@ -57,19 +53,36 @@ async def fetch_restaurant_recommendation(params: FunctionCallParams):
|
|||||||
|
|
||||||
|
|
||||||
async def get_image(params: FunctionCallParams):
|
async def get_image(params: FunctionCallParams):
|
||||||
question = params.arguments["question"]
|
"""Fetch the user image and push it to the LLM.
|
||||||
logger.debug(f"Requesting image with user_id={client_id}, question={question}")
|
|
||||||
|
|
||||||
# Request the image frame
|
When called, this function pushes a UserImageRequestFrame upstream to the
|
||||||
await params.llm.request_image_frame(
|
transport. As a result, the transport will request the user image and push a
|
||||||
user_id=client_id,
|
UserImageRawFrame downstream which will be added to the context by the LLM
|
||||||
function_name=params.function_name,
|
assistant aggregator.
|
||||||
tool_call_id=params.tool_call_id,
|
"""
|
||||||
text_content=question,
|
user_id = params.arguments["user_id"]
|
||||||
|
question = params.arguments["question"]
|
||||||
|
logger.debug(f"Requesting image with user_id={user_id}, question={question}")
|
||||||
|
|
||||||
|
# Request a user image frame and indicate that it should be added to the
|
||||||
|
# context. Also associate it to the function call.
|
||||||
|
await params.llm.push_frame(
|
||||||
|
UserImageRequestFrame(
|
||||||
|
user_id=user_id,
|
||||||
|
text=question,
|
||||||
|
append_to_context=True,
|
||||||
|
function_name=params.function_name,
|
||||||
|
tool_call_id=params.tool_call_id,
|
||||||
|
),
|
||||||
|
FrameDirection.UPSTREAM,
|
||||||
)
|
)
|
||||||
|
|
||||||
await params.result_callback(None)
|
await params.result_callback(None)
|
||||||
|
|
||||||
|
# Instead of None, it's possible to also provide a tool call answer to
|
||||||
|
# tell the LLM that we are grabbing the image to analyze.
|
||||||
|
# await params.result_callback({"result": "Image is being captured."})
|
||||||
|
|
||||||
|
|
||||||
# We store functions so objects (e.g. SileroVADAnalyzer) don't get
|
# We store functions so objects (e.g. SileroVADAnalyzer) don't get
|
||||||
# instantiated. The function will be called when the desired transport gets
|
# instantiated. The function will be called when the desired transport gets
|
||||||
@@ -138,14 +151,18 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments):
|
|||||||
)
|
)
|
||||||
get_image_function = FunctionSchema(
|
get_image_function = FunctionSchema(
|
||||||
name="get_image",
|
name="get_image",
|
||||||
description="Get an image from the video stream.",
|
description="Called when the user requests a description of their camera feed",
|
||||||
properties={
|
properties={
|
||||||
|
"user_id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The ID of the user to grab the image from",
|
||||||
|
},
|
||||||
"question": {
|
"question": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The question that the user is asking about the image.",
|
"description": "The question that the user is asking about the image",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
required=["question"],
|
required=["user_id", "question"],
|
||||||
)
|
)
|
||||||
tools = ToolsSchema(standard_tools=[weather_function, get_image_function, restaurant_function])
|
tools = ToolsSchema(standard_tools=[weather_function, get_image_function, restaurant_function])
|
||||||
|
|
||||||
@@ -169,7 +186,6 @@ indicate you should use the get_image tool are:
|
|||||||
"""
|
"""
|
||||||
messages = [
|
messages = [
|
||||||
{"role": "system", "content": system_prompt},
|
{"role": "system", "content": system_prompt},
|
||||||
{"role": "user", "content": "Say hello."},
|
|
||||||
]
|
]
|
||||||
|
|
||||||
context = LLMContext(messages, tools)
|
context = LLMContext(messages, tools)
|
||||||
@@ -209,10 +225,15 @@ indicate you should use the get_image tool are:
|
|||||||
|
|
||||||
await maybe_capture_participant_camera(transport, client)
|
await maybe_capture_participant_camera(transport, client)
|
||||||
|
|
||||||
global client_id
|
|
||||||
client_id = get_transport_client_id(transport, client)
|
client_id = get_transport_client_id(transport, client)
|
||||||
|
|
||||||
# Kick off the conversation.
|
# Kick off the conversation.
|
||||||
|
messages.append(
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": f"Please introduce yourself to the user. Use '{client_id}' as the user ID during function calls.",
|
||||||
|
}
|
||||||
|
)
|
||||||
await task.queue_frames([LLMRunFrame()])
|
await task.queue_frames([LLMRunFrame()])
|
||||||
|
|
||||||
@transport.event_handler("on_client_disconnected")
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
|||||||
from pipecat.audio.turn.smart_turn.local_smart_turn_v3 import LocalSmartTurnAnalyzerV3
|
from pipecat.audio.turn.smart_turn.local_smart_turn_v3 import LocalSmartTurnAnalyzerV3
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.audio.vad.vad_analyzer import VADParams
|
from pipecat.audio.vad.vad_analyzer import VADParams
|
||||||
from pipecat.frames.frames import LLMRunFrame
|
from pipecat.frames.frames import LLMRunFrame, TTSSpeakFrame, UserImageRequestFrame
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
@@ -26,6 +26,7 @@ from pipecat.processors.aggregators.llm_response_universal import (
|
|||||||
LLMContextAggregatorPair,
|
LLMContextAggregatorPair,
|
||||||
LLMUserAggregatorParams,
|
LLMUserAggregatorParams,
|
||||||
)
|
)
|
||||||
|
from pipecat.processors.frame_processor import FrameDirection
|
||||||
from pipecat.runner.types import RunnerArguments
|
from pipecat.runner.types import RunnerArguments
|
||||||
from pipecat.runner.utils import (
|
from pipecat.runner.utils import (
|
||||||
create_transport,
|
create_transport,
|
||||||
@@ -46,9 +47,6 @@ load_dotenv(override=True)
|
|||||||
|
|
||||||
BASE_FILENAME = "/tmp/pipecat_conversation_"
|
BASE_FILENAME = "/tmp/pipecat_conversation_"
|
||||||
|
|
||||||
# Global variable to store the client ID
|
|
||||||
client_id = ""
|
|
||||||
|
|
||||||
|
|
||||||
async def fetch_weather_from_api(params: FunctionCallParams):
|
async def fetch_weather_from_api(params: FunctionCallParams):
|
||||||
temperature = 75 if params.arguments["format"] == "fahrenheit" else 24
|
temperature = 75 if params.arguments["format"] == "fahrenheit" else 24
|
||||||
@@ -63,19 +61,29 @@ async def fetch_weather_from_api(params: FunctionCallParams):
|
|||||||
|
|
||||||
|
|
||||||
async def get_image(params: FunctionCallParams):
|
async def get_image(params: FunctionCallParams):
|
||||||
|
user_id = params.arguments["user_id"]
|
||||||
question = params.arguments["question"]
|
question = params.arguments["question"]
|
||||||
logger.debug(f"Requesting image with user_id={client_id}, question={question}")
|
logger.debug(f"Requesting image with user_id={user_id}, question={question}")
|
||||||
|
|
||||||
# Request the image frame
|
# Request a user image frame and indicate that it should be added to the
|
||||||
await params.llm.request_image_frame(
|
# context. Also associate it to the function call.
|
||||||
user_id=client_id,
|
await params.llm.push_frame(
|
||||||
function_name=params.function_name,
|
UserImageRequestFrame(
|
||||||
tool_call_id=params.tool_call_id,
|
user_id=user_id,
|
||||||
text_content=question,
|
text=question,
|
||||||
|
append_to_context=True,
|
||||||
|
function_name=params.function_name,
|
||||||
|
tool_call_id=params.tool_call_id,
|
||||||
|
),
|
||||||
|
FrameDirection.UPSTREAM,
|
||||||
)
|
)
|
||||||
|
|
||||||
await params.result_callback(None)
|
await params.result_callback(None)
|
||||||
|
|
||||||
|
# Instead of None, it's possible to also provide a tool call answer to
|
||||||
|
# tell the LLM that we are grabbing the image to analyze.
|
||||||
|
# await params.result_callback({"result": "Image is being captured."})
|
||||||
|
|
||||||
|
|
||||||
async def get_saved_conversation_filenames(params: FunctionCallParams):
|
async def get_saved_conversation_filenames(params: FunctionCallParams):
|
||||||
# Construct the full pattern including the BASE_FILENAME
|
# Construct the full pattern including the BASE_FILENAME
|
||||||
@@ -209,14 +217,18 @@ load_conversation_function = FunctionSchema(
|
|||||||
|
|
||||||
get_image_function = FunctionSchema(
|
get_image_function = FunctionSchema(
|
||||||
name="get_image",
|
name="get_image",
|
||||||
description="Get and image from the camera or video stream.",
|
description="Called when the user requests a description of their camera feed",
|
||||||
properties={
|
properties={
|
||||||
|
"user_id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The ID of the user to grab the image from",
|
||||||
|
},
|
||||||
"question": {
|
"question": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The question to to use when running inference on the acquired image.",
|
"description": "The question that the user is asking about the image",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
required=["question"],
|
required=["user_id", "question"],
|
||||||
)
|
)
|
||||||
|
|
||||||
tools = ToolsSchema(
|
tools = ToolsSchema(
|
||||||
@@ -306,10 +318,15 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments):
|
|||||||
|
|
||||||
await maybe_capture_participant_camera(transport, client)
|
await maybe_capture_participant_camera(transport, client)
|
||||||
|
|
||||||
global client_id
|
|
||||||
client_id = get_transport_client_id(transport, client)
|
client_id = get_transport_client_id(transport, client)
|
||||||
|
|
||||||
# Kick off the conversation.
|
# Kick off the conversation.
|
||||||
|
messages.append(
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": f"Please introduce yourself to the user. Use '{client_id}' as the user ID during function calls.",
|
||||||
|
}
|
||||||
|
)
|
||||||
await task.queue_frames([LLMRunFrame()])
|
await task.queue_frames([LLMRunFrame()])
|
||||||
|
|
||||||
@transport.event_handler("on_client_disconnected")
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
|||||||
Reference in New Issue
Block a user