Skip to content

Commit aaa98d8

Browse files
authored
Merge pull request #182 from restackio/agent-examples-update
Update agent examples
2 parents 532bc13 + dd138e0 commit aaa98d8

38 files changed

+480
-331
lines changed

agent_apis/pyproject.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ requires-python = ">=3.10,<3.13"
77
readme = "README.md"
88
dependencies = [
99
"pydantic>=2.10.6",
10-
"restack-ai==0.0.62",
1110
"watchfiles>=1.0.4",
1211
"python-dotenv==1.0.1",
1312
"openai>=1.61.0",
1413
"aiohttp>=3.11.12",
14+
"restack-ai>=0.0.63",
1515
]
1616

1717
[project.scripts]
@@ -22,6 +22,12 @@ schedule = "schedule_workflow:run_schedule_workflow"
2222
[tool.hatch.build.targets.sdist]
2323
include = ["src"]
2424

25+
[tool.hatch.metadata]
26+
allow-direct-references = true
27+
28+
[tool.uv.sources]
29+
restack-ai = { path = "../../../sdk/engine/libraries/python/dist/restack_ai-0.0.63-py3-none-any.whl" }
30+
2531
[tool.hatch.build.targets.wheel]
2632
include = ["src"]
2733

agent_apis/schedule_workflow.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,34 @@
11
import asyncio
2+
import sys
23
import time
3-
from restack_ai import Restack
44
from dataclasses import dataclass
55

6+
from restack_ai import Restack
7+
8+
69
@dataclass
710
class InputParams:
811
name: str
912

10-
async def main():
13+
14+
async def main() -> None:
1115
client = Restack()
1216

1317
workflow_id = f"{int(time.time() * 1000)}-MultistepWorkflow"
14-
runId = await client.schedule_workflow(
18+
run_id = await client.schedule_workflow(
1519
workflow_name="MultistepWorkflow",
1620
workflow_id=workflow_id,
17-
input=InputParams(name="Restack AI SDK User")
21+
workflow_input=InputParams(name="Restack AI SDK User"),
1822
)
1923

20-
await client.get_workflow_result(
21-
workflow_id=workflow_id,
22-
run_id=runId
23-
)
24+
await client.get_workflow_result(workflow_id=workflow_id, run_id=run_id)
2425

25-
exit(0)
26+
sys.exit(0)
2627

27-
def run_schedule_workflow():
28+
29+
def run_schedule_workflow() -> None:
2830
asyncio.run(main())
2931

32+
3033
if __name__ == "__main__":
31-
run_schedule_workflow()
34+
run_schedule_workflow()

agent_apis/src/client.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import os
2+
3+
from dotenv import load_dotenv
24
from restack_ai import Restack
35
from restack_ai.restack import CloudConnectionOptions
4-
from dotenv import load_dotenv
6+
57
# Load environment variables from a .env file
68
load_dotenv()
79

@@ -12,9 +14,6 @@
1214
api_address = os.getenv("RESTACK_ENGINE_API_ADDRESS")
1315

1416
connection_options = CloudConnectionOptions(
15-
engine_id=engine_id,
16-
address=address,
17-
api_key=api_key,
18-
api_address=api_address
17+
engine_id=engine_id, address=address, api_key=api_key, api_address=api_address
1918
)
20-
client = Restack(connection_options)
19+
client = Restack(connection_options)

agent_apis/src/functions/llm.py

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,50 @@
1-
from restack_ai.function import function, log, FunctionFailure
2-
from openai import OpenAI
3-
from dataclasses import dataclass
41
import os
2+
from dataclasses import dataclass
3+
54
from dotenv import load_dotenv
5+
from openai import OpenAI
6+
from restack_ai.function import FunctionFailure, function, log
67

78
load_dotenv()
89

10+
911
@dataclass
1012
class FunctionInputParams:
1113
user_content: str
1214
system_content: str | None = None
1315
model: str | None = None
1416

17+
18+
def raise_exception(message: str) -> None:
19+
log.error(message)
20+
raise Exception(message)
21+
22+
1523
@function.defn()
16-
async def llm(input: FunctionInputParams) -> str:
24+
async def llm(function_input: FunctionInputParams) -> str:
1725
try:
18-
log.info("llm function started", input=input)
26+
log.info("llm function started", input=function_input)
1927

20-
if (os.environ.get("RESTACK_API_KEY") is None):
21-
raise FunctionFailure("RESTACK_API_KEY is not set", non_retryable=True)
22-
23-
client = OpenAI(base_url="https://ai.restack.io", api_key=os.environ.get("RESTACK_API_KEY"))
28+
if os.environ.get("RESTACK_API_KEY") is None:
29+
error_message = "RESTACK_API_KEY is not set"
30+
raise_exception(error_message)
31+
32+
client = OpenAI(
33+
base_url="https://ai.restack.io", api_key=os.environ.get("RESTACK_API_KEY")
34+
)
2435

2536
messages = []
26-
if input.system_content:
27-
messages.append({"role": "system", "content": input.system_content})
28-
messages.append({"role": "user", "content": input.user_content})
37+
if function_input.system_content:
38+
messages.append(
39+
{"role": "system", "content": function_input.system_content}
40+
)
41+
messages.append({"role": "user", "content": function_input.user_content})
2942

3043
response = client.chat.completions.create(
31-
model=input.model or "gpt-4o-mini",
32-
messages=messages
44+
model=function_input.model or "gpt-4o-mini", messages=messages
3345
)
3446
log.info("llm function completed", response=response)
3547
return response.choices[0].message.content
3648
except Exception as e:
37-
log.error("llm function failed", error=e)
38-
raise e
49+
error_message = "llm function failed"
50+
raise FunctionFailure(error_message, non_retryable=True) from e
Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
1-
from restack_ai.function import function, log
21
import aiohttp
2+
from restack_ai.function import function, log
3+
4+
HTTP_OK = 200
5+
6+
7+
def raise_exception(message: str) -> None:
8+
log.error(message)
9+
raise Exception(message)
10+
311

412
@function.defn()
513
async def weather() -> str:
6-
url = f"https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41&current=temperature_2m,wind_speed_10m&hourly=temperature_2m,relative_humidity_2m,wind_speed_10m"
14+
url = "https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41&current=temperature_2m,wind_speed_10m&hourly=temperature_2m,relative_humidity_2m,wind_speed_10m"
715
try:
8-
async with aiohttp.ClientSession() as session:
9-
async with session.get(url) as response:
10-
log.info("response", response=response)
11-
if response.status == 200:
12-
data = await response.json()
13-
log.info("weather data", data=data)
14-
return str(data)
15-
else:
16-
log.error("Error: {response}")
17-
raise Exception(f"Error: {response.status}")
18-
except Exception as e:
16+
async with aiohttp.ClientSession() as session, session.get(url) as response:
17+
log.info("response", response=response)
18+
if response.status == HTTP_OK:
19+
data = await response.json()
20+
log.info("weather data", data=data)
21+
return str(data)
22+
error_message = f"Error: {response.status}"
23+
raise_exception(error_message)
24+
except Exception:
1925
log.error("Error: {e}")
20-
raise e
21-
26+
raise

agent_apis/src/services.py

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,36 @@
11
import asyncio
2-
import os
2+
import logging
3+
import webbrowser
4+
from pathlib import Path
5+
6+
from watchfiles import run_process
7+
8+
from src.client import client
39
from src.functions.llm import llm
410
from src.functions.weather import weather
5-
from src.client import client
611
from src.workflows.multistep import MultistepWorkflow
7-
from watchfiles import run_process
8-
import webbrowser
912

10-
async def main():
1113

14+
async def main() -> None:
1215
await client.start_service(
13-
workflows= [MultistepWorkflow],
14-
functions= [llm, weather],
16+
workflows=[MultistepWorkflow],
17+
functions=[llm, weather],
1518
)
1619

17-
def run_services():
20+
21+
def run_services() -> None:
1822
try:
1923
asyncio.run(main())
2024
except KeyboardInterrupt:
21-
print("Service interrupted by user. Exiting gracefully.")
25+
logging.info("Service interrupted by user. Exiting gracefully.")
2226

23-
def watch_services():
24-
watch_path = os.getcwd()
25-
print(f"Watching {watch_path} and its subdirectories for changes...")
27+
28+
def watch_services() -> None:
29+
watch_path = Path.cwd()
30+
logging.info("Watching %s and its subdirectories for changes...", watch_path)
2631
webbrowser.open("http://localhost:5233")
2732
run_process(watch_path, recursive=True, target=run_services)
2833

34+
2935
if __name__ == "__main__":
30-
run_services()
36+
run_services()
Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,39 @@
1-
from pydantic import BaseModel, Field
2-
from restack_ai.workflow import workflow, import_functions, log
31
from datetime import timedelta
42

3+
from pydantic import BaseModel, Field
4+
from restack_ai.workflow import import_functions, log, workflow
5+
56
with import_functions():
6-
from src.functions.llm import llm, FunctionInputParams
7+
from src.functions.llm import FunctionInputParams, llm
78
from src.functions.weather import weather
89

9-
class WorkflowInputParams ( BaseModel):
10+
11+
class WorkflowInputParams(BaseModel):
1012
name: str = Field(default="John Doe")
1113

14+
1215
@workflow.defn()
1316
class MultistepWorkflow:
1417
@workflow.run
15-
async def run(self, input: WorkflowInputParams):
16-
log.info("MultistepWorkflow started", input=input)
17-
user_content = f"Greet this person {input.name}"
18+
async def run(self, workflow_input: WorkflowInputParams) -> dict:
19+
log.info("MultistepWorkflow started", workflow_input=workflow_input)
20+
user_content = f"Greet this person {workflow_input.name}"
1821

1922
# Step 1 get weather data
2023
weather_data = await workflow.step(
21-
weather,
22-
start_to_close_timeout=timedelta(seconds=120)
24+
function=weather, start_to_close_timeout=timedelta(seconds=120)
2325
)
2426

2527
# Step 2 Generate greeting with LLM based on name and weather data
2628

2729
llm_message = await workflow.step(
28-
llm,
29-
FunctionInputParams(
30+
function=llm,
31+
workflow_input=FunctionInputParams(
3032
system_content=f"You are a personal assitant and have access to weather data {weather_data}. Always greet person with relevant info from weather data",
3133
user_content=user_content,
32-
model="gpt-4o-mini"
34+
model="gpt-4o-mini",
3335
),
34-
start_to_close_timeout=timedelta(seconds=120)
36+
start_to_close_timeout=timedelta(seconds=120),
3537
)
3638
log.info("MultistepWorkflow completed", llm_message=llm_message)
37-
return {
38-
"message": llm_message,
39-
"weather": weather_data
40-
}
39+
return {"message": llm_message, "weather": weather_data}

agent_chat/event_agent.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import asyncio
2+
import sys
3+
24
from restack_ai import Restack
35

46

5-
async def main(agent_id: str, run_id: str):
7+
async def main(agent_id: str, run_id: str) -> None:
68
client = Restack()
79

810
await client.send_agent_event(
@@ -18,11 +20,16 @@ async def main(agent_id: str, run_id: str):
1820
event_name="end",
1921
)
2022

21-
exit(0)
23+
sys.exit(0)
2224

2325

24-
def run_event_agent():
25-
asyncio.run(main(agent_id="your-agent-id", run_id="your-run-id"))
26+
def run_event_agent() -> None:
27+
asyncio.run(
28+
main(
29+
agent_id="1739788461173-AgentChat",
30+
run_id="c3937cc9-8d88-4e37-85e1-59e78cf1bf60",
31+
)
32+
)
2633

2734

2835
if __name__ == "__main__":

agent_chat/pyproject.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ dependencies = [
1010
"watchfiles>=1.0.4",
1111
"python-dotenv==1.0.1",
1212
"openai>=1.61.0",
13-
"restack-ai>=0.0.62",
13+
"restack-ai>=0.0.63",
1414
]
1515

1616
[project.scripts]
@@ -25,6 +25,12 @@ include = ["src"]
2525
[tool.hatch.build.targets.wheel]
2626
include = ["src"]
2727

28+
[tool.hatch.metadata]
29+
allow-direct-references = true
30+
31+
[tool.uv.sources]
32+
restack-ai = { path = "../../../sdk/engine/libraries/python/dist/restack_ai-0.0.63-py3-none-any.whl" }
33+
2834
[build-system]
2935
requires = ["hatchling"]
3036
build-backend = "hatchling.build"

agent_chat/schedule_agent.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
import asyncio
2+
import sys
23
import time
4+
35
from restack_ai import Restack
46

57

6-
async def main():
8+
async def main() -> None:
79
client = Restack()
810

911
agent_id = f"{int(time.time() * 1000)}-AgentChat"
10-
await client.schedule_agent(
11-
agent_name="AgentChat",
12-
agent_id=agent_id
13-
)
12+
await client.schedule_agent(agent_name="AgentChat", agent_id=agent_id)
1413

15-
exit(0)
14+
sys.exit(0)
1615

1716

18-
def run_schedule_agent():
17+
def run_schedule_agent() -> None:
1918
asyncio.run(main())
2019

2120

0 commit comments

Comments
 (0)