Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions agent_apis/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Restack AI - Agent with Third Party APIs
# Restack AI - Agent with third party APIs

This repository contains an agent with Third Party APIs
This repository contains an agent with third party APIs
It demonstrates how to set up a multi steps workflow with Weather API and OpenAI.

## Prerequisites
Expand Down
4 changes: 2 additions & 2 deletions agent_chat/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Restack AI - Agent Chat
# Restack AI - Agent with chat

This repository contains a an agent chat for Restack.
This repository contains an agent with chat for Restack.
It demonstrates how to set up a workflow to have a conversation with an AI agent.

## Prerequisites
Expand Down
2 changes: 1 addition & 1 deletion agent_rag/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Restack AI - Agent Rag
# Restack AI - Agent with RAG

This repository demonstrates how to set up a basic agent with RAG.

Expand Down
2 changes: 1 addition & 1 deletion agent_stream/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Restack AI - Agent with Stream
# Restack AI - Agent with stream

Build an Agent user can chat with and return streaming response.

Expand Down
6 changes: 4 additions & 2 deletions agent_telephony/twilio/agent_twilio/src/agents/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@
class MessagesEvent(BaseModel):
messages: list[Message]


class EndEvent(BaseModel):
end: bool


class CallInput(BaseModel):
phone_number: str


@agent.defn()
class AgentTwilio:
def __init__(self) -> None:
Expand Down Expand Up @@ -65,10 +68,9 @@ async def end(self, end: EndEvent) -> EndEvent:
log.info("Received end")
self.end = True
return end

@agent.run
async def run(self) -> None:

room = await agent.step(function=livekit_room)
self.room_id = room.name

Expand Down
26 changes: 12 additions & 14 deletions agent_telephony/twilio/livekit-trunk-setup/twilio_trunk.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def get_env_var(var_name):
exit(1)
return value


def create_livekit_trunk(client, sip_uri):
domain_name = f"livekit-trunk-{os.urandom(4).hex()}.pstn.twilio.com"
trunk = client.trunking.v1.trunks.create(
Expand All @@ -31,20 +32,17 @@ def create_livekit_trunk(client, sip_uri):
logging.info("Created new LiveKit Trunk.")
return trunk


def create_inbound_trunk(phone_number):
trunk_data = {
"trunk": {
"name": "Inbound LiveKit Trunk",
"numbers": [phone_number]
}
}
trunk_data = {"trunk": {"name": "Inbound LiveKit Trunk", "numbers": [phone_number]}}
with open("inbound_trunk.json", "w") as f:
json.dump(trunk_data, f, indent=4)

result = subprocess.run(
["lk", "sip", "inbound", "create", "inbound_trunk.json"],
capture_output=True,
text=True, check=False
text=True,
check=False,
)

if result.returncode != 0:
Expand All @@ -59,23 +57,21 @@ def create_inbound_trunk(phone_number):
logging.error("Could not find inbound trunk SID in output.")
return None


def create_dispatch_rule(trunk_sid):
dispatch_rule_data = {
"name": "Inbound Dispatch Rule",
"trunk_ids": [trunk_sid],
"rule": {
"dispatchRuleIndividual": {
"roomPrefix": "call-"
}
}
"rule": {"dispatchRuleIndividual": {"roomPrefix": "call-"}},
}
with open("dispatch_rule.json", "w") as f:
json.dump(dispatch_rule_data, f, indent=4)

result = subprocess.run(
["lk", "sip", "dispatch-rule", "create", "dispatch_rule.json"],
capture_output=True,
text=True, check=False
text=True,
check=False,
)

if result.returncode != 0:
Expand All @@ -84,6 +80,7 @@ def create_dispatch_rule(trunk_sid):

logging.info(f"Dispatch rule created: {result.stdout}")


def main():
load_dotenv()
logging.basicConfig(level=logging.INFO)
Expand All @@ -98,7 +95,7 @@ def main():
existing_trunks = client.trunking.v1.trunks.list()
livekit_trunk = next(
(trunk for trunk in existing_trunks if trunk.friendly_name == "LiveKit Trunk"),
None
None,
)

if not livekit_trunk:
Expand All @@ -110,5 +107,6 @@ def main():
if inbound_trunk_sid:
create_dispatch_rule(inbound_trunk_sid)


if __name__ == "__main__":
main()
18 changes: 9 additions & 9 deletions agent_telephony/twilio/livekit_pipeline/src/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,30 +21,30 @@
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


def validate_envs() -> None:
"""
Check for the presence of all required environment variables.
Logs a warning if any variable is missing.
"""
required_envs = {
"LIVEKIT_URL": "LiveKit server URL",
"LIVEKIT_API_KEY": "API Key for LiveKit",
"LIVEKIT_API_SECRET": "API Secret for LiveKit",
"DEEPGRAM_API_KEY": "API key for Deepgram (used for STT)",
"ELEVEN_API_KEY": "API key for ElevenLabs (used for TTS)"
"ELEVEN_API_KEY": "API key for ElevenLabs (used for TTS)",
}
for key, description in required_envs.items():
if not os.environ.get(key):
logger.warning("Environment variable %s (%s) is not set.", key, description)


# Validate environments at module load
validate_envs()


def prewarm(proc: JobProcess) -> None:
logger.info("Prewarming: loading VAD model...")
proc.userdata["vad"] = silero.VAD.load()
logger.info("VAD model loaded successfully.")


async def entrypoint(ctx: JobContext) -> None:
metadata = ctx.job.metadata

Expand Down Expand Up @@ -75,11 +75,10 @@ async def entrypoint(ctx: JobContext) -> None:
engine_api_address = os.environ.get("RESTACK_ENGINE_API_ADDRESS")
if not engine_api_address:
agent_backend_host = "http://localhost:9233"
elif not engine_api_address.startswith("https://"):
agent_backend_host = "https://" + engine_api_address
else:
if not engine_api_address.startswith("https://"):
agent_backend_host = "https://" + engine_api_address
else:
agent_backend_host = engine_api_address
agent_backend_host = engine_api_address

logger.info("Using RESTACK_ENGINE_API_ADDRESS: %s", agent_backend_host)

Expand Down Expand Up @@ -120,6 +119,7 @@ def on_metrics_collected(agent_metrics: metrics.AgentMetrics) -> None:
# Start the voice pipeline agent.
agent.start(ctx.room, participant)


if __name__ == "__main__":
cli.run_app(
WorkerOptions(
Expand Down
7 changes: 2 additions & 5 deletions agent_telephony/twilio/readme.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@

wip# Restack AI - Agent with Telephony - Twilio
# Restack AI - Agent with telephony - Twilio

Build an AI agent that do an outbound call with Twilio and can interact with in realtime with voice.

## Prerequisites

- Docker (for running Restack)
- Python 3.10 or higher
- Livekit account (for WebRTC pipeline)
- Deepgram account (For speech-to-text transcription)
- ElevenLabs account (for text-to-speech and voice cloning)
- Twilio account (for outbound calls)
- Vapi account (for outbound calls)

### Trunk setup for outbound calls with Twilio

Expand Down
12 changes: 12 additions & 0 deletions agent_telephony/vapi/agent_vapi/.env.Example
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

RESTACK_API_KEY=
VAPI_TOKEN=

# Restack Cloud (Optional)

# RESTACK_ENGINE_ID=<your-engine-id>
# RESTACK_ENGINE_API_KEY=<your-engine-api-key>
# RESTACK_ENGINE_API_ADDRESS=<your-engine-api-address>
# RESTACK_ENGINE_ADDRESS=<your-engine-address>


5 changes: 5 additions & 0 deletions agent_telephony/vapi/agent_vapi/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
__pycache__
.pytest_cache
venv
.env
.vscode
Binary file added agent_telephony/vapi/agent_vapi/agent_call.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added agent_telephony/vapi/agent_vapi/agent_replay.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions agent_telephony/vapi/agent_vapi/event_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import asyncio
import sys

from restack_ai import Restack


async def main(agent_id: str, run_id: str) -> None:
client = Restack()

await client.send_agent_event(
agent_id=agent_id,
run_id=run_id,
event_name="call",
event_input={"messages": [{"role": "user", "content": "Tell me another joke"}]},
)

sys.exit(0)


def run_event_workflow() -> None:
asyncio.run(main(agent_id="your-agent-id", run_id="your-run-id"))


if __name__ == "__main__":
run_event_workflow()
31 changes: 31 additions & 0 deletions agent_telephony/vapi/agent_vapi/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[project]
name = "agent_telephony_agent_vapi"
version = "0.0.1"
description = "An agent with streaming for Restack"
authors = [{ name = "Restack Team", email = "service@restack.io" }]
requires-python = ">=3.10,<3.13"
readme = "README.md"
dependencies = [
"pydantic>=2.10.6",
"watchfiles>=1.0.4",
"python-dotenv==1.0.1",
"openai>=1.61.0",
"restack-ai>=0.0.77",
"vapi-server-sdk>=0.4.0",
]

[project.scripts]
dev = "src.services:watch_services"
services = "src.services:run_services"
schedule = "schedule_agent:run_schedule_agent"
event = "event_agent:run_event_agent"

[tool.hatch.build.targets.sdist]
include = ["src"]

[tool.hatch.build.targets.wheel]
include = ["src"]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
Loading