dv-pipecat-ai 0.0.82.dev815__py3-none-any.whl → 0.0.82.dev857__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of dv-pipecat-ai might be problematic. Click here for more details.
- {dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/METADATA +8 -3
- {dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/RECORD +106 -79
- pipecat/adapters/base_llm_adapter.py +44 -6
- pipecat/adapters/services/anthropic_adapter.py +302 -2
- pipecat/adapters/services/aws_nova_sonic_adapter.py +40 -2
- pipecat/adapters/services/bedrock_adapter.py +40 -2
- pipecat/adapters/services/gemini_adapter.py +276 -6
- pipecat/adapters/services/open_ai_adapter.py +88 -7
- pipecat/adapters/services/open_ai_realtime_adapter.py +39 -1
- pipecat/audio/dtmf/__init__.py +0 -0
- pipecat/audio/dtmf/types.py +47 -0
- pipecat/audio/dtmf/utils.py +70 -0
- pipecat/audio/filters/aic_filter.py +199 -0
- pipecat/audio/utils.py +9 -7
- pipecat/extensions/ivr/__init__.py +0 -0
- pipecat/extensions/ivr/ivr_navigator.py +452 -0
- pipecat/frames/frames.py +156 -43
- pipecat/pipeline/llm_switcher.py +76 -0
- pipecat/pipeline/parallel_pipeline.py +3 -3
- pipecat/pipeline/service_switcher.py +144 -0
- pipecat/pipeline/task.py +68 -28
- pipecat/pipeline/task_observer.py +10 -0
- pipecat/processors/aggregators/dtmf_aggregator.py +2 -2
- pipecat/processors/aggregators/llm_context.py +277 -0
- pipecat/processors/aggregators/llm_response.py +48 -15
- pipecat/processors/aggregators/llm_response_universal.py +840 -0
- pipecat/processors/aggregators/openai_llm_context.py +3 -3
- pipecat/processors/dtmf_aggregator.py +0 -2
- pipecat/processors/filters/stt_mute_filter.py +0 -2
- pipecat/processors/frame_processor.py +18 -11
- pipecat/processors/frameworks/rtvi.py +17 -10
- pipecat/processors/metrics/sentry.py +2 -0
- pipecat/runner/daily.py +137 -36
- pipecat/runner/run.py +1 -1
- pipecat/runner/utils.py +7 -7
- pipecat/serializers/asterisk.py +20 -4
- pipecat/serializers/exotel.py +1 -1
- pipecat/serializers/plivo.py +1 -1
- pipecat/serializers/telnyx.py +1 -1
- pipecat/serializers/twilio.py +1 -1
- pipecat/services/__init__.py +2 -2
- pipecat/services/anthropic/llm.py +113 -28
- pipecat/services/asyncai/tts.py +4 -0
- pipecat/services/aws/llm.py +82 -8
- pipecat/services/aws/tts.py +0 -10
- pipecat/services/aws_nova_sonic/aws.py +5 -0
- pipecat/services/cartesia/tts.py +28 -16
- pipecat/services/cerebras/llm.py +15 -10
- pipecat/services/deepgram/stt.py +8 -0
- pipecat/services/deepseek/llm.py +13 -8
- pipecat/services/fireworks/llm.py +13 -8
- pipecat/services/fish/tts.py +8 -6
- pipecat/services/gemini_multimodal_live/gemini.py +5 -0
- pipecat/services/gladia/config.py +7 -1
- pipecat/services/gladia/stt.py +23 -15
- pipecat/services/google/llm.py +159 -59
- pipecat/services/google/llm_openai.py +18 -3
- pipecat/services/grok/llm.py +2 -1
- pipecat/services/llm_service.py +38 -3
- pipecat/services/mem0/memory.py +2 -1
- pipecat/services/mistral/llm.py +5 -6
- pipecat/services/nim/llm.py +2 -1
- pipecat/services/openai/base_llm.py +88 -26
- pipecat/services/openai/image.py +6 -1
- pipecat/services/openai_realtime_beta/openai.py +5 -2
- pipecat/services/openpipe/llm.py +6 -8
- pipecat/services/perplexity/llm.py +13 -8
- pipecat/services/playht/tts.py +9 -6
- pipecat/services/rime/tts.py +1 -1
- pipecat/services/sambanova/llm.py +18 -13
- pipecat/services/sarvam/tts.py +415 -10
- pipecat/services/speechmatics/stt.py +2 -2
- pipecat/services/tavus/video.py +1 -1
- pipecat/services/tts_service.py +15 -5
- pipecat/services/vistaar/llm.py +2 -5
- pipecat/transports/base_input.py +32 -19
- pipecat/transports/base_output.py +39 -5
- pipecat/transports/daily/__init__.py +0 -0
- pipecat/transports/daily/transport.py +2371 -0
- pipecat/transports/daily/utils.py +410 -0
- pipecat/transports/livekit/__init__.py +0 -0
- pipecat/transports/livekit/transport.py +1042 -0
- pipecat/transports/network/fastapi_websocket.py +12 -546
- pipecat/transports/network/small_webrtc.py +12 -922
- pipecat/transports/network/webrtc_connection.py +9 -595
- pipecat/transports/network/websocket_client.py +12 -481
- pipecat/transports/network/websocket_server.py +12 -487
- pipecat/transports/services/daily.py +9 -2334
- pipecat/transports/services/helpers/daily_rest.py +12 -396
- pipecat/transports/services/livekit.py +12 -975
- pipecat/transports/services/tavus.py +12 -757
- pipecat/transports/smallwebrtc/__init__.py +0 -0
- pipecat/transports/smallwebrtc/connection.py +612 -0
- pipecat/transports/smallwebrtc/transport.py +936 -0
- pipecat/transports/tavus/__init__.py +0 -0
- pipecat/transports/tavus/transport.py +770 -0
- pipecat/transports/websocket/__init__.py +0 -0
- pipecat/transports/websocket/client.py +494 -0
- pipecat/transports/websocket/fastapi.py +559 -0
- pipecat/transports/websocket/server.py +500 -0
- pipecat/transports/whatsapp/__init__.py +0 -0
- pipecat/transports/whatsapp/api.py +345 -0
- pipecat/transports/whatsapp/client.py +364 -0
- {dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/WHEEL +0 -0
- {dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/licenses/LICENSE +0 -0
- {dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/top_level.txt +0 -0
pipecat/services/llm_service.py
CHANGED
|
@@ -36,10 +36,15 @@ from pipecat.frames.frames import (
|
|
|
36
36
|
FunctionCallResultFrame,
|
|
37
37
|
FunctionCallResultProperties,
|
|
38
38
|
FunctionCallsStartedFrame,
|
|
39
|
+
LLMConfigureOutputFrame,
|
|
40
|
+
LLMFullResponseEndFrame,
|
|
41
|
+
LLMFullResponseStartFrame,
|
|
42
|
+
LLMTextFrame,
|
|
39
43
|
StartFrame,
|
|
40
44
|
StartInterruptionFrame,
|
|
41
45
|
UserImageRequestFrame,
|
|
42
46
|
)
|
|
47
|
+
from pipecat.processors.aggregators.llm_context import LLMContext
|
|
43
48
|
from pipecat.processors.aggregators.llm_response import (
|
|
44
49
|
LLMAssistantAggregatorParams,
|
|
45
50
|
LLMUserAggregatorParams,
|
|
@@ -88,7 +93,7 @@ class FunctionCallParams:
|
|
|
88
93
|
tool_call_id: str
|
|
89
94
|
arguments: Mapping[str, Any]
|
|
90
95
|
llm: "LLMService"
|
|
91
|
-
context: OpenAILLMContext
|
|
96
|
+
context: OpenAILLMContext | LLMContext
|
|
92
97
|
result_callback: FunctionCallResultCallback
|
|
93
98
|
|
|
94
99
|
|
|
@@ -129,7 +134,7 @@ class FunctionCallRunnerItem:
|
|
|
129
134
|
function_name: str
|
|
130
135
|
tool_call_id: str
|
|
131
136
|
arguments: Mapping[str, Any]
|
|
132
|
-
context: OpenAILLMContext
|
|
137
|
+
context: OpenAILLMContext | LLMContext
|
|
133
138
|
run_llm: Optional[bool] = None
|
|
134
139
|
|
|
135
140
|
|
|
@@ -177,6 +182,7 @@ class LLMService(AIService):
|
|
|
177
182
|
self._function_call_tasks: Dict[asyncio.Task, FunctionCallRunnerItem] = {}
|
|
178
183
|
self._sequential_runner_task: Optional[asyncio.Task] = None
|
|
179
184
|
self._tracing_enabled: bool = False
|
|
185
|
+
self._skip_tts: bool = False
|
|
180
186
|
|
|
181
187
|
self._register_event_handler("on_function_calls_started")
|
|
182
188
|
self._register_event_handler("on_completion_timeout")
|
|
@@ -189,6 +195,19 @@ class LLMService(AIService):
|
|
|
189
195
|
"""
|
|
190
196
|
return self._adapter
|
|
191
197
|
|
|
198
|
+
async def run_inference(self, context: LLMContext | OpenAILLMContext) -> Optional[str]:
|
|
199
|
+
"""Run a one-shot, out-of-band (i.e. out-of-pipeline) inference with the given LLM context.
|
|
200
|
+
|
|
201
|
+
Must be implemented by subclasses.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
context: The LLM context containing conversation history.
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
The LLM's response as a string, or None if no response is generated.
|
|
208
|
+
"""
|
|
209
|
+
raise NotImplementedError(f"run_inference() not supported by {self.__class__.__name__}")
|
|
210
|
+
|
|
192
211
|
def create_context_aggregator(
|
|
193
212
|
self,
|
|
194
213
|
context: OpenAILLMContext,
|
|
@@ -252,6 +271,20 @@ class LLMService(AIService):
|
|
|
252
271
|
|
|
253
272
|
if isinstance(frame, StartInterruptionFrame):
|
|
254
273
|
await self._handle_interruptions(frame)
|
|
274
|
+
elif isinstance(frame, LLMConfigureOutputFrame):
|
|
275
|
+
self._skip_tts = frame.skip_tts
|
|
276
|
+
|
|
277
|
+
async def push_frame(self, frame: Frame, direction: FrameDirection = FrameDirection.DOWNSTREAM):
|
|
278
|
+
"""Pushes a frame.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
frame: The frame to push.
|
|
282
|
+
direction: The direction of frame pushing.
|
|
283
|
+
"""
|
|
284
|
+
if isinstance(frame, (LLMTextFrame, LLMFullResponseStartFrame, LLMFullResponseEndFrame)):
|
|
285
|
+
frame.skip_tts = self._skip_tts
|
|
286
|
+
|
|
287
|
+
await super().push_frame(frame, direction)
|
|
255
288
|
|
|
256
289
|
async def _handle_interruptions(self, _: StartInterruptionFrame):
|
|
257
290
|
# logger.info("In LLM Handling interruptions")
|
|
@@ -434,7 +467,9 @@ class LLMService(AIService):
|
|
|
434
467
|
else:
|
|
435
468
|
await self._sequential_runner_queue.put(runner_item)
|
|
436
469
|
|
|
437
|
-
async def _call_start_function(
|
|
470
|
+
async def _call_start_function(
|
|
471
|
+
self, context: OpenAILLMContext | LLMContext, function_name: str
|
|
472
|
+
):
|
|
438
473
|
if function_name in self._start_callbacks.keys():
|
|
439
474
|
await self._start_callbacks[function_name](function_name, self, context)
|
|
440
475
|
elif None in self._start_callbacks.keys():
|
pipecat/services/mem0/memory.py
CHANGED
|
@@ -120,6 +120,7 @@ class Mem0MemoryService(FrameProcessor):
|
|
|
120
120
|
try:
|
|
121
121
|
logger.debug(f"Storing {len(messages)} messages in Mem0")
|
|
122
122
|
params = {
|
|
123
|
+
"async_mode": True,
|
|
123
124
|
"messages": messages,
|
|
124
125
|
"metadata": {"platform": "pipecat"},
|
|
125
126
|
"output_format": "v1.1",
|
|
@@ -163,7 +164,7 @@ class Mem0MemoryService(FrameProcessor):
|
|
|
163
164
|
("run_id", self.run_id),
|
|
164
165
|
]
|
|
165
166
|
clauses = [{name: value} for name, value in id_pairs if value is not None]
|
|
166
|
-
filters = {"
|
|
167
|
+
filters = {"OR": clauses} if clauses else {}
|
|
167
168
|
results = self.memory_client.search(
|
|
168
169
|
query=query,
|
|
169
170
|
filters=filters,
|
pipecat/services/mistral/llm.py
CHANGED
|
@@ -12,6 +12,7 @@ from loguru import logger
|
|
|
12
12
|
from openai import AsyncStream
|
|
13
13
|
from openai.types.chat import ChatCompletionChunk, ChatCompletionMessageParam
|
|
14
14
|
|
|
15
|
+
from pipecat.adapters.services.open_ai_adapter import OpenAILLMInvocationParams
|
|
15
16
|
from pipecat.frames.frames import FunctionCallFromLLM
|
|
16
17
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
|
17
18
|
from pipecat.services.openai.llm import OpenAILLMService
|
|
@@ -148,9 +149,7 @@ class MistralLLMService(OpenAILLMService):
|
|
|
148
149
|
if calls_to_execute:
|
|
149
150
|
await super().run_function_calls(calls_to_execute)
|
|
150
151
|
|
|
151
|
-
def build_chat_completion_params(
|
|
152
|
-
self, context: OpenAILLMContext, messages: List[ChatCompletionMessageParam]
|
|
153
|
-
) -> dict:
|
|
152
|
+
def build_chat_completion_params(self, params_from_context: OpenAILLMInvocationParams) -> dict:
|
|
154
153
|
"""Build parameters for Mistral chat completion request.
|
|
155
154
|
|
|
156
155
|
Handles Mistral-specific requirements including:
|
|
@@ -159,14 +158,14 @@ class MistralLLMService(OpenAILLMService):
|
|
|
159
158
|
- Core completion settings
|
|
160
159
|
"""
|
|
161
160
|
# Apply Mistral's assistant prefix requirement for API compatibility
|
|
162
|
-
fixed_messages = self._apply_mistral_assistant_prefix(messages)
|
|
161
|
+
fixed_messages = self._apply_mistral_assistant_prefix(params_from_context["messages"])
|
|
163
162
|
|
|
164
163
|
params = {
|
|
165
164
|
"model": self.model_name,
|
|
166
165
|
"stream": True,
|
|
167
166
|
"messages": fixed_messages,
|
|
168
|
-
"tools":
|
|
169
|
-
"tool_choice":
|
|
167
|
+
"tools": params_from_context["tools"],
|
|
168
|
+
"tool_choice": params_from_context["tool_choice"],
|
|
170
169
|
"frequency_penalty": self._settings["frequency_penalty"],
|
|
171
170
|
"presence_penalty": self._settings["presence_penalty"],
|
|
172
171
|
"temperature": self._settings["temperature"],
|
pipecat/services/nim/llm.py
CHANGED
|
@@ -11,6 +11,7 @@ Microservice) API while maintaining compatibility with the OpenAI-style interfac
|
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
from pipecat.metrics.metrics import LLMTokenUsage
|
|
14
|
+
from pipecat.processors.aggregators.llm_context import LLMContext
|
|
14
15
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
|
15
16
|
from pipecat.services.openai.llm import OpenAILLMService
|
|
16
17
|
|
|
@@ -47,7 +48,7 @@ class NimLLMService(OpenAILLMService):
|
|
|
47
48
|
self._has_reported_prompt_tokens = False
|
|
48
49
|
self._is_processing = False
|
|
49
50
|
|
|
50
|
-
async def _process_context(self, context: OpenAILLMContext):
|
|
51
|
+
async def _process_context(self, context: OpenAILLMContext | LLMContext):
|
|
51
52
|
"""Process a context through the LLM and accumulate token usage metrics.
|
|
52
53
|
|
|
53
54
|
This method overrides the parent class implementation to handle NVIDIA's
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# SPDX-License-Identifier: BSD 2-Clause License
|
|
5
5
|
#
|
|
6
6
|
|
|
7
|
-
"""Base
|
|
7
|
+
"""Base LLM service implementation for services that use the AsyncOpenAI client."""
|
|
8
8
|
|
|
9
9
|
import asyncio
|
|
10
10
|
import base64
|
|
@@ -23,8 +23,10 @@ from openai import (
|
|
|
23
23
|
from openai.types.chat import ChatCompletionChunk, ChatCompletionMessageParam
|
|
24
24
|
from pydantic import BaseModel, Field
|
|
25
25
|
|
|
26
|
+
from pipecat.adapters.services.open_ai_adapter import OpenAILLMInvocationParams
|
|
26
27
|
from pipecat.frames.frames import (
|
|
27
28
|
Frame,
|
|
29
|
+
LLMContextFrame,
|
|
28
30
|
LLMFullResponseEndFrame,
|
|
29
31
|
LLMFullResponseStartFrame,
|
|
30
32
|
LLMMessagesFrame,
|
|
@@ -33,6 +35,7 @@ from pipecat.frames.frames import (
|
|
|
33
35
|
VisionImageRawFrame,
|
|
34
36
|
)
|
|
35
37
|
from pipecat.metrics.metrics import LLMTokenUsage
|
|
38
|
+
from pipecat.processors.aggregators.llm_context import LLMContext
|
|
36
39
|
from pipecat.processors.aggregators.openai_llm_context import (
|
|
37
40
|
OpenAILLMContext,
|
|
38
41
|
OpenAILLMContextFrame,
|
|
@@ -45,10 +48,11 @@ from pipecat.utils.tracing.service_decorators import traced_llm
|
|
|
45
48
|
class BaseOpenAILLMService(LLMService):
|
|
46
49
|
"""Base class for all services that use the AsyncOpenAI client.
|
|
47
50
|
|
|
48
|
-
This service consumes OpenAILLMContextFrame frames,
|
|
49
|
-
to an OpenAILLMContext object. The
|
|
50
|
-
|
|
51
|
-
choices and function call
|
|
51
|
+
This service consumes OpenAILLMContextFrame or LLMContextFrame frames,
|
|
52
|
+
which contain a reference to an OpenAILLMContext or LLMContext object. The
|
|
53
|
+
context defines what is sent to the LLM for completion, including user,
|
|
54
|
+
assistant, and system messages, as well as tool choices and function call
|
|
55
|
+
configurations.
|
|
52
56
|
"""
|
|
53
57
|
|
|
54
58
|
class InputParams(BaseModel):
|
|
@@ -180,18 +184,19 @@ class BaseOpenAILLMService(LLMService):
|
|
|
180
184
|
return True
|
|
181
185
|
|
|
182
186
|
async def get_chat_completions(
|
|
183
|
-
self,
|
|
187
|
+
self, params_from_context: OpenAILLMInvocationParams
|
|
184
188
|
) -> AsyncStream[ChatCompletionChunk]:
|
|
185
189
|
"""Get streaming chat completions from OpenAI API with optional timeout and retry.
|
|
186
190
|
|
|
187
191
|
Args:
|
|
188
|
-
|
|
189
|
-
|
|
192
|
+
params_from_context: Parameters, derived from the LLM context, to
|
|
193
|
+
use for the chat completion. Contains messages, tools, and tool
|
|
194
|
+
choice.
|
|
190
195
|
|
|
191
196
|
Returns:
|
|
192
197
|
Async stream of chat completion chunks.
|
|
193
198
|
"""
|
|
194
|
-
params = self.build_chat_completion_params(
|
|
199
|
+
params = self.build_chat_completion_params(params_from_context)
|
|
195
200
|
|
|
196
201
|
if self._retry_on_timeout:
|
|
197
202
|
try:
|
|
@@ -208,16 +213,15 @@ class BaseOpenAILLMService(LLMService):
|
|
|
208
213
|
chunks = await self._client.chat.completions.create(**params)
|
|
209
214
|
return chunks
|
|
210
215
|
|
|
211
|
-
def build_chat_completion_params(
|
|
212
|
-
self, context: OpenAILLMContext, messages: List[ChatCompletionMessageParam]
|
|
213
|
-
) -> dict:
|
|
216
|
+
def build_chat_completion_params(self, params_from_context: OpenAILLMInvocationParams) -> dict:
|
|
214
217
|
"""Build parameters for chat completion request.
|
|
215
218
|
|
|
216
219
|
Subclasses can override this to customize parameters for different providers.
|
|
217
220
|
|
|
218
221
|
Args:
|
|
219
|
-
|
|
220
|
-
|
|
222
|
+
params_from_context: Parameters, derived from the LLM context, to
|
|
223
|
+
use for the chat completion. Contains messages, tools, and tool
|
|
224
|
+
choice.
|
|
221
225
|
|
|
222
226
|
Returns:
|
|
223
227
|
Dictionary of parameters for the chat completion request.
|
|
@@ -225,9 +229,6 @@ class BaseOpenAILLMService(LLMService):
|
|
|
225
229
|
params = {
|
|
226
230
|
"model": self.model_name,
|
|
227
231
|
"stream": True,
|
|
228
|
-
"messages": messages,
|
|
229
|
-
"tools": context.tools,
|
|
230
|
-
"tool_choice": context.tool_choice,
|
|
231
232
|
"stream_options": {"include_usage": True},
|
|
232
233
|
"frequency_penalty": self._settings["frequency_penalty"],
|
|
233
234
|
"presence_penalty": self._settings["presence_penalty"],
|
|
@@ -238,13 +239,43 @@ class BaseOpenAILLMService(LLMService):
|
|
|
238
239
|
"max_completion_tokens": self._settings["max_completion_tokens"],
|
|
239
240
|
}
|
|
240
241
|
|
|
242
|
+
# Messages, tools, tool_choice
|
|
243
|
+
params.update(params_from_context)
|
|
244
|
+
|
|
241
245
|
params.update(self._settings["extra"])
|
|
242
246
|
return params
|
|
243
247
|
|
|
244
|
-
async def
|
|
248
|
+
async def run_inference(self, context: LLMContext | OpenAILLMContext) -> Optional[str]:
|
|
249
|
+
"""Run a one-shot, out-of-band (i.e. out-of-pipeline) inference with the given LLM context.
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
context: The LLM context containing conversation history.
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
The LLM's response as a string, or None if no response is generated.
|
|
256
|
+
"""
|
|
257
|
+
if isinstance(context, LLMContext):
|
|
258
|
+
adapter = self.get_llm_adapter()
|
|
259
|
+
params: OpenAILLMInvocationParams = adapter.get_llm_invocation_params(context)
|
|
260
|
+
messages = params["messages"]
|
|
261
|
+
else:
|
|
262
|
+
messages = context.messages
|
|
263
|
+
|
|
264
|
+
# LLM completion
|
|
265
|
+
response = await self._client.chat.completions.create(
|
|
266
|
+
model=self.model_name,
|
|
267
|
+
messages=messages,
|
|
268
|
+
stream=False,
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
return response.choices[0].message.content
|
|
272
|
+
|
|
273
|
+
async def _stream_chat_completions_specific_context(
|
|
245
274
|
self, context: OpenAILLMContext
|
|
246
275
|
) -> AsyncStream[ChatCompletionChunk]:
|
|
247
|
-
self.logger.debug(
|
|
276
|
+
self.logger.debug(
|
|
277
|
+
f"{self}: Generating chat from LLM-specific context {context.get_messages_for_logging()}"
|
|
278
|
+
)
|
|
248
279
|
|
|
249
280
|
messages: List[ChatCompletionMessageParam] = context.get_messages()
|
|
250
281
|
|
|
@@ -263,12 +294,28 @@ class BaseOpenAILLMService(LLMService):
|
|
|
263
294
|
del message["data"]
|
|
264
295
|
del message["mime_type"]
|
|
265
296
|
|
|
266
|
-
|
|
297
|
+
params = OpenAILLMInvocationParams(
|
|
298
|
+
messages=messages, tools=context.tools, tool_choice=context.tool_choice
|
|
299
|
+
)
|
|
300
|
+
chunks = await self.get_chat_completions(params)
|
|
301
|
+
|
|
302
|
+
return chunks
|
|
303
|
+
|
|
304
|
+
async def _stream_chat_completions_universal_context(
|
|
305
|
+
self, context: LLMContext
|
|
306
|
+
) -> AsyncStream[ChatCompletionChunk]:
|
|
307
|
+
adapter = self.get_llm_adapter()
|
|
308
|
+
logger.debug(
|
|
309
|
+
f"{self}: Generating chat from universal context {adapter.get_messages_for_logging(context)}"
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
params: OpenAILLMInvocationParams = adapter.get_llm_invocation_params(context)
|
|
313
|
+
chunks = await self.get_chat_completions(params)
|
|
267
314
|
|
|
268
315
|
return chunks
|
|
269
316
|
|
|
270
317
|
@traced_llm
|
|
271
|
-
async def _process_context(self, context: OpenAILLMContext):
|
|
318
|
+
async def _process_context(self, context: OpenAILLMContext | LLMContext):
|
|
272
319
|
functions_list = []
|
|
273
320
|
arguments_list = []
|
|
274
321
|
tool_id_list = []
|
|
@@ -279,8 +326,11 @@ class BaseOpenAILLMService(LLMService):
|
|
|
279
326
|
|
|
280
327
|
await self.start_ttfb_metrics()
|
|
281
328
|
|
|
282
|
-
|
|
283
|
-
|
|
329
|
+
# Generate chat completions using either OpenAILLMContext or universal LLMContext
|
|
330
|
+
chunk_stream = await (
|
|
331
|
+
self._stream_chat_completions_specific_context(context)
|
|
332
|
+
if isinstance(context, OpenAILLMContext)
|
|
333
|
+
else self._stream_chat_completions_universal_context(context)
|
|
284
334
|
)
|
|
285
335
|
|
|
286
336
|
async for chunk in chunk_stream:
|
|
@@ -367,8 +417,9 @@ class BaseOpenAILLMService(LLMService):
|
|
|
367
417
|
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
|
368
418
|
"""Process frames for LLM completion requests.
|
|
369
419
|
|
|
370
|
-
Handles OpenAILLMContextFrame,
|
|
371
|
-
and LLMUpdateSettingsFrame to trigger LLM
|
|
420
|
+
Handles OpenAILLMContextFrame, LLMContextFrame, LLMMessagesFrame,
|
|
421
|
+
VisionImageRawFrame, and LLMUpdateSettingsFrame to trigger LLM
|
|
422
|
+
completions and manage settings.
|
|
372
423
|
|
|
373
424
|
Args:
|
|
374
425
|
frame: The frame to process.
|
|
@@ -378,10 +429,21 @@ class BaseOpenAILLMService(LLMService):
|
|
|
378
429
|
|
|
379
430
|
context = None
|
|
380
431
|
if isinstance(frame, OpenAILLMContextFrame):
|
|
381
|
-
|
|
432
|
+
# Handle OpenAI-specific context frames
|
|
433
|
+
context = frame.context
|
|
434
|
+
elif isinstance(frame, LLMContextFrame):
|
|
435
|
+
# Handle universal (LLM-agnostic) LLM context frames
|
|
436
|
+
context = frame.context
|
|
382
437
|
elif isinstance(frame, LLMMessagesFrame):
|
|
438
|
+
# NOTE: LLMMessagesFrame is deprecated, so we don't support the newer universal
|
|
439
|
+
# LLMContext with it
|
|
383
440
|
context = OpenAILLMContext.from_messages(frame.messages)
|
|
384
441
|
elif isinstance(frame, VisionImageRawFrame):
|
|
442
|
+
# This is only useful in very simple pipelines because it creates
|
|
443
|
+
# a new context. Generally we want a context manager to catch
|
|
444
|
+
# UserImageRawFrames coming through the pipeline and add them
|
|
445
|
+
# to the context.
|
|
446
|
+
# TODO: support the newer universal LLMContext with a VisionImageRawFrame equivalent?
|
|
385
447
|
context = OpenAILLMContext()
|
|
386
448
|
context.add_image_frame_message(
|
|
387
449
|
format=frame.format, size=frame.size, image=frame.image, text=frame.text
|
pipecat/services/openai/image.py
CHANGED
|
@@ -84,5 +84,10 @@ class OpenAIImageGenService(ImageGenService):
|
|
|
84
84
|
async with self._aiohttp_session.get(image_url) as response:
|
|
85
85
|
image_stream = io.BytesIO(await response.content.read())
|
|
86
86
|
image = Image.open(image_stream)
|
|
87
|
-
frame = URLImageRawFrame(
|
|
87
|
+
frame = URLImageRawFrame(
|
|
88
|
+
image=image.tobytes(),
|
|
89
|
+
size=image.size,
|
|
90
|
+
format=image.format,
|
|
91
|
+
url=image_url,
|
|
92
|
+
)
|
|
88
93
|
yield frame
|
|
@@ -23,6 +23,7 @@ from pipecat.frames.frames import (
|
|
|
23
23
|
Frame,
|
|
24
24
|
InputAudioRawFrame,
|
|
25
25
|
InterimTranscriptionFrame,
|
|
26
|
+
LLMContextFrame,
|
|
26
27
|
LLMFullResponseEndFrame,
|
|
27
28
|
LLMFullResponseStartFrame,
|
|
28
29
|
LLMMessagesAppendFrame,
|
|
@@ -31,7 +32,6 @@ from pipecat.frames.frames import (
|
|
|
31
32
|
LLMUpdateSettingsFrame,
|
|
32
33
|
StartFrame,
|
|
33
34
|
StartInterruptionFrame,
|
|
34
|
-
StopInterruptionFrame,
|
|
35
35
|
TranscriptionFrame,
|
|
36
36
|
TTSAudioRawFrame,
|
|
37
37
|
TTSStartedFrame,
|
|
@@ -343,6 +343,10 @@ class OpenAIRealtimeBetaLLMService(LLMService):
|
|
|
343
343
|
await self.reset_conversation()
|
|
344
344
|
# Run the LLM at next opportunity
|
|
345
345
|
await self._create_response()
|
|
346
|
+
elif isinstance(frame, LLMContextFrame):
|
|
347
|
+
raise NotImplementedError(
|
|
348
|
+
"Universal LLMContext is not yet supported for OpenAI Realtime."
|
|
349
|
+
)
|
|
346
350
|
elif isinstance(frame, InputAudioRawFrame):
|
|
347
351
|
if not self._audio_input_paused:
|
|
348
352
|
await self._send_user_audio(frame)
|
|
@@ -648,7 +652,6 @@ class OpenAIRealtimeBetaLLMService(LLMService):
|
|
|
648
652
|
await self.start_ttfb_metrics()
|
|
649
653
|
await self.start_processing_metrics()
|
|
650
654
|
await self._stop_interruption()
|
|
651
|
-
await self.push_frame(StopInterruptionFrame())
|
|
652
655
|
await self.push_frame(UserStoppedSpeakingFrame())
|
|
653
656
|
|
|
654
657
|
async def _maybe_handle_evt_retrieve_conversation_item_error(self, evt: events.ErrorEvent):
|
pipecat/services/openpipe/llm.py
CHANGED
|
@@ -13,9 +13,8 @@ enabling integration with OpenPipe's fine-tuning and monitoring capabilities.
|
|
|
13
13
|
from typing import Dict, List, Optional
|
|
14
14
|
|
|
15
15
|
from loguru import logger
|
|
16
|
-
from openai.types.chat import ChatCompletionMessageParam
|
|
17
16
|
|
|
18
|
-
from pipecat.
|
|
17
|
+
from pipecat.adapters.services.open_ai_adapter import OpenAILLMInvocationParams
|
|
19
18
|
from pipecat.services.openai.llm import OpenAILLMService
|
|
20
19
|
|
|
21
20
|
try:
|
|
@@ -86,22 +85,21 @@ class OpenPipeLLMService(OpenAILLMService):
|
|
|
86
85
|
)
|
|
87
86
|
return client
|
|
88
87
|
|
|
89
|
-
def build_chat_completion_params(
|
|
90
|
-
self, context: OpenAILLMContext, messages: List[ChatCompletionMessageParam]
|
|
91
|
-
) -> dict:
|
|
88
|
+
def build_chat_completion_params(self, params_from_context: OpenAILLMInvocationParams) -> dict:
|
|
92
89
|
"""Build parameters for OpenPipe chat completion request.
|
|
93
90
|
|
|
94
91
|
Adds OpenPipe-specific logging and tagging parameters.
|
|
95
92
|
|
|
96
93
|
Args:
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
params_from_context: Parameters, derived from the LLM context, to
|
|
95
|
+
use for the chat completion. Contains messages, tools, and tool
|
|
96
|
+
choice.
|
|
99
97
|
|
|
100
98
|
Returns:
|
|
101
99
|
Dictionary of parameters for the chat completion request.
|
|
102
100
|
"""
|
|
103
101
|
# Start with base parameters
|
|
104
|
-
params = super().build_chat_completion_params(
|
|
102
|
+
params = super().build_chat_completion_params(params_from_context)
|
|
105
103
|
|
|
106
104
|
# Add OpenPipe-specific parameters
|
|
107
105
|
params["openpipe"] = {
|
|
@@ -11,12 +11,11 @@ an OpenAI-compatible interface. It handles Perplexity's unique token usage
|
|
|
11
11
|
reporting patterns while maintaining compatibility with the Pipecat framework.
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
from typing import List
|
|
15
|
-
|
|
16
14
|
from openai import NOT_GIVEN
|
|
17
|
-
from openai.types.chat import ChatCompletionMessageParam
|
|
18
15
|
|
|
16
|
+
from pipecat.adapters.services.open_ai_adapter import OpenAILLMInvocationParams
|
|
19
17
|
from pipecat.metrics.metrics import LLMTokenUsage
|
|
18
|
+
from pipecat.processors.aggregators.llm_context import LLMContext
|
|
20
19
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
|
21
20
|
from pipecat.services.openai.llm import OpenAILLMService
|
|
22
21
|
|
|
@@ -53,17 +52,23 @@ class PerplexityLLMService(OpenAILLMService):
|
|
|
53
52
|
self._has_reported_prompt_tokens = False
|
|
54
53
|
self._is_processing = False
|
|
55
54
|
|
|
56
|
-
def build_chat_completion_params(
|
|
57
|
-
self, context: OpenAILLMContext, messages: List[ChatCompletionMessageParam]
|
|
58
|
-
) -> dict:
|
|
55
|
+
def build_chat_completion_params(self, params_from_context: OpenAILLMInvocationParams) -> dict:
|
|
59
56
|
"""Build parameters for Perplexity chat completion request.
|
|
60
57
|
|
|
61
58
|
Perplexity uses a subset of OpenAI parameters and doesn't support tools.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
params_from_context: Parameters, derived from the LLM context, to
|
|
62
|
+
use for the chat completion. Contains messages, tools, and tool
|
|
63
|
+
choice.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Dictionary of parameters for the chat completion request.
|
|
62
67
|
"""
|
|
63
68
|
params = {
|
|
64
69
|
"model": self.model_name,
|
|
65
70
|
"stream": True,
|
|
66
|
-
"messages": messages,
|
|
71
|
+
"messages": params_from_context["messages"],
|
|
67
72
|
}
|
|
68
73
|
|
|
69
74
|
# Add OpenAI-compatible parameters if they're set
|
|
@@ -80,7 +85,7 @@ class PerplexityLLMService(OpenAILLMService):
|
|
|
80
85
|
|
|
81
86
|
return params
|
|
82
87
|
|
|
83
|
-
async def _process_context(self, context: OpenAILLMContext):
|
|
88
|
+
async def _process_context(self, context: OpenAILLMContext | LLMContext):
|
|
84
89
|
"""Process a context through the LLM and accumulate token usage metrics.
|
|
85
90
|
|
|
86
91
|
This method overrides the parent class implementation to handle
|
pipecat/services/playht/tts.py
CHANGED
|
@@ -14,7 +14,6 @@ import io
|
|
|
14
14
|
import json
|
|
15
15
|
import struct
|
|
16
16
|
import uuid
|
|
17
|
-
import warnings
|
|
18
17
|
from typing import AsyncGenerator, Optional
|
|
19
18
|
|
|
20
19
|
import aiohttp
|
|
@@ -455,11 +454,15 @@ class PlayHTHttpTTSService(TTSService):
|
|
|
455
454
|
|
|
456
455
|
# Warn about deprecated protocol parameter if explicitly provided
|
|
457
456
|
if protocol:
|
|
458
|
-
warnings
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
457
|
+
import warnings
|
|
458
|
+
|
|
459
|
+
with warnings.catch_warnings():
|
|
460
|
+
warnings.simplefilter("always")
|
|
461
|
+
warnings.warn(
|
|
462
|
+
"The 'protocol' parameter is deprecated and will be removed in a future version.",
|
|
463
|
+
DeprecationWarning,
|
|
464
|
+
stacklevel=2,
|
|
465
|
+
)
|
|
463
466
|
|
|
464
467
|
params = params or PlayHTHttpTTSService.InputParams()
|
|
465
468
|
|
pipecat/services/rime/tts.py
CHANGED
|
@@ -323,7 +323,7 @@ class RimeTTSService(AudioContextWordTTSService):
|
|
|
323
323
|
return
|
|
324
324
|
|
|
325
325
|
logger.trace(f"{self}: flushing audio")
|
|
326
|
-
await self._get_websocket().send(json.dumps({"
|
|
326
|
+
await self._get_websocket().send(json.dumps({"operation": "flush"}))
|
|
327
327
|
self._context_id = None
|
|
328
328
|
|
|
329
329
|
async def _receive_messages(self):
|
|
@@ -7,16 +7,18 @@
|
|
|
7
7
|
"""SambaNova LLM service implementation using OpenAI-compatible interface."""
|
|
8
8
|
|
|
9
9
|
import json
|
|
10
|
-
from typing import Any, Dict,
|
|
10
|
+
from typing import Any, Dict, Optional
|
|
11
11
|
|
|
12
12
|
from loguru import logger
|
|
13
13
|
from openai import AsyncStream
|
|
14
|
-
from openai.types.chat import ChatCompletionChunk
|
|
14
|
+
from openai.types.chat import ChatCompletionChunk
|
|
15
15
|
|
|
16
|
+
from pipecat.adapters.services.open_ai_adapter import OpenAILLMInvocationParams
|
|
16
17
|
from pipecat.frames.frames import (
|
|
17
18
|
LLMTextFrame,
|
|
18
19
|
)
|
|
19
20
|
from pipecat.metrics.metrics import LLMTokenUsage
|
|
21
|
+
from pipecat.processors.aggregators.llm_context import LLMContext
|
|
20
22
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
|
21
23
|
from pipecat.services.llm_service import FunctionCallFromLLM
|
|
22
24
|
from pipecat.services.openai.llm import OpenAILLMService
|
|
@@ -67,17 +69,16 @@ class SambaNovaLLMService(OpenAILLMService): # type: ignore
|
|
|
67
69
|
logger.debug(f"Creating SambaNova client with API {base_url}")
|
|
68
70
|
return super().create_client(api_key, base_url, **kwargs)
|
|
69
71
|
|
|
70
|
-
def build_chat_completion_params(
|
|
71
|
-
self, context: OpenAILLMContext, messages: List[ChatCompletionMessageParam]
|
|
72
|
-
) -> dict:
|
|
72
|
+
def build_chat_completion_params(self, params_from_context: OpenAILLMInvocationParams) -> dict:
|
|
73
73
|
"""Build parameters for SambaNova chat completion request.
|
|
74
74
|
|
|
75
75
|
SambaNova doesn't support some OpenAI parameters like frequency_penalty,
|
|
76
76
|
presence_penalty, and seed.
|
|
77
77
|
|
|
78
78
|
Args:
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
params_from_context: Parameters, derived from the LLM context, to
|
|
80
|
+
use for the chat completion. Contains messages, tools, and tool
|
|
81
|
+
choice.
|
|
81
82
|
|
|
82
83
|
Returns:
|
|
83
84
|
Dictionary of parameters for the chat completion request.
|
|
@@ -85,9 +86,6 @@ class SambaNovaLLMService(OpenAILLMService): # type: ignore
|
|
|
85
86
|
params = {
|
|
86
87
|
"model": self.model_name,
|
|
87
88
|
"stream": True,
|
|
88
|
-
"messages": messages,
|
|
89
|
-
"tools": context.tools,
|
|
90
|
-
"tool_choice": context.tool_choice,
|
|
91
89
|
"stream_options": {"include_usage": True},
|
|
92
90
|
"temperature": self._settings["temperature"],
|
|
93
91
|
"top_p": self._settings["top_p"],
|
|
@@ -95,11 +93,16 @@ class SambaNovaLLMService(OpenAILLMService): # type: ignore
|
|
|
95
93
|
"max_completion_tokens": self._settings["max_completion_tokens"],
|
|
96
94
|
}
|
|
97
95
|
|
|
96
|
+
# Messages, tools, tool_choice
|
|
97
|
+
params.update(params_from_context)
|
|
98
|
+
|
|
98
99
|
params.update(self._settings["extra"])
|
|
99
100
|
return params
|
|
100
101
|
|
|
101
102
|
@traced_llm # type: ignore
|
|
102
|
-
async def _process_context(
|
|
103
|
+
async def _process_context(
|
|
104
|
+
self, context: OpenAILLMContext | LLMContext
|
|
105
|
+
) -> AsyncStream[ChatCompletionChunk]:
|
|
103
106
|
"""Process OpenAI LLM context and stream chat completion chunks.
|
|
104
107
|
|
|
105
108
|
This method handles the streaming response from SambaNova API, including
|
|
@@ -122,8 +125,10 @@ class SambaNovaLLMService(OpenAILLMService): # type: ignore
|
|
|
122
125
|
|
|
123
126
|
await self.start_ttfb_metrics()
|
|
124
127
|
|
|
125
|
-
chunk_stream
|
|
126
|
-
context
|
|
128
|
+
chunk_stream = await (
|
|
129
|
+
self._stream_chat_completions_specific_context(context)
|
|
130
|
+
if isinstance(context, OpenAILLMContext)
|
|
131
|
+
else self._stream_chat_completions_universal_context(context)
|
|
127
132
|
)
|
|
128
133
|
|
|
129
134
|
async for chunk in chunk_stream:
|