-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
fix: wrapper layer gaps - InteractiveRuntime lifecycle, tool resolution, cli_backend support #1857
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ce7d893
94163de
d7a41df
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,81 @@ | ||||||||||||||
| """ | ||||||||||||||
| Shared, lock-agnostic scheduler logic for both sync and async variants. | ||||||||||||||
| """ | ||||||||||||||
|
|
||||||||||||||
| import os | ||||||||||||||
| import json | ||||||||||||||
| import logging | ||||||||||||||
| from datetime import datetime | ||||||||||||||
| from typing import Any, Dict, Optional | ||||||||||||||
|
|
||||||||||||||
| logger = logging.getLogger(__name__) | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| class _BaseAgentScheduler: | ||||||||||||||
| """Shared, lock-agnostic scheduler logic — used by both sync and async variants.""" | ||||||||||||||
|
|
||||||||||||||
| is_running: bool | ||||||||||||||
| max_cost: Optional[float] | ||||||||||||||
| _execution_count: int | ||||||||||||||
| _success_count: int | ||||||||||||||
| _failure_count: int | ||||||||||||||
| _total_cost: float | ||||||||||||||
| _start_time: Optional[datetime] | ||||||||||||||
|
|
||||||||||||||
| def _build_stats( | ||||||||||||||
| self, | ||||||||||||||
| *, | ||||||||||||||
| execs: int, | ||||||||||||||
| success: int, | ||||||||||||||
| failed: int, | ||||||||||||||
| total_cost: float, | ||||||||||||||
| ) -> Dict[str, Any]: | ||||||||||||||
| """Build stats dictionary for both sync and async schedulers.""" | ||||||||||||||
| runtime = ( | ||||||||||||||
| (datetime.now() - self._start_time).total_seconds() | ||||||||||||||
| if self._start_time else 0 | ||||||||||||||
| ) | ||||||||||||||
| return { | ||||||||||||||
| "is_running": self.is_running, | ||||||||||||||
| "total_executions": execs, | ||||||||||||||
| "successful_executions": success, | ||||||||||||||
| "failed_executions": failed, | ||||||||||||||
| "success_rate": (success / execs * 100) if execs > 0 else 0, | ||||||||||||||
| "total_cost_usd": round(total_cost, 4), | ||||||||||||||
| "remaining_budget": ( | ||||||||||||||
| round(self.max_cost - total_cost, 4) if self.max_cost is not None else None | ||||||||||||||
| ), | ||||||||||||||
|
Comment on lines
+45
to
+47
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handle zero-budget correctly in shared stats.
💡 Suggested fix "remaining_budget": (
- round(self.max_cost - total_cost, 4) if self.max_cost else None
+ round(self.max_cost - total_cost, 4) if self.max_cost is not None else None
),📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
| "runtime_seconds": runtime, | ||||||||||||||
| "cost_per_execution": ( | ||||||||||||||
| round(total_cost / execs, 4) if execs > 0 else 0 | ||||||||||||||
| ), | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| def _update_state_if_daemon(self) -> None: | ||||||||||||||
| """Update ~/.praisonai/schedulers/*.json for the current PID, if present. | ||||||||||||||
|
|
||||||||||||||
| Safe for both sync and async callers — it's plain blocking file I/O that | ||||||||||||||
| runs once per execution and is wrapped in try/except. | ||||||||||||||
| """ | ||||||||||||||
| try: | ||||||||||||||
| state_dir = os.path.expanduser("~/.praisonai/schedulers") | ||||||||||||||
| if not os.path.exists(state_dir): | ||||||||||||||
| return | ||||||||||||||
| current_pid = os.getpid() | ||||||||||||||
| for fname in os.listdir(state_dir): | ||||||||||||||
| if not fname.endswith(".json"): | ||||||||||||||
| continue | ||||||||||||||
| path = os.path.join(state_dir, fname) | ||||||||||||||
| try: | ||||||||||||||
| with open(path, "r") as f: | ||||||||||||||
| state = json.load(f) | ||||||||||||||
| if state.get("pid") == current_pid: | ||||||||||||||
| state["executions"] = self._execution_count | ||||||||||||||
| state["cost"] = round(self._total_cost, 4) | ||||||||||||||
| with open(path, "w") as f: | ||||||||||||||
| json.dump(state, f, indent=2) | ||||||||||||||
| break | ||||||||||||||
| except Exception: | ||||||||||||||
| continue | ||||||||||||||
| except Exception as e: | ||||||||||||||
| logger.debug("Failed to update state: %s", e) | ||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.