Skip to content

Commit ec2ea1c

Browse files
committed
recording
1 parent 41936d4 commit ec2ea1c

20 files changed

+269
-59
lines changed

agent_telephony/twilio_livekit/agent_twilio/.env.Example

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11

22
RESTACK_API_KEY=
3+
OPENAI_API_KEY=
4+
35
LIVEKIT_API_KEY=
46
LIVEKIT_API_SECRET=
57
LIVEKIT_URL=
68

7-
LIVEKIT_SIP_ADDRESS=
89
TWILIO_PHONE_NUMBER=
910
TWILIO_TRUNK_AUTH_USERNAME=
1011
TWILIO_TRUNK_AUTH_PASSWORD=
12+
TWILIO_TRUNK_TERMINATION_SIP_URL=
1113

1214
ELEVEN_API_KEY=
1315
DEEPGRAM_API_KEY=
1416
OPENAI_API_KEY=
1517

18+
GCP_CREDENTIALS=
19+
1620
# Restack Cloud (Optional)
1721

1822
# RESTACK_ENGINE_ID=<your-engine-id>

agent_telephony/twilio_livekit/agent_twilio/event_agent.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,23 @@ async def main(agent_id: str, run_id: str) -> None:
1111
agent_id=agent_id,
1212
run_id=run_id,
1313
event_name="call",
14-
event_input={"messages": [{"role": "user", "content": "Tell me another joke"}]},
14+
event_input={
15+
"messages": [
16+
{
17+
"role": "user",
18+
"content": "What is Restack framework?",
19+
}
20+
]
21+
},
1522
)
1623

1724
sys.exit(0)
1825

1926

2027
def run_event_workflow() -> None:
21-
asyncio.run(main(agent_id="your-agent-id", run_id="your-run-id"))
28+
asyncio.run(
29+
main(agent_id="agent-id", run_id="run-id")
30+
)
2231

2332

2433
if __name__ == "__main__":

agent_telephony/twilio_livekit/agent_twilio/schedule_agent.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ async def main() -> None:
1414
agent_name=AgentStream.__name__, agent_id=agent_id
1515
)
1616

17-
await client.get_agent_result(agent_id=agent_id, run_id=run_id)
17+
await client.get_agent_result(
18+
agent_id=agent_id, run_id=run_id
19+
)
1820

1921
sys.exit(0)
2022

agent_telephony/twilio_livekit/agent_twilio/src/agents/agent.py

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@
3737
SendDataResponse,
3838
livekit_send_data,
3939
)
40+
from src.functions.livekit_start_recording import (
41+
EgressInfo,
42+
LivekitStartRecordingInput,
43+
livekit_start_recording,
44+
)
4045
from src.functions.livekit_token import (
4146
LivekitTokenInput,
4247
livekit_token,
@@ -69,6 +74,13 @@ class PipelineMetricsEvent(BaseModel):
6974
latencies: str
7075

7176

77+
class AgentTwilioOutput(BaseModel):
78+
recording_url: str
79+
livekit_room_id: str
80+
messages: list[Message]
81+
context: str
82+
83+
7284
@agent.defn()
7385
class AgentTwilio:
7486
def __init__(self) -> None:
@@ -78,7 +90,9 @@ def __init__(self) -> None:
7890
self.room_id = ""
7991

8092
@agent.event
81-
async def messages(self, messages_event: MessagesEvent) -> list[Message]:
93+
async def messages(
94+
self, messages_event: MessagesEvent
95+
) -> list[Message]:
8296
log.info(f"Received message: {messages_event.messages}")
8397
self.messages.extend(messages_event.messages)
8498

@@ -108,7 +122,9 @@ async def messages(self, messages_event: MessagesEvent) -> list[Message]:
108122
),
109123
)
110124

111-
self.messages.append(Message(role="assistant", content=fast_response))
125+
self.messages.append(
126+
Message(role="assistant", content=fast_response)
127+
)
112128
return self.messages
113129
except Exception as e:
114130
error_message = f"Error during messages: {e}"
@@ -122,7 +138,9 @@ async def call(self, call_input: CallInput) -> None:
122138
agent_id = agent_info().workflow_id
123139
run_id = agent_info().run_id
124140
try:
125-
sip_trunk_id = await agent.step(function=livekit_outbound_trunk)
141+
sip_trunk_id = await agent.step(
142+
function=livekit_outbound_trunk
143+
)
126144
await agent.step(
127145
function=livekit_call,
128146
function_input=LivekitCallInput(
@@ -135,14 +153,17 @@ async def call(self, call_input: CallInput) -> None:
135153
),
136154
)
137155
except Exception as e:
138-
error_message = f"Error during livekit_outbound_trunk: {e}"
156+
error_message = (
157+
f"Error during livekit_outbound_trunk: {e}"
158+
)
139159
raise NonRetryableError(error_message) from e
140160

141161
@agent.event
142162
async def say(self, say: str) -> SendDataResponse:
143163
log.info("Received say")
144164
return await agent.step(
145-
function=livekit_send_data, function_input=LivekitSendDataInput(text=say)
165+
function=livekit_send_data,
166+
function_input=LivekitSendDataInput(text=say),
146167
)
147168

148169
@agent.event
@@ -151,10 +172,11 @@ async def end(self, end: EndEvent) -> EndEvent:
151172
await agent.step(
152173
function=livekit_send_data,
153174
function_input=LivekitSendDataInput(
154-
room_id=self.room_id, text="Thank you for calling restack. Goodbye!"
175+
room_id=self.room_id,
176+
text="Thank you for calling restack. Goodbye!",
155177
),
156178
)
157-
await agent.sleep(1)
179+
await agent.sleep(3)
158180
await agent.step(function=livekit_delete_room)
159181

160182
self.end = True
@@ -170,7 +192,10 @@ async def context(self, context: ContextEvent) -> str:
170192
async def pipeline_metrics(
171193
self, pipeline_metrics: PipelineMetricsEvent
172194
) -> PipelineMetricsEvent:
173-
log.info("Received pipeline metrics", pipeline_metrics=pipeline_metrics)
195+
log.info(
196+
"Received pipeline metrics",
197+
pipeline_metrics=pipeline_metrics,
198+
)
174199
return pipeline_metrics
175200

176201
@agent.run
@@ -180,14 +205,34 @@ async def run(self) -> None:
180205
self.room_id = room.name
181206
await agent.step(
182207
function=livekit_token,
183-
function_input=LivekitTokenInput(room_id=self.room_id),
208+
function_input=LivekitTokenInput(
209+
room_id=self.room_id
210+
),
211+
)
212+
recording: EgressInfo = await agent.step(
213+
function=livekit_start_recording,
214+
function_input=LivekitStartRecordingInput(
215+
room_id=self.room_id
216+
),
184217
)
185218
await agent.step(
186219
function=livekit_dispatch,
187-
function_input=LivekitDispatchInput(room_id=self.room_id),
220+
function_input=LivekitDispatchInput(
221+
room_id=self.room_id
222+
),
188223
)
224+
189225
except Exception as e:
190226
error_message = f"Error during agent run: {e}"
191227
raise NonRetryableError(error_message) from e
192228
else:
193229
await agent.condition(lambda: self.end)
230+
231+
recording_url = f"https://storage.googleapis.com/{recording.room_composite.file_outputs[0].gcp.bucket}/{recording.room_composite.file_outputs[0].filepath}"
232+
233+
return AgentTwilioOutput(
234+
recording_url=recording_url,
235+
livekit_room_id=self.room_id,
236+
messages=self.messages,
237+
context=self.context,
238+
)

agent_telephony/twilio_livekit/agent_twilio/src/client.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
api_address = os.getenv("RESTACK_ENGINE_API_ADDRESS")
1515

1616
connection_options = CloudConnectionOptions(
17-
engine_id=engine_id, address=address, api_key=api_key, api_address=api_address
17+
engine_id=engine_id,
18+
address=address,
19+
api_key=api_key,
20+
api_address=api_address,
1821
)
1922
client = Restack(connection_options)

agent_telephony/twilio_livekit/agent_twilio/src/functions/context_docs.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@
33

44

55
async def fetch_content_from_url(url: str) -> str:
6-
async with aiohttp.ClientSession() as session, session.get(url) as response:
6+
async with (
7+
aiohttp.ClientSession() as session,
8+
session.get(url) as response,
9+
):
710
if response.status == 200:
811
return await response.text()
9-
error_message = f"Failed to fetch content: {response.status}"
12+
error_message = (
13+
f"Failed to fetch content: {response.status}"
14+
)
1015
raise NonRetryableError(error_message)
1116

1217

@@ -16,7 +21,9 @@ async def context_docs() -> str:
1621
docs_content = await fetch_content_from_url(
1722
"https://docs.restack.io/llms-full.txt"
1823
)
19-
log.info("Fetched content from URL", content=len(docs_content))
24+
log.info(
25+
"Fetched content from URL", content=len(docs_content)
26+
)
2027

2128
return docs_content
2229

agent_telephony/twilio_livekit/agent_twilio/src/functions/livekit_call.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ class LivekitCallInput:
1919

2020

2121
@function.defn()
22-
async def livekit_call(function_input: LivekitCallInput) -> SIPParticipantInfo:
22+
async def livekit_call(
23+
function_input: LivekitCallInput,
24+
) -> SIPParticipantInfo:
2325
try:
2426
livekit_api = api.LiveKitAPI()
2527

@@ -32,13 +34,21 @@ async def livekit_call(function_input: LivekitCallInput) -> SIPParticipantInfo:
3234
play_dialtone=True,
3335
)
3436

35-
log.info("livekit_call CreateSIPParticipantRequest: ", request=request)
37+
log.info(
38+
"livekit_call CreateSIPParticipantRequest: ",
39+
request=request,
40+
)
3641

37-
participant = await livekit_api.sip.create_sip_participant(request)
42+
participant = (
43+
await livekit_api.sip.create_sip_participant(request)
44+
)
3845

3946
await livekit_api.aclose()
4047

41-
log.info("livekit_call SIPParticipantInfo:", participant=participant)
48+
log.info(
49+
"livekit_call SIPParticipantInfo:",
50+
participant=participant,
51+
)
4252
except Exception as e:
4353
error_message = f"livekit_call function failed: {e}"
4454
raise NonRetryableError(error_message) from e

agent_telephony/twilio_livekit/agent_twilio/src/functions/livekit_create_room.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ async def livekit_create_room() -> Room:
3131
await lkapi.aclose()
3232

3333
except Exception as e:
34-
error_message = f"livekit_create_room function failed: {e}"
34+
error_message = (
35+
f"livekit_create_room function failed: {e}"
36+
)
3537
raise NonRetryableError(error_message) from e
3638

3739
else:

agent_telephony/twilio_livekit/agent_twilio/src/functions/livekit_delete_room.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,16 @@ async def livekit_delete_room() -> DeleteRoomResponse:
2020

2121
run_id = function_info().workflow_run_id
2222

23-
deleted_room = await lkapi.room.delete_room(DeleteRoomRequest(room=run_id))
23+
deleted_room = await lkapi.room.delete_room(
24+
DeleteRoomRequest(room=run_id)
25+
)
2426

2527
await lkapi.aclose()
2628

2729
except Exception as e:
28-
error_message = f"livekit_delete_room function failed: {e}"
30+
error_message = (
31+
f"livekit_delete_room function failed: {e}"
32+
)
2933
raise NonRetryableError(error_message) from e
3034

3135
else:

agent_telephony/twilio_livekit/agent_twilio/src/functions/livekit_dispatch.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ class LivekitDispatchInput:
1616

1717

1818
@function.defn()
19-
async def livekit_dispatch(function_input: LivekitDispatchInput) -> AgentDispatch:
19+
async def livekit_dispatch(
20+
function_input: LivekitDispatchInput,
21+
) -> AgentDispatch:
2022
try:
2123
lkapi = api.LiveKitAPI(
2224
url=os.getenv("LIVEKIT_API_URL"),
@@ -28,13 +30,19 @@ async def livekit_dispatch(function_input: LivekitDispatchInput) -> AgentDispatc
2830
agent_id = function_info().workflow_id
2931
run_id = function_info().workflow_run_id
3032

31-
metadata = {"agent_name": agent_name, "agent_id": agent_id, "run_id": run_id}
33+
metadata = {
34+
"agent_name": agent_name,
35+
"agent_id": agent_id,
36+
"run_id": run_id,
37+
}
3238

3339
room = function_input.room_id or run_id
3440

3541
dispatch = await lkapi.agent_dispatch.create_dispatch(
3642
api.CreateAgentDispatchRequest(
37-
agent_name=agent_name, room=room, metadata=str(metadata)
43+
agent_name=agent_name,
44+
room=room,
45+
metadata=str(metadata),
3846
)
3947
)
4048

0 commit comments

Comments
 (0)