Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9222bee

Browse files
authored
[2/n] Introduce RealtimeAgent (openai#1069)
Similar to the TS version. --- [//]: # (BEGIN SAPLING FOOTER) * __->__ openai#1069 * openai#1068
1 parent e5d3ad7 commit 9222bee

File tree

2 files changed

+105
-0
lines changed

2 files changed

+105
-0
lines changed

src/agents/realtime/agent.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
from __future__ import annotations
2+
3+
import dataclasses
4+
import inspect
5+
from collections.abc import Awaitable
6+
from dataclasses import dataclass
7+
from typing import Any, Callable, Generic, cast
8+
9+
from ..agent import AgentBase
10+
from ..lifecycle import AgentHooksBase, RunHooksBase
11+
from ..logger import logger
12+
from ..run_context import RunContextWrapper, TContext
13+
from ..util._types import MaybeAwaitable
14+
15+
RealtimeAgentHooks = AgentHooksBase[TContext, "RealtimeAgent[TContext]"]
16+
"""Agent hooks for `RealtimeAgent`s."""
17+
18+
RealtimeRunHooks = RunHooksBase[TContext, "RealtimeAgent[TContext]"]
19+
"""Run hooks for `RealtimeAgent`s."""
20+
21+
22+
@dataclass
23+
class RealtimeAgent(AgentBase, Generic[TContext]):
24+
"""A specialized agent instance that is meant to be used within a `RealtimeSession` to build
25+
voice agents. Due to the nature of this agent, some configuration options are not supported
26+
that are supported by regular `Agent` instances. For example:
27+
- `model` choice is not supported, as all RealtimeAgents will be handled by the same model
28+
within a `RealtimeSession`.
29+
- `modelSettings` is not supported, as all RealtimeAgents will be handled by the same model
30+
within a `RealtimeSession`.
31+
- `outputType` is not supported, as RealtimeAgents do not support structured outputs.
32+
- `toolUseBehavior` is not supported, as all RealtimeAgents will be handled by the same model
33+
within a `RealtimeSession`.
34+
- `voice` can be configured on an `Agent` level; however, it cannot be changed after the first
35+
agent within a `RealtimeSession` has spoken.
36+
37+
See `AgentBase` for base parameters that are shared with `Agent`s.
38+
"""
39+
40+
instructions: (
41+
str
42+
| Callable[
43+
[RunContextWrapper[TContext], RealtimeAgent[TContext]],
44+
MaybeAwaitable[str],
45+
]
46+
| None
47+
) = None
48+
"""The instructions for the agent. Will be used as the "system prompt" when this agent is
49+
invoked. Describes what the agent should do, and how it responds.
50+
51+
Can either be a string, or a function that dynamically generates instructions for the agent. If
52+
you provide a function, it will be called with the context and the agent instance. It must
53+
return a string.
54+
"""
55+
56+
hooks: RealtimeAgentHooks | None = None
57+
"""A class that receives callbacks on various lifecycle events for this agent.
58+
"""
59+
60+
def clone(self, **kwargs: Any) -> RealtimeAgent[TContext]:
61+
"""Make a copy of the agent, with the given arguments changed. For example, you could do:
62+
```
63+
new_agent = agent.clone(instructions="New instructions")
64+
```
65+
"""
66+
return dataclasses.replace(self, **kwargs)
67+
68+
async def get_system_prompt(self, run_context: RunContextWrapper[TContext]) -> str | None:
69+
"""Get the system prompt for the agent."""
70+
if isinstance(self.instructions, str):
71+
return self.instructions
72+
elif callable(self.instructions):
73+
if inspect.iscoroutinefunction(self.instructions):
74+
return await cast(Awaitable[str], self.instructions(run_context, self))
75+
else:
76+
return cast(str, self.instructions(run_context, self))
77+
elif self.instructions is not None:
78+
logger.error(f"Instructions must be a string or a function, got {self.instructions}")
79+
80+
return None

tests/realtime/test_agent.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import pytest
2+
3+
from agents import RunContextWrapper
4+
from agents.realtime.agent import RealtimeAgent
5+
6+
7+
def test_can_initialize_realtime_agent():
8+
agent = RealtimeAgent(name="test", instructions="Hello")
9+
assert agent.name == "test"
10+
assert agent.instructions == "Hello"
11+
12+
13+
@pytest.mark.asyncio
14+
async def test_dynamic_instructions():
15+
agent = RealtimeAgent(name="test")
16+
assert agent.instructions is None
17+
18+
def _instructions(ctx, agt) -> str:
19+
assert ctx.context is None
20+
assert agt == agent
21+
return "Dynamic"
22+
23+
agent = RealtimeAgent(name="test", instructions=_instructions)
24+
instructions = await agent.get_system_prompt(RunContextWrapper(context=None))
25+
assert instructions == "Dynamic"

0 commit comments

Comments
 (0)