Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a new Anam Avatar plugin package and workspace entry, implements AnamAvatarPublisher to stream agent audio/video to Anam, includes docs, an example CLI agent, tests, and exposes the plugin via optional extras. (50 words) Changes
Sequence Diagram(s)sequenceDiagram
participant Agent as Agent
participant Publisher as AnamAvatarPublisher
participant Anam as AnamClient
participant Tracks as Audio/Video Tracks
Agent->>Publisher: attach_agent()
Publisher->>Publisher: subscribe to TTS & realtime audio events
Publisher->>Anam: connect() / create session
Anam-->>Publisher: connection_established / session_ready
Publisher->>Tracks: start receiver tasks
Agent->>Publisher: emit TTS/realtime audio chunk
Publisher->>Publisher: resample to 24kHz mono (serialize)
Publisher->>Anam: send_audio_input_chunk()
Anam-->>Tracks: video_frames() / audio_frames()
Tracks->>Agent: published frames delivered
Agent->>Publisher: other participant turn starts
Publisher->>Anam: interrupt()
Agent->>Publisher: finish()
Publisher->>Anam: close()
Publisher->>Tracks: stop & cancel tasks
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (5)
plugins/anam/vision_agents/plugins/anam/anam_avatar_publisher.py (3)
218-221: Background tasks lack exception handling callbacks.If
_audio_receiveror_video_receiverraises an unhandled exception, it will be silently lost. Consider adding a done callback to log exceptions, similar to patterns used elsewhere in the codebase.♻️ Suggested improvement
+ def _task_done_callback(self, task: asyncio.Task) -> None: + if not task.cancelled() and task.exception() is not None: + logger.exception( + "Background task failed", exc_info=task.exception() + ) + async def _connect(self) -> None: # ... existing code ... if self._audio_receiver_task is None: self._audio_receiver_task = asyncio.create_task(self._audio_receiver()) + self._audio_receiver_task.add_done_callback(self._task_done_callback) if self._video_receiver_task is None: self._video_receiver_task = asyncio.create_task(self._video_receiver()) + self._video_receiver_task.add_done_callback(self._task_done_callback)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugins/anam/vision_agents/plugins/anam/anam_avatar_publisher.py` around lines 218 - 221, The background tasks created for _audio_receiver and _video_receiver are started without done callbacks so their exceptions can be lost; update the task creation in the block that assigns self._audio_receiver_task and self._video_receiver_task to attach a done callback (e.g., a helper like _log_task_exceptions or an inline callback) which checks task.exception() and logs any exception via the existing logger, ensuring both asyncio.create_task(self._audio_receiver()) and asyncio.create_task(self._video_receiver()) register that callback so errors aren’t silently dropped.
248-257: Clearing_connectedevent infinallymay cause issues on reconnection.If the connection succeeds but a subsequent operation fails, clearing the event in
finallymeans a reconnection attempt would need to wait for theCONNECTION_ESTABLISHEDevent again, which may never fire if already connected.Consider clearing only on timeout or explicit disconnect.
♻️ Suggested fix
async def _wait_connected(self) -> None: try: await asyncio.wait_for( self._connected.wait(), timeout=self._connect_timeout ) except asyncio.TimeoutError: logger.error("Timed out waiting for Anam connection to be established") + self._connected.clear() raise - finally: - self._connected.clear()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugins/anam/vision_agents/plugins/anam/anam_avatar_publisher.py` around lines 248 - 257, The current _wait_connected method clears the self._connected event in a finally block which can wipe the connected flag even when connection succeeded; change it to only clear the event on timeout or explicit disconnect: remove the finally self._connected.clear(), move the clear into the asyncio.TimeoutError except block (call self._connected.clear() there before re-raising), and ensure any disconnect logic (e.g., an explicit disconnect handler or method like _handle_disconnect/_on_disconnect) is responsible for clearing self._connected when the connection actually drops so reconnection waits behave correctly.
259-268: Same concern for_session_readyevent clearing.The
finallyblock clears_session_readyunconditionally, which could cause the same reconnection issue as_wait_connected.♻️ Suggested fix
async def _wait_session_ready(self) -> None: try: await asyncio.wait_for( self._session_ready.wait(), timeout=self._session_ready_timeout ) except asyncio.TimeoutError: logger.error("Timed out waiting for Anam session to get ready") + self._session_ready.clear() raise - finally: - self._session_ready.clear()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugins/anam/vision_agents/plugins/anam/anam_avatar_publisher.py` around lines 259 - 268, The finally block in _wait_session_ready unconditionally clears the asyncio.Event _session_ready causing spurious reconnection behavior; change it so the event is only cleared after a successful wait: remove the unconditional finally clear and instead call self._session_ready.clear() immediately after the await in _wait_session_ready (inside the try after await completes), leaving the except asyncio.TimeoutError handler to log and re-raise without clearing the event; reference _wait_session_ready and _session_ready to locate the change.plugins/anam/README.md (1)
1-3: Incomplete README documentation.The README contains only a placeholder TODO. Consider documenting the plugin's purpose, configuration options (e.g.,
ANAM_API_KEY,ANAM_AVATAR_IDenvironment variables), and a basic usage example before merging.Would you like me to draft documentation content based on the implementation in
anam_avatar_publisher.py?🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugins/anam/README.md` around lines 1 - 3, The README currently has a TODO; update plugins/anam/README.md to document the plugin purpose, required configuration and usage: explain what the Anam Avatar Plugin does, list required environment variables (e.g., ANAM_API_KEY, ANAM_AVATAR_ID) and any optional settings, and provide a minimal usage example showing how to invoke the functionality exposed by anam_avatar_publisher.py (reference the main function(s) or CLI entry in anam_avatar_publisher.py and demonstrate expected inputs/outputs and error handling). Keep it concise, include mention of authentication, where to find logs or returned avatar artifacts, and any runtime prerequisites.plugins/anam/example/anam_avatar_example.py (1)
2-2: Use modern type annotation syntax.Per coding guidelines, prefer
dict[str, T]overDict. Also,Anyis discouraged in type annotations.♻️ Suggested fix
-from typing import Any, Dict +from typing import AnyAnd update line 22:
- async def get_weather(location: str) -> Dict[str, Any]: + async def get_weather(location: str) -> dict[str, object]:🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugins/anam/example/anam_avatar_example.py` at line 2, Replace the legacy typing imports and Any usage: remove "from typing import Any, Dict" and use the built-in generic alias (e.g., dict[str, ...]) instead, and replace the vague Any with a concrete type (or object if truly generic); update the type annotation used at line 22 to use dict[str, X] (where X is a specific type like str, int, or object) instead of Dict or Any so the code follows modern annotation syntax and avoids Any.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@plugins/anam/example/anam_avatar_example.py`:
- Line 47: Fix the typo in the comment string "Have the agent join the
call/roomz" by replacing "roomz" with "room" so the comment reads "Have the
agent join the call/room"; update the comment where it appears (search for the
exact text "Have the agent join the call/roomz") to ensure correct spelling.
In `@plugins/anam/vision_agents/plugins/anam/anam_avatar_publisher.py`:
- Around line 137-138: The except block currently catches Exception broadly when
calling self._exit_stack.aclose() in the close() path of AnamAvatarPublisher,
which violates the guideline to catch specific exceptions; inspect what context
managers are registered in self._exit_stack and identify the concrete exceptions
they may raise (for example: asyncio.CancelledError, OSError, RuntimeError, or
any SDK-specific exceptions from the Anam SDK) and replace the bare except
Exception with explicit except clauses for those expected exceptions (e.g.,
except asyncio.CancelledError: and except OSError: and except AnamSdkError:),
preserving the logger.warning("Failed to close Anam avatar publisher",
exc_info=True) behavior for each specific except branch and leaving other
unexpected exceptions to propagate.
- Around line 230-239: The _on_connection_closed callback currently calls await
self.close(), which can re-enter because close() triggers _exit_stack.aclose()
and fires the CONNECTION_CLOSED event again; remove the direct call to close()
from _on_connection_closed and instead update internal state (e.g., set a
_connected or _closed boolean) and log/emit any non-reentrant notifications so
the application-level lifecycle can perform shutdown; also make close()
idempotent by adding a _closed guard (check/set self._closed at start of
close()) so concurrent/duplicate calls are safe and no recursive await occurs
from _on_connection_closed or event handlers.
---
Nitpick comments:
In `@plugins/anam/example/anam_avatar_example.py`:
- Line 2: Replace the legacy typing imports and Any usage: remove "from typing
import Any, Dict" and use the built-in generic alias (e.g., dict[str, ...])
instead, and replace the vague Any with a concrete type (or object if truly
generic); update the type annotation used at line 22 to use dict[str, X] (where
X is a specific type like str, int, or object) instead of Dict or Any so the
code follows modern annotation syntax and avoids Any.
In `@plugins/anam/README.md`:
- Around line 1-3: The README currently has a TODO; update
plugins/anam/README.md to document the plugin purpose, required configuration
and usage: explain what the Anam Avatar Plugin does, list required environment
variables (e.g., ANAM_API_KEY, ANAM_AVATAR_ID) and any optional settings, and
provide a minimal usage example showing how to invoke the functionality exposed
by anam_avatar_publisher.py (reference the main function(s) or CLI entry in
anam_avatar_publisher.py and demonstrate expected inputs/outputs and error
handling). Keep it concise, include mention of authentication, where to find
logs or returned avatar artifacts, and any runtime prerequisites.
In `@plugins/anam/vision_agents/plugins/anam/anam_avatar_publisher.py`:
- Around line 218-221: The background tasks created for _audio_receiver and
_video_receiver are started without done callbacks so their exceptions can be
lost; update the task creation in the block that assigns
self._audio_receiver_task and self._video_receiver_task to attach a done
callback (e.g., a helper like _log_task_exceptions or an inline callback) which
checks task.exception() and logs any exception via the existing logger, ensuring
both asyncio.create_task(self._audio_receiver()) and
asyncio.create_task(self._video_receiver()) register that callback so errors
aren’t silently dropped.
- Around line 248-257: The current _wait_connected method clears the
self._connected event in a finally block which can wipe the connected flag even
when connection succeeded; change it to only clear the event on timeout or
explicit disconnect: remove the finally self._connected.clear(), move the clear
into the asyncio.TimeoutError except block (call self._connected.clear() there
before re-raising), and ensure any disconnect logic (e.g., an explicit
disconnect handler or method like _handle_disconnect/_on_disconnect) is
responsible for clearing self._connected when the connection actually drops so
reconnection waits behave correctly.
- Around line 259-268: The finally block in _wait_session_ready unconditionally
clears the asyncio.Event _session_ready causing spurious reconnection behavior;
change it so the event is only cleared after a successful wait: remove the
unconditional finally clear and instead call self._session_ready.clear()
immediately after the await in _wait_session_ready (inside the try after await
completes), leaving the except asyncio.TimeoutError handler to log and re-raise
without clearing the event; reference _wait_session_ready and _session_ready to
locate the change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: f6da3e73-d7dc-4c82-9d24-97cc8dd50423
⛔ Files ignored due to path filters (1)
uv.lockis excluded by!**/*.lock
📒 Files selected for processing (9)
agents-core/pyproject.tomlplugins/anam/README.mdplugins/anam/example/anam_avatar_example.pyplugins/anam/py.typedplugins/anam/pyproject.tomlplugins/anam/tests/test_anam_plugin.pyplugins/anam/vision_agents/plugins/anam/__init__.pyplugins/anam/vision_agents/plugins/anam/anam_avatar_publisher.pypyproject.toml
plugins/anam/vision_agents/plugins/anam/anam_avatar_publisher.py
Outdated
Show resolved
Hide resolved
62bd38b to
ee84704
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
plugins/anam/tests/test_anam_plugin.py (1)
14-19: Consider adding type annotation to**overrides.For completeness with the coding guidelines requiring type annotations everywhere:
♻️ Proposed refinement
-def _make_publisher(**overrides) -> AnamAvatarPublisher: +def _make_publisher(**overrides: str | int | None) -> AnamAvatarPublisher:🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugins/anam/tests/test_anam_plugin.py` around lines 14 - 19, Add a type annotation for the variadic kwargs in _make_publisher: change the signature to annotate **overrides as Dict[str, Any] (or dict[str, Any] for py3.9+) and import Any and Dict from typing if not already present; keep the rest of the function and the call to AnamAvatarPublisher the same so the merged kwargs remain type-checked.plugins/anam/example/anam_avatar_example.py (2)
18-25: Add short Google-style docstrings to public functions.The functions
setup_llm,create_agent, andjoin_callare public entry points lacking docstrings. Add concise Google-style docstrings with Args and Returns sections where applicable.Proposed diff
def setup_llm(model: str = "gemini-3.1-flash-lite-preview") -> gemini.LLM: + """Create an LLM client and register example weather tool. + + Args: + model: Gemini model name. + + Returns: + Configured Gemini LLM instance. + """ llm = gemini.LLM(model) @@ async def create_agent(**kwargs) -> Agent: + """Create and configure an avatar-enabled agent. + + Returns: + Configured agent instance. + """ llm = setup_llm() @@ async def join_call(agent: Agent, call_type: str, call_id: str, **kwargs) -> None: + """Join a call and run a short starter interaction. + + Args: + agent: Agent instance. + call_type: Call type. + call_id: Call identifier. + """ call = await agent.create_call(call_type, call_id)Also applies to lines 28-41 and 44-53.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugins/anam/example/anam_avatar_example.py` around lines 18 - 25, Add concise Google-style docstrings to the public functions setup_llm, create_agent, and join_call: for each function (setup_llm, create_agent, join_call) add a one-line summary, an Args section listing parameters and types (e.g., model: str for setup_llm), and a Returns section describing the return type (e.g., gemini.LLM for setup_llm or Agent/None as appropriate). Keep docstrings short, placed immediately under each def, and use Google-style formatting with parameter types and brief descriptions.
28-28: Add type annotations to variadic kwargs.Both callback signatures accept
**kwargsbut lack type annotations. Per the coding guideline to "use type annotations everywhere with modern syntax," add**kwargs: objectto both functions:Proposed diff
-async def create_agent(**kwargs) -> Agent: +async def create_agent(**kwargs: object) -> Agent:-async def join_call(agent: Agent, call_type: str, call_id: str, **kwargs) -> None: +async def join_call(agent: Agent, call_type: str, call_id: str, **kwargs: object) -> None:🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugins/anam/example/anam_avatar_example.py` at line 28, The functions using variadic keyword args (e.g., create_agent and the related callback functions in this file) currently declare **kwargs without type annotations; update their signatures to annotate the kwargs as **kwargs: object to comply with the project’s modern type-annotation guideline (replace occurrences of **kwargs with **kwargs: object in create_agent and the other callback function signatures).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@plugins/anam/example/anam_avatar_example.py`:
- Around line 2-3: Remove the unused legacy typing import "Any" from the import
statement "from typing import Any, Dict" and update the type annotation that
currently uses "Dict" (the dict type at the top-level annotation on line where
Dict is used) to the modern built-in generic syntax "dict[str, object]" instead
of "Dict[str, Any]"; after this change you can drop the typing import entirely
(or only import names still needed) so the module uses Python's built-in
generics and no longer relies on Any.
In `@plugins/anam/tests/test_anam_plugin.py`:
- Around line 46-49: The test test_init_custom_resolution currently only asserts
the track type; update it to also verify the custom resolution by calling
_make_publisher(width=640, height=480), obtaining track via
publish_video_track(), asserting isinstance(track, QueuedVideoTrack) and then
asserting the track's reported dimensions match 640x480 (e.g., check attributes
or a settings method such as track.width and track.height or
track.get_settings()['width']/['height'] as appropriate) so the test confirms
the resolution was applied.
---
Nitpick comments:
In `@plugins/anam/example/anam_avatar_example.py`:
- Around line 18-25: Add concise Google-style docstrings to the public functions
setup_llm, create_agent, and join_call: for each function (setup_llm,
create_agent, join_call) add a one-line summary, an Args section listing
parameters and types (e.g., model: str for setup_llm), and a Returns section
describing the return type (e.g., gemini.LLM for setup_llm or Agent/None as
appropriate). Keep docstrings short, placed immediately under each def, and use
Google-style formatting with parameter types and brief descriptions.
- Line 28: The functions using variadic keyword args (e.g., create_agent and the
related callback functions in this file) currently declare **kwargs without type
annotations; update their signatures to annotate the kwargs as **kwargs: object
to comply with the project’s modern type-annotation guideline (replace
occurrences of **kwargs with **kwargs: object in create_agent and the other
callback function signatures).
In `@plugins/anam/tests/test_anam_plugin.py`:
- Around line 14-19: Add a type annotation for the variadic kwargs in
_make_publisher: change the signature to annotate **overrides as Dict[str, Any]
(or dict[str, Any] for py3.9+) and import Any and Dict from typing if not
already present; keep the rest of the function and the call to
AnamAvatarPublisher the same so the merged kwargs remain type-checked.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 7532a06b-467f-483d-bb2f-11d57f9f123e
⛔ Files ignored due to path filters (1)
uv.lockis excluded by!**/*.lock
📒 Files selected for processing (9)
agents-core/pyproject.tomlplugins/anam/README.mdplugins/anam/example/anam_avatar_example.pyplugins/anam/py.typedplugins/anam/pyproject.tomlplugins/anam/tests/test_anam_plugin.pyplugins/anam/vision_agents/plugins/anam/__init__.pyplugins/anam/vision_agents/plugins/anam/anam_avatar_publisher.pypyproject.toml
✅ Files skipped from review due to trivial changes (4)
- agents-core/pyproject.toml
- plugins/anam/README.md
- pyproject.toml
- plugins/anam/pyproject.toml
🚧 Files skipped from review as they are similar to previous changes (2)
- plugins/anam/vision_agents/plugins/anam/init.py
- plugins/anam/vision_agents/plugins/anam/anam_avatar_publisher.py
Agent stops the published tracks automatically after it leaves the call. Stopping track before that will cause errors in logs
35dfcab to
954edce
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
plugins/anam/vision_agents/plugins/anam/anam_avatar_publisher.py (1)
232-239:⚠️ Potential issue | 🟠 MajorClear stale publisher state when the connection closes.
This callback only logs, so
_real_session,_audio_input_stream, and the receiver task refs remain populated after an unexpected disconnect. Later audio/interrupt handlers will still target a dead session, and_connect()cannot establish a new one because it short-circuits once_real_sessionis set.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugins/anam/vision_agents/plugins/anam/anam_avatar_publisher.py` around lines 232 - 239, In _on_connection_closed, clear the stale publisher state so future reconnects and handlers don't target a dead session: reset self._real_session to None, set self._audio_input_stream to None, and cancel/await and clear the receiver task reference (e.g., self._receiver_task) if it exists; do this inside the existing async _on_connection_closed method (before/after the log as appropriate) so that _connect() will not short-circuit on a leftover _real_session and audio/interrupt handlers won't use dead streams.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@plugins/anam/vision_agents/plugins/anam/anam_avatar_publisher.py`:
- Around line 47-48: Validate that constructor width and height are even
integers in the QueuedVideoTrack __init__ (the parameters width and height
shown) because the placeholder frame is generated from raw constructor sizes and
odd dimensions produce an odd first frame; if either dimension is not an even
positive int, raise a ValueError with a clear message (e.g., "width must be a
positive even integer") and mirror the same validation for the other
constructor(s) referenced around lines 66-68 so all entry points that create
placeholder frames enforce even dimensions; keep add_frame normalization as-is
but ensure __init__ prevents invalid sizes up front.
- Around line 220-223: The current background tasks created for
_audio_receiver() and _video_receiver() can fail silently; replace the bare
asyncio.create_task calls so failures are observed—either use the standardized
helper _run_tool_in_background(self._audio_receiver(), ...) and
_run_tool_in_background(self._video_receiver(), ...) or attach an
add_done_callback to each created task that logs exceptions and runs cleanup
(e.g., task.add_done_callback(lambda t: _log_and_cleanup_bg_task(t,
"audio_receiver"))). Ensure you reference and update the members
_audio_receiver_task and _video_receiver_task so they store the returned wrapped
task from _run_tool_in_background or the task with the done-callback attached.
- Around line 126-142: The close() method must stop the QueuedVideoTrack so its
recv() no longer serves the last frame; locate the video track instance used by
this publisher (e.g. the QueuedVideoTrack stored on the class such as
self._video_track or similar) and call its stop() before or when
cancelling/awaiting video tasks (referencing QueuedVideoTrack.recv and
self._video_receiver_task). Add a safe nil-check (if track is not None) and call
track.stop() (catch/log exceptions if needed) so downstream consumers are not
left with a frozen avatar after close().
---
Duplicate comments:
In `@plugins/anam/vision_agents/plugins/anam/anam_avatar_publisher.py`:
- Around line 232-239: In _on_connection_closed, clear the stale publisher state
so future reconnects and handlers don't target a dead session: reset
self._real_session to None, set self._audio_input_stream to None, and
cancel/await and clear the receiver task reference (e.g., self._receiver_task)
if it exists; do this inside the existing async _on_connection_closed method
(before/after the log as appropriate) so that _connect() will not short-circuit
on a leftover _real_session and audio/interrupt handlers won't use dead streams.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 30062fb1-bc6f-485f-853b-7c038bfe3b12
⛔ Files ignored due to path filters (1)
uv.lockis excluded by!**/*.lock
📒 Files selected for processing (9)
agents-core/pyproject.tomlplugins/anam/README.mdplugins/anam/example/anam_avatar_example.pyplugins/anam/py.typedplugins/anam/pyproject.tomlplugins/anam/tests/test_anam_plugin.pyplugins/anam/vision_agents/plugins/anam/__init__.pyplugins/anam/vision_agents/plugins/anam/anam_avatar_publisher.pypyproject.toml
✅ Files skipped from review due to trivial changes (6)
- plugins/anam/vision_agents/plugins/anam/init.py
- pyproject.toml
- agents-core/pyproject.toml
- plugins/anam/pyproject.toml
- plugins/anam/README.md
- plugins/anam/example/anam_avatar_example.py
🚧 Files skipped from review as they are similar to previous changes (1)
- plugins/anam/tests/test_anam_plugin.py
An implementation of Anam avatar plugin
plugins/anam/example/anam_avatar_example.pyANAM_API_KEYwith itANAM_AVATAR_IDwith itSummary by CodeRabbit
New Features
anamand included inall-plugins.Documentation
Examples
Tests
Chores