Skip to content

Sawra/runagent cloud support#69

Merged
sawradip merged 14 commits into
mainfrom
sawra/runagent_cloud_support
Sep 25, 2025
Merged

Sawra/runagent cloud support#69
sawradip merged 14 commits into
mainfrom
sawra/runagent_cloud_support

Conversation

@sawradip
Copy link
Copy Markdown
Contributor

@sawradip sawradip commented Sep 25, 2025

Summary by CodeRabbit

  • New Features

    • Added CLI commands: logs, invocation, cleanup, and cleanup-logs.
    • Agent uploads now compute fingerprints to detect duplicates.
  • Improvements

    • Streamlined CLI framework selection, clearer status and error messages.
    • Uploads: stronger validation, progress feedback, and improved error handling.
    • Middleware sync state is explicit; template connectivity checks are faster.
  • Breaking Changes

    • Removed deploy-local CLI command.
    • SDK upload_agent no longer accepts a framework parameter; automatic framework detection removed.
  • Chores

    • Version bumped to 0.1.22 across packages.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Sep 25, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Version bumped to 0.1.22 across multiple language bindings and packaging. Framework handling was refactored to a new Framework enum; CLI, SDK, RestClient, DB, server, and template/template-detection tooling were updated; new agent-id utilities and fingerprinting were added; several CLI commands and sync/deployment flows were changed.

Changes

Cohort / File(s) Summary of changes
Multi-language version bump
pyproject.toml, runagent/__version__.py, runagent-go/runagent/version.go, runagent-rust/runagent/Cargo.toml, runagent-ts/package.json
Bumped package/version constants from 0.1.20 to 0.1.22 and aligned tooling targets.
CLI: commands & registration
runagent/cli/commands.py, runagent/cli/main.py
Refactored framework option handling; added commands (logs, invocation, cleanup); adjusted init/serve signatures and behavior; commented-out deploy_local registration.
CLI utilities for frameworks
runagent/cli/utils.py
New decorator add_framework_options and helper get_selected_framework to add boolean flags and enforce single-framework selection.
SDK DB: agent modeling & address management
runagent/sdk/db.py
Added is_local and fingerprint fields on Agent; added fingerprint lookup, add_remote_agent, update_agent_fingerprint, get_agent_address, extended add_agent with capacity checks and auto host/port allocation, and force_delete_agent.
Middleware sync flags
runagent/sdk/deployment/middleware_sync.py
Introduced sync_enabled and enabled booleans; simplified checks and status reporting for middleware sync.
Remote deployment: simplified upload
runagent/sdk/deployment/remote.py
Removed framework param from upload_agent; commented-out framework detection/metadata in the upload path.
RestClient: upload/deploy & error handling
runagent/sdk/rest_client.py
Reworked base URL construction and error handling; added limits caching; added zip creation, metadata upload, zip upload helpers; fingerprint/duplicate detection and prompts; progress updates; deploy_agent/start_agent orchestration; local deployment info persistence and improved post-upload processing.
SDK surface/API changes
runagent/sdk/sdk.py
Removed detect_framework from public API; simplified upload_agent signature; improved run_agent error diagnostics and docstring updates.
Server executor uses enum
runagent/sdk/server/framework/__init__.py
Switched executor mapping and get_executor() signature to accept Framework enum instances instead of strings.
Local server: framework serialization
runagent/sdk/server/local_server.py
Replaced usages of .value with direct Framework enum when composing payloads and sync data.
Streaming error behavior toggle
runagent/sdk/server/socket_utils.py
Added DISABLE_TRY_CATCH env var to optionally re-raise exceptions during agent stream handling instead of swallowing/logging.
Template connectivity check
runagent/sdk/template_manager.py
Replaced heavy listing with git ls-remote --heads subprocess probe for repo/branch connectivity; extended exception handling.
Framework detection type & validators
runagent/utils/agent.py
detect_framework now returns Framework enum; validators updated to use .value where a string is required; import paths adjusted.
Agent id & fingerprint utilities
runagent/utils/agent_id.py
New module: generate_agent_id, folder fingerprinting (SHA-256 of file metadata), framework/name/env detection, entrypoint heuristics, and metadata aggregation utilities.
Enum reorganization
deleted runagent/utils/enums.py
Removed old enums and type unions; replaced by new framework enum module.
Framework enum module
runagent/utils/enums/framework.py
Added Framework enum with members, validation, category helpers, and cached subsets for pythonic/webhook classifications.
Schema serialization updates
runagent/utils/schema.py
RunAgentConfig.framework typed to Framework; added to_dict() helpers to serialize enums and datetimes (ISO strings) for models.
Changelog & release tooling
CHANGELOG.md, release.sh
Updated changelog entry and release script flow (pre-staging changelog generation, commit-before-tag, push sequence).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant CLI as CLI
  participant SDK as RunAgentSDK
  participant RC as RestClient
  participant DB as Local DB
  participant API as Remote Server

  User->>CLI: runagent deploy <folder>
  CLI->>SDK: upload_agent(folder)
  SDK->>RC: deploy_agent(folder_path)
  RC->>RC: validate & fingerprint folder
  RC->>DB: lookup by fingerprint
  alt fingerprint matches existing
    RC->>CLI: prompt overwrite or new
  end
  RC->>RC: zip folder, build metadata
  RC->>API: POST /agents/metadata
  API-->>RC: agent_id
  RC->>API: PUT /agents/{id}/upload (zip)
  API-->>RC: upload complete
  RC->>API: POST /agents/{id}/start
  API-->>RC: started/status
  RC->>DB: save/update local deployment info
  RC-->>SDK: return result
  SDK-->>CLI: display result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

I hop with joy, version leaps anew,
Enums take place, fingerprints in view.
Zips that travel, uploads that sing,
CLI commands dance and servers spring.
A rabbit cheers—deploys on cue. 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The PR title includes the branch prefix and a vague reference to “cloud support” without clearly summarizing the extensive CLI enhancements, SDK refactoring, version bumps, and remote deployment changes introduced; it is neither concise nor specific about the primary functionality added. Please revise the title to a concise, descriptive sentence that highlights the core change, for example: “Add cloud support and remote deployment features to RunAgent CLI and SDK.”
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1560294 and e024e5c.

⛔ Files ignored due to path filters (1)
  • runagent-ts/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (11)
  • CHANGELOG.md (1 hunks)
  • pyproject.toml (3 hunks)
  • release.sh (2 hunks)
  • runagent-go/runagent/version.go (1 hunks)
  • runagent-rust/runagent/Cargo.toml (1 hunks)
  • runagent-ts/package.json (1 hunks)
  • runagent/__init__.py (1 hunks)
  • runagent/__version__.py (1 hunks)
  • runagent/cli/main.py (1 hunks)
  • runagent/sdk/server/framework/__init__.py (2 hunks)
  • runagent/sdk/template_manager.py (2 hunks)

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: 13

Caution

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

⚠️ Outside diff range comments (6)
runagent/sdk/db.py (1)

1055-1062: Replacing primary key must update all dependent tables

replace_agent updates AgentRun but not AgentInvocation or AgentLog. Without ON UPDATE CASCADE FKs, this leaves dangling references or may violate constraints.

Update all related tables within the same transaction:

                 # Update run records to point to new agent ID
                 session.query(AgentRun).filter(
                     AgentRun.agent_id == old_agent_id
                 ).update({"agent_id": new_agent_id})
+                # Update invocation records
+                session.query(AgentInvocation).filter(
+                    AgentInvocation.agent_id == old_agent_id
+                ).update({"agent_id": new_agent_id})
+                # Update log records
+                session.query(AgentLog).filter(
+                    AgentLog.agent_id == old_agent_id
+                ).update({"agent_id": new_agent_id})

Consider wrapping with FK pragma enabled and verifying integrity after commit.

runagent/cli/commands.py (1)

703-706: Fix broken local deployment path: undefined deploy_local

deploy_local is commented out, but deploy() still invokes it. This will raise at runtime when --local is used.

Apply this diff to route local deployment through the existing serve command:

-            # Use deploy_local command logic
-            ctx = click.get_current_context()
-            ctx.invoke(deploy_local, folder=folder, framework=framework)
+            # Use serve to run locally (deploy_local is not available)
+            ctx = click.get_current_context()
+            ctx.invoke(
+                serve,
+                port=None,
+                host="127.0.0.1",
+                debug=False,
+                replace=None,
+                no_animation=True,
+                animation_style="field",
+                path=Path(folder),
+            )
runagent/sdk/sdk.py (1)

45-50: self.local is never initialized but heavily used (AttributeError risk)

self.local = LocalDeployment(self.config) is commented out, yet many methods call self.local.* (list_local_agents, get_local_capacity, run_agent when local=True, cleanup_local_database, get_local_stats, get_agent_info with local=True, etc.). This will crash at runtime.

Two possible fixes:

  • Reinstate the local deployment manager:
-        # self.local = LocalDeployment(self.config)
+        from .local import LocalDeployment  # adjust path to actual module
+        self.local = LocalDeployment(self.config)
  • Or refactor those methods to use DBService/LocalServer directly (e.g., list agents via self.db_service.list_agents(), capacity via self.db_service.get_database_capacity_info(), local runs via a local runner/server abstraction).
    Please address before release.
runagent/sdk/rest_client.py (3)

753-756: Fix incorrect call: upload_agent takes one arg; passing metadata raises TypeError.

This will crash deploy_agent.

-        upload_result = self.upload_agent(folder_path, metadata)
+        upload_result = self.upload_agent(folder_path)

79-82: Remove default Content-Type: application/json to prevent broken multipart uploads.

Setting a session-level Content-Type forces uploads with files= to use the wrong header. Let requests set it automatically.

-        # Set default headers
-        self.session.headers.update({
-            "accept": "application/json",
-            "content-type": "application/json",
-        })
+        # Set default headers (let `requests` set Content-Type appropriately per request)
+        self.session.headers.update({
+            "accept": "application/json",
+        })
@@
-        # Special case for file uploads - remove content-type for multipart
-        if files and "content-type" in self.session.headers:
-            request_headers["content-type"] = None
+        # No Content-Type override here; requests will set it when using `json=` or `files=`

Also applies to: 149-151


723-729: Make _process_start_result resilient if data is missing.

Avoid KeyError when success is true but response lacks data.

-            result_data = result["data"]
-            endpoint = result_data.get("endpoint")
+            result_data = result.get("data", {})  # fallback to empty
+            endpoint = result_data.get("endpoint")
🧹 Nitpick comments (22)
runagent/cli/utils.py (1)

6-36: Hard-coded text makes CLI help awkward for new frameworks

We embed the framework name twice (“Use LANGCHAIN framework”), which reads clunky and needs editing every time we add or rename frameworks. Consider deriving a readable label (e.g. capitalize words) so help text adjusts automatically.

For example:

-        help_text = f"Use {framework.value.upper()} framework"
+        help_text = f"Use the {framework.value.replace('_', ' ').title()} framework"
runagent/sdk/server/socket_utils.py (1)

260-266: Re-raise gating should parse env var value and note client notification gap

When DISABLE_TRY_CATCH is set, you re-raise and skip sending the "stream_error" to the client. That’s fine for debugging, but it changes client behavior. Also, check the env var by value, not mere presence.

Consider this tighter gate:

-                if os.getenv('DISABLE_TRY_CATCH'):
-                    raise
+                if os.getenv('DISABLE_TRY_CATCH', '').lower() in ('1', 'true', 'yes', 'on'):
+                    raise

Please confirm that the missing client "stream_error" message under DISABLE_TRY_CATCH is intentional.

runagent/sdk/server/local_server.py (3)

34-36: Remove duplicate imports

PortManager and detect_framework/get_agent_config are imported earlier; these re-imports are redundant.

- from runagent.utils.port import PortManager
- from runagent.utils.agent import detect_framework, get_agent_config
+ # (duplicates removed)

160-164: Ensure consistent framework serialization in DB client_info

Storing the string with .value is good. Ensure all outward-facing responses also use string values for consistency.


621-623: Health response should stringify enum

FastAPI will often encode enums, but to avoid surprises, return the string value.

-                "framework": self.agent_framework,
+                "framework": self.agent_framework.value,
runagent/sdk/deployment/middleware_sync.py (2)

140-147: Avoid getattr for own attributes and simplify

You own these attributes; direct access is cleaner. Also, don’t compute middleware availability with getattr.

-        return {
-            "sync_enabled": getattr(self, 'sync_enabled', False),
-            "api_configured": bool(getattr(self, 'api_key', None)),
-            "auth_validated": getattr(self.config, '_config', {}).get("auth_validated", False),
-            "base_url": getattr(self.config, 'base_url', None),
-            "middleware_available": self._test_middleware_connection() if getattr(self, 'rest_client', None) else False
-        }
+        return {
+            "sync_enabled": self.sync_enabled,
+            "api_configured": bool(self.api_key),
+            "auth_validated": getattr(self.config, "_config", {}).get("auth_validated", False),
+            "base_url": getattr(self.config, "base_url", None),
+            "middleware_available": self._test_middleware_connection() if self.rest_client else False,
+        }

187-205: Use JSON body for HTTP calls

If the underlying client expects JSON (common), prefer json= over data= to match server expectations.

-                    self.rest_client.http.post, 
-                    endpoint, 
-                    data=data, 
+                    self.rest_client.http.post,
+                    endpoint,
+                    json=data,
                     timeout=30
-                    self.rest_client.http.put, 
-                    endpoint, 
-                    data=data, 
+                    self.rest_client.http.put,
+                    endpoint,
+                    json=data,
                     timeout=30

Confirm the RestClient.http interface; if it’s requests, json= is appropriate; if it’s something else, adjust accordingly.

Also applies to: 200-207

runagent/sdk/db.py (4)

1254-1266: Accessing new fields depends on migrated schema

agent.is_local and agent.fingerprint read will fail if columns weren’t added. Guard via the migration above; otherwise wrap reads with defaults.

-                    "is_local": agent.is_local,
-                    "fingerprint": agent.fingerprint,
+                    "is_local": getattr(agent, "is_local", True),
+                    "fingerprint": getattr(agent, "fingerprint", None),

Note: This only papers over reads; you still need the migration to avoid SELECT errors.


1284-1321: Add Optional typing and narrow exception handling

  • Type hint optional params to satisfy static checks.
  • Avoid blanket except Exception if possible.
-    def get_agent_by_fingerprint(self, fingerprint: str) -> Optional[Dict]:
+    def get_agent_by_fingerprint(self, fingerprint: str) -> Optional[Dict]:
         """Get agent information by fingerprint"""
         with self.db_manager.get_session() as session:
             try:
                 agent = session.query(Agent).filter(
                     Agent.fingerprint == fingerprint
                 ).first()
                 ...
-            except Exception as e:
+            except Exception as e:
                 console.print(f"Error getting agent by fingerprint from database: {e}")
                 return None

And at the function definition level (earlier import already includes Optional):

-        framework: str = None,
-        fingerprint: str = None,
+        framework: Optional[str] = None,
+        fingerprint: Optional[str] = None,

1368-1408: Remove f-string without placeholders and address lint nits

One message uses an f-string with no interpolation.

-                            "error": f"Agent with same fingerprint already exists",
+                            "error": "Agent with same fingerprint already exists",

1417-1433: Fingerprint update (LGTM), but consider return info

Method is fine. Optionally return whether a row was actually updated to aid callers.

-                session.commit()
-                return True
+                session.commit()
+                return True  # consider returning agent.fingerprint == fingerprint for idempotence
runagent/cli/commands.py (4)

890-896: Guard None return from get_middleware_sync()

get_middleware_sync() may return None; calling is_sync_enabled() on None will raise. You already catch exceptions, but a small guard avoids noisy logs and improves UX.

-            sync_service = get_middleware_sync()
-            sync_enabled = sync_service.is_sync_enabled()
+            sync_service = get_middleware_sync()
+            sync_enabled = sync_service.is_sync_enabled() if sync_service else False

300-305: Preserve original exception context when re‑raising

Chain the original ValueError when converting to click.UsageError for better debuggability (ruff B904).

-            except ValueError as e:
-                raise click.UsageError(str(e))
+            except ValueError as e:
+                raise click.UsageError(str(e)) from e

307-313: Avoid ValueError on relative path calculation

relative_to(Path.cwd()) raises if project_path is outside CWD. Fall back to absolute path for display.

-        relative_project_path = project_path.relative_to(Path.cwd())
+        try:
+            relative_project_path = project_path.relative_to(Path.cwd())
+        except ValueError:
+            relative_project_path = project_path

314-314: Remove unnecessary f-strings with no placeholders

These f-strings have no interpolations (ruff F541). Drop the f prefix.

-        console.print(f"\n🚀 [bold]Initializing project:[/bold]")
+        console.print("\n🚀 [bold]Initializing project:[/bold]")
-            console.print(f"\n✅ [green]Project initialized successfully![/green]")
+            console.print("\n✅ [green]Project initialized successfully![/green]")
-            console.print(f"\n📝 [bold]Next steps:[/bold]")
+            console.print("\n📝 [bold]Next steps:[/bold]")
-                console.print(f"  3. Install dependencies: [cyan]pip install -r requirements.txt[/cyan]")
+                console.print("  3. Install dependencies: [cyan]pip install -r requirements.txt[/cyan]")
-                console.print(f"  3. Configure webhook endpoints in your workflow")
+                console.print("  3. Configure webhook endpoints in your workflow")
-            console.print(
-                f"  5. Test: [cyan]Test the agent with any of our SDKs. For more details, refer to: [link]https://docs.run-agent.ai/sdk/overview[/link][/cyan]"
-            )
+            console.print(
+                "  5. Test: [cyan]Test the agent with any of our SDKs. For more details, refer to: [link]https://docs.run-agent.ai/sdk/overview[/link][/cyan]"
+            )

Also applies to: 335-335, 341-341, 345-345, 348-348, 354-354

runagent/utils/schema.py (2)

65-71: Leverage Pydantic config to serialize enums/datetimes

You already added a manual to_dict. Enabling Pydantic’s model_config ensures consistent JSON-ready dumps across the board and reduces custom logic.

-    # model_config = ConfigDict(
-    #     use_enum_values=True,  # Automatically convert enums to values
-    #     json_encoders={
-    #         datetime: lambda v: v.isoformat()  # Custom datetime serialization
-    #     }
-    # )
+    model_config = ConfigDict(
+        use_enum_values=True,
+        json_encoders={datetime: lambda v: v.isoformat()},
+    )

Note: Keeping to_dict() is fine for explicit control, but with the config above, model_dump() will already emit the desired shapes.


88-100: Minor: simplify to_dict() with model_config enabled

If you enable model_config, framework and created_at should already be serialized properly; the enum/datetime conversion code can be dropped. If you keep it, no harm—just redundant.

runagent/utils/agent_id.py (1)

28-45: Improve fingerprint robustness and exclusions

  • __pycache__ check on filename won’t exclude compiled files inside that dir; explicitly skip paths containing it.
  • Consider guarding JSON dump/hash with stable ordering (you already set sort_keys=True—good).
-    for file_path in sorted(folder_path.rglob("*")):
+    for file_path in sorted(folder_path.rglob("*")):
         if file_path.is_file() and not file_path.name.startswith("."):
-            # Skip unnecessary files
-            if file_path.name in ["__pycache__", ".DS_Store", "Thumbs.db"]:
+            # Skip unnecessary files/paths
+            if "__pycache__" in file_path.parts or file_path.name in [".DS_Store", "Thumbs.db"]:
                 continue
             if file_path.suffix in [".pyc", ".pyo", ".log"]:
                 continue
runagent/utils/enums/framework.py (1)

19-30: Prefer class-level frozensets over “cache” methods.

The “cache” methods recompute a frozenset each call. Define class-level frozensets once and use them.

 class Framework(Enum):
@@
-    @classmethod
-    def _pythonic_frameworks_cache(cls) -> t.FrozenSet['Framework']:
-        return frozenset({
-                cls.AG2, cls.AGNO, cls.AUTOGEN, cls.CREWAI,
-                cls.LANGCHAIN, cls.LANGGRAPH, cls.LETTA,
-                cls.LLAMAINDEX, cls.OPENAI
-            })
+    _PYTHONIC: t.ClassVar[t.FrozenSet['Framework']] = frozenset({
+        AG2, AGNO, AUTOGEN, CREWAI, LANGCHAIN, LANGGRAPH, LETTA, LLAMAINDEX, OPENAI
+    })
@@
-    @classmethod
-    def _webhook_frameworks_cache(cls) -> t.FrozenSet['Framework']:
-        return frozenset({cls.N8N})
+    _WEBHOOK: t.ClassVar[t.FrozenSet['Framework']] = frozenset({N8N})
@@
     @classmethod
     def get_pythonic_frameworks(cls) -> t.FrozenSet['Framework']:
         """Get all pythonic frameworks (cached)"""
-        return cls._pythonic_frameworks_cache()
+        return cls._PYTHONIC
@@
     @classmethod
     def get_webhook_frameworks(cls) -> t.FrozenSet['Framework']:
         """Get all webhook frameworks (cached)"""
-        return cls._webhook_frameworks_cache()
+        return cls._WEBHOOK

Also applies to: 31-40

runagent/sdk/rest_client.py (3)

518-523: Optional: remove f-strings without placeholders (F541).

A few console.print(f"...") strings have no braces. Drop the f prefix to satisfy Ruff.

As per static analysis hints.

Also applies to: 653-656


22-27: Optional: remove unused imports.

get_agent_metadata and base64 are unused.

Also applies to: 30-30


844-1262: Remove large commented-out legacy code.

This obscures the current implementation and increases maintenance overhead.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 85ff10d and 1560294.

⛔ Files ignored due to path filters (1)
  • runagent-ts/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (22)
  • pyproject.toml (3 hunks)
  • runagent-go/runagent/version.go (1 hunks)
  • runagent-rust/runagent/Cargo.toml (1 hunks)
  • runagent-ts/package.json (1 hunks)
  • runagent/__version__.py (1 hunks)
  • runagent/cli/commands.py (8 hunks)
  • runagent/cli/main.py (1 hunks)
  • runagent/cli/utils.py (1 hunks)
  • runagent/sdk/db.py (4 hunks)
  • runagent/sdk/deployment/middleware_sync.py (3 hunks)
  • runagent/sdk/deployment/remote.py (2 hunks)
  • runagent/sdk/rest_client.py (9 hunks)
  • runagent/sdk/sdk.py (2 hunks)
  • runagent/sdk/server/framework/__init__.py (2 hunks)
  • runagent/sdk/server/local_server.py (6 hunks)
  • runagent/sdk/server/socket_utils.py (2 hunks)
  • runagent/sdk/template_manager.py (2 hunks)
  • runagent/utils/agent.py (5 hunks)
  • runagent/utils/agent_id.py (1 hunks)
  • runagent/utils/enums.py (0 hunks)
  • runagent/utils/enums/framework.py (1 hunks)
  • runagent/utils/schema.py (3 hunks)
💤 Files with no reviewable changes (1)
  • runagent/utils/enums.py
🧰 Additional context used
🧬 Code graph analysis (10)
runagent/cli/utils.py (1)
runagent/utils/enums/framework.py (2)
  • Framework (6-156)
  • get_selectable_frameworks (42-44)
runagent/utils/schema.py (2)
runagent/constants.py (1)
  • Framework (50-55)
runagent/utils/response.py (1)
  • to_dict (5-60)
runagent/sdk/deployment/remote.py (2)
runagent/sdk/rest_client.py (1)
  • upload_agent (535-700)
runagent/sdk/sdk.py (1)
  • upload_agent (234-252)
runagent/sdk/server/local_server.py (1)
runagent/utils/agent_id.py (1)
  • generate_agent_id (12-14)
runagent/sdk/server/framework/__init__.py (2)
runagent/constants.py (1)
  • Framework (50-55)
runagent/utils/schema.py (2)
  • PythonicEntryPoint (18-27)
  • WebHookEntryPoint (30-40)
runagent/sdk/sdk.py (2)
runagent/sdk/rest_client.py (1)
  • upload_agent (535-700)
runagent/sdk/deployment/remote.py (1)
  • upload_agent (54-75)
runagent/utils/agent.py (2)
runagent/constants.py (1)
  • Framework (50-55)
runagent/utils/enums/framework.py (1)
  • is_pythonic (64-66)
runagent/sdk/db.py (1)
runagent/cli/commands.py (1)
  • status (1129-1326)
runagent/sdk/rest_client.py (6)
runagent/utils/config.py (1)
  • Config (17-320)
runagent/utils/agent_id.py (3)
  • generate_agent_id (12-14)
  • generate_agent_fingerprint (17-45)
  • get_agent_metadata (104-122)
runagent/utils/agent.py (2)
  • get_agent_config (10-93)
  • validate_agent (112-138)
runagent/sdk/deployment/remote.py (1)
  • upload_agent (54-75)
runagent/sdk/sdk.py (1)
  • upload_agent (234-252)
runagent/utils/schema.py (2)
  • to_dict (88-100)
  • to_dict (189-197)
runagent/cli/commands.py (5)
runagent/cli/utils.py (2)
  • add_framework_options (6-16)
  • get_selected_framework (20-35)
runagent/utils/enums/framework.py (4)
  • get_selectable_frameworks (42-44)
  • is_pythonic (64-66)
  • is_webhook (68-70)
  • is_default (72-74)
runagent/sdk/sdk.py (3)
  • list_templates (121-133)
  • init_project (151-176)
  • upload_agent (234-252)
runagent/utils/agent_id.py (1)
  • generate_agent_id (12-14)
runagent/utils/agent.py (1)
  • detect_framework (96-109)
🪛 Ruff (0.13.1)
runagent/sdk/template_manager.py

29-29: subprocess call: check for execution of untrusted input

(S603)


30-30: Starting a process with a partial executable path

(S607)

runagent/cli/utils.py

31-33: Avoid specifying long messages outside the exception class

(TRY003)

runagent/utils/agent_id.py

73-73: Do not use bare except

(E722)


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

(S110)


98-98: Do not use bare except

(E722)


98-99: try-except-pass detected, consider logging the exception

(S110)


148-148: Do not use bare except

(E722)


154-154: Do not use bare except

(E722)


154-155: try-except-pass detected, consider logging the exception

(S110)


181-181: Do not use bare except

(E722)


181-182: try-except-pass detected, consider logging the exception

(S110)

runagent/sdk/server/framework/__init__.py

19-19: Unused function argument: agent_entrypoints

(ARG001)

runagent/utils/agent.py

141-141: Unused function argument: config

(ARG001)


141-141: Unused function argument: dynamic_loading

(ARG001)


141-141: Unused function argument: folder_path

(ARG001)

runagent/sdk/db.py

1318-1318: Do not catch blind exception: Exception

(BLE001)


1326-1326: PEP 484 prohibits implicit Optional

Convert to Optional[T]

(RUF013)


1327-1327: PEP 484 prohibits implicit Optional

Convert to Optional[T]

(RUF013)


1376-1376: f-string without any placeholders

Remove extraneous f prefix

(F541)


1401-1407: Consider moving this statement to an else block

(TRY300)


1409-1409: Do not catch blind exception: Exception

(BLE001)


1413-1413: Use explicit conversion flag

Replace with conversion flag

(RUF010)


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

(TRY300)


1429-1429: Do not catch blind exception: Exception

(BLE001)

runagent/utils/enums/framework.py

53-53: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


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

(TRY003)


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

(TRY300)


89-89: Redefinition of unused from_string from line 47

(F811)


95-95: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


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

(TRY003)


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

(TRY300)


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

(TRY300)

runagent/sdk/rest_client.py

433-433: Do not catch blind exception: Exception

(BLE001)


434-434: Use explicit conversion flag

Replace with conversion flag

(RUF010)


503-503: f-string without any placeholders

Remove extraneous f prefix

(F541)


507-507: Do not catch blind exception: Exception

(BLE001)


508-508: Use explicit conversion flag

Replace with conversion flag

(RUF010)


546-546: f-string without any placeholders

Remove extraneous f prefix

(F541)


552-552: f-string without any placeholders

Remove extraneous f prefix

(F541)


561-561: f-string without any placeholders

Remove extraneous f prefix

(F541)


566-566: f-string without any placeholders

Remove extraneous f prefix

(F541)


567-567: Do not catch blind exception: Exception

(BLE001)


568-568: Use explicit conversion flag

Replace with conversion flag

(RUF010)


587-587: f-string without any placeholders

Remove extraneous f prefix

(F541)


611-611: f-string without any placeholders

Remove extraneous f prefix

(F541)


615-615: f-string without any placeholders

Remove extraneous f prefix

(F541)


627-627: f-string without any placeholders

Remove extraneous f prefix

(F541)


628-628: f-string without any placeholders

Remove extraneous f prefix

(F541)


629-629: f-string without any placeholders

Remove extraneous f prefix

(F541)


630-630: f-string without any placeholders

Remove extraneous f prefix

(F541)

runagent/cli/commands.py

304-304: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


314-314: f-string without any placeholders

Remove extraneous f prefix

(F541)


335-335: f-string without any placeholders

Remove extraneous f prefix

(F541)


341-341: f-string without any placeholders

Remove extraneous f prefix

(F541)


345-345: f-string without any placeholders

Remove extraneous f prefix

(F541)


348-348: f-string without any placeholders

Remove extraneous f prefix

(F541)


354-354: f-string without any placeholders

Remove extraneous f prefix

(F541)


375-375: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


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

(TRY003)


615-615: Abstract raise to an inner function

(TRY301)


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

(TRY003)


617-617: f-string without any placeholders

Remove extraneous f prefix

(F541)

🔇 Additional comments (10)
runagent/sdk/server/local_server.py (3)

71-73: Use enum instance for executor selection (LGTM)

Switching to the enum for agent_framework aligns with get_executor expecting a Framework enum.


108-121: Serialize framework as string for middleware sync (LGTM)

Using self.agent_framework.value for the sync payload avoids enum serialization issues.


551-553: Use centralized agent ID generation (LGTM)

Switch to generate_agent_id() improves consistency and testability.

runagent/sdk/deployment/middleware_sync.py (1)

21-23: Explicit flags for sync state (LGTM)

Introducing sync_enabled/enabled improves clarity over getattr checks.

runagent/sdk/deployment/remote.py (1)

54-76: LGTM: upload signature simplification is coherent with RestClient API

Removing framework detection from upload_agent and delegating to RestClient.upload_agent(folder_path=...) aligns with the new flow. Folder validation is retained. No issues found.

runagent/utils/enums/framework.py (1)

21-25: Confirm categorization: should OPENAI be in pythonic set?

OPENAI is currently classified as pythonic. Verify this matches runtime expectations elsewhere (e.g., validation and deployment flows).

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

3-3: Version alignment confirmed

The crate version bump to 0.1.21 lines up with the rest of the release artifacts. ✅

runagent-ts/package.json (1)

3-3: TS package version looks good

Thanks for keeping the TypeScript client in sync at 0.1.21.

runagent-go/runagent/version.go (1)

4-4: Go SDK version bump acknowledged

Version now reflects 0.1.21—consistent with the other SDKs.

runagent/__version__.py (1)

1-1: Python package version updated

__version__ now matches the 0.1.21 release tag. 👍

Comment thread pyproject.toml Outdated
Comment on lines 106 to 107
python_version = "0.1.21"
warn_return_any = true
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

Restore valid interpreter targets for mypy & Ruff

Both python_version (Line 106) and target-version (Line 162) now point to the package’s semver ("0.1.21"), but these settings must remain valid Python interpreter identifiers (e.g., "3.11" / "py311"). With the current values, mypy and Ruff will bail out on startup because they can’t parse the version strings, which blocks lint/type-check steps. Please revert to a real interpreter target.

-[tool.mypy]
-python_version = "0.1.21"
+[tool.mypy]
+python_version = "3.11"-[tool.ruff]
-target-version = "0.1.21"
+[tool.ruff]
+target-version = "py311"

Adjust the exact interpreter number if you support a different runtime, but it must follow the formats above to keep the tooling working.

Also applies to: 162-163

🤖 Prompt for AI Agents
In pyproject.toml around lines 106-107 and 162-163, the python_version and
target-version settings were set to the package semver "0.1.21" which is invalid
for mypy/Ruff; change python_version to a valid interpreter identifier like
"3.11" (or whichever supported runtime you use, e.g., "3.10", "3.12") and change
target-version to a valid Ruff target such as "py311" (or corresponding
py310/py312) so both tools can parse the interpreter target and resume
linting/type-checking.

Comment thread runagent/cli/main.py
runagent.add_command(commands.init)
runagent.add_command(commands.template)
runagent.add_command(commands.deploy_local)
# runagent.add_command(commands.deploy_local)
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

Don’t silently drop deploy_local

Commenting out the registration removes the runagent deploy_local CLI command, breaking existing workflows without warning. If the command is truly deprecated, please remove it formally (code + tests + docs) and note the breaking change; otherwise, keep it registered until a proper replacement ships.

🤖 Prompt for AI Agents
In runagent/cli/main.py at line 16 the deploy_local command registration has
been commented out, which silently removes the `runagent deploy_local` CLI
command; restore the CLI by uncommenting or re-adding the registration
(runagent.add_command(commands.deploy_local)) so the command remains available,
or if you intend to deprecate it, remove the commented line entirely and instead
add a formal deprecation: update tests to reflect removal, add a deprecation
notice in the CLI entrypoint that informs users and returns a clear error/exit
code, and update docs/release notes to record the breaking change.

Comment thread runagent/sdk/db.py
Comment on lines +48 to 50
is_local = Column(Boolean, default=True) # True for local agents, False for remote uploads
fingerprint = Column(String, nullable=True) # Agent folder fingerprint for duplicate detection
deployed_at = Column(DateTime, default=func.current_timestamp())
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

New columns require migration; current create_all() won’t add them

Adding is_local and fingerprint to Agent will break on existing SQLite DBs: queries will error with “no such column” until a migration runs. Base.metadata.create_all() doesn’t alter existing tables.

Add a lightweight migration in DBManager._init_database to ALTER TABLE when columns are missing. Example (outside this range):

# In DBManager._init_database(), after create_all()
from sqlalchemy import inspect, text
insp = inspect(self.engine)
cols = {c["name"] for c in insp.get_columns("agents")}
with self.engine.begin() as conn:
    if "is_local" not in cols:
        conn.execute(text("ALTER TABLE agents ADD COLUMN is_local BOOLEAN DEFAULT 1"))
    if "fingerprint" not in cols:
        conn.execute(text("ALTER TABLE agents ADD COLUMN fingerprint TEXT"))

Also ensure any queries touching these columns handle absent data gracefully on first run.

🤖 Prompt for AI Agents
In runagent/sdk/db.py around lines 48-50, adding is_local and fingerprint fields
will break existing SQLite DBs because create_all() won't alter tables; update
DBManager._init_database (immediately after
Base.metadata.create_all(self.engine)) to inspect the agents table and run ALTER
TABLE to add is_local BOOLEAN with default true (1) and fingerprint TEXT when
those columns are missing, using a connection.begin() and executing ALTER
statements; finally, ensure code that queries these columns tolerates missing
values on first run (e.g., use .get with defaults or coalesce) until the
migration runs.

Comment on lines +593 to +596
from rich.prompt import Confirm
overwrite = Confirm.ask("Do you want to overwrite the existing agent?", default=False)

if not overwrite:
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 interactive prompts in SDK in non-interactive environments.

Confirm.ask/Prompt.ask can block when used programmatically. Default to safe choices when not interactive.

-                from rich.prompt import Confirm
-                overwrite = Confirm.ask("Do you want to overwrite the existing agent?", default=False)
+                from rich.prompt import Confirm
+                overwrite = Confirm.ask("Do you want to overwrite the existing agent?", default=False) if console.is_interactive else False
@@
-                from rich.prompt import Prompt
-                choice = Prompt.ask(
-                    "What would you like to do?",
-                    choices=["overwrite", "new", "cancel"],
-                    default="new"
-                )
+                from rich.prompt import Prompt
+                if console.is_interactive:
+                    choice = Prompt.ask(
+                        "What would you like to do?",
+                        choices=["overwrite", "new", "cancel"],
+                        default="new"
+                    )
+                else:
+                    choice = "new"

Also applies to: 618-624

🤖 Prompt for AI Agents
In runagent/sdk/rest_client.py around lines 593-596 (and similarly 618-624), the
code uses rich.prompt Confirm/Prompt which can block in non-interactive
environments; replace interactive prompts with a non-blocking default behavior:
detect if stdin is a TTY or accept an explicit parameter/env flag to control
overwrite, default to False (do not overwrite) when not interactive, and log the
decision. Update function signatures or callers to accept an optional overwrite
boolean (or use os.environ/argument) and remove Confirm.ask/Prompt.ask calls so
the SDK never blocks when used programmatically.

Comment on lines 527 to +665
with Progress(
SpinnerColumn(),
TextColumn("[bold green]Uploading...[/bold green]"),
TextColumn("[bold green]{task.description}[/bold green]"),
BarColumn(bar_width=40),
TextColumn("[bold]{task.percentage:>3.0f}%"),
TimeElapsedColumn(),
TimeRemainingColumn(),
console=console,
) as progress:
upload_task = progress.add_task("Uploading...", total=100)
result = self._upload_to_server_secure(zip_path, upload_metadata, progress, upload_task)
upload_task = progress.add_task("Initializing upload...", total=100)
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

Guard temp zip cleanup to avoid masking errors (UnboundLocalError).

If an exception occurs before zip_path is set, os.unlink(zip_path) will raise and hide the original error.

             with Progress(
                 SpinnerColumn(),
                 TextColumn("[bold green]{task.description}[/bold green]"),
                 BarColumn(bar_width=40),
                 TextColumn("[bold]{task.percentage:>3.0f}%"),
                 TimeElapsedColumn(),
                 TimeRemainingColumn(),
                 console=console,
             ) as progress:
+                zip_path = None
                 upload_task = progress.add_task("Initializing upload...", total=100)
@@
-            # Clean up zip file
-            os.unlink(zip_path)
+            # Clean up zip file
+            if zip_path and os.path.exists(zip_path):
+                os.unlink(zip_path)

Also applies to: 690-691

Comment on lines +62 to +74
try:
with open(folder_path / "requirements.txt", "r") as f:
content = f.read().lower()
if "langchain" in content:
return "langchain"
elif "crewai" in content:
return "crewai"
elif "autogen" in content:
return "autogen"
elif "openai" in content:
return "openai"
except:
pass
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Replace bare excepts with safe, explicit handling

Bare except: hides real errors (ruff E722/S110). Use except Exception as e: and follow the project pattern with DISABLE_TRY_CATCH to optionally re-raise.

-        except:
-            pass
+        except Exception:
+            if os.getenv("DISABLE_TRY_CATCH"):
+                raise
+            # best-effort detection; ignore errors
+            pass

Apply similarly to the other try/except blocks in this file.

Also applies to: 86-100, 148-155, 174-182

🧰 Tools
🪛 Ruff (0.13.1)

73-73: Do not use bare except

(E722)


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

(S110)

🤖 Prompt for AI Agents
In runagent/utils/agent_id.py around lines 62 to 74 (and apply same changes to
blocks at 86-100, 148-155, 174-182), replace the bare "except:" handlers with
explicit "except Exception as e:" and follow the repository pattern by checking
DISABLE_TRY_CATCH to re-raise when set; log or ignore the exception as
appropriate (e.g., process_logger.debug or similar) and only suppress when
DISABLE_TRY_CATCH is false, ensuring exceptions are not silently swallowed.

Comment thread runagent/utils/agent.py
Comment on lines +133 to +137
if config.framework.is_pythonic():
is_valid, details = validate_pythonic_agent(config, dynamic_loading, folder_path)
else:
is_valid, details = validate_webhook_agent(config, dynamic_loading, folder_path)

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

Handle framework being unset before calling enum helpers

For configs that omit the framework field we now raise AttributeError because config.framework is None and we call is_pythonic() on it. Previously we treated missing frameworks as non-pythonic and continued validation. Please guard against None (or default to Framework.DEFAULT) before invoking the helper.

Apply this diff to short-circuit safely:

-    if config.framework.is_pythonic():
+    framework = config.framework or Framework.DEFAULT
+    if framework.is_pythonic():
-        is_valid, details = validate_pythonic_agent(config, dynamic_loading, folder_path)
+        is_valid, details = validate_pythonic_agent(config, dynamic_loading, folder_path)
     else:
         is_valid, details = validate_webhook_agent(config, dynamic_loading, folder_path)
🤖 Prompt for AI Agents
In runagent/utils/agent.py around lines 133 to 137, calling
config.framework.is_pythonic() will raise AttributeError when framework is None;
guard against None (or default to Framework.DEFAULT) before invoking enum
helper. Update the code to obtain a safe_framework = config.framework if
config.framework is not None else Framework.DEFAULT (or use getattr(config,
"framework", Framework.DEFAULT)), then call safe_framework.is_pythonic() to
choose between validate_pythonic_agent and validate_webhook_agent; ensure
Framework is imported/available in this module and keep behavior identical for
existing non-None frameworks.

Comment thread runagent/utils/agent.py
Comment on lines +237 to 238
validation_details["framework"] = detect_framework(folder_path).value
except Exception as e:
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

detect_framework may now return None

When no framework is declared we bubble up None, then immediately access .value, which crashes. Please fall back to Framework.DEFAULT (or raise a helpful validation error) so validation remains resilient.

Apply this diff to cover the missing case:

-        validation_details["framework"] = detect_framework(folder_path).value
+        detected = detect_framework(folder_path)
+        validation_details["framework"] = (detected or Framework.DEFAULT).value
📝 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
validation_details["framework"] = detect_framework(folder_path).value
except Exception as e:
detected = detect_framework(folder_path)
validation_details["framework"] = (detected or Framework.DEFAULT).value
except Exception as e:
🧰 Tools
🪛 Ruff (0.13.1)

238-238: Do not catch blind exception: Exception

(BLE001)

🤖 Prompt for AI Agents
In runagent/utils/agent.py around lines 237 to 238, detect_framework(...) can
return None but the code immediately accesses .value causing a crash; update the
code to handle the None case by falling back to Framework.DEFAULT (or raise a
clear validation error). Concretely, capture the result of
detect_framework(folder_path) into a variable, check if it is None and if so set
validation_details["framework"] to Framework.DEFAULT.value (or raise a
ValidationError with a helpful message), otherwise set it to the detected
framework's .value; ensure Framework is imported and add a brief unit test or
assertion to cover the None case.

Comment on lines +49 to +54
try:
return cls(value)
except ValueError:
valid_values = [f.value for f in cls]
raise ValueError(f"Invalid framework: '{value}'. Valid options: {valid_values}")

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

Preserve original exception context in from_string.

Chain the original ValueError for easier debugging (B904).

-        try:
-            return cls(value)
-        except ValueError:
-            valid_values = [f.value for f in cls]
-            raise ValueError(f"Invalid framework: '{value}'. Valid options: {valid_values}")
+        try:
+            return cls(value)
+        except ValueError as err:
+            valid_values = [f.value for f in cls]
+            raise ValueError(f"Invalid framework: '{value}'. Valid options: {valid_values}") from err

As per static analysis hints.

📝 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
try:
return cls(value)
except ValueError:
valid_values = [f.value for f in cls]
raise ValueError(f"Invalid framework: '{value}'. Valid options: {valid_values}")
try:
return cls(value)
except ValueError as err:
valid_values = [f.value for f in cls]
raise ValueError(f"Invalid framework: '{value}'. Valid options: {valid_values}") from err
🧰 Tools
🪛 Ruff (0.13.1)

53-53: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


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

(TRY003)

🤖 Prompt for AI Agents
In runagent/utils/enums/framework.py around lines 49 to 54, the from_string
method catches a ValueError but does not preserve the original exception
context; modify the except block to capture the original exception (e.g., except
ValueError as e:) and re-raise the new ValueError using exception chaining
(raise ValueError(f"Invalid framework: '{value}'. Valid options:
{valid_values}") from e) so the original traceback is preserved for debugging.

Comment on lines +88 to +105
@classmethod
def from_string(cls, framework_str: str) -> 'Framework':
"""Convert string to Framework enum with validation"""
try:
return cls(framework_str)
except ValueError:
valid_frameworks = [f.value for f in cls]
raise ValueError(f"Invalid framework: '{framework_str}'. Valid options: {valid_frameworks}")

@classmethod
def is_valid_framework_string(cls, framework_str: str) -> bool:
"""Check if string is a valid framework"""
try:
cls(framework_str)
return True
except ValueError:
return False

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

Remove duplicate methods (F811) and keep a single source of truth.

There’s a second from_string and a duplicate validity checker. This shadows the earlier method (F811) and adds unnecessary surface area. Remove the duplicate block and keep the earlier from_string + validate_framework_str.

-    # Class methods for string validation and conversion
-    @classmethod
-    def from_string(cls, framework_str: str) -> 'Framework':
-        """Convert string to Framework enum with validation"""
-        try:
-            return cls(framework_str)
-        except ValueError:
-            valid_frameworks = [f.value for f in cls]
-            raise ValueError(f"Invalid framework: '{framework_str}'. Valid options: {valid_frameworks}")
-
-    @classmethod
-    def is_valid_framework_string(cls, framework_str: str) -> bool:
-        """Check if string is a valid framework"""
-        try:
-            cls(framework_str)
-            return True
-        except ValueError:
-            return False

As per static analysis hints.

📝 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
@classmethod
def from_string(cls, framework_str: str) -> 'Framework':
"""Convert string to Framework enum with validation"""
try:
return cls(framework_str)
except ValueError:
valid_frameworks = [f.value for f in cls]
raise ValueError(f"Invalid framework: '{framework_str}'. Valid options: {valid_frameworks}")
@classmethod
def is_valid_framework_string(cls, framework_str: str) -> bool:
"""Check if string is a valid framework"""
try:
cls(framework_str)
return True
except ValueError:
return False
🧰 Tools
🪛 Ruff (0.13.1)

89-89: Redefinition of unused from_string from line 47

(F811)


95-95: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


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

(TRY003)


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

(TRY300)

🤖 Prompt for AI Agents
In runagent/utils/enums/framework.py around lines 88 to 105, there is a
duplicate from_string and validity-checker block that shadows earlier
definitions (F811); remove this duplicated block entirely and keep the earlier
from_string plus validate_framework_str as the single source of truth; update
any local calls in the file to use the existing validate_framework_str (or the
original from_string) if they were referencing the duplicate, and run
tests/static analysis to confirm the duplicate is gone and no references are
broken.

@sawradip sawradip merged commit 4d93780 into main Sep 25, 2025
1 check was pending
@coderabbitai coderabbitai Bot mentioned this pull request Oct 29, 2025
This was referenced Nov 7, 2025
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