Merge pull request #2077 from yousifa/mcp-http-gemini-support
Mcp http gemini support
This commit is contained in:
@@ -18,8 +18,8 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask
|
|||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
from pipecat.services.deepgram.stt import DeepgramSTTService
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
|
from pipecat.services.google.llm import GoogleLLMService
|
||||||
from pipecat.services.mcp_service import MCPClient
|
from pipecat.services.mcp_service import MCPClient
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
|
||||||
from pipecat.transports.base_transport import BaseTransport, TransportParams
|
from pipecat.transports.base_transport import BaseTransport, TransportParams
|
||||||
from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams
|
from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams
|
||||||
from pipecat.transports.services.daily import DailyParams
|
from pipecat.transports.services.daily import DailyParams
|
||||||
@@ -58,7 +58,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
|
|||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
)
|
)
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o-mini")
|
llm = GoogleLLMService(api_key=os.getenv("GOOGLE_API_KEY"), model="gemini-2.0-flash")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Github MCP docs: https://github.com/github/github-mcp-server
|
# Github MCP docs: https://github.com/github/github-mcp-server
|
||||||
|
|||||||
@@ -583,6 +583,17 @@ class GeminiMultimodalLiveLLMService(LLMService):
|
|||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def needs_mcp_alternate_schema(self) -> bool:
|
||||||
|
"""Check if this LLM service requires alternate MCP schema.
|
||||||
|
|
||||||
|
Google/Gemini has stricter JSON schema validation and requires
|
||||||
|
certain properties to be removed or modified for compatibility.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True for Google/Gemini services.
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
def set_audio_input_paused(self, paused: bool):
|
def set_audio_input_paused(self, paused: bool):
|
||||||
"""Set the audio input pause state.
|
"""Set the audio input pause state.
|
||||||
|
|
||||||
|
|||||||
@@ -717,6 +717,17 @@ class GoogleLLMService(LLMService):
|
|||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def needs_mcp_alternate_schema(self) -> bool:
|
||||||
|
"""Check if this LLM service requires alternate MCP schema.
|
||||||
|
|
||||||
|
Google/Gemini has stricter JSON schema validation and requires
|
||||||
|
certain properties to be removed or modified for compatibility.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True for Google/Gemini services.
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
def _create_client(self, api_key: str):
|
def _create_client(self, api_key: str):
|
||||||
self._client = genai.Client(api_key=api_key)
|
self._client = genai.Client(api_key=api_key)
|
||||||
|
|
||||||
|
|||||||
@@ -355,6 +355,17 @@ class LLMService(AIService):
|
|||||||
return True
|
return True
|
||||||
return function_name in self._functions.keys()
|
return function_name in self._functions.keys()
|
||||||
|
|
||||||
|
def needs_mcp_alternate_schema(self) -> bool:
|
||||||
|
"""Check if this LLM service requires alternate MCP schema.
|
||||||
|
|
||||||
|
Some LLM services have stricter JSON schema validation and require
|
||||||
|
certain properties to be removed or modified for compatibility.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if MCP schemas should be cleaned for this service, False otherwise.
|
||||||
|
"""
|
||||||
|
return False
|
||||||
|
|
||||||
async def run_function_calls(self, function_calls: Sequence[FunctionCallFromLLM]):
|
async def run_function_calls(self, function_calls: Sequence[FunctionCallFromLLM]):
|
||||||
"""Execute a sequence of function calls from the LLM.
|
"""Execute a sequence of function calls from the LLM.
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ class MCPClient(BaseObject):
|
|||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self._server_params = server_params
|
self._server_params = server_params
|
||||||
self._session = ClientSession
|
self._session = ClientSession
|
||||||
|
self._needs_alternate_schema = False
|
||||||
|
|
||||||
if isinstance(server_params, StdioServerParameters):
|
if isinstance(server_params, StdioServerParameters):
|
||||||
self._client = stdio_client
|
self._client = stdio_client
|
||||||
@@ -80,9 +81,48 @@ class MCPClient(BaseObject):
|
|||||||
Returns:
|
Returns:
|
||||||
A ToolsSchema containing all successfully registered tools.
|
A ToolsSchema containing all successfully registered tools.
|
||||||
"""
|
"""
|
||||||
|
# Check once if the LLM needs alternate strict schema
|
||||||
|
self._needs_alternate_schema = llm and llm.needs_mcp_alternate_schema()
|
||||||
tools_schema = await self._register_tools(llm)
|
tools_schema = await self._register_tools(llm)
|
||||||
return tools_schema
|
return tools_schema
|
||||||
|
|
||||||
|
def _get_alternate_schema_for_strict_validation(self, schema: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""Get an alternate JSON schema to be compatible with LLMs that have strict validation.
|
||||||
|
|
||||||
|
Some LLMs have stricter validation and don't allow certain schema properties
|
||||||
|
that are valid in standard JSON Schema.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
schema: The JSON schema to get an alternate schema for
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
An alternate schema compatible with strict validation
|
||||||
|
"""
|
||||||
|
if not isinstance(schema, dict):
|
||||||
|
return schema
|
||||||
|
|
||||||
|
alternate_schema = {}
|
||||||
|
|
||||||
|
for key, value in schema.items():
|
||||||
|
# Skip additionalProperties as some LLMs don't like additionalProperties: false
|
||||||
|
if key == "additionalProperties":
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Recursively get alternate schema for nested objects
|
||||||
|
if isinstance(value, dict):
|
||||||
|
alternate_schema[key] = self._get_alternate_schema_for_strict_validation(value)
|
||||||
|
elif isinstance(value, list):
|
||||||
|
alternate_schema[key] = [
|
||||||
|
self._get_alternate_schema_for_strict_validation(item)
|
||||||
|
if isinstance(item, dict)
|
||||||
|
else item
|
||||||
|
for item in value
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
alternate_schema[key] = value
|
||||||
|
|
||||||
|
return alternate_schema
|
||||||
|
|
||||||
def _convert_mcp_schema_to_pipecat(
|
def _convert_mcp_schema_to_pipecat(
|
||||||
self, tool_name: str, tool_schema: Dict[str, Any]
|
self, tool_name: str, tool_schema: Dict[str, Any]
|
||||||
) -> FunctionSchema:
|
) -> FunctionSchema:
|
||||||
@@ -100,6 +140,11 @@ class MCPClient(BaseObject):
|
|||||||
properties = tool_schema["input_schema"].get("properties", {})
|
properties = tool_schema["input_schema"].get("properties", {})
|
||||||
required = tool_schema["input_schema"].get("required", [])
|
required = tool_schema["input_schema"].get("required", [])
|
||||||
|
|
||||||
|
# Only get alternate schema for LLMs that need strict schema validation
|
||||||
|
if self._needs_alternate_schema:
|
||||||
|
logger.debug("Getting alternate schema for strict validation")
|
||||||
|
properties = self._get_alternate_schema_for_strict_validation(properties)
|
||||||
|
|
||||||
schema = FunctionSchema(
|
schema = FunctionSchema(
|
||||||
name=tool_name,
|
name=tool_name,
|
||||||
description=tool_schema["description"],
|
description=tool_schema["description"],
|
||||||
@@ -280,7 +325,8 @@ class MCPClient(BaseObject):
|
|||||||
try:
|
try:
|
||||||
# Convert the schema
|
# Convert the schema
|
||||||
function_schema = self._convert_mcp_schema_to_pipecat(
|
function_schema = self._convert_mcp_schema_to_pipecat(
|
||||||
tool_name, {"description": tool.description, "input_schema": tool.inputSchema}
|
tool_name,
|
||||||
|
{"description": tool.description, "input_schema": tool.inputSchema},
|
||||||
)
|
)
|
||||||
|
|
||||||
# Register the wrapped function
|
# Register the wrapped function
|
||||||
|
|||||||
Reference in New Issue
Block a user