-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add async extension in Python binding in order to use asyncio m…
…ore conveniently docs: fix local registry and names (#89) feat: enhance default_extension_go for template mode (#88) Co-authored-by: Hu Yueh-Wei <[email protected]> docs: add a tutorial for running model locally in a Python extension (#90) Co-authored-by: Hu Yueh-Wei <[email protected]> fix: rename test cases feat: upload runtime only, ignore others (#93) fix: no need to package src (#92) feat: add a python script for auto update package versions according … (#85) Co-authored-by: Hu Yueh-Wei <[email protected]> docs: updating ten_agent_server from astra_agents_dev (#95) * docs: updating ten_agent_server from astra_agents_dev Updating ten_agent_server from astra_agents_dev * docs: fixing typo Fixing typo fix: fix some integration test cases (#94) feat: add cargo config auto-gen (#51) Co-authored-by: Hu Yueh-Wei <[email protected]> chore: update version and publish to cloud store automatically (#97) Co-authored-by: Hu Yueh-Wei <[email protected]> fix: refine codes fix: update versions of dependencies fix: refine codes fix: add TenEnvAsync class for AsyncExtension fix: add more comments chore: improve and supplement Python test cases fix: refine naming fix: refine naming fix: refine codes fix: join thread in destructor of AsyncExtension fix: forked repo can not get version since tag is not synced (#99) doc: update summary part feat: add linux/arm64 (#98) Co-authored-by: Hu Yueh-Wei <[email protected]> chore: update to latest ten_gn chore: add more CI test cases (#136) fix: join thread before on_deinit_done to prevent from memory leak in Python binding fix: create a new Python thread to avoid blocking extension thread
- Loading branch information
Showing
62 changed files
with
2,327 additions
and
79 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
140 changes: 140 additions & 0 deletions
140
core/src/ten_runtime/binding/python/interface/ten/async_extension.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
# | ||
# Copyright © 2024 Agora | ||
# This file is part of TEN Framework, an open source project. | ||
# Licensed under the Apache License, Version 2.0, with certain conditions. | ||
# Refer to the "LICENSE" file in the root directory for more information. | ||
# | ||
import asyncio | ||
import threading | ||
from typing import final | ||
from libten_runtime_python import _Extension | ||
from .video_frame import VideoFrame | ||
from .audio_frame import AudioFrame | ||
from .ten_env import TenEnv | ||
from .cmd import Cmd | ||
from .data import Data | ||
from .async_ten_env import AsyncTenEnv | ||
|
||
|
||
class AsyncExtension(_Extension): | ||
def __init__(self, name: str) -> None: | ||
self._ten_stop_event = asyncio.Event() | ||
|
||
def __del__(self) -> None: | ||
self._ten_stop_event.set() | ||
if hasattr(self, "_ten_thread"): | ||
self._ten_thread.join() | ||
|
||
async def _thread_routine(self, ten_env: TenEnv): | ||
self._ten_loop = asyncio.get_running_loop() | ||
self._async_ten_env = AsyncTenEnv( | ||
ten_env, self._ten_loop, self._ten_thread | ||
) | ||
|
||
await self.on_configure(self._async_ten_env) | ||
|
||
# Suspend the thread until stopEvent is set. | ||
await self._ten_stop_event.wait() | ||
|
||
await self.on_deinit(self._async_ten_env) | ||
|
||
async def _stop_thread(self): | ||
self._ten_stop_event.set() | ||
|
||
@final | ||
def _proxy_on_configure(self, ten_env: TenEnv) -> None: | ||
# We pass the TenEnv object to another Python thread without worrying | ||
# about the thread safety issue of the TenEnv API, because the actual | ||
# execution logic of all TenEnv APIs occurs in the extension thread. | ||
# We only need to ensure that the TenEnv object should remain valid | ||
# while it is being used. The way to achieve this is to ensure that the | ||
# Python thread remains alive until TenEnv.on_deinit_done is called. | ||
self._ten_thread = threading.Thread( | ||
target=asyncio.run, args=(self._thread_routine(ten_env),) | ||
) | ||
self._ten_thread.start() | ||
|
||
@final | ||
def _proxy_on_init(self, ten_env: TenEnv) -> None: | ||
asyncio.run_coroutine_threadsafe( | ||
self.on_init(self._async_ten_env), self._ten_loop | ||
) | ||
|
||
@final | ||
def _proxy_on_start(self, ten_env: TenEnv) -> None: | ||
asyncio.run_coroutine_threadsafe( | ||
self.on_start(self._async_ten_env), self._ten_loop | ||
) | ||
|
||
@final | ||
def _proxy_on_stop(self, ten_env: TenEnv) -> None: | ||
asyncio.run_coroutine_threadsafe( | ||
self.on_stop(self._async_ten_env), self._ten_loop | ||
) | ||
|
||
@final | ||
def _proxy_on_deinit(self, ten_env: TenEnv) -> None: | ||
asyncio.run_coroutine_threadsafe(self._stop_thread(), self._ten_loop) | ||
|
||
@final | ||
def _proxy_on_cmd(self, ten_env: TenEnv, cmd: Cmd) -> None: | ||
asyncio.run_coroutine_threadsafe( | ||
self.on_cmd(self._async_ten_env, cmd), self._ten_loop | ||
) | ||
|
||
@final | ||
def _proxy_on_data(self, ten_env: TenEnv, data: Data) -> None: | ||
asyncio.run_coroutine_threadsafe( | ||
self.on_data(self._async_ten_env, data), self._ten_loop | ||
) | ||
|
||
@final | ||
def _proxy_on_video_frame( | ||
self, ten_env: TenEnv, video_frame: VideoFrame | ||
) -> None: | ||
asyncio.run_coroutine_threadsafe( | ||
self.on_video_frame(self._async_ten_env, video_frame), | ||
self._ten_loop, | ||
) | ||
|
||
@final | ||
def _proxy_on_audio_frame( | ||
self, ten_env: TenEnv, audio_frame: AudioFrame | ||
) -> None: | ||
asyncio.run_coroutine_threadsafe( | ||
self.on_audio_frame(self._async_ten_env, audio_frame), | ||
self._ten_loop, | ||
) | ||
|
||
# Override these methods in your extension | ||
|
||
async def on_configure(self, async_ten_env: AsyncTenEnv) -> None: | ||
async_ten_env.on_configure_done() | ||
|
||
async def on_init(self, async_ten_env: AsyncTenEnv) -> None: | ||
async_ten_env.on_init_done() | ||
|
||
async def on_start(self, async_ten_env: AsyncTenEnv) -> None: | ||
async_ten_env.on_start_done() | ||
|
||
async def on_stop(self, async_ten_env: AsyncTenEnv) -> None: | ||
async_ten_env.on_stop_done() | ||
|
||
async def on_deinit(self, async_ten_env: AsyncTenEnv) -> None: | ||
async_ten_env.on_deinit_done() | ||
|
||
async def on_cmd(self, async_ten_env: AsyncTenEnv, cmd: Cmd) -> None: | ||
pass | ||
|
||
async def on_data(self, async_ten_env: AsyncTenEnv, data: Data) -> None: | ||
pass | ||
|
||
async def on_video_frame( | ||
self, async_ten_env: AsyncTenEnv, video_frame: VideoFrame | ||
) -> None: | ||
pass | ||
|
||
async def on_audio_frame( | ||
self, async_ten_env: AsyncTenEnv, audio_frame: AudioFrame | ||
) -> None: | ||
pass |
58 changes: 58 additions & 0 deletions
58
core/src/ten_runtime/binding/python/interface/ten/async_ten_env.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
# | ||
# Copyright © 2024 Agora | ||
# This file is part of TEN Framework, an open source project. | ||
# Licensed under the Apache License, Version 2.0, with certain conditions. | ||
# Refer to the "LICENSE" file in the root directory for more information. | ||
# | ||
from asyncio import AbstractEventLoop | ||
import asyncio | ||
import threading | ||
from .cmd import Cmd | ||
from .cmd_result import CmdResult | ||
from .ten_env import TenEnv | ||
|
||
|
||
class AsyncTenEnv(TenEnv): | ||
|
||
def __init__( | ||
self, ten_env: TenEnv, loop: AbstractEventLoop, thread: threading.Thread | ||
) -> None: | ||
self._internal = ten_env._internal | ||
self._ten_loop = loop | ||
self._ten_thread = thread | ||
ten_env._set_release_handler(lambda: self._on_release()) | ||
|
||
def __del__(self) -> None: | ||
pass | ||
|
||
async def send_cmd(self, cmd: Cmd) -> CmdResult: | ||
q = asyncio.Queue(1) | ||
self._internal.send_cmd( | ||
cmd, | ||
lambda ten_env, result: asyncio.run_coroutine_threadsafe( | ||
q.put(result), self._ten_loop | ||
), # type: ignore | ||
) | ||
return await q.get() | ||
|
||
async def send_json(self, json_str: str) -> CmdResult: | ||
q = asyncio.Queue(1) | ||
self._internal.send_json( | ||
json_str, | ||
lambda ten_env, result: asyncio.run_coroutine_threadsafe( | ||
q.put(result), self._ten_loop | ||
), # type: ignore | ||
) | ||
return await q.get() | ||
|
||
def _deinit_routine(self) -> None: | ||
self._ten_thread.join() | ||
self._internal.on_deinit_done() | ||
|
||
def _on_release(self) -> None: | ||
if hasattr(self, "_deinit_thread"): | ||
self._deinit_thread.join() | ||
|
||
def on_deinit_done(self) -> None: | ||
self._deinit_thread = threading.Thread(target=self._deinit_routine) | ||
self._deinit_thread.start() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.