mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-06-28 08:49:42 +02:00
feat: add hybrid text + recording functionality in agents (#191)
* feat: add recording feature in agents * chore: pin pipecat version * feat: show usage in UI * chore: update pipecat
This commit is contained in:
parent
f075bcb623
commit
494c60d774
43 changed files with 2865 additions and 397 deletions
|
|
@ -13,14 +13,12 @@ from unittest.mock import AsyncMock, Mock, patch
|
|||
|
||||
import pytest
|
||||
|
||||
from api.services.workflow.pipecat_engine_utils import (
|
||||
get_function_schema,
|
||||
update_llm_context,
|
||||
)
|
||||
from api.services.workflow.pipecat_engine_custom_tools import get_function_schema
|
||||
from api.services.workflow.tools.custom_tool import (
|
||||
execute_http_tool,
|
||||
tool_to_function_schema,
|
||||
)
|
||||
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
||||
from pipecat.frames.frames import (
|
||||
FunctionCallInProgressFrame,
|
||||
FunctionCallResultFrame,
|
||||
|
|
@ -862,11 +860,27 @@ class TestCustomToolManagerUnit:
|
|||
assert result_received["status"] == "success"
|
||||
|
||||
|
||||
def _update_llm_context(context, system_message, functions):
|
||||
"""Inline helper replicating the old update_llm_context for tests."""
|
||||
tools_schema = ToolsSchema(standard_tools=functions)
|
||||
previous_interactions = context.messages
|
||||
|
||||
if previous_interactions and previous_interactions[0]["role"] == "system":
|
||||
messages = [system_message] + previous_interactions[1:]
|
||||
else:
|
||||
messages = [system_message] + previous_interactions
|
||||
|
||||
context.set_messages(messages)
|
||||
|
||||
if functions:
|
||||
context.set_tools(tools_schema)
|
||||
|
||||
|
||||
class TestUpdateLLMContext:
|
||||
"""Tests for update_llm_context function."""
|
||||
"""Tests for _update_llm_context inline logic."""
|
||||
|
||||
def test_replaces_system_message(self):
|
||||
"""Test that update_llm_context replaces existing system messages."""
|
||||
"""Test that _update_llm_context replaces existing system messages."""
|
||||
context = LLMContext()
|
||||
context.set_messages(
|
||||
[
|
||||
|
|
@ -877,7 +891,7 @@ class TestUpdateLLMContext:
|
|||
)
|
||||
|
||||
new_system = {"role": "system", "content": "New system message"}
|
||||
update_llm_context(context, new_system, [])
|
||||
_update_llm_context(context, new_system, [])
|
||||
|
||||
messages = context.messages
|
||||
# Should have new system message at the start
|
||||
|
|
@ -902,7 +916,7 @@ class TestUpdateLLMContext:
|
|||
)
|
||||
|
||||
new_system = {"role": "system", "content": "New prompt"}
|
||||
update_llm_context(context, new_system, [])
|
||||
_update_llm_context(context, new_system, [])
|
||||
|
||||
messages = context.messages
|
||||
assert len(messages) == 5
|
||||
|
|
@ -923,7 +937,7 @@ class TestUpdateLLMContext:
|
|||
]
|
||||
|
||||
new_system = {"role": "system", "content": "New prompt with tools"}
|
||||
update_llm_context(context, new_system, functions)
|
||||
_update_llm_context(context, new_system, functions)
|
||||
|
||||
# Verify tools were set
|
||||
tools = context.tools
|
||||
|
|
@ -936,7 +950,7 @@ class TestUpdateLLMContext:
|
|||
context.set_messages([{"role": "system", "content": "Old"}])
|
||||
|
||||
new_system = {"role": "system", "content": "New prompt without tools"}
|
||||
update_llm_context(context, new_system, [])
|
||||
_update_llm_context(context, new_system, [])
|
||||
|
||||
# Tools should not be set (or remain None)
|
||||
# Note: The function only calls set_tools if functions is truthy
|
||||
|
|
@ -952,7 +966,7 @@ class TestUpdateLLMContext:
|
|||
new_system = {"role": "system", "content": "Initial prompt"}
|
||||
functions = [get_function_schema("test_func", "A test function")]
|
||||
|
||||
update_llm_context(context, new_system, functions)
|
||||
_update_llm_context(context, new_system, functions)
|
||||
|
||||
messages = context.messages
|
||||
assert len(messages) == 1
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
"""Integration tests for CustomToolManager with update_llm_context.
|
||||
"""Integration tests for CustomToolManager with LLM context updates.
|
||||
|
||||
This module tests the full flow of:
|
||||
1. CustomToolManager fetching and converting tool schemas
|
||||
2. update_llm_context setting those tools on the LLM context
|
||||
2. Setting those tools on the LLM context
|
||||
3. Verifying the context is properly configured for LLM generation
|
||||
"""
|
||||
|
||||
|
|
@ -10,16 +10,32 @@ from unittest.mock import AsyncMock, patch
|
|||
|
||||
import pytest
|
||||
|
||||
from api.services.workflow.pipecat_engine_custom_tools import CustomToolManager
|
||||
from api.services.workflow.pipecat_engine_utils import (
|
||||
from api.services.workflow.pipecat_engine_custom_tools import (
|
||||
CustomToolManager,
|
||||
get_function_schema,
|
||||
update_llm_context,
|
||||
)
|
||||
from api.tests.conftest import MockToolModel
|
||||
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
||||
from pipecat.processors.aggregators.llm_context import LLMContext
|
||||
|
||||
|
||||
def _update_llm_context(context, system_message, functions):
|
||||
"""Inline helper replicating the update_llm_context logic for tests."""
|
||||
tools_schema = ToolsSchema(standard_tools=functions)
|
||||
previous_interactions = context.messages
|
||||
|
||||
if previous_interactions and previous_interactions[0]["role"] == "system":
|
||||
messages = [system_message] + previous_interactions[1:]
|
||||
else:
|
||||
messages = [system_message] + previous_interactions
|
||||
|
||||
context.set_messages(messages)
|
||||
|
||||
if functions:
|
||||
context.set_tools(tools_schema)
|
||||
|
||||
|
||||
class TestCustomToolManagerContextIntegration:
|
||||
"""Integration tests for CustomToolManager with LLMContext."""
|
||||
|
||||
|
|
@ -69,7 +85,7 @@ class TestCustomToolManagerContextIntegration:
|
|||
"role": "system",
|
||||
"content": "You are a scheduling assistant with access to weather and booking tools.",
|
||||
}
|
||||
update_llm_context(context, new_system, schemas)
|
||||
_update_llm_context(context, new_system, schemas)
|
||||
|
||||
# Verify context was updated correctly
|
||||
messages = context.messages
|
||||
|
|
@ -195,7 +211,7 @@ class TestCustomToolManagerContextIntegration:
|
|||
"role": "system",
|
||||
"content": "Assistant with calculator and weather tools",
|
||||
}
|
||||
update_llm_context(context, new_system, all_functions)
|
||||
_update_llm_context(context, new_system, all_functions)
|
||||
|
||||
# Verify all tools are present
|
||||
tools = context.tools
|
||||
|
|
@ -259,7 +275,7 @@ class TestCustomToolManagerContextIntegration:
|
|||
)
|
||||
|
||||
new_system = {"role": "system", "content": "Updated weather assistant"}
|
||||
update_llm_context(context, new_system, schemas)
|
||||
_update_llm_context(context, new_system, schemas)
|
||||
|
||||
messages = context.messages
|
||||
# System + user + assistant(tool_call) + tool + assistant = 5
|
||||
|
|
@ -296,7 +312,7 @@ class TestCustomToolManagerContextIntegration:
|
|||
context.set_messages([{"role": "system", "content": "Old"}])
|
||||
|
||||
new_system = {"role": "system", "content": "No tools available"}
|
||||
update_llm_context(context, new_system, [])
|
||||
_update_llm_context(context, new_system, [])
|
||||
|
||||
# Context should have updated message but no tools set
|
||||
assert context.messages[0]["content"] == "No tools available"
|
||||
|
|
@ -362,7 +378,7 @@ class TestCustomToolManagerContextIntegration:
|
|||
# Update context - pass schema directly
|
||||
context = LLMContext()
|
||||
context.set_messages([{"role": "system", "content": "Old"}])
|
||||
update_llm_context(
|
||||
_update_llm_context(
|
||||
context, {"role": "system", "content": "Order assistant"}, schemas
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -68,9 +68,7 @@ class ContextCapturingMockLLM(MockLLMService):
|
|||
{
|
||||
"step": self._current_step,
|
||||
"messages": messages_snapshot,
|
||||
"system_prompt": messages_snapshot[0]["content"]
|
||||
if messages_snapshot
|
||||
else None,
|
||||
"system_prompt": self._settings.system_instruction,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -101,12 +99,10 @@ class ContextCapturingMockLLM(MockLLMService):
|
|||
return False
|
||||
|
||||
def get_system_prompt_at_step(self, step: int) -> str:
|
||||
"""Get the system prompt from context at a specific step."""
|
||||
"""Get the system prompt from settings at a specific step."""
|
||||
ctx = self.get_context_at_step(step)
|
||||
if ctx and ctx["messages"]:
|
||||
first_msg = ctx["messages"][0]
|
||||
if first_msg.get("role") == "system":
|
||||
return first_msg.get("content", "")
|
||||
if ctx:
|
||||
return ctx.get("system_prompt") or ""
|
||||
return ""
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ class TestPipecatEngineToolCalls:
|
|||
)
|
||||
|
||||
# Assert that the context was updated with END_CALL_SYSTEM_PROMPT
|
||||
assert context.messages[0]["content"] == END_CALL_SYSTEM_PROMPT
|
||||
assert llm._settings.system_instruction == END_CALL_SYSTEM_PROMPT
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_parallel_builtin_and_transition_calls_through_engine_1(
|
||||
|
|
@ -233,7 +233,7 @@ class TestPipecatEngineToolCalls:
|
|||
)
|
||||
|
||||
# Assert that the context was updated with END_CALL_SYSTEM_PROMPT
|
||||
assert context.messages[0]["content"] == END_CALL_SYSTEM_PROMPT
|
||||
assert llm._settings.system_instruction == END_CALL_SYSTEM_PROMPT
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_parallel_builtin_and_transition_calls_through_engine_with_text(
|
||||
|
|
@ -281,7 +281,7 @@ class TestPipecatEngineToolCalls:
|
|||
)
|
||||
|
||||
# Assert that the context was updated with END_CALL_SYSTEM_PROMPT
|
||||
assert context.messages[0]["content"] == END_CALL_SYSTEM_PROMPT
|
||||
assert llm._settings.system_instruction == END_CALL_SYSTEM_PROMPT
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_single_transition_call_through_engine(
|
||||
|
|
@ -315,4 +315,4 @@ class TestPipecatEngineToolCalls:
|
|||
)
|
||||
|
||||
# Assert that the context was updated with END_CALL_SYSTEM_PROMPT
|
||||
assert context.messages[0]["content"] == END_CALL_SYSTEM_PROMPT
|
||||
assert llm._settings.system_instruction == END_CALL_SYSTEM_PROMPT
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue