Skip to content

Commit f1be8b8

Browse files
Merge pull request #101 from restackio/agentApis
Agent apis
2 parents 381777e + 7bb00a0 commit f1be8b8

File tree

14 files changed

+285
-0
lines changed

14 files changed

+285
-0
lines changed

agent-apis/.env.example

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
OPENAI_API_KEY=
2+
3+
# Restack
4+
5+
RESTACK_ENGINE_ID=
6+
RESTACK_ENGINE_ADDRESS=
7+
RESTACK_ENGINE_API_KEY=
8+
9+
RESTACK_CLOUD_TOKEN=

agent-apis/package.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"name": "ts-example-openai",
3+
"version": "1.0.0",
4+
"description": "Basic OpenAI example with Restack TypeScript library",
5+
"main": "index.js",
6+
"scripts": {
7+
"start": "ts-node src/services.ts",
8+
"start.watch": "nodemon src/services.ts",
9+
"dev": "npm run start.watch",
10+
"build": "tsc --build",
11+
"clean": "rm -rf node_modules",
12+
"schedule": "ts-node ./scheduleWorkflow.ts"
13+
},
14+
"nodemonConfig": {
15+
"execMap": {
16+
"ts": "ts-node"
17+
},
18+
"ext": "ts",
19+
"watch": [
20+
"src"
21+
]
22+
},
23+
"dependencies": {
24+
"@restackio/ai": "^0.0.104",
25+
"@temporalio/workflow": "1.11.6",
26+
"openai": "^4.80.1"
27+
},
28+
"devDependencies": {
29+
"@types/node": "^20.16.9",
30+
"nodemon": "^2.0.22",
31+
"ts-node": "^10.9.2"
32+
}
33+
}

agent-apis/readme.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Restack AI - Agent with Third Party APIs
2+
3+
This repository contains an agent with Third Party APIs
4+
It demonstrates how to set up a multi steps workflow with Weather API and OpenAI.
5+
6+
## Prerequisites
7+
8+
- **Node 20+**
9+
10+
## Start Restack
11+
12+
To start the Restack, use the following Docker command:
13+
14+
```bash
15+
docker run -d --pull always --name restack -p 5233:5233 -p 6233:6233 -p 7233:7233 ghcr.io/restackio/restack:main
16+
```
17+
18+
## Install dependencies and start services
19+
20+
```bash
21+
npm install
22+
npm run dev
23+
```
24+
25+
## Run agent
26+
27+
### from UI
28+
29+
You can run workflows from the UI by clicking the "Run" button.
30+
31+
![Run workflows from UI](./workflow_get.png)
32+
33+
### from API
34+
35+
You can run workflows from the API by using the generated endpoint:
36+
37+
`POST http://localhost:6233/api/workflows/multistepWorkflow`
38+
39+
### from any client
40+
41+
You can run workflows with any client connected to Restack, for example:
42+
43+
If using uv:
44+
45+
```bash
46+
uv run schedule-workflow
47+
```
48+
49+
If using pip:
50+
51+
```bash
52+
python -c "from src.schedule_workflow import run_schedule_workflow; run_schedule_workflow()"
53+
```
54+
55+
executes `schedule_workflow.py` which will connect to Restack and execute the `multistepWorkflow` workflow.
56+
57+
## See run results
58+
59+
### from UI
60+
61+
You can see the run results in the UI.
62+
63+
![See run results from UI](./workflow_run.png)
64+
65+
## Deploy on Restack Cloud
66+
67+
To deploy the application on Restack, you can create an account at [https://console.restack.io](https://console.restack.io)

agent-apis/scheduleWorkflow.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { client } from "./src/client";
2+
3+
export type InputSchedule = {
4+
name: string;
5+
};
6+
7+
async function scheduleWorkflow(input: InputSchedule) {
8+
try {
9+
const workflowId = `${Date.now()}-multistepWorkflow`;
10+
const runId = await client.scheduleWorkflow({
11+
workflowName: "multistepWorkflow",
12+
workflowId,
13+
input,
14+
});
15+
16+
const result = await client.getWorkflowResult({ workflowId, runId });
17+
18+
console.log("Workflow result:", result);
19+
20+
process.exit(0); // Exit the process successfully
21+
} catch (error) {
22+
console.error("Error scheduling workflow:", error);
23+
process.exit(1); // Exit the process with an error code
24+
}
25+
}
26+
27+
scheduleWorkflow({
28+
name: "test",
29+
});

agent-apis/src/client.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import Restack from "@restackio/ai";
2+
3+
import "dotenv/config";
4+
5+
export const connectionOptions = {
6+
engineId: process.env.RESTACK_ENGINE_ID!,
7+
address: process.env.RESTACK_ENGINE_ADDRESS!,
8+
apiKey: process.env.RESTACK_ENGINE_API_KEY!,
9+
apiAddress: process.env.RESTACK_ENGINE_API_ADDRESS!,
10+
};
11+
12+
export const client = new Restack(
13+
process.env.RESTACK_ENGINE_API_KEY ? connectionOptions : undefined
14+
);

agent-apis/src/functions/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from "./llm";
2+
export * from "./weather";

agent-apis/src/functions/llm.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { FunctionFailure, log } from "@restackio/ai/function";
2+
import OpenAI from "openai/index";
3+
import { ChatCompletionCreateParamsNonStreaming } from "openai/resources/chat/completions";
4+
import { ChatModel } from "openai/resources/index";
5+
6+
export type UsageOutput = { tokens: number; cost: number };
7+
8+
export type OpenAIChatInput = {
9+
userContent: string;
10+
systemContent?: string;
11+
model?: ChatModel;
12+
};
13+
14+
export const llm = async ({
15+
userContent,
16+
systemContent = "",
17+
model = "gpt-4o-mini",
18+
}: OpenAIChatInput): Promise<string> => {
19+
try {
20+
const openai = new OpenAI({
21+
baseURL: "https://ai.restack.io",
22+
apiKey: process.env.RESTACK_API_KEY,
23+
});
24+
25+
const chatParams: ChatCompletionCreateParamsNonStreaming = {
26+
messages: [
27+
...(systemContent
28+
? [{ role: "system" as const, content: systemContent }]
29+
: []),
30+
{ role: "user" as const, content: userContent },
31+
],
32+
33+
model,
34+
};
35+
36+
log.debug("OpenAI chat completion params", {
37+
chatParams,
38+
});
39+
40+
const completion = await openai.chat.completions.create(chatParams);
41+
42+
return completion.choices[0].message.content ?? "";
43+
} catch (error) {
44+
throw FunctionFailure.nonRetryable(`Error OpenAI chat: ${error}`);
45+
}
46+
};
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { FunctionFailure, log } from "@restackio/ai/function";
2+
3+
export const weather = async (): Promise<string> => {
4+
const url =
5+
"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";
6+
7+
try {
8+
const response = await fetch(url);
9+
log.info("response", { response });
10+
11+
if (response.ok) {
12+
const data = await response.json();
13+
log.info("weather data", { data });
14+
return JSON.stringify(data);
15+
} else {
16+
log.error(`Error: ${response.status}`);
17+
throw new Error(`Error: ${response.status}`);
18+
}
19+
} catch (error) {
20+
log.error(`Error: ${error}`);
21+
throw FunctionFailure.nonRetryable(`Error fetching weather data: ${error}`);
22+
}
23+
};

agent-apis/src/services.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { llm, weather } from "./functions";
2+
import { client } from "./client";
3+
4+
async function services() {
5+
const workflowsPath = require.resolve("./workflows");
6+
try {
7+
await Promise.all([
8+
client.startService({
9+
workflowsPath,
10+
functions: { llm, weather },
11+
}),
12+
]);
13+
14+
console.log("Services running successfully.");
15+
} catch (e) {
16+
console.error("Failed to run services", e);
17+
}
18+
}
19+
20+
services().catch((err) => {
21+
console.error("Error running services:", err);
22+
});

agent-apis/src/workflows/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./multistep";

0 commit comments

Comments
 (0)