diff --git a/agent_apis/README.md b/agent_apis/README.md index df8aff29..61143cc0 100644 --- a/agent_apis/README.md +++ b/agent_apis/README.md @@ -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 diff --git a/agent_chat/README.md b/agent_chat/README.md index b9ffd5a1..b97ce76e 100644 --- a/agent_chat/README.md +++ b/agent_chat/README.md @@ -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 diff --git a/agent_rag/README.md b/agent_rag/README.md index f6379143..12b96a68 100644 --- a/agent_rag/README.md +++ b/agent_rag/README.md @@ -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. diff --git a/agent_stream/README.md b/agent_stream/README.md index c3bd1645..feea7158 100644 --- a/agent_stream/README.md +++ b/agent_stream/README.md @@ -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. diff --git a/agent_telephony/twilio/agent_twilio/src/agents/agent.py b/agent_telephony/twilio/agent_twilio/src/agents/agent.py index 68ad08ec..e3de0eb8 100644 --- a/agent_telephony/twilio/agent_twilio/src/agents/agent.py +++ b/agent_telephony/twilio/agent_twilio/src/agents/agent.py @@ -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: @@ -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 diff --git a/agent_telephony/twilio/livekit-trunk-setup/twilio_trunk.py b/agent_telephony/twilio/livekit-trunk-setup/twilio_trunk.py index 9ef345a8..7e026e21 100644 --- a/agent_telephony/twilio/livekit-trunk-setup/twilio_trunk.py +++ b/agent_telephony/twilio/livekit-trunk-setup/twilio_trunk.py @@ -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( @@ -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: @@ -59,15 +57,12 @@ 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) @@ -75,7 +70,8 @@ def create_dispatch_rule(trunk_sid): 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: @@ -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) @@ -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: @@ -110,5 +107,6 @@ def main(): if inbound_trunk_sid: create_dispatch_rule(inbound_trunk_sid) + if __name__ == "__main__": main() diff --git a/agent_telephony/twilio/livekit_pipeline/src/pipeline.py b/agent_telephony/twilio/livekit_pipeline/src/pipeline.py index fc0dda00..acca2520 100644 --- a/agent_telephony/twilio/livekit_pipeline/src/pipeline.py +++ b/agent_telephony/twilio/livekit_pipeline/src/pipeline.py @@ -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 @@ -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) @@ -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( diff --git a/agent_telephony/twilio/readme.md b/agent_telephony/twilio/readme.md index e457fa0c..edf1cfce 100644 --- a/agent_telephony/twilio/readme.md +++ b/agent_telephony/twilio/readme.md @@ -1,5 +1,5 @@ -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. @@ -7,10 +7,7 @@ Build an AI agent that do an outbound call with Twilio and can interact with in - 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 diff --git a/agent_telephony/vapi/agent_vapi/.env.Example b/agent_telephony/vapi/agent_vapi/.env.Example new file mode 100644 index 00000000..d08417bc --- /dev/null +++ b/agent_telephony/vapi/agent_vapi/.env.Example @@ -0,0 +1,12 @@ + +RESTACK_API_KEY= +VAPI_TOKEN= + +# Restack Cloud (Optional) + +# RESTACK_ENGINE_ID= +# RESTACK_ENGINE_API_KEY= +# RESTACK_ENGINE_API_ADDRESS= +# RESTACK_ENGINE_ADDRESS= + + diff --git a/agent_telephony/vapi/agent_vapi/.gitignore b/agent_telephony/vapi/agent_vapi/.gitignore new file mode 100644 index 00000000..cf5930dc --- /dev/null +++ b/agent_telephony/vapi/agent_vapi/.gitignore @@ -0,0 +1,5 @@ +__pycache__ +.pytest_cache +venv +.env +.vscode diff --git a/agent_telephony/vapi/agent_vapi/agent_call.png b/agent_telephony/vapi/agent_vapi/agent_call.png new file mode 100644 index 00000000..0b63ca8f Binary files /dev/null and b/agent_telephony/vapi/agent_vapi/agent_call.png differ diff --git a/agent_telephony/vapi/agent_vapi/agent_replay.png b/agent_telephony/vapi/agent_vapi/agent_replay.png new file mode 100644 index 00000000..5debb8bb Binary files /dev/null and b/agent_telephony/vapi/agent_vapi/agent_replay.png differ diff --git a/agent_telephony/vapi/agent_vapi/event_agent.py b/agent_telephony/vapi/agent_vapi/event_agent.py new file mode 100644 index 00000000..53002603 --- /dev/null +++ b/agent_telephony/vapi/agent_vapi/event_agent.py @@ -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() diff --git a/agent_telephony/vapi/agent_vapi/pyproject.toml b/agent_telephony/vapi/agent_vapi/pyproject.toml new file mode 100644 index 00000000..4b66f8ab --- /dev/null +++ b/agent_telephony/vapi/agent_vapi/pyproject.toml @@ -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" diff --git a/agent_telephony/vapi/agent_vapi/readme.md b/agent_telephony/vapi/agent_vapi/readme.md new file mode 100644 index 00000000..fd55ed15 --- /dev/null +++ b/agent_telephony/vapi/agent_vapi/readme.md @@ -0,0 +1,176 @@ + +# Restack AI - Agent with telephony - Vapi + +Build an AI agent that do an outbound call with Vapi and can interact with in realtime with voice. + +## Prerequisites + +- Docker (for running Restack) +- Python 3.10 or higher +- Vapi account + +### Create a Vapi account + +See more on [Vapi documentation](https://docs.vapi.ai/quickstart/phone/outbound) + +## Start Restack + +To start the Restack, use the following Docker command: + +```bash +docker run -d --pull always --name restack -p 5233:5233 -p 6233:6233 -p 7233:7233 -p 9233:9233 ghcr.io/restackio/restack:main +``` + +## Configure environment variables + +In all subfolders, duplicate the `env.example` file and rename it to `.env`. + +Obtain a Restack API Key to interact with the 'gpt-4o-mini' model at no cost from [Restack Cloud](https://console.restack.io/starter) + + +## Start Restack Agent with Twilio + +### Start python shell + +If using uv: + +```bash +uv venv && source .venv/bin/activate +``` + +If using pip: + +```bash +python -m venv .venv && source .venv/bin/activate +``` + +### Install dependencies + +If using uv: + +```bash +uv sync +uv run dev +``` + +If using pip: + +```bash +pip install -e . +python -c "from src.services import watch_services; watch_services()" +``` + +## Start Livekit voice pipeline + +### Start python shell + +If using uv: + +```bash +uv venv && source .venv/bin/activate +``` + +If using pip: + +```bash +python -m venv .venv && source .venv/bin/activate +``` + +### Install dependencies + +If using uv: + +```bash +uv sync +uv run python src/pipeline.py dev +``` + +If using pip: + +```bash +pip install -e . +python src/pipeline.py dev +``` + +## Configure Your Environment Variables + +Duplicate the `env.example` file and rename it to `.env`. + +Obtain a Restack API Key to interact with the 'gpt-4o-mini' model at no cost from [Restack Cloud](https://console.restack.io/starter) + +## Create a new Agent + +### from UI + +Run the agent from the UI by clicking the "Run" button for the agent "`POST http://localhost:6233/api/agents/AgentVapi` +". + +### from API + +Run the agent from the API by using the generated endpoint: + +`POST http://localhost:6233/api/agents/AgentVapi` + +Restack agent will create a Livekit WebRTC room and will ask a livekit pipeline to connect to it, ready for a phone call. + +## Trigger an outbound phone call + +### from API + +You can send events to the agent by using the following endpoint: + +`PUT http://localhost:6233/api/agents/AgentVapi/:agentId/:runId` + +with the payload: + +```json +{ + "eventName": "call", + "eventInput": { + "assistant_id": "assistant_id", + "phone_number": "+XXXXXXX" + } +} +``` + +to trigger an outbound phone call. + +or + +```json +{ + "eventName": "end" +} +``` + +to end the conversation with the agent. + +### from any client + +You can send event to the agent workflows with any client connected to Restack, for example: + +Modify agent_id and run_id in event_agent.py and then run: + +If using uv: + +```bash +uv run event +``` + +If using pip: + +```bash +python -c "from src.event_agent import run_event_agent; run_event_agent()" +``` + +![Trigger outbound call](./agent_call.png) + +## Follow the agent run + +You can replay and follow the agent run in the UI. + +![Replay agent run](./agent_replay.png) + +## Deploy on Restack Cloud + +To deploy the application on Restack, you can create an account at [https://console.restack.io](https://console.restack.io) diff --git a/agent_telephony/vapi/agent_vapi/schedule_agent.py b/agent_telephony/vapi/agent_vapi/schedule_agent.py new file mode 100644 index 00000000..20fdcb35 --- /dev/null +++ b/agent_telephony/vapi/agent_vapi/schedule_agent.py @@ -0,0 +1,27 @@ +import asyncio +import sys +import time + +from restack_ai import Restack +from src.agents.agent import AgentStream + + +async def main() -> None: + client = Restack() + + agent_id = f"{int(time.time() * 1000)}-{AgentStream.__name__}" + run_id = await client.schedule_agent( + agent_name=AgentStream.__name__, agent_id=agent_id + ) + + await client.get_agent_result(agent_id=agent_id, run_id=run_id) + + sys.exit(0) + + +def run_schedule_agent() -> None: + asyncio.run(main()) + + +if __name__ == "__main__": + run_schedule_agent() diff --git a/agent_telephony/vapi/agent_vapi/src/__init__.py b/agent_telephony/vapi/agent_vapi/src/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/agent_telephony/vapi/agent_vapi/src/agents/__init__.py b/agent_telephony/vapi/agent_vapi/src/agents/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/agent_telephony/vapi/agent_vapi/src/agents/agent.py b/agent_telephony/vapi/agent_vapi/src/agents/agent.py new file mode 100644 index 00000000..c06ef655 --- /dev/null +++ b/agent_telephony/vapi/agent_vapi/src/agents/agent.py @@ -0,0 +1,65 @@ +from datetime import timedelta + +from pydantic import BaseModel +from restack_ai.agent import agent, import_functions, log + +with import_functions(): + from src.functions.llm_chat import LlmChatInput, Message, llm_chat + from src.functions.vapi_call import VapiCallInput, vapi_call + + +class MessagesEvent(BaseModel): + messages: list[Message] + + +class EndEvent(BaseModel): + end: bool + + +class CallInput(BaseModel): + assistant_id: str + phone_number: str + + +@agent.defn() +class AgentVapi: + def __init__(self) -> None: + self.end = False + self.messages: list[Message] = [] + + @agent.event + async def messages(self, messages_event: MessagesEvent) -> list[Message]: + log.info(f"Received message: {messages_event.messages}") + self.messages.extend(messages_event.messages) + + assistant_message = await agent.step( + function=llm_chat, + function_input=LlmChatInput(messages=self.messages), + start_to_close_timeout=timedelta(seconds=120), + ) + self.messages.append(Message(role="assistant", content=str(assistant_message))) + return self.messages + + @agent.event + async def call(self, call_input: CallInput) -> None: + log.info("Call", call_input=call_input) + assistant_id = call_input.assistant_id + phone_number = call_input.phone_number + + return await agent.step( + function=vapi_call, + function_input=VapiCallInput( + assistant_id=assistant_id, + phone_number=phone_number, + ), + ) + + @agent.event + async def end(self, end: EndEvent) -> EndEvent: + log.info("Received end") + self.end = True + return end + + @agent.run + async def run(self) -> None: + await agent.condition(lambda: self.end) diff --git a/agent_telephony/vapi/agent_vapi/src/client.py b/agent_telephony/vapi/agent_vapi/src/client.py new file mode 100644 index 00000000..885bf8ea --- /dev/null +++ b/agent_telephony/vapi/agent_vapi/src/client.py @@ -0,0 +1,19 @@ +import os + +from dotenv import load_dotenv +from restack_ai import Restack +from restack_ai.restack import CloudConnectionOptions + +# Load environment variables from a .env file +load_dotenv() + + +engine_id = os.getenv("RESTACK_ENGINE_ID") +address = os.getenv("RESTACK_ENGINE_ADDRESS") +api_key = os.getenv("RESTACK_ENGINE_API_KEY") +api_address = os.getenv("RESTACK_ENGINE_API_ADDRESS") + +connection_options = CloudConnectionOptions( + engine_id=engine_id, address=address, api_key=api_key, api_address=api_address +) +client = Restack(connection_options) diff --git a/agent_telephony/vapi/agent_vapi/src/functions/__init__.py b/agent_telephony/vapi/agent_vapi/src/functions/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/agent_telephony/vapi/agent_vapi/src/functions/llm_chat.py b/agent_telephony/vapi/agent_vapi/src/functions/llm_chat.py new file mode 100644 index 00000000..52f570c9 --- /dev/null +++ b/agent_telephony/vapi/agent_vapi/src/functions/llm_chat.py @@ -0,0 +1,52 @@ +import os +from typing import TYPE_CHECKING, Literal + +from openai import OpenAI +from pydantic import BaseModel, Field +from restack_ai.function import function, log, stream_to_websocket + +from src.client import api_address + +if TYPE_CHECKING: + from openai.resources.chat.completions import ChatCompletionChunk, Stream + + +class Message(BaseModel): + role: Literal["system", "user", "assistant"] + content: str + + +class LlmChatInput(BaseModel): + system_content: str | None = None + model: str | None = None + messages: list[Message] = Field(default_factory=list) + stream: bool = True + + +@function.defn() +async def llm_chat(function_input: LlmChatInput) -> str: + try: + client = OpenAI( + base_url="https://ai.restack.io", api_key=os.environ.get("RESTACK_API_KEY") + ) + + if function_input.system_content: + # Insert the system message at the beginning + function_input.messages.insert( + 0, Message(role="system", content=function_input.system_content) + ) + + # Convert Message objects to dictionaries + messages_dicts = [message.model_dump() for message in function_input.messages] + # Get the streamed response from OpenAI API + response: Stream[ChatCompletionChunk] = client.chat.completions.create( + model=function_input.model or "gpt-4o-mini", + messages=messages_dicts, + stream=True, + ) + + return await stream_to_websocket(api_address=api_address, data=response) + + except Exception as e: + log.error("llm_chat function failed", error=str(e)) + raise diff --git a/agent_telephony/vapi/agent_vapi/src/functions/vapi_call.py b/agent_telephony/vapi/agent_vapi/src/functions/vapi_call.py new file mode 100644 index 00000000..377d0a3b --- /dev/null +++ b/agent_telephony/vapi/agent_vapi/src/functions/vapi_call.py @@ -0,0 +1,33 @@ +import os +from dataclasses import dataclass + +from restack_ai.function import FunctionFailure, function, log +from vapi import AsyncVapi, Call + + +@dataclass +class VapiCallInput: + assistant_id: str + phone_number: str + + +@function.defn() +async def vapi_call(function_input: VapiCallInput) -> Call: + try: + client = AsyncVapi( + token=os.getenv("VAPI_TOKEN"), + ) + + call = await client.calls.create( + assistant_id=function_input.assistant_id, + phone_number_id=function_input.phone_number, + ) + + log.info("vapi_call: ", call=call) + + except Exception as e: + log.error("vapi_call function failed", error=str(e)) + failure_message = "vapi_call function failed: " + str(e) + raise FunctionFailure(failure_message, non_retryable=True) from e + else: + return call diff --git a/agent_telephony/vapi/agent_vapi/src/services.py b/agent_telephony/vapi/agent_vapi/src/services.py new file mode 100644 index 00000000..8ca0b99e --- /dev/null +++ b/agent_telephony/vapi/agent_vapi/src/services.py @@ -0,0 +1,36 @@ +import asyncio +import logging +import webbrowser +from pathlib import Path + +from watchfiles import run_process + +from src.agents.agent import AgentVapi +from src.client import client +from src.functions.llm_chat import llm_chat +from src.functions.vapi_call import vapi_call + + +async def main() -> None: + await client.start_service( + agents=[AgentVapi], + functions=[llm_chat, vapi_call], + ) + + +def run_services() -> None: + try: + asyncio.run(main()) + except KeyboardInterrupt: + logging.info("Service interrupted by user. Exiting gracefully.") + + +def watch_services() -> None: + watch_path = Path.cwd() + logging.info("Watching %s and its subdirectories for changes...", watch_path) + webbrowser.open("http://localhost:5233") + run_process(watch_path, recursive=True, target=run_services) + + +if __name__ == "__main__": + run_services() diff --git a/agent_todo/README.md b/agent_todo/README.md index db52834c..5415f084 100644 --- a/agent_todo/README.md +++ b/agent_todo/README.md @@ -1,6 +1,6 @@ -# Restack AI - Quickstart +# Restack AI - Agent with todo list -This repository contains a quickstart for Restack. +This repository contains an agent with todo list for Restack. It demonstrates how to set up a basic agent with tool call with a basic workflow and functions. ## Prerequisites diff --git a/agent_tool/README.md b/agent_tool/README.md index 05ee5ada..c956defb 100644 --- a/agent_tool/README.md +++ b/agent_tool/README.md @@ -1,7 +1,6 @@ -# Restack AI - Agent Tool +# Restack AI - Agent with tool calling -This repository contains a tool for an AI agent. -It demonstrates how to set up a basic agent with functions calling. +This repository demonstrates how to set up a basic agent with tool calling. Follow [the step-by-step tutorial](https://docs.restack.io/examples/projects/agent#tutorial) on how to customize the agent tool example/ diff --git a/agent_video/README.md b/agent_video/README.md index 6a80fe46..16efef96 100644 --- a/agent_video/README.md +++ b/agent_video/README.md @@ -1,4 +1,4 @@ -# Restack AI - Agent with Video +# Restack AI - Agent with video Build an AI agent that users can interact with in realtime on a video call. diff --git a/agent_video/src/functions/pipeline.py b/agent_video/src/functions/pipeline.py index f15948e2..fd2f2d3d 100644 --- a/agent_video/src/functions/pipeline.py +++ b/agent_video/src/functions/pipeline.py @@ -44,7 +44,10 @@ async def pipecat_pipeline(function_input: PipecatPipelineInput) -> str: else: agent_backend_host = engine_api_address - log.info("Using RESTACK_ENGINE_API_ADDRESS", agent_backend_host=agent_backend_host) + log.info( + "Using RESTACK_ENGINE_API_ADDRESS", + agent_backend_host=agent_backend_host, + ) agent_url = f"{agent_backend_host}/stream/agents/{function_input.agent_name}/{function_input.agent_id}/{function_input.agent_run_id}" log.info("Agent URL", agent_url=agent_url) @@ -150,7 +153,10 @@ async def run_pipeline(): try: await runner.run(task) except Exception as e: - log.error("Pipeline runner encountered an error, cancelling pipeline", error=e) + log.error( + "Pipeline runner encountered an error, cancelling pipeline", + error=e, + ) # Cancel the pipeline task if an error occurs within the pipeline runner. await task.cancel() raise e diff --git a/agent_video/src/workflows/room.py b/agent_video/src/workflows/room.py index 5c0959b2..56db9eb9 100644 --- a/agent_video/src/workflows/room.py +++ b/agent_video/src/workflows/room.py @@ -1,7 +1,13 @@ from datetime import timedelta from pydantic import BaseModel -from restack_ai.workflow import import_functions, log, workflow, workflow_info, ParentClosePolicy +from restack_ai.workflow import ( + ParentClosePolicy, + import_functions, + log, + workflow, + workflow_info, +) from src.agents.agent import AgentVideo @@ -22,7 +28,7 @@ async def run(self) -> RoomWorkflowOutput: agent=AgentVideo, agent_id=agent_id, start_to_close_timeout=timedelta(minutes=20), - parent_close_policy=ParentClosePolicy.ABANDON + parent_close_policy=ParentClosePolicy.ABANDON, ) log.info("Agent started", agent=agent) diff --git a/agent_voice/livekit/README.md b/agent_voice/livekit/README.md index 64cdfd71..0a9d2b8b 100644 --- a/agent_voice/livekit/README.md +++ b/agent_voice/livekit/README.md @@ -1,4 +1,4 @@ -# Restack AI - Agent with Voice +# Restack AI - Agent with voice Build an AI agent that users can interact with in realtime with voice. diff --git a/agent_voice/pipecat/pipecat_pipeline/README.md b/agent_voice/pipecat/pipecat_pipeline/README.md index f6e77f6d..4618db8d 100644 --- a/agent_voice/pipecat/pipecat_pipeline/README.md +++ b/agent_voice/pipecat/pipecat_pipeline/README.md @@ -1,2 +1,3 @@ +# Restack AI - Agent with voice -wip \ No newline at end of file +Build an AI agent that users can interact with in realtime with voice. \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 4a3fab14..5664315e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "examples-python" version = "0.0.1" -requires-python = ">=3.10" +requires-python = ">=3.10,<3.13" [dependency-groups] dev = [