Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate OpenAI realtime API #281

Merged
merged 29 commits into from
Dec 30, 2024
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
3437af4
Integrate OpenAI realtime API
sternakt Dec 23, 2024
a64a4f6
Refactor client to use official OpenAIClient
sternakt Dec 24, 2024
10fa708
Fix realtime websocket notebook
sternakt Dec 24, 2024
5127fb7
wip
davorrunje Dec 27, 2024
7711c63
Merge remote-tracking branch 'origin/main' into rewrite-OpenAIRealtim…
davorrunje Dec 27, 2024
9b8638b
wip
davorrunje Dec 27, 2024
f1cabb9
RealtimeClient refactored
davorrunje Dec 28, 2024
790e079
Merge remote-tracking branch 'origin/main' into rewrite-OpenAIRealtim…
davorrunje Dec 28, 2024
808456d
wip
davorrunje Dec 29, 2024
333d321
Merge remote-tracking branch 'origin/main' into rewrite-OpenAIRealtim…
davorrunje Dec 29, 2024
edb1f88
Merge remote-tracking branch 'origin/main' into rewrite-OpenAIRealtim…
davorrunje Dec 29, 2024
e6994bb
Merge branch 'rewrite-OpenAIRealtimeClient' into rewrite-OpenAIRealti…
davorrunje Dec 29, 2024
4d52469
wip
davorrunje Dec 29, 2024
01e60db
Licence fixed
davorrunje Dec 29, 2024
97f3447
wip
davorrunje Dec 30, 2024
ff24cdc
wip
davorrunje Dec 30, 2024
0c150f8
Fix function calling, move logger property to RealtimeObserver
sternakt Dec 30, 2024
79ea89b
Polish RealtimeAgent example notebooks
sternakt Dec 30, 2024
4eb061d
Add warning for running the notebook in colab
sternakt Dec 30, 2024
a5a2e42
Merge remote-tracking branch 'origin/main' into rewrite-OpenAIRealtim…
davorrunje Dec 30, 2024
73d9cf8
polishing
davorrunje Dec 30, 2024
1b47831
polishing
davorrunje Dec 30, 2024
31c8299
Merge remote-tracking branch 'origin/main' into rewrite-OpenAIRealtim…
davorrunje Dec 30, 2024
7ec19ae
Merge branch 'rewrite-OpenAIRealtimeClient' into rewrite-OpenAIRealti…
davorrunje Dec 30, 2024
c09fd02
Merge pull request #313 from ag2ai/rewrite-OpenAIRealtimeClient-davor
davorrunje Dec 30, 2024
9f4cbd8
polishing
davorrunje Dec 30, 2024
91bdb29
resolve conflicts
davorrunje Dec 30, 2024
4f22f00
fastapi added to test extra
davorrunje Dec 30, 2024
029d6cb
OpenAI test fix in CI
davorrunje Dec 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions autogen/agentchat/realtime_agent/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Copyright (c) 2023 - 2024, Owners of https://github.com/ag2ai
#
# SPDX-License-Identifier: Apache-2.0

from .function_observer import FunctionObserver
from .realtime_agent import RealtimeAgent
from .twilio_observer import TwilioAudioAdapter
from .websocket_observer import WebsocketAudioAdapter
from .realtime_observer import RealtimeObserver
from .twilio_audio_adapter import TwilioAudioAdapter
from .websocket_audio_adapter import WebSocketAudioAdapter

__all__ = ["RealtimeAgent", "FunctionObserver", "TwilioAudioAdapter", "WebsocketAudioAdapter"]
__all__ = ["FunctionObserver", "RealtimeAgent", "RealtimeObserver", "TwilioAudioAdapter", "WebSocketAudioAdapter"]
192 changes: 0 additions & 192 deletions autogen/agentchat/realtime_agent/client.py

This file was deleted.

67 changes: 30 additions & 37 deletions autogen/agentchat/realtime_agent/function_observer.py
Original file line number Diff line number Diff line change
@@ -1,48 +1,37 @@
# Copyright (c) 2023 - 2024, Owners of https://github.com/ag2ai
#
# SPDX-License-Identifier: Apache-2.0
#
# Portions derived from https://github.com/microsoft/autogen are under the MIT License.
# SPDX-License-Identifier: MIT

import asyncio
import json
import logging
from typing import TYPE_CHECKING, Any
from logging import Logger, getLogger
from typing import TYPE_CHECKING, Any, Optional

from asyncer import asyncify
from pydantic import BaseModel

from .realtime_observer import RealtimeObserver

if TYPE_CHECKING:
from .realtime_agent import RealtimeAgent

logger = logging.getLogger(__name__)


class FunctionObserver(RealtimeObserver):
"""Observer for handling function calls from the OpenAI Realtime API."""

def __init__(self, agent: "RealtimeAgent") -> None:
"""Observer for handling function calls from the OpenAI Realtime API.

Args:
agent (RealtimeAgent): The realtime agent attached to the observer.
"""
super().__init__()
self._agent = agent
def __init__(self, *, logger: Optional[Logger] = None) -> None:
"""Observer for handling function calls from the OpenAI Realtime API."""
super().__init__(logger=logger)

async def update(self, response: dict[str, Any]) -> None:
async def on_event(self, event: dict[str, Any]) -> None:
"""Handle function call events from the OpenAI Realtime API.

Args:
response (dict[str, Any]): The response from the OpenAI Realtime API.
event (dict[str, Any]): The event from the OpenAI Realtime API.
"""
if response.get("type") == "response.function_call_arguments.done":
logger.info(f"Received event: {response['type']}", response)
if event["type"] == "response.function_call_arguments.done":
self.logger.info(f"Received event: {event['type']}", event)
await self.call_function(
call_id=response["call_id"], name=response["name"], kwargs=json.loads(response["arguments"])
call_id=event["call_id"],
name=event["name"],
kwargs=json.loads(event["arguments"]),
)

async def call_function(self, call_id: str, name: str, kwargs: dict[str, Any]) -> None:
Expand All @@ -54,33 +43,37 @@ async def call_function(self, call_id: str, name: str, kwargs: dict[str, Any]) -
kwargs (Any[str, Any]): The arguments to pass to the function.
"""

if name in self._agent.realtime_functions:
_, func = self._agent.realtime_functions[name]
if name in self.agent._registred_realtime_functions:
_, func = self.agent._registred_realtime_functions[name]
func = func if asyncio.iscoroutinefunction(func) else asyncify(func)
try:
result = await func(**kwargs)
except Exception:
result = "Function call failed"
logger.warning(f"Function call failed: {name}")
self.logger.info(f"Function call failed: {name=}, {kwargs=}", stack_info=True)

if isinstance(result, BaseModel):
result = result.model_dump_json()
elif not isinstance(result, str):
result = json.dumps(result)
try:
result = json.dumps(result)
except Exception:
result = str(result)

await self.client.function_result(call_id, result)

async def run(self) -> None:
"""Run the observer.

Initialize the session with the OpenAI Realtime API.
"""
await self.initialize_session()
await self.realtime_client.send_function_result(call_id, result)

async def initialize_session(self) -> None:
"""Add registered tools to OpenAI with a session update."""
session_update = {
"tools": [schema for schema, _ in self._agent.realtime_functions.values()],
"tools": [schema for schema, _ in self.agent._registred_realtime_functions.values()],
"tool_choice": "auto",
}
await self.client.session_update(session_update)
await self.realtime_client.session_update(session_update)

async def run_loop(self) -> None:
"""Run the observer loop."""
pass


if TYPE_CHECKING:
function_observer: RealtimeObserver = FunctionObserver()
Loading
Loading