From e04da334d7e1232eb8b79a610e22c41b14724c28 Mon Sep 17 00:00:00 2001 From: sahil suman Date: Sat, 11 Jan 2025 17:50:58 +0530 Subject: [PATCH 1/8] add support for openrouter. Signed-off-by: sahil suman --- dot-env.template | 3 + .../07u-interruptible-openrouter.py | 96 +++++++++++++++++++ src/pipecat/services/openrouter.py | 62 ++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 examples/foundational/07u-interruptible-openrouter.py create mode 100644 src/pipecat/services/openrouter.py diff --git a/dot-env.template b/dot-env.template index 597c85260..a4a978ba4 100644 --- a/dot-env.template +++ b/dot-env.template @@ -60,3 +60,6 @@ SIMLI_FACE_ID=... # Krisp KRISP_MODEL_PATH=... + +# OpenRouter +OPENROUTER_API_KEY=... diff --git a/examples/foundational/07u-interruptible-openrouter.py b/examples/foundational/07u-interruptible-openrouter.py new file mode 100644 index 000000000..9aee79e16 --- /dev/null +++ b/examples/foundational/07u-interruptible-openrouter.py @@ -0,0 +1,96 @@ +import asyncio +import os +import sys + +import aiohttp +from dotenv import load_dotenv +from loguru import logger +from runner import configure + +from pipecat.frames.frames import LLMMessagesFrame +from pipecat.pipeline.pipeline import Pipeline +from pipecat.pipeline.runner import PipelineRunner +from pipecat.pipeline.task import PipelineTask, PipelineParams +from pipecat.services.azure import AzureTTSService +from pipecat.services.openrouter import OpenRouterLLMService +from pipecat.services.openai import OpenAILLMContext +from pipecat.transports.services.daily import DailyParams, DailyTransport +from pipecat.audio.vad.silero import SileroVADAnalyzer + +load_dotenv(override=True) + +logger.remove(0) +logger.add(sys.stderr, level="DEBUG") + + +async def main(): + async with aiohttp.ClientSession() as session: + (room_url, token) = await configure(session) + + transport = DailyTransport( + room_url, + token, + "Chatbot", + DailyParams( + audio_out_enabled=True, + transcription_enabled=True, + vad_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + vad_audio_passthrough=True, + ), + ) + + tts = AzureTTSService( + api_key=os.getenv("AZURE_API_KEY"), + region="eastus", + voice="en-US-JennyNeural", + params=AzureTTSService.InputParams( + language="en-US", + rate="1.1", + style="chat", + ), + ) + + llm = OpenRouterLLMService( + api_key=os.getenv("OPENROUTER_API_KEY"), model="microsoft/phi-4" + ) + messages = [ + { + "role": "system", + "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative, helpful, and brief way.", + } + ] + context = OpenAILLMContext(messages=messages) + context_aggregator = llm.create_context_aggregator(context) + + pipeline = Pipeline( + [ + transport.input(), + context_aggregator.user(), + llm, + tts, + transport.output(), + context_aggregator.assistant(), + ] + ) + + task = PipelineTask( + pipeline, + PipelineParams(allow_interruptions=True), + ) + + @transport.event_handler("on_first_participant_joined") + async def on_first_participant_joined(transport, participant): + await transport.capture_participant_transcription(participant["id"]) + messages.append( + {"role": "system", "content": "Please introduce yourself to the user."} + ) + await task.queue_frames([LLMMessagesFrame(messages)]) + + runner = PipelineRunner() + + await runner.run(task) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/src/pipecat/services/openrouter.py b/src/pipecat/services/openrouter.py new file mode 100644 index 000000000..62552f83a --- /dev/null +++ b/src/pipecat/services/openrouter.py @@ -0,0 +1,62 @@ +# +# Copyright (c) 2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +from typing import Dict, List + +from loguru import logger + +from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext +from pipecat.services.openai import OpenAILLMService + +try: + from openai import AsyncStream, OpenAI + from openai.types.chat import ChatCompletionChunk, ChatCompletionMessageParam +except ModuleNotFoundError as e: + logger.error(f"Exception: {e}") + logger.error( + "In order to use OpenRouter, you need to `pip install pipecat-ai[openai]`. Also, set `OPENROUTER_API_KEY` environment variable." + ) + raise Exception(f"Missing module: {e}") + + +class OpenRouterLLMService(OpenAILLMService): + def __init__( + self, + *, + api_key: str | None = None, + model: str = "openai/gpt-4o-latest", + base_url: str = "https://openrouter.ai/api/v1", + **kwargs, + ): + super().__init__( + api_key=api_key, + base_url=base_url, + model=model, + **kwargs, + ) + + def create_client(self, api_key=None, base_url=None, **kwargs): + logger.debug(f"Creating OpenRouter client with api {base_url}") + return super().create_client(api_key, base_url, **kwargs) + + async def get_chat_completions( + self, context: OpenAILLMContext, messages: List[ChatCompletionMessageParam] + ) -> AsyncStream[ChatCompletionChunk]: + params = { + "model": self.model_name, + "stream": True, + "messages": messages, + "temperature": self._settings["temperature"], + "top_p": self._settings["top_p"], + "max_tokens": self._settings["max_tokens"], + "frequency_penalty": self._settings["frequency_penalty"], + "presence_penalty": self._settings["presence_penalty"], + } + + params.update(self._settings["extra"]) + + chunks = await self._client.chat.completions.create(**params) + return chunks From 11381a536f30775a69cfb7b858b2aa69f9d7bf59 Mon Sep 17 00:00:00 2001 From: sahil suman Date: Wed, 15 Jan 2025 01:00:33 +0530 Subject: [PATCH 2/8] added example for function calling and made the required changes. Signed-off-by: sahil suman --- .../07u-interruptible-openrouter.py | 96 --------------- .../14l-function-calling-openrouter.py | 111 ++++++++++++++++++ pyproject.toml | 1 + src/pipecat/services/openrouter.py | 2 +- 4 files changed, 113 insertions(+), 97 deletions(-) delete mode 100644 examples/foundational/07u-interruptible-openrouter.py create mode 100644 examples/foundational/14l-function-calling-openrouter.py diff --git a/examples/foundational/07u-interruptible-openrouter.py b/examples/foundational/07u-interruptible-openrouter.py deleted file mode 100644 index 9aee79e16..000000000 --- a/examples/foundational/07u-interruptible-openrouter.py +++ /dev/null @@ -1,96 +0,0 @@ -import asyncio -import os -import sys - -import aiohttp -from dotenv import load_dotenv -from loguru import logger -from runner import configure - -from pipecat.frames.frames import LLMMessagesFrame -from pipecat.pipeline.pipeline import Pipeline -from pipecat.pipeline.runner import PipelineRunner -from pipecat.pipeline.task import PipelineTask, PipelineParams -from pipecat.services.azure import AzureTTSService -from pipecat.services.openrouter import OpenRouterLLMService -from pipecat.services.openai import OpenAILLMContext -from pipecat.transports.services.daily import DailyParams, DailyTransport -from pipecat.audio.vad.silero import SileroVADAnalyzer - -load_dotenv(override=True) - -logger.remove(0) -logger.add(sys.stderr, level="DEBUG") - - -async def main(): - async with aiohttp.ClientSession() as session: - (room_url, token) = await configure(session) - - transport = DailyTransport( - room_url, - token, - "Chatbot", - DailyParams( - audio_out_enabled=True, - transcription_enabled=True, - vad_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - vad_audio_passthrough=True, - ), - ) - - tts = AzureTTSService( - api_key=os.getenv("AZURE_API_KEY"), - region="eastus", - voice="en-US-JennyNeural", - params=AzureTTSService.InputParams( - language="en-US", - rate="1.1", - style="chat", - ), - ) - - llm = OpenRouterLLMService( - api_key=os.getenv("OPENROUTER_API_KEY"), model="microsoft/phi-4" - ) - messages = [ - { - "role": "system", - "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative, helpful, and brief way.", - } - ] - context = OpenAILLMContext(messages=messages) - context_aggregator = llm.create_context_aggregator(context) - - pipeline = Pipeline( - [ - transport.input(), - context_aggregator.user(), - llm, - tts, - transport.output(), - context_aggregator.assistant(), - ] - ) - - task = PipelineTask( - pipeline, - PipelineParams(allow_interruptions=True), - ) - - @transport.event_handler("on_first_participant_joined") - async def on_first_participant_joined(transport, participant): - await transport.capture_participant_transcription(participant["id"]) - messages.append( - {"role": "system", "content": "Please introduce yourself to the user."} - ) - await task.queue_frames([LLMMessagesFrame(messages)]) - - runner = PipelineRunner() - - await runner.run(task) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/examples/foundational/14l-function-calling-openrouter.py b/examples/foundational/14l-function-calling-openrouter.py new file mode 100644 index 000000000..0c885e0ae --- /dev/null +++ b/examples/foundational/14l-function-calling-openrouter.py @@ -0,0 +1,111 @@ +import asyncio +import os +import sys + +import aiohttp +from dotenv import load_dotenv +from loguru import logger +from runner import configure + +from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.pipeline.pipeline import Pipeline +from pipecat.pipeline.runner import PipelineRunner +from pipecat.pipeline.task import PipelineParams, PipelineTask +from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext +from pipecat.services.openrouter import OpenRouterLLMService +from pipecat.services.azure import AzureTTSService +from pipecat.transports.services.daily import DailyParams, DailyTransport + +load_dotenv(override=True) + +logger.remove(0) +logger.add(sys.stderr, level="DEBUG") + + +async def get_weather(function_name, tool_call_id, arguments, llm, context, result_callback): + location = arguments["location"] + await result_callback(f"The weather in {location} is currently 72 degrees and sunny.") + + +async def main(): + async with aiohttp.ClientSession() as session: + (room_url, token) = await configure(session) + + transport = DailyTransport( + room_url, + token, + "Respond bot", + DailyParams( + audio_out_enabled=True, + transcription_enabled=True, + vad_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + ) + + tts = AzureTTSService( + api_key=os.getenv("AZURE_API_KEY"), + region="eastus", + voice="en-US-JennyNeural", + params=AzureTTSService.InputParams(language="en-US", rate="1.1", style="cheerful"), + ) + + llm = OpenRouterLLMService( + api_key=os.getenv("OPENROUTER_API_KEY"), model="openai/chatgpt-4o-latest" + ) + llm.register_function("get_weather", get_weather) + + tools = [ + { + "name": "get_weather", + "description": "Get the current weather in a given location", + "input_schema": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "The city and state, e.g. San Francisco, CA", + } + }, + "required": ["location"], + }, + } + ] + + messages = [ + { + "role": "system", + "content": "You are a helpful assistant and whenever you asked about weather trigger get_weather function.", + }, + {"role": "user", "content": "Say 'hello' to start the conversation."}, + ] + + context = OpenAILLMContext(messages, tools) + context_aggregator = llm.create_context_aggregator(context) + + pipeline = Pipeline( + [ + transport.input(), # Transport user input + context_aggregator.user(), # User spoken responses + llm, # LLM + tts, # TTS + transport.output(), # Transport bot output + context_aggregator.assistant(), # Assistant spoken responses and tool context + ] + ) + + task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True, enable_metrics=True)) + + @transport.event_handler("on_first_participant_joined") + async def on_first_participant_joined(transport, participant): + await transport.capture_participant_transcription(participant["id"]) + # Kick off the conversation. + await task.queue_frames([context_aggregator.user().get_context_frame()]) + + runner = PipelineRunner() + + await runner.run(task) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/pyproject.toml b/pyproject.toml index f0809b305..f1bfd2a28 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,6 +76,7 @@ soundfile = [ "soundfile~=0.12.1" ] together = [ "openai~=1.59.0" ] websocket = [ "websockets~=13.1", "fastapi~=0.115.0" ] whisper = [ "faster-whisper~=1.1.0" ] +openrouter = [ "openai~=1.59.0" ] [tool.setuptools.packages.find] # All the following settings are optional: diff --git a/src/pipecat/services/openrouter.py b/src/pipecat/services/openrouter.py index 62552f83a..5b06dda33 100644 --- a/src/pipecat/services/openrouter.py +++ b/src/pipecat/services/openrouter.py @@ -17,7 +17,7 @@ try: except ModuleNotFoundError as e: logger.error(f"Exception: {e}") logger.error( - "In order to use OpenRouter, you need to `pip install pipecat-ai[openai]`. Also, set `OPENROUTER_API_KEY` environment variable." + "In order to use OpenRouter, you need to `pip install pipecat-ai[openrouter]`. Also, set `OPENROUTER_API_KEY` environment variable." ) raise Exception(f"Missing module: {e}") From 4d307d26d894f8d294388f095d8e3e0a9f0b5b15 Mon Sep 17 00:00:00 2001 From: sahil suman Date: Wed, 15 Jan 2025 02:19:05 +0530 Subject: [PATCH 3/8] made the required changes. Signed-off-by: sahil suman --- .../14l-function-calling-openrouter.py | 87 +++++++++++++------ src/pipecat/services/openrouter.py | 33 +++---- 2 files changed, 72 insertions(+), 48 deletions(-) diff --git a/examples/foundational/14l-function-calling-openrouter.py b/examples/foundational/14l-function-calling-openrouter.py index 0c885e0ae..2535160e9 100644 --- a/examples/foundational/14l-function-calling-openrouter.py +++ b/examples/foundational/14l-function-calling-openrouter.py @@ -1,3 +1,9 @@ +# +# Copyright (c) 2024–2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + import asyncio import os import sys @@ -5,6 +11,7 @@ import sys import aiohttp from dotenv import load_dotenv from loguru import logger +from openai.types.chat import ChatCompletionToolParam from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer @@ -12,8 +19,8 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext -from pipecat.services.openrouter import OpenRouterLLMService from pipecat.services.azure import AzureTTSService +from pipecat.services.openrouter import OpenRouterLLMService from pipecat.transports.services.daily import DailyParams, DailyTransport load_dotenv(override=True) @@ -22,9 +29,17 @@ logger.remove(0) logger.add(sys.stderr, level="DEBUG") -async def get_weather(function_name, tool_call_id, arguments, llm, context, result_callback): - location = arguments["location"] - await result_callback(f"The weather in {location} is currently 72 degrees and sunny.") +async def start_fetch_weather(function_name, llm, context): + # note: we can't push a frame to the LLM here. the bot + # can interrupt itself and/or cause audio overlapping glitches. + # possible question for Aleix and Chad about what the right way + # to trigger speech is, now, with the new queues/async/sync refactors. + # await llm.push_frame(TextFrame("Let me check on that.")) + logger.debug(f"Starting fetch_weather_from_api with function_name: {function_name}") + + +async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback): + await result_callback({"conditions": "nice", "temperature": "75"}) async def main(): @@ -51,33 +66,41 @@ async def main(): ) llm = OpenRouterLLMService( - api_key=os.getenv("OPENROUTER_API_KEY"), model="openai/chatgpt-4o-latest" + api_key=os.getenv("OPENROUTER_API_KEY"), model="openai/gpt-4o-2024-11-20" ) - llm.register_function("get_weather", get_weather) + # Register a function_name of None to get all functions + # sent to the same callback with an additional function_name parameter. + llm.register_function(None, fetch_weather_from_api, start_callback=start_fetch_weather) tools = [ - { - "name": "get_weather", - "description": "Get the current weather in a given location", - "input_schema": { - "type": "object", - "properties": { - "location": { - "type": "string", - "description": "The city and state, e.g. San Francisco, CA", - } + ChatCompletionToolParam( + type="function", + function={ + "name": "get_current_weather", + "description": "Get the current weather", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "The city and state, e.g. San Francisco, CA", + }, + "format": { + "type": "string", + "enum": ["celsius", "fahrenheit"], + "description": "The temperature unit to use. Infer this from the users location.", + }, + }, + "required": ["location", "format"], }, - "required": ["location"], }, - } + ) ] - messages = [ { "role": "system", - "content": "You are a helpful assistant and whenever you asked about weather trigger get_weather function.", + "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.", }, - {"role": "user", "content": "Say 'hello' to start the conversation."}, ] context = OpenAILLMContext(messages, tools) @@ -85,16 +108,24 @@ async def main(): pipeline = Pipeline( [ - transport.input(), # Transport user input - context_aggregator.user(), # User spoken responses - llm, # LLM - tts, # TTS - transport.output(), # Transport bot output - context_aggregator.assistant(), # Assistant spoken responses and tool context + transport.input(), + context_aggregator.user(), + llm, + tts, + transport.output(), + context_aggregator.assistant(), ] ) - task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True, enable_metrics=True)) + task = PipelineTask( + pipeline, + PipelineParams( + allow_interruptions=True, + enable_metrics=True, + enable_usage_metrics=True, + report_only_initial_ttfb=True, + ), + ) @transport.event_handler("on_first_participant_joined") async def on_first_participant_joined(transport, participant): diff --git a/src/pipecat/services/openrouter.py b/src/pipecat/services/openrouter.py index 5b06dda33..f9067cdb1 100644 --- a/src/pipecat/services/openrouter.py +++ b/src/pipecat/services/openrouter.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # @@ -23,6 +23,18 @@ except ModuleNotFoundError as e: class OpenRouterLLMService(OpenAILLMService): + """A service for interacting with OpenRouter's API using the OpenAI-compatible interface. + + This service extends OpenAILLMService to connect to OpenRouter's API endpoint while + maintaining full compatibility with OpenAI's interface and functionality. + + Args: + api_key (str): The API key for accessing OpenRouter's API + base_url (str, optional): The base URL for OpenRouter API. Defaults to "https://openrouter.ai/api/v1" + model (str, optional): The model identifier to use. Defaults to "openai/gpt-4o-2024-11-20" + **kwargs: Additional keyword arguments passed to OpenAILLMService + """ + def __init__( self, *, @@ -41,22 +53,3 @@ class OpenRouterLLMService(OpenAILLMService): def create_client(self, api_key=None, base_url=None, **kwargs): logger.debug(f"Creating OpenRouter client with api {base_url}") return super().create_client(api_key, base_url, **kwargs) - - async def get_chat_completions( - self, context: OpenAILLMContext, messages: List[ChatCompletionMessageParam] - ) -> AsyncStream[ChatCompletionChunk]: - params = { - "model": self.model_name, - "stream": True, - "messages": messages, - "temperature": self._settings["temperature"], - "top_p": self._settings["top_p"], - "max_tokens": self._settings["max_tokens"], - "frequency_penalty": self._settings["frequency_penalty"], - "presence_penalty": self._settings["presence_penalty"], - } - - params.update(self._settings["extra"]) - - chunks = await self._client.chat.completions.create(**params) - return chunks From 329da5033884608b1dc9224866471e2e37b4d94a Mon Sep 17 00:00:00 2001 From: Sahil Suman <34382211+sahilsuman933@users.noreply.github.com> Date: Wed, 15 Jan 2025 02:20:22 +0530 Subject: [PATCH 4/8] Update src/pipecat/services/openrouter.py Co-authored-by: Mark Backman --- src/pipecat/services/openrouter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipecat/services/openrouter.py b/src/pipecat/services/openrouter.py index f9067cdb1..4c99fb767 100644 --- a/src/pipecat/services/openrouter.py +++ b/src/pipecat/services/openrouter.py @@ -39,7 +39,7 @@ class OpenRouterLLMService(OpenAILLMService): self, *, api_key: str | None = None, - model: str = "openai/gpt-4o-latest", + model: str = "openai/gpt-4o-2024-11-20", base_url: str = "https://openrouter.ai/api/v1", **kwargs, ): From 62e9e6bc5a64234bfa8c3458483368213dfdfe0a Mon Sep 17 00:00:00 2001 From: sahil suman Date: Wed, 15 Jan 2025 02:21:58 +0530 Subject: [PATCH 5/8] changed the file name. Signed-off-by: sahil suman --- ...n-calling-openrouter.py => 14m-function-calling-openrouter.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/foundational/{14l-function-calling-openrouter.py => 14m-function-calling-openrouter.py} (100%) diff --git a/examples/foundational/14l-function-calling-openrouter.py b/examples/foundational/14m-function-calling-openrouter.py similarity index 100% rename from examples/foundational/14l-function-calling-openrouter.py rename to examples/foundational/14m-function-calling-openrouter.py From 885bc32827b5895f4bf85ef5dc008840c24c60a7 Mon Sep 17 00:00:00 2001 From: sahil suman Date: Wed, 15 Jan 2025 02:53:04 +0530 Subject: [PATCH 6/8] added changes in changelog. Signed-off-by: sahil suman --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6655ed9d..608b8201b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +<<<<<<< Updated upstream - Added a new `WebsocketService` based class for TTS services, containing base functions and retry logic. @@ -21,6 +22,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `run_llm`: Controls whether to trigger LLM completion - `on_context_updated`: Optional callback triggered after context update +======= +- Added `DeepSeekLLMService` for DeepSeek integration with an OpenAI-compatible + interface. Added foundational example `14l-function-calling-deepseek.py`. + +>>>>>>> Stashed changes - Added a new foundational example `07e-interruptible-playht-http.py` for easy testing of `PlayHTHttpTTSService`. From 6f3b0fdf73f2c28aa775f0e74716bf0861a8de6b Mon Sep 17 00:00:00 2001 From: sahil suman Date: Wed, 15 Jan 2025 02:56:16 +0530 Subject: [PATCH 7/8] fix changelog Signed-off-by: sahil suman --- CHANGELOG.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 608b8201b..0103f2a03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -<<<<<<< Updated upstream +- Added `DeepSeekLLMService` for DeepSeek integration with an OpenAI-compatible + interface. Added foundational example `14l-function-calling-deepseek.py`. + - Added a new `WebsocketService` based class for TTS services, containing base functions and retry logic. @@ -22,11 +24,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `run_llm`: Controls whether to trigger LLM completion - `on_context_updated`: Optional callback triggered after context update -======= -- Added `DeepSeekLLMService` for DeepSeek integration with an OpenAI-compatible - interface. Added foundational example `14l-function-calling-deepseek.py`. - ->>>>>>> Stashed changes - Added a new foundational example `07e-interruptible-playht-http.py` for easy testing of `PlayHTHttpTTSService`. From 11fc08ef2406f0251e110612202a88bc4125b16b Mon Sep 17 00:00:00 2001 From: sahil suman Date: Wed, 15 Jan 2025 02:57:09 +0530 Subject: [PATCH 8/8] fix changelog Signed-off-by: sahil suman --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0103f2a03..43f402306 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Added `DeepSeekLLMService` for DeepSeek integration with an OpenAI-compatible - interface. Added foundational example `14l-function-calling-deepseek.py`. +- Added `OpenRouter` for OpenRouter integration with an OpenAI-compatible + interface. Added foundational example `14m-function-calling-openrouter.py`. - Added a new `WebsocketService` based class for TTS services, containing base functions and retry logic.