Sawra/runagent cloud support#69
Conversation
- Updated all SDK versions to 0.1.21 - Generated changelog with git-cliff
|
Caution Review failedThe pull request is closed. WalkthroughVersion bumped to 0.1.22 across multiple language bindings and packaging. Framework handling was refactored to a new Changes
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (11)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 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_agentupdates 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: undefineddeploy_local
deploy_localis commented out, butdeploy()still invokes it. This will raise at runtime when--localis used.Apply this diff to route local deployment through the existing
servecommand:- # 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.localis never initialized but heavily used (AttributeError risk)
self.local = LocalDeployment(self.config)is commented out, yet many methods callself.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 viaself.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_agenttakes one arg; passingmetadataraisesTypeError.This will crash
deploy_agent.- upload_result = self.upload_agent(folder_path, metadata) + upload_result = self.upload_agent(folder_path)
79-82: Remove defaultContent-Type: application/jsonto 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_resultresilient ifdatais missing.Avoid
KeyErrorwhensuccessis true but response lacksdata.- 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 frameworksWe 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 gapWhen 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'): + raisePlease 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
PortManageranddetect_framework/get_agent_configare 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_infoStoring the string with
.valueis good. Ensure all outward-facing responses also use string values for consistency.
621-623: Health response should stringify enumFastAPI 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 simplifyYou 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 callsIf the underlying client expects JSON (common), prefer
json=overdata=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=30Confirm the
RestClient.httpinterface; if it’srequests,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_localandagent.fingerprintread 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 Exceptionif 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 NoneAnd 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 nitsOne 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 infoMethod 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 idempotencerunagent/cli/commands.py (4)
890-896: Guard None return fromget_middleware_sync()
get_middleware_sync()may return None; callingis_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‑raisingChain the original
ValueErrorwhen converting toclick.UsageErrorfor 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 ifproject_pathis 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 placeholdersThese f-strings have no interpolations (ruff F541). Drop the
fprefix.- 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/datetimesYou 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: simplifyto_dict()with model_config enabledIf you enable model_config,
frameworkandcreated_atshould 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"]: continuerunagent/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._WEBHOOKAlso 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 thefprefix to satisfy Ruff.As per static analysis hints.
Also applies to: 653-656
22-27: Optional: remove unused imports.
get_agent_metadataandbase64are 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
⛔ Files ignored due to path filters (1)
runagent-ts/package-lock.jsonis 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_frameworkaligns withget_executorexpecting aFrameworkenum.
108-121: Serialize framework as string for middleware sync (LGTM)Using
self.agent_framework.valuefor 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/enabledimproves clarity overgetattrchecks.runagent/sdk/deployment/remote.py (1)
54-76: LGTM: upload signature simplification is coherent with RestClient APIRemoving framework detection from
upload_agentand delegating toRestClient.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: shouldOPENAIbe in pythonic set?
OPENAIis 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 confirmedThe 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 goodThanks for keeping the TypeScript client in sync at 0.1.21.
runagent-go/runagent/version.go (1)
4-4: Go SDK version bump acknowledged
Versionnow 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. 👍
| python_version = "0.1.21" | ||
| warn_return_any = true |
There was a problem hiding this comment.
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.
| runagent.add_command(commands.init) | ||
| runagent.add_command(commands.template) | ||
| runagent.add_command(commands.deploy_local) | ||
| # runagent.add_command(commands.deploy_local) |
There was a problem hiding this comment.
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.
| 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()) |
There was a problem hiding this comment.
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.
| from rich.prompt import Confirm | ||
| overwrite = Confirm.ask("Do you want to overwrite the existing agent?", default=False) | ||
|
|
||
| if not overwrite: |
There was a problem hiding this comment.
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.
| 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) |
There was a problem hiding this comment.
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
| 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 |
There was a problem hiding this comment.
🛠️ 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
+ passApply 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.
| 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) | ||
|
|
There was a problem hiding this comment.
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.
| validation_details["framework"] = detect_framework(folder_path).value | ||
| except Exception as e: |
There was a problem hiding this comment.
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.
| 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.
| try: | ||
| return cls(value) | ||
| except ValueError: | ||
| valid_values = [f.value for f in cls] | ||
| raise ValueError(f"Invalid framework: '{value}'. Valid options: {valid_values}") | ||
|
|
There was a problem hiding this comment.
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 errAs 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.
| 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.
| @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 | ||
|
|
There was a problem hiding this comment.
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 FalseAs 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.
| @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.
- Updated all SDK versions to 0.1.22 - Generated changelog with git-cliff
Summary by CodeRabbit
New Features
Improvements
Breaking Changes
Chores