Skip to content

Commit 69a5b7a

Browse files
Merge pull request #137 from restackio/audioEndpoint
Update audio translation example
2 parents abf227f + 1474c4e commit 69a5b7a

10 files changed

+143
-129
lines changed

audio_transcript/README.md

+40-46
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,65 @@
1-
# Restack AI - Audio transcript and translation example
2-
3-
This example showcases how to transcribe an mp3 audio and then later translate the generated text to a target language, all done in a single workflow defined with Restack AI.
1+
# Restack AI - Audio translation example
42

3+
This example showcases how to transcribe an mp3 audio and then translate the generated text to a target language, all done in a single workflow defined with Restack AI.
54

65
## Prerequisites
76

7+
- Docker (for running Restack)
88
- Python 3.10 or higher
99
- Poetry (for dependency management)
10-
- Docker (for running the Restack services)
1110

12-
## Usage
11+
## Start Restack
12+
13+
To start the Restack, use the following Docker command:
14+
15+
```bash
16+
docker run -d --pull always --name restack -p 5233:5233 -p 6233:6233 -p 7233:7233 ghcr.io/restackio/restack:main
17+
```
18+
19+
## Start python shell
1320

14-
1. Run Restack local engine with Docker:
21+
```bash
22+
poetry env use 3.10 && poetry shell
23+
```
1524

16-
```bash
17-
docker run -d --pull always --name restack -p 5233:5233 -p 6233:6233 -p 7233:7233 ghcr.io/restackio/restack:main
18-
```
25+
## Install dependencies
1926

20-
2. Open the web UI to see the workflows:
27+
```bash
28+
poetry install
29+
```
2130

22-
```bash
23-
http://localhost:5233
24-
```
31+
```bash
32+
poetry env info # Optional: copy the interpreter path to use in your IDE (e.g. Cursor, VSCode, etc.)
33+
```
2534

26-
3. Clone this repository:
35+
```bash
36+
poetry run dev
37+
```
2738

28-
```bash
29-
git clone https://github.com/restackio/examples-python
30-
cd examples-python/examples/get-started
31-
```
32-
33-
4. Create .env file with: STRIPE_SECRET_KEY and OPENAI_API_KEY
39+
## Run workflows
3440

35-
4. Install dependencies using Poetry:
41+
### from UI
3642

37-
```bash
38-
poetry env use 3.12
39-
```
43+
You can run workflows from the UI by clicking the "Run" button.
4044

41-
```bash
42-
poetry shell
43-
```
45+
![Run workflows from UI](./ui-screenshot.png)
4446

45-
```bash
46-
poetry install
47-
```
47+
### from API
4848

49-
```bash
50-
poetry env info # Optional: copy the interpreter path to use in your IDE (e.g. Cursor, VSCode, etc.)
51-
```
49+
You can run workflows from the API by using the generated endpoint:
5250

53-
5. Run the services:
51+
`POST http://localhost:6233/api/workflows/TranscribeTranslateWorkflow`
5452

55-
```bash
56-
poetry run services
57-
```
53+
### from any client
5854

59-
This will start the Restack service with the defined workflows and functions.
55+
You can run workflows with any client connected to Restack, for example:
6056

61-
6. In a new terminal, schedule the workflow:
57+
```bash
58+
poetry run schedule
59+
```
6260

63-
```bash
64-
poetry shell
65-
```
61+
executes `schedule_workflow.py` which will connect to Restack and execute the `TranscribeTranslateWorkflow` workflow.
6662

67-
```bash
68-
poetry run schedule
69-
```
63+
## Deploy on Restack Cloud
7064

71-
This will schedule the `TranscribeTranslateWorkflow` and print the result.
65+
To deploy the application on Restack, you can create an account at [https://console.restack.io](https://console.restack.io)

audio_transcript/pyproject.toml

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[tool.poetry]
22
name = "audio_transcript"
33
version = "0.0.1"
4-
description = "Send emails with sendgrid"
4+
description = "Transcribe audio with OpenAI Whisper and translate the text with OpenAI GPT-4o-mini"
55
authors = [
66
"Restack Team <[email protected]>",
77
]
@@ -12,14 +12,16 @@ packages = [{include = "src"}]
1212
python = ">=3.10,<4.0"
1313
pydantic = "^2.10.3"
1414
python-dotenv = "1.0.1"
15-
openai = "^1.57.2"
16-
restack-ai = "^0.0.48"
15+
restack-ai = "^0.0.53"
16+
openai = "^1.59.8"
17+
watchfiles = "^1.0.4"
1718

1819
[build-system]
1920
requires = ["poetry-core"]
2021
build-backend = "poetry.core.masonry.api"
2122

2223
[tool.poetry.scripts]
24+
dev = "src.services:watch_services"
2325
services = "src.services:run_services"
2426
schedule = "schedule_workflow:run_schedule_workflow"
2527
schedule_failure = "schedule_workflow_failure:run_schedule_workflow_failure"

audio_transcript/schedule_workflow.py

+7-13
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,23 @@
11
import asyncio
22
import time
33
from restack_ai import Restack
4-
from dataclasses import dataclass
5-
64
from dotenv import load_dotenv
5+
from src.workflows.transcribe_translate import WorkflowInputParams
76

87
load_dotenv()
98

10-
@dataclass
11-
class InputParams:
12-
file_path: str
13-
target_language: str
14-
15-
async def main(input: InputParams):
9+
async def main(input: WorkflowInputParams):
1610
client = Restack()
1711

1812
workflow_id = f"{int(time.time() * 1000)}-TranscribeTranslateWorkflow"
1913

2014
run_id = await client.schedule_workflow(
2115
workflow_name="TranscribeTranslateWorkflow",
2216
workflow_id=workflow_id,
23-
input={
24-
"file_path": input.file_path,
25-
"target_language": input.target_language
26-
}
17+
input=WorkflowInputParams(
18+
file_path=input.file_path,
19+
target_language=input.target_language
20+
)
2721
)
2822

2923
await client.get_workflow_result(
@@ -34,7 +28,7 @@ async def main(input: InputParams):
3428
exit(0)
3529

3630
def run_schedule_workflow():
37-
asyncio.run(main(InputParams(file_path="./test.mp3", target_language="Spanish")))
31+
asyncio.run(main(WorkflowInputParams(file_path="./test.mp3", target_language="Spanish")))
3832

3933
if __name__ == "__main__":
4034
run_schedule_workflow()

audio_transcript/src/functions/transcribe_audio.py

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from restack_ai.function import function, FunctionFailure
1+
from restack_ai.function import function, FunctionFailure, log
22
from dataclasses import dataclass
33
from openai import OpenAI
44
import os
@@ -17,10 +17,13 @@ async def transcribe_audio(input: TranscribeAudioInput):
1717

1818
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
1919

20-
response = client.audio.transcriptions.create(
21-
model="whisper-1",
22-
file=open(input.file_path, "rb")
23-
)
20+
try:
21+
response = client.audio.transcriptions.create(
22+
model="whisper-1",
23+
file=open(input.file_path, "rb")
24+
)
25+
except Exception as error:
26+
log.error("An error occurred during transcription", error)
2427

2528
return response.text
2629

audio_transcript/src/functions/translate_text.py

+16-13
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,22 @@ async def translate_text(input: TranslateTextInput):
1818

1919
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
2020

21-
response = client.chat.completions.create(
22-
model="gpt-4o-mini",
23-
messages=[
24-
{
25-
"role": "system",
26-
"content": "You are a helpful assistant that translates text from one language to another."
27-
},
28-
{
29-
"role": "user",
30-
"content": f"Translate the following text to {input.target_language}: {input.text}"
31-
}
32-
]
33-
)
21+
try:
22+
response = client.chat.completions.create(
23+
model="gpt-4o-mini",
24+
messages=[
25+
{
26+
"role": "system",
27+
"content": "You are a helpful assistant that translates text from one language to another."
28+
},
29+
{
30+
"role": "user",
31+
"content": f"Translate the following text to {input.target_language}: {input.text}"
32+
}
33+
]
34+
)
35+
except Exception as error:
36+
log.error("An error occurred during translation", error)
3437

3538
return response.choices[0].message.content
3639

audio_transcript/src/services.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
from src.workflows.transcribe_translate import TranscribeTranslateWorkflow
44
from src.functions.transcribe_audio import transcribe_audio
55
from src.functions.translate_text import translate_text
6+
from watchfiles import run_process
7+
import webbrowser
8+
import os
69

710
async def main():
811
await asyncio.gather(
@@ -13,7 +16,16 @@ async def main():
1316
)
1417

1518
def run_services():
16-
asyncio.run(main())
19+
try:
20+
asyncio.run(main())
21+
except KeyboardInterrupt:
22+
print("Service interrupted by user. Exiting gracefully.")
23+
24+
def watch_services():
25+
watch_path = os.getcwd()
26+
print(f"Watching {watch_path} and its subdirectories for changes...")
27+
webbrowser.open("http://localhost:5233")
28+
run_process(watch_path, recursive=True, target=run_services)
1729

1830
if __name__ == "__main__":
19-
run_services()
31+
run_services()

audio_transcript/src/workflows/transcribe_translate.py

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
from restack_ai.workflow import workflow, import_functions, log
2-
from dataclasses import dataclass
3-
2+
from pydantic import BaseModel, Field
43
with import_functions():
54
from src.functions.transcribe_audio import transcribe_audio, TranscribeAudioInput
65
from src.functions.translate_text import translate_text, TranslateTextInput
76

8-
@dataclass
9-
class WorkflowInputParams:
10-
file_path: str
11-
target_language: str
7+
class WorkflowInputParams(BaseModel):
8+
file_path: str = Field(default="/test.mp3")
9+
target_language: str = Field(default="fr")
1210

1311
@workflow.defn()
1412
class TranscribeTranslateWorkflow:

audio_transcript/ui-screenshot.png

173 KB
Loading

copilot.sh

-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ A Restack backend application should be structured as follows:
2626
- env.example
2727
- README.md
2828
- Dockerfile
29-
- restack_up.py
3029
3130
All these files are mandatory.
3231

0 commit comments

Comments
 (0)