Skip to content

feat: Persistent with serverless #107

Merged
RadeenXALNW merged 8 commits into
mainfrom
rad/persistent
Dec 4, 2025
Merged

feat: Persistent with serverless #107
RadeenXALNW merged 8 commits into
mainfrom
rad/persistent

Conversation

@RadeenXALNW
Copy link
Copy Markdown
Collaborator

@RadeenXALNW RadeenXALNW commented Dec 4, 2025

Add Persistent Memory Support Across All SDKs

🎯 Overview

This PR introduces Persistent Memory support across all RunAgent SDKs (Python, Dart, and Rust), enabling agents to maintain context and state across executions. This revolutionary feature breaks the traditional stateless serverless limitation, allowing agents to remember user preferences, conversation history, and workflow state.

✨ Features Added

Persistent Memory System

  • user_id: User identifier for memory isolation and multi-tenant support
  • persistent_memory: Boolean flag to enable persistent storage across agent executions
  • Cross-language support: Works identically across Python, Dart, and Rust SDKs
  • Serverless-optimized: Fastest persistent memory system designed for serverless architectures

📦 Changes by SDK

Python SDK (runagent)

CLI Updates

  • ✅ Added --user-id option to runagent run command
  • ✅ Added --persistent-memory flag to runagent run command
  • ✅ Added --user-id option to runagent run-stream command
  • ✅ Added --persistent-memory flag to runagent run-stream command

Client Updates

  • RunAgentClient now accepts user_id and persistent_memory parameters
  • ✅ Parameters are passed through to REST and WebSocket clients
  • ✅ Backward compatible (both parameters are optional)

Example:

runagent run \
  --id agent-id \
  --tag entrypoint \
  --user-id user123 \
  --persistent-memory \
  --query="Hello"
client = RunAgentClient(
    agent_id="agent-id",
    entrypoint_tag="entrypoint",
    user_id="user123",
    persistent_memory=True
)

Dart SDK (runagent-dart)

Configuration Updates

  • ✅ Added userId field to RunAgentClientConfig
  • ✅ Added persistentMemory field to RunAgentClientConfig
  • ✅ Updated RunAgentClient to store and forward these parameters
  • ✅ Updated RestClient.runAgent() to include userId and persistentMemory in request payload
  • ✅ Updated SocketClient.runStream() to include userId and persistentMemory in WebSocket messages

Example:

final client = await RunAgentClient.create(
  RunAgentClientConfig.create(
    agentId: 'agent-id',
    entrypointTag: 'entrypoint',
    userId: 'user123',
    persistentMemory: true,
  ),
);

Rust SDK (runagent-rust)

Configuration Updates

  • ✅ Added user_id: Option<String> to RunAgentClientConfig
  • ✅ Added persistent_memory: Option<bool> to RunAgentClientConfig
  • ✅ Added builder methods: .with_user_id() and .with_persistent_memory()
  • ✅ Updated RunAgentClient struct to store these values
  • ✅ Updated RestClient::run_agent() to accept and forward user_id and persistent_memory
  • ✅ Updated SocketClient::run_stream() to accept and forward user_id and persistent_memory
  • ✅ Added getters user_id() and persistent_memory() to RunAgentClient
  • ✅ Updated blocking wrapper to expose these getters

Example:

let client = RunAgentClient::new(
    RunAgentClientConfig::new("agent-id", "entrypoint")
        .with_user_id("user123")
        .with_persistent_memory(true)
).await?;

🧪 Test Scripts

Rust Test (test_scripts/rust/test_lightrag)

  • ✅ Created new test crate for LightRAG agent
  • ✅ Demonstrates user_id and persistent_memory usage
  • ✅ Mirrors Python test structure with both ingest and query clients
  • ✅ Uses same agent ID and configuration as Python test

Dart Test (test_scripts/dart/test_lightrag)

  • ✅ Created new Dart test project for LightRAG agent
  • ✅ Demonstrates userId and persistentMemory usage
  • ✅ Includes ingestFromFile() and queryRag() functions
  • ✅ Matches Python test structure and functionality

📚 Documentation Updates

Main README (README.md)

  • ✅ Added comprehensive "Persistent Memory: Revolutionary Serverless Memory System" section
  • ✅ Explained why persistent memory matters (breaking stateless limitations)
  • ✅ Provided code examples for all SDKs (Python, JavaScript, Rust, Dart)
  • ✅ Listed key benefits and use cases
  • ✅ Updated "Action Memory System" section to reference Persistent Memory foundation

Rust SDK README (runagent-rust/runagent/README.md)

  • ✅ Added user_id and persistent_memory to configuration overview table
  • ✅ Added descriptions in configuration notes section
  • ✅ Added .with_user_id() and .with_persistent_memory() to API Reference
  • ✅ Added example showing persistent memory usage with remote agent

🔄 Version Updates

  • Dart SDK: 0.1.420.1.43
  • Rust SDK: 0.1.410.1.42
  • Updated CHANGELOG.md for Dart SDK

🎯 Use Cases Enabled

This feature enables:

  • Conversational AI: Maintain context across multiple user interactions
  • Personalization: Remember user preferences and adapt responses
  • Multi-Step Processes: Track progress through complex workflows
  • Learning Systems: Agents that improve based on interaction history
  • Session Management: Maintain state across distributed systems

🔒 Security & Isolation

  • Each user_id has isolated memory space
  • Secure multi-tenant support
  • No data leakage between users
  • Serverless-optimized for low-latency access

✅ Testing

  • ✅ All SDKs tested with LightRAG agent example
  • ✅ Python CLI commands tested with new flags
  • ✅ Backward compatibility verified (parameters are optional)
  • ✅ Cross-language consistency verified

📋 Checklist

  • Python SDK CLI updated
  • Python SDK client updated
  • Dart SDK configuration updated
  • Dart SDK clients (REST & WebSocket) updated
  • Rust SDK configuration updated
  • Rust SDK clients (REST & WebSocket) updated
  • Rust test script created
  • Dart test script created
  • Main README updated
  • Rust SDK README updated
  • Version numbers bumped
  • Changelog updated (Dart)

🚀 Ready for Publishing

  • Dart SDK ready for pub.dev (v0.1.43)
  • Rust SDK ready for crates.io (v0.1.42)
  • Python SDK changes are backward compatible (no version bump needed for this feature)

Breaking Changes: None - All new parameters are optional and backward compatible.

Migration Guide: No migration needed. Existing code continues to work. To enable persistent memory, simply add user_id and persistent_memory parameters to your client configuration.

Summary by CodeRabbit

  • New Features

    • Persistent Memory system enables cross-run data retention and session management for serverless agents.
    • User ID support for tracking and managing agent sessions across multiple users.
    • LightRAG integration with persistent storage for knowledge graph-based Q&A workflows.
    • Session resumption capability to continue conversations with context preservation.
  • Documentation

    • Added Persistent Memory documentation with multi-language examples (Python, JavaScript, Rust, Dart).
    • New LightRAG integration guide and API reference.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 4, 2025

Walkthrough

This pull request introduces persistent memory support across the RunAgent SDK, adding user_id and persistent_memory optional parameters throughout Python, Dart, and Rust clients. New examples demonstrate session-based chat and RAG workflows. Configuration and test infrastructure are updated to support persistent storage.

Changes

Cohort / File(s) Change Summary
Documentation & README
README.md
Added "Persistent Memory" feature documentation with cross-language support, implementation notes, and use cases. Updated "Action Memory System" section to reference Persistent Memory foundation.
Python SDK Core
runagent/client/client.py, runagent/sdk/rest_client.py, runagent/sdk/socket_client.py, runagent/sdk/sdk.py, runagent/sdk/deployment/remote.py
Extended RunAgentClient and SDK classes with user_id and persistent_memory parameters; parameters propagated through RestClient and SocketClient request payloads. Added validation requiring user_id when persistent_memory=True. Enhanced local execution error handling with agent context.
Python CLI Commands
runagent/cli/commands/run.py, runagent/cli/commands/run_stream.py
Added --user-id and --persistent-memory CLI options to run and run-stream commands; parameters forwarded to RunAgentClient instantiation.
Python Utilities
runagent/utils/schema.py, runagent/utils/serializer.py
Added persistent_folders field to RunAgentConfig. Enhanced deserializer with empty-string guards and default-value returns for various data types (string, integer, number, boolean, array, object).
Dart SDK
runagent-dart/lib/src/types/types.dart, runagent-dart/lib/src/client/runagent_client.dart, runagent-dart/lib/src/client/rest_client.dart, runagent-dart/lib/src/client/socket_client.dart, runagent-dart/pubspec.yaml, runagent-dart/.dart_tool/package_graph.json, runagent-dart/CHANGELOG.md
Added userId and persistentMemory fields to RunAgentClientConfig; propagated through RunAgentClient, RestClient, and SocketClient. Version bumped 0.1.41 → 0.1.43. Updated CHANGELOG documenting new parameters.
Rust SDK
runagent-rust/runagent/Cargo.toml, runagent-rust/runagent/README.md, runagent-rust/runagent/src/client/runagent_client.rs, runagent-rust/runagent/src/client/rest_client.rs, runagent-rust/runagent/src/client/socket_client.rs, runagent-rust/runagent/src/blocking.rs
Added user_id and persistent_memory to RunAgentClientConfig with builder methods; propagated through RunAgentClient initialization and REST/Socket client invocations. Version bumped 0.1.41 → 0.1.42. Updated README documenting new configuration options.
Agno Persistent Example
examples/agno_persistent/agent.py, examples/agno_persistent/requirements.txt, examples/agno_persistent/runagent.config.json
New persistent session-managed chat agent example using SQLite backend (rad/agents.db). Exports get_chat_agent, agent_print_response, agent_print_response_stream functions. Supports session resumption and streaming responses.
LightRAG Persistent Example
examples/lightrag_persistent/README.md, examples/lightrag_persistent/agent.py, examples/lightrag_persistent/requirements.txt, examples/lightrag_persistent/runagent.config.json
New LightRAG-based RAG workflow example with async/sync helpers. Exports ingest_text_async, query_rag_async, ingest_text, query_rag, and get_rag_instance. Includes comprehensive README with setup and API reference.
Agno Template
templates/agno/agno_persistent/*
New persistent memory template mirroring examples/agno_persistent with SQLite-backed session agent and multi-mode response streaming.
Configuration Updates
examples/journalist_agent/agent/runagent.config.json, examples/journalist_agent/dart_sdk/lib/main.dart, examples/lead-agent/lead-score-flow/runagent.config.json, examples/trip_planner/agent/runagent.config.json, templates/agno/default/runagent.config.json
Updated agent_ids, added persistent_folders fields to config files. Updated trip_planner metadata (agent_name, description, framework, timestamps). Updated dart examples with new agent_ids and simplified messaging.
Test Utilities & Package Manifests
examples/journalist_agent/dart_sdk/.dart_tool/package_graph.json, examples/lead-agent/runagent_sdk/python/test_sdk.py, examples/trip_planner/sdk/python/test.py, test_scripts/python/client_test_*.py, test_scripts/dart/test_lightrag/*, test_scripts/rust/test_lightrag/*
Updated agent_ids across test scripts. Added new LightRAG test implementations in Python, Dart, and Rust demonstrating ingest and query flows. Added test for agno persistent example with session management. Updated Dart/Rust package manifests and Cargo.toml.
Test Data
test/rag_test.txt, test/test_lightrag.py
Added comprehensive world overview dataset for RAG testing. Added standalone LightRAG integration test script.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Areas requiring extra attention:

  • Serializer changes (runagent/utils/serializer.py): Enhanced empty-string and type-specific default handling; verify all data type fallbacks are sensible and don't mask real errors.
  • Persistent memory validation (runagent/sdk/sdk.py): New ValidationError when persistent_memory=True without user_id; confirm error message clarity and placement are appropriate.
  • New persistent examples (examples/agno_persistent/, examples/lightrag_persistent/): Verify SQLite initialization, session resumption logic, and streaming payload structures match expected agent framework behavior.
  • Multi-language SDK propagation: Confirm parameter threading through Python, Dart, and Rust clients is consistent (parameter names, defaults, null/None handling).

Possibly related PRs

Suggested reviewers

  • sawradip

🐰 Persistent memories now flow,
Through users and sessions below,
Python, Dart, and Rust unite,
Making stateful calls shine bright,
Hop forward with this feature show! 🌟

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is vague and does not clearly convey the main change; it uses non-descriptive terms like 'Persistent with serverless' that lack specificity. Consider a more descriptive title that clearly summarizes the primary change, such as 'feat: Add persistent memory support across SDKs' or 'feat: Persistent memory for multi-tenant serverless agents'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch rad/persistent

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
examples/journalist_agent/dart_sdk/.dart_tool/package_graph.json (1)

1-124: Do not commit auto-generated .dart_tool/ artifacts to version control.

The .dart_tool/package_graph.json file is auto-generated by Dart's pub tool and should be excluded from version control. Committing these files creates merge conflicts, stale dependency snapshots, and maintenance burden. Add .dart_tool/ to the root .gitignore:

.dart_tool/

This issue affects multiple Dart projects in the repository (examples/journalist_agent/dart_sdk/, runagent-dart/, and test_scripts/dart/*/).

🧹 Nitpick comments (31)
examples/trip_planner/agent/runagent.config.json (1)

2-5: Agent metadata overly generic for documentation.

The agent configuration has been simplified to generic values: agent_name is now "trip planner" and description is "My AI agent". For an example, these descriptive fields should provide context. Consider whether more meaningful descriptions would better serve users learning from this example.

examples/agno_persistent/requirements.txt (1)

1-2: Requirements are minimal; consider optional pinning

The dependencies are sufficient for the example. If you want more reproducible example runs, consider pinning to known‑good versions in the future.

examples/lightrag_persistent/requirements.txt (1)

1-3: Dependencies cover the LightRAG example

The required libraries for the persistent LightRAG example are declared appropriately. As with other examples, pinning versions later could help avoid breakage from upstream changes.

templates/agno/agno_persistent/requirements.txt (1)

1-2: Template requirements are aligned with AGNO usage

agno and openai are the expected deps for the AGNO persistent template. If template stability over time becomes a concern, consider pinning or documenting tested versions.

runagent-dart/lib/src/client/rest_client.dart (1)

58-92: Persistent memory parameters are wired correctly

  • Adding userId and persistentMemory as optional named parameters keeps the API backward compatible.
  • Building a typed Map<String, dynamic> payload and only including user_id when non‑null and persistent_memory when true matches the intended server contract and Python parity.
  • No issues with existing timeout, auth, or error‑translation behavior around this change.

One minor follow‑up you may consider later: extend the method doc comment to mention userId and persistentMemory so IDE tooltips stay in sync with the API.

runagent/utils/schema.py (1)

96-102: Persistent folders field integrates cleanly with existing serialization

The persistent_folders field is well-aligned with how env_vars and other optional fields are handled, and to_dict() will correctly preserve empty lists while stripping None values. If you expect only relative paths or want to guard against invalid entries, consider adding a small validator on RunAgentConfig to enforce those constraints, but that’s strictly optional.

runagent-dart/CHANGELOG.md (1)

8-18: Changelog entry matches API changes; remember to finalize release date

The 0.1.43 notes accurately describe the new userId / persistentMemory behavior and propagation through clients. Before tagging/publishing, replace 2024-XX-XX with the actual release date to keep the changelog consistent.

test/rag_test.txt (1)

1-20: Optional wording cleanup in narrative text

This file is fine as‑is functionally. If you care about style polish, you could drop “about” in “estimated at about 8.25 billion people” to avoid redundancy (“estimated at 8.25 billion people” reads a bit tighter), but that’s entirely optional.

runagent-rust/runagent/README.md (1)

48-55: Rust README updates align with new persistence API

The added example using .with_user_id() / .with_persistent_memory(true) and the new rows in the settings + builder tables accurately reflect the new config fields and default behavior. If you revisit this snippet later, you might consider also showing use runagent::{RunAgentClient, RunAgentClientConfig}; (and use std::env; if you keep env::var) so the example is copy‑paste runnable, but that’s purely a doc nicety.

Also applies to: 64-66, 69-71, 275-276

runagent/utils/serializer.py (1)

261-303: Redundant empty payload checks in type branches.

After the empty payload check at lines 262-269 returns early, the per-type checks (e.g., lines 276-277, 280-281, 285-286, etc.) can never trigger since we've already handled the empty case. Consider removing these redundant guards.

             # Parse payload based on type
             if data_type == "null":
                 return None
             elif data_type == "string":
                 # Payload is JSON string, parse to get the actual string
-                if not payload or not payload.strip():
-                    return ""
                 return json.loads(payload)
             elif data_type == "integer":
-                if not payload or not payload.strip():
-                    return 0
                 value = json.loads(payload)
                 return int(value)
             elif data_type == "number":
-                if not payload or not payload.strip():
-                    return 0.0
                 value = json.loads(payload)
                 return float(value)
             elif data_type == "boolean":
-                if not payload or not payload.strip():
-                    return False
                 return json.loads(payload)
             elif data_type in ("array", "object"):
                 # Parse JSON to get structured data
-                if not payload or not payload.strip():
-                    return [] if data_type == "array" else {}
                 return json.loads(payload)
             else:
                 # Unknown type, try to parse as JSON
                 self.logger.warning(f"Unknown type '{data_type}', attempting JSON parse")
-                if not payload or not payload.strip():
-                    return None
                 return json.loads(payload)
templates/agno/agno_persistent/agent.py (2)

73-73: Use explicit Optional type hint for nullable parameter.

Per PEP 484, prompt: str = None should be prompt: Optional[str] = None. Optional is already imported.

-def agent_print_response(prompt: str = None, user: str = "user", session_id: Optional[str] = None, new_session: bool = False, **kwargs):
+def agent_print_response(prompt: Optional[str] = None, user: str = "user", session_id: Optional[str] = None, new_session: bool = False, **kwargs):

126-126: Apply the same Optional type hint fix here.

-def agent_print_response_stream(prompt: str = None, user: str = "user", session_id: Optional[str] = None, new_session: bool = False, **kwargs):
+def agent_print_response_stream(prompt: Optional[str] = None, user: str = "user", session_id: Optional[str] = None, new_session: bool = False, **kwargs):
templates/agno/agno_persistent/runagent.config.json (1)

2-3: Consider a more descriptive agent description.

The description "My AI agent" is generic placeholder text. Consider something like "Persistent chat agent with SQLite-backed session memory" to better describe the agent's capabilities.

test_scripts/python/client_test_agno.py (1)

4-7: Align or document differing agent_id values

The commented example and the active RunAgentClient use different agent_id values. That can be confusing for someone uncommenting the top block or copying it as a template. Consider either:

  • Updating the commented block to use the same agent_id as the active client, or
  • Adding a brief comment that the IDs are from different deployments.

Also applies to: 20-23

examples/agno_persistent/runagent.config.json (1)

8-12: Avoid machine-specific paths and clarify deployment‑specific agent_id

The config structure looks correct for a persistent Agno agent, but two minor nits:

  • template_source.path is an absolute path to a specific machine (/home/azureuser/...). For templates checked into the repo, a relative path or a more generic value is less confusing for other users.
  • agent_id is deployment‑specific. In docs/scripts that reference this config, it can help to remind readers that this value will change on redeploy and should be updated from runagent deploy output.

Also applies to: 28-32

examples/lightrag_persistent/README.md (1)

14-21: Add a language to the fenced code block (markdownlint MD040)

The file-structure code block is missing a language spec, which triggers MD040. You can tag it as plain text:

-```
+```text
 lightrag_agent/
 ├── agent.py                 # Main agent implementation
 ├── requirements.txt         # Python dependencies
 ├── runagent.config.json    # RunAgent configuration
 ├── test_sdk.py             # SDK test script
 └── README.md               # This file

</blockquote></details>
<details>
<summary>examples/lightrag_persistent/runagent.config.json (1)</summary><blockquote>

`8-12`: **Polish metadata (path and description text)**

The config itself looks structurally correct. Two small metadata cleanups you might consider:

- `template_source.path` is machine‑specific (`/home/azureuser/...`); a relative or generic path is friendlier for other environments.
- The description string could be tightened, e.g.:
  - From: `"Lightrag is agentic RAG framework"`
  - To: `"LightRAG is an agentic RAG framework"`

Purely cosmetic, but improves portability and readability.



Also applies to: 3-3

</blockquote></details>
<details>
<summary>runagent/sdk/deployment/remote.py (1)</summary><blockquote>

`101-133`: **Input parsing and RestClient delegation look correct; consider noting/mitigating mutation**

The new `run_agent` implementation correctly:

- Pulls out `user_id`, `persistent_memory`, and `entrypoint_tag`.
- Separates `input_args` / `input_kwargs`.
- Treats any remaining top‑level keys as entrypoint kwargs.
- Forwards everything to `RestClient.run_agent(...)` with the new parameters.

One small behavioral detail: this function mutates `input_data` in place via multiple `pop` calls. That’s fine if callers always pass a fresh dict, but if they ever reuse the same object, keys will have been stripped. Two options:

- Document in the docstring that `input_data` is consumed/mutated, or
- Work on a shallow copy:

```python
def run_agent(self, agent_id: str, input_data: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]:
    data = dict(input_data)  # shallow copy
    user_id = data.pop("user_id", None)
    ...

Not required for correctness, but makes the API behavior clearer and more robust.

runagent/sdk/rest_client.py (1)

1481-1482: Use explicit Optional or union syntax for type hint.

PEP 484 prohibits implicit Optional. Use Optional[str] or str | None instead of str = None.

Apply this diff:

-        user_id: str = None,
+        user_id: Optional[str] = None,
test_scripts/dart/test_lightrag/.dart_tool/package_config.json (1)

1-106: Consider excluding generated Dart tool files from version control.

This is an auto-generated file containing environment-specific absolute paths (e.g., /home/azureuser). The .dart_tool/ directory is typically added to .gitignore to avoid:

  • Merge conflicts from developer-specific paths
  • Repository bloat from generated files
  • Build reproducibility issues across environments

Verify that .dart_tool/ is in your .gitignore:

#!/bin/bash
# Check if .dart_tool is properly ignored
if grep -q "^\.dart_tool/" .gitignore; then
  echo "✓ .dart_tool is already ignored"
else
  echo "✗ .dart_tool is not in .gitignore"
  echo "Consider adding: echo '.dart_tool/' >> .gitignore"
fi
test/test_lightrag.py (1)

1-1: Make script executable or remove shebang.

The shebang is present but the file is not executable.

Make the file executable:

#!/bin/bash
chmod +x test/test_lightrag.py
test_scripts/dart/test_lightrag/lib/main.dart (1)

10-32: Consider consistent error handling across functions.

ingestFromFile returns null on error (line 14), while queryRag doesn't have explicit error handling and would propagate exceptions. For consistency, consider either:

  1. Both functions return null on error, or
  2. Both functions throw exceptions

If exceptions are preferred:

   if (!await file.exists()) {
-    print('Error: File not found: $filePath');
-    return null;
+    throw Exception('File not found: $filePath');
   }
test_scripts/python/client_test_lightrag.py (2)

4-7: Hardcoded configuration may limit test portability.

Consider using environment variables for AGENT_ID and USER_ID to enable running tests against different agents without code changes:

AGENT_ID = os.getenv("RUNAGENT_AGENT_ID", "63751c14-0ed5-426c-ab44-aa94e5505bed")
USER_ID = os.getenv("RUNAGENT_USER_ID", "rad123")

48-60: Main block contains commented-out code and incomplete flow.

The test executes only query_rag while ingestion and completion messages are commented out. If this is intentional for quick iteration, consider adding a command-line argument or environment variable to control which steps run, rather than relying on commented code.

runagent/client/client.py (2)

25-25: Type hints use implicit Optional; consider explicit union syntax.

Per PEP 484 and static analysis (RUF013), parameters with None defaults should use explicit optional types:

-    def __init__(self, agent_id: str, entrypoint_tag: str, local: bool = True, host: str = None, port: int = None, user_id: str = None, persistent_memory: bool = False):
+    def __init__(self, agent_id: str, entrypoint_tag: str, local: bool = True, host: str | None = None, port: int | None = None, user_id: str | None = None, persistent_memory: bool = False):

84-86: Remove extraneous f prefix from string without placeholders.

Static analysis (F541) correctly flags line 85:

-            console.print(f"[cyan]Detected streaming entrypoint, using WebSocket streaming[/cyan]")
+            console.print("[cyan]Detected streaming entrypoint, using WebSocket streaming[/cyan]")
examples/agno_persistent/agent.py (2)

73-73: Type hint uses implicit Optional.

Per static analysis (RUF013), use explicit union:

-def agent_print_response(prompt: str = None, user: str = "user", session_id: Optional[str] = None, new_session: bool = False, **kwargs):
+def agent_print_response(prompt: str | None = None, user: str = "user", session_id: str | None = None, new_session: bool = False, **kwargs):

Note: You're mixing Optional[str] (imported) with implicit optional. Pick one style—str | None is more modern (Python 3.10+).


126-126: Same type hint issue as line 73.

Apply the same fix for consistency:

-def agent_print_response_stream(prompt: str = None, user: str = "user", session_id: Optional[str] = None, new_session: bool = False, **kwargs):
+def agent_print_response_stream(prompt: str | None = None, user: str = "user", session_id: str | None = None, new_session: bool = False, **kwargs):
examples/lightrag_persistent/agent.py (3)

29-48: Use explicit Optional type hints and log cleanup failures.

Per static analysis (Ruff RUF013), PEP 484 prohibits implicit Optional. Additionally, the bare except: pass silently swallows cleanup errors, which can hide storage finalization issues.

Apply this diff:

+import logging
+
+logger = logging.getLogger(__name__)
+
-async def ingest_text_async(text: str = None, **kwargs):
+async def ingest_text_async(text: str | None = None, **kwargs):
     if text is None:
         text = kwargs.get('content', '')
     
     if not text:
         return {"status": "error", "message": "No text provided"}
     
     rag = None
     try:
         rag = await get_rag_instance()
         await rag.ainsert(text)
         return {"status": "success", "message": f"Ingested {len(text)} characters"}
     except Exception as e:
         return {"status": "error", "message": str(e)}
     finally:
         if rag:
             try:
                 await rag.finalize_storages()
-            except:
-                pass
+            except Exception as cleanup_err:
+                logger.warning("Storage finalization failed: %s", cleanup_err)

51-73: Apply same type hint and logging fixes here.

Same issues as ingest_text_async: implicit Optional and silent cleanup errors.

Apply this diff:

-async def query_rag_async(query: str = None, mode: str = "hybrid", **kwargs):
+async def query_rag_async(query: str | None = None, mode: str = "hybrid", **kwargs):
     if query is None:
         query = kwargs.get('question', '')
     
     if not query:
         return {"status": "error", "message": "No query provided"}
     
     if mode not in ["naive", "local", "global", "hybrid"]:
         mode = "hybrid"
     
     rag = None
     try:
         rag = await get_rag_instance()
         result = await rag.aquery(query, param=QueryParam(mode=mode))
         return {"status": "success", "query": query, "mode": mode, "result": result}
     except Exception as e:
         return {"status": "error", "message": str(e)}
     finally:
         if rag:
             try:
                 await rag.finalize_storages()
-            except:
-                pass
+            except Exception as cleanup_err:
+                logger.warning("Storage finalization failed: %s", cleanup_err)

76-81: Update type hints in sync wrappers for consistency.

-def ingest_text(text: str = None, **kwargs):
+def ingest_text(text: str | None = None, **kwargs):
     return asyncio.run(ingest_text_async(text, **kwargs))


-def query_rag(query: str = None, mode: str = "hybrid", **kwargs):
+def query_rag(query: str | None = None, mode: str = "hybrid", **kwargs):
     return asyncio.run(query_rag_async(query, mode, **kwargs))
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ade2c4e and 8889700.

📒 Files selected for processing (53)
  • README.md (3 hunks)
  • examples/agno_persistent/agent.py (1 hunks)
  • examples/agno_persistent/requirements.txt (1 hunks)
  • examples/agno_persistent/runagent.config.json (1 hunks)
  • examples/journalist_agent/agent/runagent.config.json (1 hunks)
  • examples/journalist_agent/dart_sdk/.dart_tool/package_graph.json (4 hunks)
  • examples/journalist_agent/dart_sdk/lib/main.dart (1 hunks)
  • examples/lead-agent/lead-score-flow/runagent.config.json (1 hunks)
  • examples/lead-agent/runagent_sdk/python/test_sdk.py (1 hunks)
  • examples/lightrag_persistent/README.md (1 hunks)
  • examples/lightrag_persistent/agent.py (1 hunks)
  • examples/lightrag_persistent/requirements.txt (1 hunks)
  • examples/lightrag_persistent/runagent.config.json (1 hunks)
  • examples/trip_planner/agent/requirements.txt (1 hunks)
  • examples/trip_planner/agent/runagent.config.json (1 hunks)
  • examples/trip_planner/sdk/python/test.py (2 hunks)
  • runagent-dart/.dart_tool/package_graph.json (1 hunks)
  • runagent-dart/CHANGELOG.md (1 hunks)
  • runagent-dart/lib/src/client/rest_client.dart (2 hunks)
  • runagent-dart/lib/src/client/runagent_client.dart (5 hunks)
  • runagent-dart/lib/src/client/socket_client.dart (2 hunks)
  • runagent-dart/lib/src/types/types.dart (4 hunks)
  • runagent-dart/pubspec.yaml (1 hunks)
  • runagent-rust/runagent/Cargo.toml (1 hunks)
  • runagent-rust/runagent/README.md (3 hunks)
  • runagent-rust/runagent/src/blocking.rs (1 hunks)
  • runagent-rust/runagent/src/client/rest_client.rs (2 hunks)
  • runagent-rust/runagent/src/client/runagent_client.rs (9 hunks)
  • runagent-rust/runagent/src/client/socket_client.rs (3 hunks)
  • runagent/cli/commands/run.py (2 hunks)
  • runagent/cli/commands/run_stream.py (2 hunks)
  • runagent/client/client.py (5 hunks)
  • runagent/sdk/deployment/remote.py (1 hunks)
  • runagent/sdk/rest_client.py (2 hunks)
  • runagent/sdk/sdk.py (3 hunks)
  • runagent/sdk/socket_client.py (4 hunks)
  • runagent/utils/schema.py (1 hunks)
  • runagent/utils/serializer.py (3 hunks)
  • templates/agno/agno_persistent/agent.py (1 hunks)
  • templates/agno/agno_persistent/requirements.txt (1 hunks)
  • templates/agno/agno_persistent/runagent.config.json (1 hunks)
  • templates/agno/default/runagent.config.json (1 hunks)
  • test/rag_test.txt (1 hunks)
  • test/test_lightrag.py (1 hunks)
  • test_scripts/dart/test_lightrag/.dart_tool/package_config.json (1 hunks)
  • test_scripts/dart/test_lightrag/.dart_tool/package_graph.json (1 hunks)
  • test_scripts/dart/test_lightrag/lib/main.dart (1 hunks)
  • test_scripts/dart/test_lightrag/pubspec.yaml (1 hunks)
  • test_scripts/python/client_test_agno.py (2 hunks)
  • test_scripts/python/client_test_agno_persistent.py (1 hunks)
  • test_scripts/python/client_test_lightrag.py (1 hunks)
  • test_scripts/rust/test_lightrag/Cargo.toml (1 hunks)
  • test_scripts/rust/test_lightrag/src/main.rs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (15)
test_scripts/rust/test_lightrag/src/main.rs (4)
runagent-rust/runagent/src/client/rest_client.rs (1)
  • new (21-41)
runagent-rust/runagent/src/blocking.rs (2)
  • new (50-57)
  • new (197-224)
runagent-rust/runagent/src/client/runagent_client.rs (2)
  • new (125-139)
  • new (218-326)
runagent-rust/runagent/src/client/socket_client.rs (1)
  • new (23-36)
runagent-rust/runagent/src/blocking.rs (1)
runagent-rust/runagent/src/client/runagent_client.rs (2)
  • user_id (590-592)
  • persistent_memory (595-597)
runagent-rust/runagent/src/client/socket_client.rs (2)
runagent-rust/runagent/src/blocking.rs (2)
  • user_id (148-150)
  • persistent_memory (153-155)
runagent-rust/runagent/src/client/runagent_client.rs (2)
  • user_id (590-592)
  • persistent_memory (595-597)
examples/lead-agent/runagent_sdk/python/test_sdk.py (1)
runagent-rust/runagent/src/client/runagent_client.rs (1)
  • agent_id (575-577)
runagent-rust/runagent/src/client/rest_client.rs (4)
runagent-rust/runagent/src/blocking.rs (2)
  • user_id (148-150)
  • persistent_memory (153-155)
runagent-rust/runagent/src/client/runagent_client.rs (2)
  • user_id (590-592)
  • persistent_memory (595-597)
runagent-rust/runagent/src/utils/serializer.rs (3)
  • serde_json (44-44)
  • serde_json (71-71)
  • serde_json (95-95)
examples/lead-agent/backend-rust/src/main.rs (3)
  • serde_json (161-161)
  • serde_json (168-168)
  • serde_json (175-175)
runagent/sdk/sdk.py (2)
runagent/sdk/rest_client.py (1)
  • ValidationError (61-63)
runagent/sdk/exceptions.py (1)
  • ValidationError (23-26)
examples/lightrag_persistent/agent.py (2)
examples/lightrag_agent_(developing)/services/rag_service.py (2)
  • lightrag (223-227)
  • llm_model_func (77-91)
test_scripts/python/client_test_lightrag.py (1)
  • query_rag (41-45)
runagent/sdk/socket_client.py (1)
runagent/client/client.py (1)
  • run_stream (159-190)
test_scripts/python/client_test_lightrag.py (1)
runagent/client/client.py (1)
  • run (72-157)
runagent/cli/commands/run.py (4)
runagent-rust/runagent/src/client/rest_client.rs (1)
  • default (45-48)
runagent-rust/runagent/src/client/runagent_client.rs (6)
  • default (106-120)
  • run (374-376)
  • agent_id (575-577)
  • user_id (590-592)
  • persistent_memory (595-597)
  • entrypoint_tag (580-582)
runagent-rust/runagent/src/blocking.rs (5)
  • run (62-64)
  • agent_id (133-135)
  • user_id (148-150)
  • persistent_memory (153-155)
  • entrypoint_tag (138-140)
runagent/client/client.py (1)
  • run (72-157)
test_scripts/python/client_test_agno_persistent.py (1)
runagent/client/client.py (2)
  • RunAgentClient (23-333)
  • run (72-157)
examples/agno_persistent/agent.py (4)
runagent/cli/commands/db.py (1)
  • db (48-50)
runagent/sdk/rest_client.py (1)
  • get (214-216)
runagent/client/client.py (1)
  • run (72-157)
examples/rag_agent/frontend/app.js (1)
  • content (209-209)
test/test_lightrag.py (2)
examples/lightrag_agent_(developing)/services/rag_service.py (2)
  • lightrag (223-227)
  • llm_model_func (77-91)
runagent/client/client.py (1)
  • run (72-157)
runagent-rust/runagent/src/client/runagent_client.rs (2)
runagent-rust/runagent/src/blocking.rs (2)
  • user_id (148-150)
  • persistent_memory (153-155)
runagent-rust/runagent/src/types/errors.rs (1)
  • config (119-123)
test_scripts/python/client_test_agno.py (2)
runagent-rust/runagent/src/blocking.rs (1)
  • agent_id (133-135)
runagent-rust/runagent/src/client/runagent_client.rs (1)
  • agent_id (575-577)
🪛 LanguageTool
test/rag_test.txt

[style] ~9-~9: This phrase is redundant. Consider writing “estimated at”.
Context: ...s of late 2025, the world population is estimated at about 8.25 billion people. ([DataReportal...

(ESTIMATED_AT_ABOUT)


[uncategorized] ~43-~43: Do not mix variants of the same word (‘organization’ and ‘organisation’) within a single text.
Context: ...* According to the World Meteorological Organization (WMO), **2024 was the warmest year on r...

(EN_WORD_COHERENCY)


[uncategorized] ~43-~43: Do not mix variants of the same word (‘organization’ and ‘organisation’) within a single text.
Context: ... global warming. ([World Meteorological Organization][8]) * The increase in greenhouse-gas ...

(EN_WORD_COHERENCY)


[uncategorized] ~49-~49: Do not mix variants of the same word (‘organization’ and ‘organisation’) within a single text.
Context: ...n more severe exposure). ([World Health Organization][11]) * The environmental challenges g...

(EN_WORD_COHERENCY)

🪛 markdownlint-cli2 (0.18.1)
examples/lightrag_persistent/README.md

14-14: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🪛 Ruff (0.14.7)
runagent/sdk/sdk.py

351-351: Avoid specifying long messages outside the exception class

(TRY003)

runagent/client/client.py

25-25: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)


25-25: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)


25-25: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)


85-85: f-string without any placeholders

Remove extraneous f prefix

(F541)


127-127: Do not catch blind exception: Exception

(BLE001)


135-135: Do not catch blind exception: Exception

(BLE001)

templates/agno/agno_persistent/agent.py

73-73: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)


126-126: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)

examples/lightrag_persistent/agent.py

29-29: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)


41-41: Do not catch blind exception: Exception

(BLE001)


47-47: Do not use bare except

(E722)


47-48: try-except-pass detected, consider logging the exception

(S110)


51-51: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)


65-65: Consider moving this statement to an else block

(TRY300)


66-66: Do not catch blind exception: Exception

(BLE001)


72-72: Do not use bare except

(E722)


72-73: try-except-pass detected, consider logging the exception

(S110)


76-76: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)


80-80: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)

runagent/utils/serializer.py

248-248: Abstract raise to an inner function

(TRY301)


248-248: Avoid specifying long messages outside the exception class

(TRY003)

examples/agno_persistent/agent.py

73-73: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)


126-126: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)

runagent/sdk/rest_client.py

1481-1481: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)

test/test_lightrag.py

1-1: Shebang is present but file is not executable

(EXE001)


23-23: f-string without any placeholders

Remove extraneous f prefix

(F541)


23-23: String contains ambiguous (INFORMATION SOURCE). Did you mean i (LATIN SMALL LETTER I)?

(RUF001)

🔇 Additional comments (63)
templates/agno/default/runagent.config.json (1)

28-28: Clarify the purpose of the agent_id change.

The agent_id UUID has been modified, but this change appears unrelated to the persistent memory feature introduced in this PR. Please clarify:

  • Is this UUID change intentional or accidental?
  • Does this affect any dependent systems or existing agent configurations?
  • Should this change be included in this PR, or should it be reverted/moved to a separate issue?
examples/lead-agent/lead-score-flow/runagent.config.json (1)

28-28: Configuration ID update appears intentional.

The agent_id change aligns with the PR's agent configuration standardization mentioned in the summary.

examples/journalist_agent/dart_sdk/.dart_tool/package_graph.json (1)

14-21: No action required: Package version in package_graph.json is correct.

The version 0.1.41 shown for the runagent package in this file is the accurate, current version. There is no evidence of a PR objective to bump the version to 0.1.43. The file is located in .dart_tool/ (a generated/cached directory) and correctly reflects the installed package version.

Likely an incorrect or invalid review comment.

examples/trip_planner/agent/runagent.config.json (1)

27-32: New persistence configuration fields align with PR objectives.

The addition of env_vars, agent_id, auth_settings, and persistent_folders fields supports the persistent memory feature described in the PR. The structure is sound, and the UUID-based agent_id provides appropriate uniqueness. These fields complement the broader persistence support being introduced.

runagent-rust/runagent/Cargo.toml (1)

3-3: Version bump looks consistent with the described SDK changes.

No issues from this manifest side; keeping dependencies and features unchanged is appropriate for this incremental release.

examples/lead-agent/runagent_sdk/python/test_sdk.py (1)

5-5: Agent ID update looks fine

The new agent_id wiring is consistent with the rest of the example; no functional issues here.

runagent-dart/pubspec.yaml (1)

3-3: Version bump is consistent

version: 0.1.43 aligns with the rest of the Dart SDK changes; nothing else to adjust here.

runagent-dart/.dart_tool/package_graph.json (1)

8-8: Package graph version matches pubspec

The runagent package version in the package graph is correctly updated to 0.1.43 and stays consistent with pubspec.yaml.

examples/trip_planner/sdk/python/test.py (1)

10-10: Trip planner example updates are consistent

The new AGENT_ID and the commented streaming block’s local=False setting both align this script with remote agent usage. The example remains coherent and ready for users to plug in their own agent ID.

Also applies to: 46-46

README.md (1)

517-614: Persistent Memory docs are clear and consistent across SDKs

The new Persistent Memory section does a good job explaining the serverless‑memory concept and shows accurate, parallel examples for Python, JavaScript, Rust, and Dart using user_id / persistent_memory. The adjusted Action Memory copy also correctly positions it as building on this foundation. No issues from a technical/docs perspective.

Also applies to: 629-639

runagent-dart/lib/src/client/socket_client.dart (1)

20-28: WebSocket streaming now correctly propagates userId and persistentMemory

Adding userId / persistentMemory as optional named parameters and conditionally injecting user_id / persistent_memory into requestData keeps the API backward‑compatible while aligning the WebSocket payload with the REST/Python behavior. The typed <String, dynamic>{} map is also a nice touch for clarity.

Also applies to: 63-77

runagent-dart/lib/src/types/types.dart (2)

32-50: LGTM! Clean addition of persistent memory configuration fields.

The new userId and persistentMemory fields are properly added as optional parameters, maintaining backward compatibility. The implementation is consistent with the existing field patterns in the class.


63-78: Factory method correctly forwards new parameters.

The create factory properly accepts and passes through the userId and persistentMemory parameters, maintaining consistency with the constructor.

runagent/utils/serializer.py (2)

61-64: Empty input handling is reasonable.

Returning None for empty/whitespace input prevents JSON parse errors. The warning log aids debugging.


245-248: Verify intended behavior difference between deserialize methods.

deserialize_object returns None for empty input (line 64), while deserialize_object_from_structured raises ValueError (line 248). If this asymmetry is intentional (structured format requires valid input), consider adding a brief comment explaining the rationale.

templates/agno/agno_persistent/agent.py (2)

146-163: Well-structured streaming implementation.

The streaming handler properly yields session metadata first, streams content chunks, and confirms session completion. The structured output format with type field enables consumers to distinguish metadata from content.


40-45: No action needed. Agno's db.get_sessions() defaults to sorting by created_at in descending order (newest first), so existing_sessions[0] correctly retrieves the latest session. This behavior is documented in Agno's Sessions API and confirmed in their Agent session examples.

templates/agno/agno_persistent/runagent.config.json (2)

14-25: Entrypoints correctly reference agent functions.

The two entrypoints properly map to agent_print_response and agent_print_response_stream functions in agent.py, matching the non-streaming and streaming patterns.


32-32: persistent_folders correctly configured.

The simi folder is properly listed, matching the SQLite database path "simi/agents.db" in agent.py.

runagent-dart/lib/src/client/runagent_client.dart (4)

18-31: Clean addition of persistent memory fields to client.

The userId and persistentMemory fields are properly declared with appropriate types and defaults. The private constructor correctly initializes these fields.


117-118: Configuration values properly forwarded.

The create factory correctly extracts userId and persistentMemory from the config, with a sensible default of false for persistentMemory when not specified.


269-276: REST client call correctly includes new parameters.

The runAgent call properly forwards userId and persistentMemory to enable persistent memory on the server side.


375-382: WebSocket streaming call correctly includes new parameters.

The runStream call properly forwards userId and persistentMemory, maintaining parity with the REST path.

examples/journalist_agent/dart_sdk/lib/main.dart (1)

8-8: Agent ID and log message update look good

The new agentId and simplified log line are clear and consistent with the described config change. No functional issues from this diff.

Also applies to: 14-14

runagent-rust/runagent/src/blocking.rs (1)

147-155: Blocking client accessors cleanly mirror async client

The new user_id and persistent_memory accessors are straightforward delegations to the inner async client and follow the same pattern as agent_id, entrypoint_tag, and extra_params. This keeps the blocking API in sync with the async one without adding complexity.

runagent/sdk/rest_client.py (1)

1507-1512: LGTM!

The conditional logic correctly adds user_id and persistent_memory to the request payload only when provided. This aligns with the cross-language API surface described in the PR.

test/test_lightrag.py (2)

14-24: LGTM!

The test properly clears old data before starting fresh. The hardcoded test directory path (prova/test_rag_storage) makes it clear this is test data, and the logging is informative.


89-94: LGTM!

Good practice to validate required environment variables before execution with a clear error message and usage instructions.

runagent/cli/commands/run_stream.py (2)

65-75: LGTM!

CLI options are well-documented with clear help text that references the SDK implementation. The flag default for --persistent-memory is appropriate.


191-199: LGTM!

The new parameters are correctly propagated to RunAgentClient, maintaining backward compatibility while enabling persistent memory features for streaming agents.

runagent-rust/runagent/src/client/rest_client.rs (1)

187-218: LGTM!

The implementation correctly mirrors the Python SDK pattern for adding persistent storage parameters. The use of Option<&str> for user_id is idiomatic Rust, and the conditional insertion logic using as_object_mut() is appropriate.

runagent/sdk/sdk.py (2)

345-351: LGTM!

The validation correctly enforces that user_id must be provided when persistent_memory=True. This is essential for proper data isolation in multi-tenant scenarios and prevents potential data leakage.

Note: The static analysis hint about the long message string is a minor style preference and doesn't affect functionality.


354-384: LGTM!

The enhanced error handling for local agents is a valuable improvement. It provides actionable diagnostic information by checking deployment status, source existence, and agent state, which significantly improves developer experience when debugging local agent issues.

test_scripts/dart/test_lightrag/lib/main.dart (2)

35-54: LGTM!

The queryRag function correctly configures the RunAgent client with persistent memory parameters and provides a sensible default for the query mode.


56-85: LGTM!

The main function has appropriate error handling for a CLI test script, with specific handling for RunAgentError that extracts and displays suggestions, and a fallback for unexpected errors. The exit codes are correct for error conditions.

runagent/cli/commands/run.py (2)

67-77: LGTM!

CLI options are well-documented and consistent with the streaming command implementation. The help text appropriately references the SDK for detailed documentation.


224-232: LGTM!

The new parameters are correctly passed to RunAgentClient while maintaining backward compatibility. The implementation is consistent with the streaming command.

test_scripts/python/client_test_lightrag.py (1)

27-38: Ingestion error not propagated; consider handling exceptions.

If ingest_client.run() fails, the exception will propagate uncaught. If silent failure is intentional, wrap in try-except; otherwise, the current behavior is acceptable for a test script.

runagent-rust/runagent/src/client/socket_client.rs (2)

100-113: Payload insertion logic is correct and defensive.

The conditional insertion of user_id and persistent_memory matches the Python SDK behavior. The as_object_mut() pattern is safe since the JSON value is guaranteed to be an object from the json! macro above.


76-77: Type difference between RunAgentClientConfig and run_stream is intentional and properly handled.

The method takes persistent_memory: bool while RunAgentClientConfig uses Option<bool>. This is a deliberate design: the config allows callers to leave it unset (None), and the conversion to a concrete bool happens at SocketClient initialization via .unwrap_or(false). Callers of run_stream do not need to unwrap—the adapter layer handles this transparently.

runagent/sdk/socket_client.py (4)

61-61: Signature extended correctly with new parameters.

The async method now accepts user_id and persistent_memory with sensible defaults. The parameter order (after entrypoint_tag, before **input_kwargs) ensures backward compatibility.


95-99: Payload augmentation is correct.

The conditional insertion of user_id (when not None) and persistent_memory (when truthy) matches the expected server contract and aligns with the Rust implementation.


159-159: Sync method signature mirrors async correctly.

Parameter additions are consistent between run_stream and run_stream_async, maintaining API symmetry.


197-201: Sync path payload logic duplicates async path.

The logic is identical to lines 95-99. This duplication is acceptable given the different async/sync implementations, but if this pattern expands, consider extracting a helper to build the common request payload.

runagent/client/client.py (5)

35-37: Persistent storage settings stored correctly.

The new instance variables user_id and persistent_memory are properly initialized and documented.


89-94: Persistent storage parameters correctly forwarded to REST client.

The user_id and persistent_memory values are properly passed through to rest_client.run_agent().


112-115: Direct dict handling added for response payload.

The new branch correctly handles cases where data_field is a dict that should be treated as the actual response. The ordering of checks (after result_data and before fallback) is appropriate.


127-137: Fallback deserialization with broad exception catching is acceptable here.

While static analysis flags broad Exception catches (BLE001), this is a deliberate graceful degradation pattern. The fallback chain (structured → direct → raw) ensures robustness against various response formats. Logging at debug level is appropriate.


159-174: Streaming path correctly updated with persistent storage parameters.

The run_stream method properly forwards user_id and persistent_memory to socket_client.run_stream(), maintaining consistency with the non-streaming path.

examples/agno_persistent/agent.py (5)

12-12: Module-level database singleton is appropriate for this example.

The single SqliteDb instance at module level provides shared session storage. The comment about automatic path mapping (rad/persistent/rad) is helpful for understanding serverless behavior.


87-89: Empty string fallback for missing prompt may cause unexpected agent behavior.

When both prompt and kwargs['message'] are missing, prompt becomes an empty string. Consider raising an error or using a more explicit default:

if prompt is None:
    prompt = kwargs.get('message')
    if prompt is None:
        raise ValueError("Either 'prompt' or 'message' keyword argument is required")

Alternatively, if empty prompts are valid for your use case, this is acceptable.


97-123: Response extraction handles multiple formats robustly.

The cascading checks for .content, .messages, .text, and fallback to str() provide good compatibility with different response object types from the agent.


153-158: Streaming implementation with metadata yields is well-structured.

The pattern of yielding session_info first, then content chunks, then session_end provides clear stream semantics for clients. The hasattr check handles different chunk types gracefully.


40-45: The code's assumption is correct—SqliteDb.get_sessions() returns sessions sorted by created_at in descending order (newest first), so existing_sessions[0] reliably retrieves the latest session. No changes needed.

examples/lightrag_persistent/agent.py (2)

1-11: LGTM!

Imports and module setup are appropriate for a LightRAG example.


13-26: LGTM!

The RAG instance initialization is clean. Storage and pipeline status are properly initialized before returning.

runagent-rust/runagent/src/client/runagent_client.rs (7)

26-30: LGTM!

Field design is appropriate: user_id is optional while persistent_memory defaults to false, maintaining backward compatibility. The comments referencing Python SDK parity are helpful.


98-102: LGTM!

Config fields are correctly typed as Option to support the builder pattern while allowing defaults.


177-189: LGTM!

Builder methods follow the established pattern in this file and are idiomatic Rust.


316-318: LGTM!

Config values are correctly consumed with unwrap_or(false) providing a sensible default for persistent_memory.


547-557: LGTM!

Streaming path correctly mirrors the REST path, passing the same parameters for consistent behavior.


589-598: LGTM!

Getter methods return appropriate types and match the blocking wrapper signatures shown in blocking.rs.


395-405: Code looks good—as_deref() is the correct idiomatic choice for converting Option<String> to Option<&str>, and all parameters match the RestClient::run_agent signature.

Comment on lines +15 to +20
lightrag_agent/
├── agent.py # Main agent implementation
├── requirements.txt # Python dependencies
├── runagent.config.json # RunAgent configuration
├── test_sdk.py # SDK test script
└── README.md # This file
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Tighten naming and path consistency in the docs

A few small inconsistencies that may confuse users trying to follow the example:

  • The tree shows a folder lightrag_agent/, while the actual example path in the repo is examples/lightrag_persistent/. Consider aligning the name or explicitly stating this is a logical project layout.
  • “Persistent Storage” mentions rad/rag_storage/, while the response examples use "working_dir": "rad/rag_storage" vs "working_dir": "rag_storage". It would help to clarify whether working_dir is expected to be a full path (rad/rag_storage) or a relative folder name (rag_storage) and keep it consistent across examples.

These are documentation-only tweaks but will make the README easier to follow.

Also applies to: 107-109, 121-128, 139-147

@@ -1,4 +1,5 @@
ag2
autogen==0.10.1
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for ag2 imports in the examples/trip_planner directory
echo "=== Searching for ag2 imports ==="
rg -n "import ag2|from ag2" examples/trip_planner/ || echo "No ag2 imports found"

# Search for autogen imports in the examples/trip_planner directory
echo -e "\n=== Searching for autogen imports ==="
rg -n "import autogen|from autogen" examples/trip_planner/ || echo "No autogen imports found"

# Check full requirements.txt context
echo -e "\n=== Full requirements.txt content ==="
cat -n examples/trip_planner/agent/requirements.txt

# Check if autogen==0.10.1 exists on PyPI
echo -e "\n=== Checking autogen version on PyPI ==="
curl -s https://pypi.org/pypi/autogen/json | jq '.releases | keys | sort' | grep -A2 -B2 "0.10.1" || echo "Checking specific version..."
curl -s https://pypi.org/pypi/autogen/0.10.1/json 2>/dev/null | jq '.info | {version, requires_python}' || echo "Version 0.10.1 may not exist"

Repository: runagent-dev/runagent

Length of output: 641


Remove ag2[openai] and reconcile autogen dependency specification.

The codebase has been correctly migrated to use autogen imports (confirmed in trip_agent.py), but requirements.txt contains an inconsistency: line 5 still references ag2[openai] instead of autogen[openai]. Additionally, line 4 explicitly pins openai, while line 5 pulls the same package indirectly via ag2[openai], creating redundant dependencies.

Change line 5 from ag2[openai] to autogen[openai] to match the actual package being used, or remove the extra dependency if autogen==0.10.1 already satisfies the openai requirement through its own dependencies.

🤖 Prompt for AI Agents
In examples/trip_planner/agent/requirements.txt around lines 1-5, replace the
outdated ag2[openai] entry (line 5) to match the code's autogen usage: either
change ag2[openai] to autogen[openai] or remove the ag2[openai] line entirely if
autogen==0.10.1 already brings in openai; also remove the explicit openai pin on
line 4 if you choose autogen[openai] to avoid redundant pins, ensuring the
requirements reflect only the actual package names and avoid duplicate openai
installs.

Comment on lines +8 to +12
"template_source": {
"repo_url": "https://github.com/runagent-dev/runagent.git",
"author": "runagent-cli",
"path": "/home/azureuser/runagent/examples/trip_planner/agent"
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Remove absolute path from template_source configuration.

Line 11 contains an absolute path /home/azureuser/runagent/examples/trip_planner/agent that is environment-specific and will not work across different machines or CI/CD environments. This should be either a relative path or use environment variables.

Apply this diff to use a relative path:

  "template_source": {
    "repo_url": "https://github.com/runagent-dev/runagent.git",
    "author": "runagent-cli",
-   "path": "/home/azureuser/runagent/examples/trip_planner/agent"
+   "path": "examples/trip_planner/agent"
  },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"template_source": {
"repo_url": "https://github.com/runagent-dev/runagent.git",
"author": "runagent-cli",
"path": "/home/azureuser/runagent/examples/trip_planner/agent"
},
"template_source": {
"repo_url": "https://github.com/runagent-dev/runagent.git",
"author": "runagent-cli",
"path": "examples/trip_planner/agent"
},
🤖 Prompt for AI Agents
In examples/trip_planner/agent/runagent.config.json around lines 8 to 12, the
template_source.path currently uses an absolute, environment-specific path
(/home/azureuser/runagent/examples/trip_planner/agent); change it to a portable
location by replacing the absolute path with a relative path (e.g.,
"../examples/trip_planner/agent" relative to the repo root) or reference an
environment variable (e.g., "${TEMPLATE_PATH}") and update any docs or startup
scripts to set that variable.

Comment on lines +9 to +12
# Single SQLite DB for all agents/sessions
# Note: The backend automatically maps "rad" folder to /persistent/rad
# So we can use relative path "rad/agents.db" and it will persist across VM restarts
db = SqliteDb(db_file="simi/agents.db")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Stale comment: mentions "rad" folder but code uses "simi".

The comment at lines 10-11 references "rad" folder mapping, but the actual path is "simi/agents.db". Update the comment to reflect the actual folder name or correct the path.

 # Single SQLite DB for all agents/sessions
-# Note: The backend automatically maps "rad" folder to /persistent/rad
-# So we can use relative path "rad/agents.db" and it will persist across VM restarts
+# Note: The backend automatically maps "simi" folder to /persistent/simi
+# So we can use relative path "simi/agents.db" and it will persist across VM restarts
 db = SqliteDb(db_file="simi/agents.db")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Single SQLite DB for all agents/sessions
# Note: The backend automatically maps "rad" folder to /persistent/rad
# So we can use relative path "rad/agents.db" and it will persist across VM restarts
db = SqliteDb(db_file="simi/agents.db")
# Single SQLite DB for all agents/sessions
# Note: The backend automatically maps "simi" folder to /persistent/simi
# So we can use relative path "simi/agents.db" and it will persist across VM restarts
db = SqliteDb(db_file="simi/agents.db")
🤖 Prompt for AI Agents
In templates/agno/agno_persistent/agent.py around lines 9 to 12, the comment
refers to a "rad" folder mapping while the code instantiates the DB with
"simi/agents.db"; update the comment to match the actual directory name (simi)
or change the db_file to "rad/agents.db" so comment and code are consistent.
Make a single-source change: either edit the comment to mention the "simi"
folder mapping and persistence, or update the db_file argument to use
"rad/agents.db" if "rad" is the intended persistent path.

Comment on lines +1 to +124
{
"roots": [
"test_lightrag"
],
"packages": [
{
"name": "test_lightrag",
"version": "0.1.0",
"dependencies": [
"runagent"
],
"devDependencies": []
},
{
"name": "runagent",
"version": "0.1.41",
"dependencies": [
"http",
"web_socket_channel"
]
},
{
"name": "web_socket_channel",
"version": "2.4.5",
"dependencies": [
"async",
"crypto",
"stream_channel",
"web"
]
},
{
"name": "http",
"version": "1.6.0",
"dependencies": [
"async",
"http_parser",
"meta",
"web"
]
},
{
"name": "web",
"version": "0.5.1",
"dependencies": []
},
{
"name": "stream_channel",
"version": "2.1.4",
"dependencies": [
"async"
]
},
{
"name": "crypto",
"version": "3.0.7",
"dependencies": [
"typed_data"
]
},
{
"name": "async",
"version": "2.13.0",
"dependencies": [
"collection",
"meta"
]
},
{
"name": "meta",
"version": "1.17.0",
"dependencies": []
},
{
"name": "http_parser",
"version": "4.1.2",
"dependencies": [
"collection",
"source_span",
"string_scanner",
"typed_data"
]
},
{
"name": "typed_data",
"version": "1.4.0",
"dependencies": [
"collection"
]
},
{
"name": "collection",
"version": "1.19.1",
"dependencies": []
},
{
"name": "string_scanner",
"version": "1.4.1",
"dependencies": [
"source_span"
]
},
{
"name": "source_span",
"version": "1.10.1",
"dependencies": [
"collection",
"path",
"term_glyph"
]
},
{
"name": "term_glyph",
"version": "1.2.2",
"dependencies": []
},
{
"name": "path",
"version": "1.9.1",
"dependencies": []
}
],
"configVersion": 1
} No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Generated file should not be committed to version control.

Files in .dart_tool/ are auto-generated by Dart's tooling (via dart pub get, etc.) and should not be committed to the repository. These files are:

  • Regenerated automatically on each developer's machine
  • Prone to merge conflicts and diff noise
  • Not meant for version control

Add .dart_tool/ to .gitignore if it isn't already present, and remove this file from the PR. Developers will regenerate it automatically when they run dart pub get.

🤖 Prompt for AI Agents
In test_scripts/dart/test_lightrag/.dart_tool/package_graph.json lines 1-124:
this is a generated file that should not be committed; remove it from the PR and
ensure .dart_tool/ is ignored. Delete the file from version control (e.g.,
remove it from the commit or run git rm --cached) and add or confirm an entry
for .dart_tool/ in the repository .gitignore so generated Dart tooling files are
not tracked going forward.

Comment on lines +8 to +11
dependencies:
runagent:
path: /home/azureuser/runagent/runagent-dart

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid hardcoded absolute path for local runagent dependency

Using /home/azureuser/runagent/runagent-dart ties this test setup to a single machine and will fail on other dev/CI environments. Please switch to a repo‑relative path (e.g., ../runagent-dart or similar, depending on your layout).

 dependencies:
   runagent:
-    path: /home/azureuser/runagent/runagent-dart
+    path: ../runagent-dart  # adjust to the correct relative path in this repo
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
dependencies:
runagent:
path: /home/azureuser/runagent/runagent-dart
dependencies:
runagent:
path: ../runagent-dart # adjust to the correct relative path in this repo
🤖 Prompt for AI Agents
In test_scripts/dart/test_lightrag/pubspec.yaml around lines 8 to 11, the
dependency path for runagent is an absolute machine-specific path
(/home/azureuser/runagent/runagent-dart); change it to a repo-relative path (for
example ../runagent-dart or ../runagent/runagent-dart depending on the
repository layout) so the test can run on other dev/CI machines, update the path
accordingly in pubspec.yaml, and verify the referenced directory exists relative
to this test package and adjust CI job workspace paths if necessary.

Comment on lines +39 to +47
# Create client with persistent storage settings
# user_id and persistent_memory are set at client level for persistent storage
ra = RunAgentClient(
agent_id="c778c025-4c9f-4466-886e-14845efe664b",
entrypoint_tag="agno_print_response",
local=False,
user_id="prova", # User ID for persistent storage (VM-level)
persistent_memory=True # Enable persistent storage
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Align new_session usage with the explanatory comment

The active test block is labeled:

# First message - creating a new session

but the call is:

result = ra.run(
    ...,
    session_id="f927c786-87c1-45c4-a201-aaa1711b8f53",
    new_session=False
)

If this is truly the first message of a new session, you’d typically either:

  • Set new_session=True and let the agent create a fresh session_id, or
  • Keep new_session=False but adjust the comment to reflect that you’re resuming an existing session by ID.

Clarifying this will make the example less confusing for anyone copying it as a reference.

Also applies to: 49-56, 58-60

🤖 Prompt for AI Agents
In test_scripts/python/client_test_agno_persistent.py around lines 39-47 (and
similarly for blocks at 49-56 and 58-60), the comment states "First message -
creating a new session" while the call passes session_id and new_session=False;
either update the code to actually create a new session by setting
new_session=True and omitting a fixed session_id (or generate a fresh one) or
keep new_session=False and change the comment to indicate this is resuming an
existing session by ID; make the same consistent change in the other two blocks
so comments and parameters align.

Comment on lines +6 to +14
[dependencies]
# RunAgent Rust SDK from local path
runagent = { path = "/home/azureuser/runagent/runagent-rust/runagent" }

# Required dependencies
tokio = { version = "1.0", features = ["full"] }
serde_json = "1.0"
anyhow = "1.0"
futures = "0.3"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid absolute path for local runagent dependency.

Using /home/azureuser/... makes this test crate non-portable outside your machine (including CI). Prefer a relative path from this manifest to runagent-rust/runagent.

For example, from test_scripts/rust/test_lightrag to runagent-rust/runagent:

-runagent = { path = "/home/azureuser/runagent/runagent-rust/runagent" }
+runagent = { path = "../../../runagent-rust/runagent" }

Adjust the relative path if the directory layout differs.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[dependencies]
# RunAgent Rust SDK from local path
runagent = { path = "/home/azureuser/runagent/runagent-rust/runagent" }
# Required dependencies
tokio = { version = "1.0", features = ["full"] }
serde_json = "1.0"
anyhow = "1.0"
futures = "0.3"
[dependencies]
# RunAgent Rust SDK from local path
runagent = { path = "../../../runagent-rust/runagent" }
# Required dependencies
tokio = { version = "1.0", features = ["full"] }
serde_json = "1.0"
anyhow = "1.0"
futures = "0.3"
🤖 Prompt for AI Agents
In test_scripts/rust/test_lightrag/Cargo.toml lines 6-14, the runagent
dependency uses an absolute path (/home/azureuser/...), making the crate
non-portable; replace that absolute path with a relative path from
test_scripts/rust/test_lightrag to the runagent-rust/runagent directory (e.g.
adjust to something like ../../runagent-rust/runagent depending on your repo
layout), update the path entry in Cargo.toml, and verify by running cargo
metadata or cargo build to ensure Cargo resolves the local dependency correctly.

Comment on lines +9 to +58
#[tokio::main]
async fn main() -> runagent::RunAgentResult<()> {
// Ingest client (persistent memory enabled)
let ingest_client = RunAgentClient::new(RunAgentClientConfig {
agent_id: AGENT_ID.to_string(),
entrypoint_tag: "ingest_text".to_string(),
local: Some(LOCAL_MODE),
host: None,
port: None,
api_key: None,
base_url: None,
extra_params: None,
enable_registry: None,
user_id: Some(USER_ID.to_string()),
persistent_memory: Some(true),
})
.await?;

// Query client (same user_id + persistent_memory)
let query_client = RunAgentClient::new(RunAgentClientConfig {
agent_id: AGENT_ID.to_string(),
entrypoint_tag: "query_rag".to_string(),
local: Some(LOCAL_MODE),
host: None,
port: None,
api_key: None,
base_url: None,
extra_params: None,
enable_registry: None,
user_id: Some(USER_ID.to_string()),
persistent_memory: Some(true),
})
.await?;

// Example query (same as Python test)
let question = "population prediction";
println!("============================================================");
println!("STEP: Query RAG");
println!("============================================================");

let result = query_client
.run(&[
("query", json!(question)),
// mode is optional; default is handled server-side but we match Python example
("mode", json!("hybrid")),
])
.await?;

println!("Result: {}", result);
Ok(())
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clarify or remove unused ingest_client to match the intended LightRAG flow.

You construct an “Ingest client (persistent memory enabled)” but never use ingest_client afterward; only query_client.run(...) is called. This both produces an unused-variable warning and likely diverges from the Python LightRAG test flow that actually performs an ingest step.

Consider either:

  • Wiring in an explicit ingest step, e.g. calling ingest_client.run(&[("text", json!(...))]) before the query, or
  • If ingest is intentionally done elsewhere, remove the unused client (and its comment) to avoid confusion:
-    // Ingest client (persistent memory enabled)
-    let ingest_client = RunAgentClient::new(RunAgentClientConfig {
-        agent_id: AGENT_ID.to_string(),
-        entrypoint_tag: "ingest_text".to_string(),
-        local: Some(LOCAL_MODE),
-        host: None,
-        port: None,
-        api_key: None,
-        base_url: None,
-        extra_params: None,
-        enable_registry: None,
-        user_id: Some(USER_ID.to_string()),
-        persistent_memory: Some(true),
-    })
-    .await?;
-
-    // Query client (same user_id + persistent_memory)
+    // Query client (same user_id + persistent_memory)

The persistent memory configuration on query_client itself looks correct.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#[tokio::main]
async fn main() -> runagent::RunAgentResult<()> {
// Ingest client (persistent memory enabled)
let ingest_client = RunAgentClient::new(RunAgentClientConfig {
agent_id: AGENT_ID.to_string(),
entrypoint_tag: "ingest_text".to_string(),
local: Some(LOCAL_MODE),
host: None,
port: None,
api_key: None,
base_url: None,
extra_params: None,
enable_registry: None,
user_id: Some(USER_ID.to_string()),
persistent_memory: Some(true),
})
.await?;
// Query client (same user_id + persistent_memory)
let query_client = RunAgentClient::new(RunAgentClientConfig {
agent_id: AGENT_ID.to_string(),
entrypoint_tag: "query_rag".to_string(),
local: Some(LOCAL_MODE),
host: None,
port: None,
api_key: None,
base_url: None,
extra_params: None,
enable_registry: None,
user_id: Some(USER_ID.to_string()),
persistent_memory: Some(true),
})
.await?;
// Example query (same as Python test)
let question = "population prediction";
println!("============================================================");
println!("STEP: Query RAG");
println!("============================================================");
let result = query_client
.run(&[
("query", json!(question)),
// mode is optional; default is handled server-side but we match Python example
("mode", json!("hybrid")),
])
.await?;
println!("Result: {}", result);
Ok(())
#[tokio::main]
async fn main() -> runagent::RunAgentResult<()> {
// Query client (same user_id + persistent_memory)
let query_client = RunAgentClient::new(RunAgentClientConfig {
agent_id: AGENT_ID.to_string(),
entrypoint_tag: "query_rag".to_string(),
local: Some(LOCAL_MODE),
host: None,
port: None,
api_key: None,
base_url: None,
extra_params: None,
enable_registry: None,
user_id: Some(USER_ID.to_string()),
persistent_memory: Some(true),
})
.await?;
// Example query (same as Python test)
let question = "population prediction";
println!("============================================================");
println!("STEP: Query RAG");
println!("============================================================");
let result = query_client
.run(&[
("query", json!(question)),
// mode is optional; default is handled server-side but we match Python example
("mode", json!("hybrid")),
])
.await?;
println!("Result: {}", result);
Ok(())
}

@RadeenXALNW RadeenXALNW merged commit d0d7407 into main Dec 4, 2025
2 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Feb 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant