Compare commits

..
Author SHA1 Message Date
MervinPraison fff694625c Release v4.6.71 2026-06-23 00:53:51 +01:00
MervinPraison 7bcac63d6d chore: move runtime capability root tests into unit test folders
Relocate PR #2072 runtime tests from repo root into tests/unit/runtime and
tests/unit/agent; remove sys.path hacks and use pytest patterns.
2026-06-23 00:52:53 +01:00
MervinPraison d0a89050b2 docs: auto-update api.md [skip ci] 2026-06-22 23:51:42 +00:00
32d149967b fix: DoctorContractProtocol for runtime config migration (#2075)
* feat: implement DoctorContractProtocol for runtime config migration (fixes #1941)

- Add DoctorContractProtocol in praisonaiagents/runtime/doctor_protocol.py
- Implement built-in CliBackendMigrationRule for cli_backend -> models.default.runtime migration
- Add plugin discovery via praisonai.doctor_contracts entry points
- Wire into 'praisonai doctor runtime --fix' CLI command
- Include comprehensive unit tests
- Support idempotent migrations with backup creation
- Protocol-driven design: core protocols in SDK, heavy implementations in wrapper

* fix: address critical issues found in code review

- Fix @register_check decorator to include required arguments (id, title, description, category)
- Update function signature to accept DoctorConfig parameter
- Fix shallow copy bug by using deepcopy to prevent mutation of original config
- Add type checks for roles dict to prevent AttributeError
- Fix dead validation code in register_rule (isinstance check)
- Remove unnecessary abc.abstractmethod decorators from Protocol
- Prioritize importlib.metadata over deprecated pkg_resources
- Return ERROR status when explicitly specified config file not found
- Serialize YAML before opening file to prevent corruption on errors
- Remove test files from repository root

Co-authored-by: Mervin Praison <[email protected]>

* fix: remove duplicate RUNTIME enum value in CheckCategory

Co-authored-by: Mervin Praison <[email protected]>

* fix: correct import path for DoctorRulesRegistry in tests

- Fixed import to use doctor_registry instead of registry module
- All 14 tests now pass successfully after rebase

* fix: correct runtime __init__.py merge - use lazy loading for doctor protocol

- Removed direct imports of doctor_protocol and doctor_registry to avoid import-time performance regression
- Added doctor protocol components to _LAZY_GROUPS for proper lazy loading
- Maintains all exports in __all__ while following the lazy loading pattern
- Fixes merge issue where both changes were accepted but not properly integrated

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-23 00:51:22 +01:00
MervinPraison b40a93f044 docs: auto-update api.md [skip ci] 2026-06-22 23:49:35 +00:00
praisonai-triage-agent[bot]andMervinPraison 0cd5f16d71 fix: add fail-fast YAML schema validation with actionable errors (#2128)
* fix: add fail-fast YAML schema validation with actionable errors (fixes #2125)

- Add comprehensive Pydantic-based schema validation for agents/tasks/workflow
- Implement fail-fast validation with aggregated error reporting
- Add cross-reference validation for agents, tasks, tools, and handoffs
- Create 'praisonai validate' CLI command for pre-commit/CI validation
- Update agents_generator to fail on validation errors (not just warn)
- Add pydantic dependency for schema validation

Key improvements:
- Required fields are now enforced (role, goal, backstory)
- Type checking for all configuration values
- Cross-references validated (tasks->agents, tools->definitions, handoffs->roles)
- Unknown fields detected with suggestions
- Aggregated error messages show all problems at once
- --strict mode treats warnings as errors

Co-authored-by: MervinPraison <[email protected]>

* fix: address all 6 validation issues from CodeRabbit review

- Fix JSON output for missing files in validate command
- Fix stream alias not enabling streaming field (use model_validator before)
- Add validation to reject unknown workflow step types
- Fix handoff validation order-dependency (two-pass validation)
- Handle non-dict YAML input gracefully without crashing
- Remove overly permissive endswith('Tool') fallback

These fixes ensure fail-fast validation works correctly with proper error
aggregation and backward compatibility as intended.

* fix: make schema validation compatible with existing YAML formats

- Change llm/function_calling_llm from Optional[str] to Optional[Union[str, Dict[str, Any]]] to support dict format with 'model' key
- Add tasks field to AgentConfig to support nested task definitions
- Add dependencies to known_top_level fields in validator
- Add task normalization logic to convert dict tasks to TaskConfig objects

These changes ensure backward compatibility with all existing YAML configurations while maintaining validation capabilities.

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-23 00:49:13 +01:00
praisonai-triage-agent[bot]andpraisonai-triage-agent[bot] 39e068b791 feat: add industry templates for faster agent workforce deployment (#2143)
* feat: add industry templates for faster agent workforce deployment (fixes #2142)

- Added 5 industry-specific templates based on SRAO Framework
- Manufacturing: order processing, inventory, scheduling, quality control
- Energy: wind farm monitoring, predictive maintenance, power forecasting
- Healthcare: emergency triage with HIPAA compliance, resource allocation
- Agriculture: precision farming, pest detection, yield prediction
- Transportation: infrastructure monitoring with LiDAR, safety assessment

Each template includes:
- Complete agent definitions with specific roles
- I/O schemas using Pydantic for structured data handling
- SLA requirements for performance guarantees
- Fallback strategies for reliability
- Cross-industry reusable patterns (70% code reuse)

These templates enable rapid deployment of agent workforces with minimal setup time, allowing users to quickly adapt PraisonAI to their specific industry needs.

Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>

* fix: address critical issues in industry templates

- Add missing Optional import in agriculture_template.py
- Fix __init__.py to properly import template modules
- Fix None in resources_dispatched list in transportation_template.py
- Add missing affected_area field in spray recommendation
- Fix maintenance_type to use documented values (preventive vs routine)

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>
2026-06-23 00:47:44 +01:00
praisonai-triage-agent[bot]andMervin Praison 98ac919de2 fix: gateway resume can silently lose events - add integrity checks (#2155)
* fix: gateway resume can silently lose events - add integrity checks (fixes #2153)

- Track oldest retained cursor in session buffer
- Add resync_required flag when requested cursor is below oldest retained
- Send authoritative snapshot instead of partial replay when gap detected
- Add top-level seq field to all events for client-side gap detection
- Update wire protocol documentation with new resume fields

Co-authored-by: MervinPraison <[email protected]>

* fix: address reviewer feedback on gateway integrity checks

- Add input validation for 'since' cursor to prevent type errors
- Include token_stream and tool_call_stream events in seq stamping
- Clarify get_oldest_cursor() semantics with documentation
- Document resume_or_create_session() replay_events caveat

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-23 00:47:00 +01:00
Mervin Praisonandpraisonai-triage-agent[bot] bf97b9556e fix: add protocol validation to framework adapter registry (#2083)
- Add _validate_adapter method to enforce required keyword-only parameters
- Override create() to validate adapters on instantiation
- Handle TypeError in is_available() for invalid adapters
- Ensures all framework adapters implement the correct protocol signature

Fixes #1654

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
2026-06-23 00:46:40 +01:00
8ccf2a7aec fix: Runtime capability matrix and hook compatibility (#2072)
* feat: implement runtime capability matrix and hook compatibility system

- Add RuntimeCapability enum with 12 capability flags (native_hooks, tool_loop, streaming_deltas, etc.)
- Implement RuntimeCapabilityMatrix for declaring runtime capability sets
- Add AgentRuntimeProtocol requiring capabilities() method for all runtimes
- Create validate_capabilities() with fail-fast validation and CapabilityValidationError
- Add RuntimeConfig to agent configuration system with required_capabilities support
- Integrate runtime parameter in Agent constructor with validation at creation time
- Update CliBackendProtocol to require capability reporting for consistency
- Provide built-in capability matrices for native and plugin harness runtimes
- Add comprehensive unit tests and validation for all functionality
- Enable fail-fast capability validation at config/preflight time instead of mid-turn

Fixes #1936

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

* fix: address critical issues from code review

- Fix capability validation to check actual runtime capabilities instead of always-true native matrix
- Remove duplicate incompatible CliBackendProtocol definition
- Fix hard-coded CI paths in test files for portability
- Add proper type safety and error handling
- Add list/set support to resolve_runtime function
- Improve error messages with remediation guidance
- Handle empty required_capabilities safely
- Serialize enum values properly in to_dict()

Co-authored-by: Mervin Praison <[email protected]>

* chore: remove temporary validation test file

Co-authored-by: Mervin Praison <[email protected]>

* fix: update runtime config resolution to support both AgentRuntimeConfig and RuntimeConfig

- Modified _resolve_runtime_config to handle both turn-based (AgentRuntimeConfig) and capability validation (RuntimeConfig) systems
- Added _validate_runtime_capabilities method to perform capability validation
- Ensures compatibility with both main's turn-based runtime and PR's capability system

Co-authored-by: Mervin Praison <[email protected]>

* fix: update runtime capability mapping to handle all documented runtime IDs

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Claude <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-23 00:43:14 +01:00
0d1b699671 fix: wrapper layer framework adapter issues - protocol-driven dispatch, fail-fast stubs, conditional tool registration (#2086)
- Add resolve_alias() to FrameworkAdapter protocol for version selection
- Add AutoGenFamilyAdapter as router for autogen version dispatch
- Add resolve() override to properly create concrete adapter instances
- Mark unimplemented adapters (v0.4, AG2) with NotImplementedError
- Fix autogen_v2 dispatch by setting correct name attribute
- Move autogen tool registration to be conditional on framework family
- Include autogen_v2 in autogen family tool registration guard

Fixes #1590

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-23 00:27:06 +01:00
MervinPraison 0adde0ea1f chore: move test_artifact_store.py into context unit tests
Relocate root-level artifact store test to tests/unit/context/ and remove
sys.path hack for proper package imports.
2026-06-23 00:12:14 +01:00
praisonai-triage-agent[bot]andMervin Praison a030599171 fix: implement ArtifactStore for retrievable tool output overflow (#2149)
* fix: implement ArtifactStore for retrievable tool output overflow (fixes #2148)

- Add concrete FileSystemArtifactStore implementing ArtifactStoreProtocol
- Wire into tool_execution.py to spill large outputs instead of discarding
- Add artifact retrieval tools (head, tail, grep, chunk, load)
- Add ToolOutputConfig for configurable limits (max_bytes, retention_days)
- Register retrieval tools dynamically on first artifact creation
- Add garbage collection for old artifacts

This preserves full tool outputs that exceed limits in retrievable artifacts instead of silently truncating and discarding the overflow.

* fix: address critical security and architectural issues in artifact store

- Add path traversal protection by validating artifact paths are within store directory
- Replace global artifact store with thread-local storage for multi-agent support
- Preserve artifact references through context manager truncation
- Add missing artifact_load and artifact_list tools to registration
- Fix tool_output_limit being overwritten by tracking custom config
- Propagate tool_output config in clone_for_channel for cloned agents
- Add proper error logging for artifact cleanup failures
- Add validation for ToolOutputConfig parameters
- Fix run_id filter in list_artifacts when agent_id not provided
- Fix cleanup_old_artifacts to handle days=0 correctly
- Fix secret redaction to cover JSON and list content
- Fix artifact_load to use correct mime_type from metadata

Co-authored-by: Mervin Praison <[email protected]>

* fix: reduce Agent parameter bloat by merging artifact config into ToolConfig

- Merged ToolOutputConfig fields into existing ToolConfig class
- Removed tool_output parameter from Agent to avoid bloat
- All tool-related config now consolidated in one place (ToolConfig)
- Fixed NameErrors in artifact_grep and artifact_list tools
- Path traversal protection already in place
- Tests pass successfully

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-23 00:09:36 +01:00
MervinPraison 4189c3b6cb Release v4.6.70 2026-06-23 00:06:11 +01:00
MervinPraison fd7ff10e39 Merge branch 'main' of https://github.com/MervinPraison/PraisonAI 2026-06-23 00:04:43 +01:00
cfd7d7c8a9 fix(examples): merge validation fixes phases 2-4 into main (stacked on #1855) (#1893)
* fix(examples): resolve syntax errors and align with consolidated-params API

* fix: correct TypeError in 3 example files by using proper parameter names

- Fix MemoryConfig(memory=...) → user_id=... in agent_centric_api.py
- Fix Session(memory=...) → user_id=... in session-example.py
- Fix build_context_for_task(memory=...) → user_id=... in memory-quality-example.py

Co-authored-by: Mervin Praison <[email protected]>

* fix(examples): align AgentTeam memory and knowledge configs with v4.6.52 API

* fix(examples): phase-3 API alignment for consolidated params on v4.6.52

* fix(examples): phase-4 API alignment for 40 validation failures on v4.6.52

* fix: resolve critical RouterAgent **kwargs bug and add missing if __name__ guards

- Fixed TypeError in RouterAgent._build_system_prompt() by removing **kwargs from super() call
- Added if __name__ == '__main__' guards to 6 example files to prevent auto-execution on import
- Addresses Gemini Code Assist review feedback

Co-authored-by: Mervin Praison <[email protected]>

* fix: resolve missing AgentTeam import and RouterAgent kwargs forwarding

- Fix missing AgentTeam import in self-reflection-optimization-patterns.py
- Forward kwargs properly in RouterAgent._build_system_prompt to super()
- Addresses critical issues identified by Gemini code reviewer

Co-authored-by: Mervin Praison <[email protected]>

* fix(examples): remove merge-conflict residue for PR #1893

---------

Co-authored-by: Dhivya-Bharathy <[email protected]>
Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-23 00:03:04 +01:00
praisonai-triage-agent[bot]andMervin Praison 40948a8912 fix: address wrapper layer gaps - modal sandbox, DRY violations, double AgentOps init (#2095)
* fix: address wrapper layer gaps - modal sandbox, DRY violations, double AgentOps init

- Fix modal sandbox import path from non-existent modal_sandbox.py to correct modal.py
- Prevent silent fallback to subprocess when requested sandbox type unavailable
- Deduplicate ~180 lines between run/arun methods in PraisonAI adapter
- Remove duplicate AgentOps initialization that was creating two sessions

Fixes #2094

* fix: address critical wrapper layer issues from reviewer feedback

- Fix import path from non-existent ._base to correct .base module
- Restore model resolution for dict-form llm configs (e.g. {model: 'groq/llama3-70b-8192'})
- Restore model-scoped and provider-scoped runtime resolution paths for backward compatibility
- Fix unused loop variable (task_name → _task_name) per Python conventions
- Add error handling for missing task keys with sensible defaults
- Remove unused is_available import in arun method

Co-authored-by: Mervin Praison <[email protected]>

* fix: add missing is_available() method to PraisonAIAdapter

The adapter was inheriting from FrameworkAdapter (Protocol) instead of
BaseFrameworkAdapter, causing is_available() to return None. This made
the framework appear unavailable regardless of actual installation status.

Added explicit is_available() implementation that properly checks for
praisonaiagents package availability.

* fix: add missing resolve() method to PraisonAIAdapter

- Add resolve() method that returns self (PraisonAI has no variants)
- This fixes the AttributeError that was crashing all executions
- All other critical issues were already resolved in the rebased code

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-23 00:00:20 +01:00
MervinPraison d58bc4142e docs: auto-update feature parity trackers [skip ci] 2026-06-22 22:59:11 +00:00
ef29cbdfe9 feat: add debug log on successful branch checkout (#2088)
Add logger.debug call after successful git checkout -b command in github_create_branch function

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 23:58:46 +01:00
2eeb84fb6a fix: restore _delivery.py removed while still imported by durable delivery (#2129)
PR #2054 deleted bots/_delivery.py believing it was superseded by
bots/delivery.py, but delivery.py only provides DeliveryRouter and
ChannelDirectory. DurableDelivery, deliver_with_retry, and deliver_chunked
remain referenced by _durable_adapter.py and bots/__init__.py, causing
ModuleNotFoundError on any durable outbound delivery usage.

Restore the module and add regression tests for the public exports.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-22 23:56:50 +01:00
MervinPraison f601ca0ced chore: move root examples and tests into proper package folders
Relocate LLM config example, gateway prototype, and OpenAI compatibility
tests out of the repo root; remove sys.path hack and gate integration tests
behind PRAISONAI_TEST_OPENAI_COMPAT.
2026-06-22 23:54:52 +01:00
MervinPraison 5bd74ba160 docs: auto-update api.md [skip ci] 2026-06-22 22:49:44 +00:00
823f5639bc fix: Agent runtime lacks jittered exponential backoff for API and tool retries (#2082)
* feat: implement jittered exponential backoff for LLM API retries (fixes #1805)

- Add retry_utils.py with configurable jittered exponential backoff
- Add RetryBackoffConfig to Agent constructor with sensible defaults
- Implement _chat_completion_with_retry() wrapper for LLM API calls
- Wire OnRetryInput hook with delay and attempt information
- Add interrupt-aware sleep to prevent blocking during shutdown
- Apply retry logic to main chat, reflection, and completion calls

Resolves thundering-herd behaviour on rate limits (HTTP 429) and
transient network/service failures with graceful backoff strategy.

Co-authored-by: MervinPraison <[email protected]>

* fix: address critical issues identified by code reviewers

- Fix jitter overflow: clamp delay after jitter to respect max_delay
- Fix context-overflow regression: remove hardcoded _retry_depth=1
- Add RetryBackoffConfig export to __init__.py for documented imports
- Add comprehensive validation with clear error messages
- Make interruption terminal to prevent continued retries
- Add async retry wrapper _achat_completion_with_retry for async paths
- Fix clone propagation: include retry config in clone_for_channel
- Update sync chat calls to use retry wrapper

Fixes all P1 and P2 issues identified by Greptile, CodeRabbit, and Copilot.
Maintains backward compatibility and follows AGENTS.md guidelines.

Co-authored-by: Mervin Praison <[email protected]>

* fix: resolve critical infinite recursion and parameter passing issues in retry logic

- Fix infinite recursion: _chat_completion_with_retry now calls _execute_unified_chat_completion instead of _chat_completion
- Add missing stream_callback and emit_events parameters to _chat_completion_with_retry signature
- Restore proper call chain: _chat_impl -> _chat_completion -> _chat_completion_with_retry -> _execute_unified_chat_completion
- Use module-level logger instead of root logger for consistency
- Use interruptible_sleep helper in async retry to reduce code duplication
- Update docstrings to accurately describe jitter behavior (additive positive only)

This ensures all synchronous LLM calls work correctly without infinite recursion or TypeError, while preserving essential wrapper logic like context compaction, hooks, and tracing.

* fix: prevent double retry in streaming fallback

- Check for LLMError in streaming fallback to avoid double retry when retries are exhausted
- LLMErrors that have already been through retry logic are now re-raised immediately
- Other exceptions continue to fall back to non-streaming as before

This addresses the issue identified by Greptile where exhausted retry errors were being caught and retried again in the streaming fallback path.

* chore: remove temporary test file

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 23:49:25 +01:00
praisonai-triage-agent[bot]andMervin Praison 4dccfc41f1 fix: remove duplicate session-manager setup logic across bot platform adapters (#2135)
* fix: remove duplicate session-manager setup logic across bot platform adapters (fixes #2134)

- Created build_session_manager helper function in _session.py
- Updated all 7 bot adapters to use the new helper
- Removed ~15-20 lines of duplicate code per adapter
- Maintains identical runtime behavior with no API changes

Co-authored-by: MervinPraison <[email protected]>

* fix: improve session store error handling and remove unused imports

- Distinguish between ImportError (silent fallback) and other exceptions (log warning)
- Remove unused BotSessionManager imports from agentmail.py and linear.py
- Addresses CodeRabbit and Greptile review feedback

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 23:47:17 +01:00
praisonai-triage-agent[bot]andMervin Praison e9dcc5f7b8 fix: add protocol version negotiation and resilient reconnection to gateway (#2131)
* fix: add protocol version negotiation and resilient reconnection to gateway (fixes #2130)

- Core SDK: Added protocol version constants and negotiation types
- Core SDK: Added sequence numbers to GatewayEvent for gap detection
- Server: Implemented version negotiation in join handshake
- Server: Added sequence numbering and gap detection support
- Server: Enhanced resume with presence/health snapshot
- Client: Created reconnecting Python client with exponential backoff
- Client: Implemented gap detection and cursor-based resumption

Co-authored-by: MervinPraison <[email protected]>

* fix: address reviewer feedback on gateway client and server

- Fix GatewayClient.connect() to be non-blocking by running connection loop in background task
- Add ValueError catch for version_unsupported to prevent infinite retry on permanent errors
- Fix cursor update logic to handle zero values correctly (use 'is not None' check)
- Add session persistence for sequence and protocol_version fields
- Add protocol version validation with proper type checking for untrusted JSON
- Fix sequence number in joined message to align with replay events to prevent false gap detection
- Fix off-by-one error in reconnect attempts counter
- Add timeout to WebSocket join handshake to prevent indefinite blocking
- Remove unused imports (time, field, GatewayMessage)

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 23:46:50 +01:00
MervinPraison c19a33eb7f docs: auto-update api.md [skip ci] 2026-06-22 22:46:16 +00:00
praisonai-triage-agent[bot]andMervinPraison 3ac1b6a46b feat: add model catalogue and model-selection UX (#2127)
* feat: add model catalogue and model-selection UX (fixes #2123)

- Add 'praisonai models' command with list, describe, and validate subcommands
- Create ModelCatalogue class with litellm integration and graceful fallback
- Add model validation to LLM resolution with 'did you mean' suggestions
- Validate model strings in YAML agent configurations
- Warn when tools configured for non-tool-calling models
- Cache model metadata locally with TTL to reduce API calls
- Support filtering by provider and search patterns

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve critical bugs in model catalogue implementation

- Fix TypeError: model_list is a list attribute not callable in litellm
- Change validate_model default to False to preserve backward compatibility
- Move model validation before provider resolution to ensure correct key/base selection
- Add validation for fallback models from stored credentials
- Skip catalogue validation for local YAML model aliases
- Ensure consistent JSON output in CLI when litellm unavailable

Co-authored-by: Mervin Praison <[email protected]>

* fix: prevent typer.Exit from being swallowed by generic exception handlers

- Add explicit typer.Exit handlers in describe_model and validate_model commands
- Re-raise typer.Exit to avoid spurious error messages when models are not found
- Preserve exception context with 'from e' for better debugging

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 23:45:52 +01:00
praisonai-triage-agent[bot]andMervin Praison 307b0ff947 fix: add auth-aware first-run onboarding to prevent cryptic failures (#2126)
* fix: add auth-aware first-run onboarding to prevent cryptic failures (fixes #2124)

- Added is_configured() helper in llm/credentials.py to detect if credentials are available
- Added credential check in cli/app.py before launching TUI for no-arg invocation
- Added credential check in cli/commands/run.py before executing run commands
- Both checks offer to launch setup wizard in interactive mode
- Non-interactive/CI contexts get clear actionable error messages
- Configured users see no change in behavior or startup latency

This ensures new users get proper guidance instead of cryptic mid-run failures when no API keys are configured.

* fix: address reviewer feedback on auth-aware onboarding

- Remove hardcoded gpt-4o-mini model in app.py credential check
- Add credential re-verification after setup wizard completes
- Add session error handling in profiled execution paths
- Remove dead code _check_api_key_available function

These fixes ensure the credential gate works for all providers,
not just OpenAI, and properly handles edge cases with session
management and post-setup verification.

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-22 23:45:11 +01:00
8f6a6624b8 fix: wrapper layer architectural fixes (fixes #1602) (#2085)
- Fix Gap 1: Remove OpenAI client singleton in auto.py
  * Replace global _openai_clients cache with per-call client creation
  * Eliminates thread-safety issues and multi-agent state sharing
  * Prevents silent resource leaks on cache eviction

- Fix Gap 2: Change unsafe 0.0.0.0 default bindings to 127.0.0.1
  * Update api/call.py to default to 127.0.0.1 with --host parameter
  * Add warning when binding to 0.0.0.0 without --public flag
  * Fix HTTPApproval example to use safe default (127.0.0.1)

- Fix Gap 3: Consolidate duplicated ScheduleParser classes
  * Remove duplicate ScheduleParser from scheduler/base.py
  * Use single canonical implementation from scheduler/shared.py
  * Update imports to maintain backward compatibility

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 23:44:06 +01:00
161b32105c fix: AgentRuntimeProtocol and built-in embedded praisonai runtime (#2071)
* feat: implement AgentRuntimeProtocol and built-in embedded praisonai runtime

- Add AgentRuntimeProtocol in praisonaiagents/runtime/protocols.py with minimal contract
- Implement RuntimeRegistry with thread-safe registration and entry points discovery
- Create built-in 'praisonai' runtime that wraps existing ChatMixin/execution logic
- Add comprehensive unit tests for protocol compliance and registry functionality
- Add entry point support for plugin runtime registration via 'praisonai.runtimes'
- Include documentation and README for runtime extension by plugin authors

Fixes #1934

Co-authored-by: MervinPraison <[email protected]>

* fix: correct Agent constructor params and streaming implementation

- Fix system_prompt -> instructions parameter mapping
- Remove invalid stream, max_tokens, temperature from Agent constructor
- Fix streaming to use achat(stream=True) instead of non-existent astream()
- Add proper async context manager usage for resource cleanup
- Remove try/except pass blocks from tests that were hiding failures

Addresses critical issues identified by multiple reviewers (Gemini, Greptile)

* fix: add missing registry methods and handle empty API key response

* fix: move runtime tests to main test folder and address reviewer feedback

- Move tests from praisonaiagents/runtime/tests/ to tests/unit/runtime/
- Fix imports to use absolute paths
- Add missing list_names() and is_available() methods to RuntimeRegistry
- Document streaming limitation in stream_turn() method
- Improve API key error handling

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 23:42:15 +01:00
aa5e05906c fix: All ~78 Typer command groups imported and registered on every CLI invocation before dispatch (#2068)
* fix: implement lazy CLI command loading to eliminate import bloat

- Remove redundant module-level register_commands() call at app.py:685
- Add command name→module mapping for lazy loading
- Implement get_command_names() to return command names without imports
- Update dispatch logic in __main__.py to use lazy command discovery
- Add explicit register_commands() call in cli/main.py for profile command
- Preserve all existing CLI functionality while optimizing cold start

This fixes the issue where all ~78 command groups were eagerly imported
on every CLI invocation, including simple operations like 'praisonai "hello"'
that don't use any sub-commands.

Fixes #1950

Co-authored-by: MervinPraison <[email protected]>

* fix: address PR review feedback from lazy CLI loading implementation

- Fix test breakage: Add register_commands() fixture to TestTyperApp, TestGlobalOptions, and TestExitCodes
- Remove dead code: Delete unused load_command_module() function
- Add maintainability comment explaining hardcoded retrieval commands

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 23:41:22 +01:00
8d655819f2 fix: resolve wrapper layer security gaps (fixes #1962) (#2062)
- Add consistent is_protected() checks to search_replace.py and append_to_file()
- Make execute_command() enforce DANGEROUS_COMMANDS with allow_dangerous parameter
- Remove duplicate AgentOps init from _select_autogen_version()
- Replace eager agentops import with lazy framework availability check
- Add threading lock and persistent file handle to AuditLogHook for thread safety
- Fix enable_injection_defense() to return both hook IDs for proper cleanup

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 23:40:49 +01:00
praisonai-triage-agent[bot]andMervin Praison 157c4282e8 fix: consolidate duplicated workflow engine in memory/workflows.py (~1,983 LOC) into a re-export (#2119)
* fix: consolidate duplicated workflow engine in memory/workflows.py into re-export shim (fixes #2117)

Replaced ~2,000 lines of duplicate workflow engine code in praisonaiagents/memory/workflows.py
with a thin re-export shim that imports from the canonical praisonaiagents/workflows/workflows.py.

This is a structural consolidation only - all public import paths and behaviour are preserved:
- Backward-compatible imports from memory.workflows still work
- Tests that explicitly import from memory.workflows remain functional
- The canonical engine in workflows/workflows.py is the single source of truth

Changes:
- Reduced memory/workflows.py from 1,983 LOC to 48 LOC
- Re-exports WorkflowContext, StepResult, Workflow, WorkflowManager, Task, and pattern helpers
- Maintains backward compatibility aliases (StepInput, StepOutput)
- Eliminates maintenance burden of duplicate implementations

Co-authored-by: MervinPraison <[email protected]>

* fix: add missing result type re-exports for backward compatibility

The old memory/workflows.py imported StepError, WorkflowResult, StepStatus,
and ErrorStrategy from workflows.results, making them accessible to external
consumers. Add these re-exports to preserve full backward compatibility.

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 23:18:34 +01:00
MervinPraison 8b938c6494 docs: auto-update documentation parity trackers [skip ci] 2026-06-22 22:18:32 +00:00
praisonai-triage-agent[bot]andMervin Praison c6f9e80cba fix: add interactive action registry for button/select callbacks (#2108)
* fix: add interactive action registry for button/select callbacks (fixes #2104)

- Created InteractiveRegistry and dispatch protocol in core SDK
- Added encode_action/decode_callback for typed action handling
- Updated Telegram, Discord, and Slack adapters to use registry
- Registered handlers for 'command' and 'pair' namespaces
- Maintains backward compatibility with existing pairing callbacks

Co-authored-by: MervinPraison <[email protected]>

* fix: address critical issues in interactive registry implementation

- Fix dispatch() to properly return False when handlers return None
- Replace global registry singleton with create_registry() for adapter-specific instances
- Fix invalid BotMessage constructor arguments (chat_id, command, command_args moved to metadata)
- Remove redundant TYPE_CHECKING imports
- Remove dead code for 'approve' command special case
- Use field(default_factory=dict) for proper dataclass default

Co-authored-by: Mervin Praison <[email protected]>

* fix: remove duplicate BotOSProtocol import from TYPE_CHECKING block

The import was redundant as BotOSProtocol is already imported at runtime (line 23) and needs to be available for the __all__ export list.

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 23:18:07 +01:00
81c2cf8e88 fix: gateway session data loss and restore deleted _delivery module (#2102)
* fix: gateway session data loss and restore deleted _delivery module

- Resume gateway sessions from the latest session_data snapshot instead of
  the initial empty one written at session creation
- Persist active sessions on gateway stop() via close_session()
- Restore bots/_delivery.py accidentally removed in #2054; imports for
  DurableDelivery and DurableAdapterMixin were still live
- Add regression tests for resume, shutdown persist, and delivery imports

Co-authored-by: Mervin Praison <[email protected]>

* fix: address code review feedback from reviewers

- Store reference to typing indicator background task to prevent GC
- Fix redundant final edit when streaming completes successfully
- Fix test_stop_persists_active_sessions to properly test persistence
- Add channel_id parameter to rate limiter for per-channel throttling

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
2026-06-22 23:17:29 +01:00
MervinPraison 91746bebcc docs: auto-update api.md [skip ci] 2026-06-22 22:17:15 +00:00
34938545b7 fix: [Gap] OpenAI/OpenResponses Gateway shape: discovery/health unified app; /v1/chat only on optional RA (#2091)
* feat: Add OpenAI-compatible HTTP API layer (fixes #1327)

- Implement OpenAICompatProvider with standard endpoints:
  - POST /v1/chat/completions (with streaming)
  - POST /v1/completions
  - GET /v1/models
  - POST /v1/tools/invoke (PraisonAI extension)
- Add 'praisonai serve openai' command for compatibility layer
- Create comprehensive test suite with OpenAI Python client
- Enable drop-in replacement for OpenAI client libraries

Addresses missing OpenAI-compatible HTTP surface vs OpenClaw Gateway.
Uses existing PraisonAI capabilities.completions infrastructure with
OpenAI request/response format conversion.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>

* fix: Address critical issues in OpenAI compatibility layer

- Fix import errors: use create_unified_app instead of non-existent create_server
- Fix streaming: use text/event-stream media type and avoid double execution
- Fix health check: remove live LLM calls that consume tokens
- Add input validation: guard against None input data
- Add --agents-url parameter for proper agent routing

Addresses review feedback from Gemini, Greptile, and CodeRabbit

* fix: Handle streaming errors and tool_calls in OpenAI compatibility layer

- Fix streaming error handling: error events are now properly sent to clients instead of being silently dropped
- Fix tool_calls in streaming: tool calls are now included in streamed responses instead of being lost
- Addresses critical issues identified by Greptile reviewer

Co-authored-by: Mervin Praison <[email protected]>

* fix: Address OpenAI compatibility layer protocol issues

- Remove advertised but unimplemented embeddings capability
- Add required initial role chunk in streaming (OpenAI SDK requirement)
- Remove tool_calls: null from non-tool responses (violates OpenAI schema)

These fixes ensure strict OpenAI API compliance for better client compatibility.

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-22 23:16:52 +01:00
MervinPraison 7bb52b9f07 Release v4.6.68 2026-06-22 23:15:14 +01:00
MervinPraison a1c56fd2ba docs: auto-update feature parity trackers [skip ci] 2026-06-22 21:59:44 +00:00
praisonai-triage-agent[bot]andMervinPraison a207997441 fix: remove unreferenced dead code in core SDK (#2058) (#2164)
Removed three unreferenced modules that were never imported or used:
- praisonaiagents/agent/prepared_turn_mixin.py (418 lines)
- praisonaiagents/agent/turn_execution_mixin.py (355 lines)
- praisonaiagents/telemetry/token_telemetry.py (87 lines)

These modules defined classes that were never wired into Agent or imported anywhere. Removing ~860 lines of dead code for better maintainability.

Fixes #2162

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 22:59:19 +01:00
praisonai-triage-agent[bot]andMervinPraison fdbf260ad0 fix: resolve duplicate EventType key in lazy imports map (fixes #2161) (#2163)
- Renamed gateway EventType to GatewayEventType in _LAZY_IMPORTS
- Eliminates silent shadowing while preserving all functionality
- Follows existing convention (cf. ObservabilityEventType)

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 22:59:05 +01:00
praisonai-triage-agent[bot]andMervin Praison d6337fc462 fix: add platform/channel awareness to gateway agents (#2160)
* fix: add platform/channel awareness to gateway agents (fixes #2157)

- Extended SessionContext in core to include Origin and ReachableTarget for platform awareness
- Added detect_chat_type() helper to identify chat types from platform patterns
- Enhanced ChannelDirectory with describe_targets() method
- Modified _build_system_prompt() to inject session context into agent prompts
- Updated BotSessionManager to build and set enriched context with origin and targets
- Created SessionContextProvider for managing platform-aware context (wrapper layer)

This enables agents to:
- Know where messages originate (platform, chat type, display name)
- See reachable delivery targets (home channels and aliases)
- Correctly resolve 'here' and cross-platform delivery requests
- Stop hallucinating non-existent channels

Co-authored-by: MervinPraison <[email protected]>

* fix: critical bugs in platform awareness implementation

- Fix undefined chat_name variable causing NameError and session context loss
- Move session context injection AFTER caching to prevent cross-user leakage
- Improve exception handling with specific ImportError and debug logging
- Fix Telegram chat type detection to return 'unknown' for ambiguous -100 prefix
- Remove redundant double asdict() serialization in SessionContext.to_dict()
- Delete unused SessionContextProvider class that duplicated code

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 22:58:52 +01:00
30d250e76c fix: add backpressure and slow-consumer flow control to gateway (#2156)
* fix: add backpressure and slow-consumer flow control to gateway (fixes #2152)

- Add max_inbox config to SessionConfig for bounded message queues (default 256)
- Add max_buffered_bytes config to GatewayConfig for slow consumer detection (default 1MB)
- Create prototype implementation showing bounded queue and flow control patterns
- Prevents unbounded memory growth from flooding clients
- Detects and disconnects slow consumers to protect gateway stability

Co-authored-by: MervinPraison <[email protected]>

* fix: address reviewer feedback on backpressure implementation

- Add drain loop to prototype to fix _is_executing getting stuck
- Update MultiChannelGatewayConfig.from_dict to forward new config fields
- Add validation for negative values in SessionConfig and GatewayConfig
- Improve error logging with exc_info=True

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-22 22:58:06 +01:00
MervinPraison 6af30d2d07 docs: auto-update documentation parity trackers [skip ci] 2026-06-22 21:57:40 +00:00
praisonai-triage-agent[bot]andMervin Praison b1ce2fb117 fix: add versioned handshake with capability negotiation to gateway protocol (#2154)
* fix: add versioned handshake with capability negotiation to gateway protocol (fixes #2151)

- Add protocol version constants and negotiation
- Add structured connect error codes (auth_required, protocol_unsupported, etc.)
- Add HelloParams, HelloResult, HelloError data structures
- Implement hello/hello_ok/hello_error message exchange
- Support capability-based feature discovery
- Communicate policy limits (max_payload, heartbeat_ms)
- Maintain backward compatibility with existing join/joined messages

Co-authored-by: MervinPraison <[email protected]>

* fix: address critical issues in hello handshake implementation

- Fix caps: null causing TypeError by adding None check
- Fix wire parser to support both HelloParams format (protocol_min/max, capabilities) and legacy format
- Fix hard-coded policy limits to use config values (heartbeat_interval)
- Remove unused HelloParams import
- Validate session belongs to requested agent and rebind client_id
- Only advertise implemented features (remove abort method, conditional streaming)

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 22:57:15 +01:00
praisonai-triage-agent[bot]andMervinPraison d4b9d37a81 fix: resolve three critical wrapper gaps (#2147)
* fix: resolve three critical wrapper gaps (fixes #2146)

- Gap 1: Add missing _models_cache and _models_lock in auto.py to fix NameError
- Gap 2: Fix race condition in ToolResolver by moving cache read inside lock
- Gap 3: Add async agent support to sync executor via shared dispatch logic

Co-authored-by: MervinPraison <[email protected]>

* fix: update stale docstring in PraisonAgentExecutor

The docstring incorrectly stated agents must have start() method, but with the new dispatch logic they can have either start() or astart() methods.

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 22:54:05 +01:00
praisonai-triage-agent[bot]andMervinPraison 51720c9236 fix: remove unreferenced dead code in core SDK (#2058) (#2141)
Remove fully-shadowed dead StorageMixin class from memory module. All 10 methods
of StorageMixin were redefined directly in Memory class body, making the mixin
unreachable dead code. Removed ~233 lines of stale storage implementation that
had already diverged from the live code.

- Remove StorageMixin from Memory base class list
- Remove import of StorageMixin
- Delete memory/storage.py file
- No behavior change - Memory class methods remain unchanged

Fixes #2138

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 22:53:33 +01:00
praisonai-triage-agent[bot]andMervin Praison 18949bfc9c fix: consolidate duplicate resolve_* functions in feature_configs.py (#2140)
* fix: consolidate duplicate resolve_* functions in feature_configs.py (fixes #2137)

Replaced duplicate resolver implementations in feature_configs.py with thin
delegation wrappers to the canonical param_resolver.py versions. This eliminates
code duplication while preserving backward compatibility.

Changes:
- resolve_memory, resolve_knowledge, resolve_planning, resolve_reflection
- resolve_guardrails, resolve_web, resolve_caching, resolve_autonomy
- resolve_tool_search (kept simple implementation with note about zero references)
- resolve_tools kept unchanged (used in production by agent.py)

All public API names preserved and tests remain compatible.

Co-authored-by: MervinPraison <[email protected]>

* fix: address backward compatibility regressions in resolver consolidation

- Fix resolve_guardrails to wrap callables in GuardrailConfig(validator=fn)
- Fix resolve_memory to handle valkey and custom backend strings gracefully
- Fix resolve_memory to passthrough unrecognized instances instead of returning None

These changes maintain backward compatibility while still delegating to the
canonical param_resolver.py implementations where possible.

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 22:53:06 +01:00
praisonai-triage-agent[bot]andMervin Praison a983d4921a fix: add intentional silence capability for gateway bots (#2133)
* fix: add intentional silence capability for gateway bots (fixes #2132)

- Add core silence protocol module with NO_REPLY token detection
- Update wrapper's shared delivery path to suppress messages on silence
- Add allow_silence and silence_token config options
- Initialize _allow_silence in all bot adapters
- Add CLI support with --allow-silence and --group-policy flags
- Enable agents to selectively stay silent in group chats

Co-authored-by: Mervin Praison <[email protected]>

* fix: address critical issues in silence capability implementation

- Add missing allow_silence and silence_token fields to BotConfig dataclass
- Fix silence detection logic to avoid false positives with malformed brackets
- Fix ImportError fallback to respect custom silence tokens
- Fix bot adapters to honor fire_message_sending cancel results
- Fix YAML serialization for new silence fields in BotCapabilities

These fixes resolve the TypeError at runtime and ensure the silence feature
works correctly across all bot platforms.

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-22 22:51:35 +01:00
praisonai-triage-agent[bot]andMervin Praison a89fff5b58 fix: address three production gaps - unsafe defaults, silent fallbacks, and concurrency issues (#2122)
* fix: address three production gaps - unsafe defaults, silent fallbacks, and concurrency issues (fixes #2121)

Gap 1 - Approval & Permission Defaults:
- Changed ApprovalSpec default to enabled=True with default_policy='prompt'
- Added per-tool granularity with approve_tools dict
- Added install_hook() method for enforcement
- Changed ClaudeCodeBackend to use --permission-mode default by default
- Added auth check to all HTTP verbs (GET, POST, DELETE) with constant-time comparison

Gap 2 - Silent Fallback Routing:
- DB adapter now raises ValueError for unknown URLs
- LLM registry raises ValueError for unknown models
- Daytona sandbox raises NotImplementedError until real implementation
- LazyCache caches and re-raises ImportErrors instead of caching None

Gap 3 - Concurrency Issues:
- AsyncBridge excludes self from cancellation, releases lock before wait
- LockMap buckets locks by event loop ID with weak references
- ToolRegistry tracks multiple resolvers with weak references
- Added missing _models_cache and _models_lock to auto.py

* fix: address critical reviewer feedback on production gaps

- Fix approval hook signature to match praisonaiagents API (BeforeToolInput, HookResult)
- Add missing default_policy and approve_tools fields to from_yaml() allowed set
- Fix race condition in ToolRegistry by protecting _resolvers access with lock
- Prevent infinite loop in LockMap when all locks are held
- Fix DaytonaSandbox to raise NotImplementedError directly (no wrapping)
- Fix auth check order in MCP HTTP transport to prevent info disclosure
- Update error messages and docstrings for accuracy

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-22 22:49:05 +01:00
praisonai-triage-agent[bot]andMervin Praison 5c2fc72c97 fix: merge three duplicated signature→JSON-Schema generators into one shared helper (#2120)
* fix: merge three duplicated signature→JSON-Schema generators into one shared helper (fixes #2118)

- Extract common schema generation logic into build_parameters_schema() in tools/schema.py
- Update BaseTool._generate_parameters_schema() to use shared helper
- Update decorator._generate_schema_from_func() to use shared helper with injected param filtering
- Update decorator._schema_from_function() to use shared helper
- Fix is_injected_type() to handle bare Injected annotations (not just Injected[T])
- Fix resolve_injected_value() to handle bare Injected annotations

This consolidates the duplicate logic with no behavior changes, making the codebase more maintainable.

Co-authored-by: MervinPraison <[email protected]>

* fix: add exception safety and injected parameter filtering to schema generation

- Wrapped inspect.signature() and get_type_hints() calls in try/except to handle forward references and built-ins gracefully (fixes P1 issue from Greptile)
- Added injected parameter filtering to _schema_from_function to prevent leaking internal state parameters
- Fixed linting issue by renaming unused variable 'param' to '_param'
- Improved exception handling in build_parameters_schema to be more resilient

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 22:46:17 +01:00
praisonai-triage-agent[bot] 16fe986176 fix: remove duplicate BotCapabilities construction across seven bot commands (fixes #2114) (#2116)
Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
2026-06-22 22:45:01 +01:00
praisonai-triage-agent[bot]andMervinPraison c6d2fdaaec fix: remove unreferenced dead code in core SDK (#2058) (#2115)
Remove dead AutoGen adapter registration that triggers heavy praisonai_tools import.
The registration was never used by runtime code and caused ~3200ms import overhead.

- Removed register_builtin_autogen_adapters() call from AgentsGenerator.__init__
- Kept deprecated methods for backward compatibility
- AutoGen support unaffected (provided by framework_adapters/)

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 22:44:42 +01:00
praisonai-triage-agent[bot]andpraisonai-triage-agent[bot] f9a88fc843 feat: add AI LLM gateway integration support (#2112)
* feat: add AI LLM gateway integration support (fixes #2111)

- Add gateway providers for OpenRouter, LiteLLM Proxy, and custom gateways
- Support for 100+ LLM providers through unified OpenAI-compatible endpoints
- Built-in caching, fallback, and analytics through gateway services
- Minimal implementation using existing LiteLLM infrastructure
- Add comprehensive tests and examples

Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>

* fix: address gateway registration and config mutation issues

- Fix lazy loading registration gap by eagerly registering gateways on module import
- Fix provider_id inconsistency for LiteLLMProxyProvider (now correctly returns 'litellm-proxy')
- Fix config dictionary mutation by creating shallow copy in __init__
- Remove hardcoded API keys from example, use environment variables instead
- Enable previously skipped test using monkeypatch to mock litellm module

Addresses review feedback from Greptile, CodeRabbit, and other reviewers

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>
2026-06-22 22:44:29 +01:00
praisonai-triage-agent[bot]andMervin Praison b4dbb52703 fix: add ergonomic proactive/scheduled delivery routing (#2110)
* fix: add ergonomic proactive/scheduled delivery routing with home channels and tokens (fixes #2109)

- Core SDK: Added HomeChannelRegistryProtocol and DeliveryResolverProtocol
- Core SDK: Extended DeliveryTarget with 'deliver' field for routing tokens
- Core SDK: Added 'origin' field to ScheduleJob to track creation location
- Wrapper: Implemented HomeChannelRegistry for persisting home channels
- Wrapper: Implemented DeliveryResolver for token resolution (origin, platform, all)
- Wrapper: Created EnhancedScheduledAgentExecutor with token support
- CLI: Added --deliver option to schedule add command
- Bots: Added /sethome command to set home channels

Co-authored-by: MervinPraison <[email protected]>

* fix: address critical delivery routing issues from review

- Fix token-based delivery being bypassed when delivery handler configured
- Fix job history showing delivered=False after successful enhanced delivery
- Add validation for channel/channel_id pairing and origin token requirements
- Remove sensitive chat IDs from info logs (moved to debug level)
- Validate and reject malformed explicit delivery tokens
- Add 30-second timeout protection for all delivery operations
- Properly set job.origin when deliver='origin' is specified

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 22:43:47 +01:00
praisonai-triage-agent[bot]andMervin Praison f95c07a8c8 fix: implement diff-driven selective reload for gateway config (#2107)
* fix: implement diff-driven selective reload for gateway config (fixes #2105)

- Add config diffing to detect specific changes
- Implement selective reload based on change paths:
  - Agent changes: recreate agents only
  - Single channel changes: restart that channel only
  - Structural changes: full restart (fallback)
- Add debouncing and error resilience to config watcher
- Store last loaded config for comparison
- Prevent unnecessary channel restarts on unrelated changes

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve critical issues in diff-driven selective reload

- Fix _start_single_channel to use _create_bot and _run_bot_safe pattern
- Convert _channel_tasks from list to dict for proper per-channel tracking
- Fix _restart_channel to cancel asyncio tasks before stopping bots
- Align auto_approve_tools parsing with start_channels (accepts 1, yes, on)
- Optimize agent key access using next(iter(...)) instead of list creation
- Populate _channel_bots in _start_single_channel to enable proper restarts

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 22:41:30 +01:00
praisonai-triage-agent[bot]andMervinPraison 7363190f77 fix: gateway loses in-flight execution and queued messages on disconnect (#2106)
* fix: gateway loses in-flight execution and queued messages on disconnect (fixes #2103)

- Extended to_dict() to serialize pending_inbox queue and is_executing flag
- Extended from_dict() to restore queued messages and execution state
- Added _drain_active_sessions() method for graceful shutdown with timeout
- Modified stop() to accept drain_timeout parameter and perform graceful drain
- Added logic to restart queue processing when session is resumed with pending work
- Fixed PID lock release check to prevent NoneType error

This ensures that transient disconnects, restarts, and shutdowns no longer silently abandon queued messages or in-flight executions. Sessions are now properly persisted and resumed with their full state including pending work.

Co-authored-by: MervinPraison <[email protected]>

* fix: address critical race condition and code quality issues

- Fix race condition in session resume by marking executing BEFORE creating task
- Replace deprecated asyncio.get_event_loop() with time.monotonic()
- Move test file from root to proper tests/unit/gateway directory
- Improve pythonic style by removing explicit boolean comparisons

Critical fix: Prevents duplicate queue processors when messages arrive during
session resume, which could cause out-of-order message processing.

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 22:40:48 +01:00
praisonai-triage-agent[bot]andMervin Praison ce6a7b0eeb feat: declarative permission policy across CLI, YAML and Python (#2101)
* feat: declarative permission policy across CLI, YAML and Python (fixes #2098)

- Add from_config() method to PermissionRule for declarative rule creation
- Add load_rules_from_config() to PermissionManager for bulk rule loading
- Add permissions field to ApprovalConfig for CI-safe policies
- Extend CLI with --allow/--deny/--permissions flags for runtime rules
- Support permissions block in agents.yaml (global and per-agent)
- Update non-interactive mode to evaluate declarative rules before denying
- Wire permissions through approval resolution pipeline
- Maintain backward compatibility with existing interactive prompts

This enables CI-safe, non-interactive execution with pre-defined permission
policies instead of blanket deny, while preserving the existing interactive
permission engine for attended usage.

Co-authored-by: MervinPraison <[email protected]>

* fix: address critical reviewer feedback for declarative permissions

- Support multiple --allow/--deny patterns by using List[str] type
- Don't persist ephemeral CI/YAML rules to disk (add persist=False flag)
- Remove hardcoded non_interactive=True to preserve backward compatibility
- Add deduplication logic to prevent duplicate rules
- Fix redundant permission check in non-interactive _prompt_user
- Add error handling for invalid permission rules with helpful messages
- Fix redundant condition in _build_target_string
- Use consistent wildcard format (tool:*) for pattern matching

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 22:40:10 +01:00
MervinPraison 9b26b35f1e docs: auto-update api.md [skip ci] 2026-06-22 21:39:30 +00:00
praisonai-triage-agent[bot]andMervin Praison aaf55f85da fix: add overflow-safe tool output with persistent storage (#2100)
* fix: add overflow-safe tool output with persistent storage (fixes #2097)

- Created ToolOutputStore for managing persistent tool output storage
- Store full tool outputs under ~/.praisonai/cache/tool_outputs/ when truncated
- Add references to stored files in truncated previews for later retrieval
- Support TTL-based cleanup with configurable retention (default 24 hours)
- Apply to both string results and dict fields uniformly
- Backward compatible: no changes when output is under limit

The agent can now retrieve omitted content using existing file/read tools
by accessing the stored path referenced in the truncated output.

Co-authored-by: MervinPraison <[email protected]>

* fix: address critical bugs in tool output store implementation

- Fix thread-unsafe global singleton with threading.Lock
- Fix Windows path backslash issue in re.sub by using callable replacement
- Fix run_id scoping: pass run_id through truncation chain
- Fix field-level call_id collisions with unique suffix
- Fix test isolation: use temp directory instead of real cache
- Fix implicit optional type annotation (RUF013)

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 22:39:07 +01:00
praisonai-triage-agent[bot]andMervinPraison ee9876f5f9 fix: make built-in edit tool precise and conflict-safe (#2099)
* fix: make built-in edit tool precise and conflict-safe (fixes #2096)

- Add ambiguity detection: fail when old_string matches multiple times without replace_all
- Add staleness checking: optional expected_hash parameter prevents conflicting edits
- Add line-ending preservation: detect and preserve CRLF/LF and BOM
- Generate bounded unified diff: show what changed in the success message
- Add read_file method to cache content hash for subsequent staleness checks

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve hash mismatch and UTF-16 handling issues in edit_tools

- Fix hash mismatch: read_file now uses binary mode matching edit_file
- Remove UTF-16 support: properly reject UTF-16 files with clear error
- Fix ellipsis formatting: only add ... when strings are actually truncated
- Ensure consistent BOM handling between read_file and edit_file

Addresses critical issues identified by reviewers:
- Greptile: Hash mismatch for BOM/CRLF files
- CodeRabbit: UTF-16 decode error, text vs binary mode
- Minor formatting improvements

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 22:38:11 +01:00
287db4847b fix: Agent max_retry_limit config is silently ignored; tool failures have no retry/backoff path (#2081)
* fix: implement retry configuration with exponential backoff for tool failures (fixes #1809)

- Add retry configuration fields to ExecutionConfig (retry_initial_delay, retry_backoff_factor, retry_jitter)
- Implement BackoffPolicy class for exponential backoff with jitter
- Add retry loop to tool execution that respects max_retry_limit from ExecutionConfig
- Honor ToolExecutionError.is_retryable for retry decisions
- Add backoff delays to guardrail retries (sync and async versions)
- Tool failures now retry with exponential backoff instead of immediate termination
- Guardrail retries no longer hammer LLM API with immediate re-calls

Co-authored-by: MervinPraison <[email protected]>

* fix: address reviewer feedback on retry implementation

- Wire ExecutionConfig into guardrail retries by storing _execution_config on Agent instance
- Add max_delay cap (60s) to BackoffPolicy to prevent unbounded exponential growth
- Add validation for retry parameters in ExecutionConfig.__post_init__
- Use shared BackoffPolicy in guardrail retry logic to eliminate code duplication
- Fixes issues identified by Greptile, CodeRabbit, and Copilot reviewers

Co-authored-by: Mervin Praison <[email protected]>

* fix: correct critical retry logic bugs identified by reviewers

- Fix attribute name mismatch: '_execution_config' not 'execution_config'
  This was causing all user-configured retry settings to be silently ignored
- Fix off-by-one retry count: max_retry_limit now correctly means number of retries
  With max_retry_limit=1, we now get 1 retry (2 total attempts) instead of 0 retries
- Fix error wrapping: exceptions from hooks are now properly wrapped in ToolExecutionError
  This ensures uniform error handling and observability

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 22:35:04 +01:00
praisonai-triage-agent[bot]andMervin Praison bdf0cdc3b8 fix: implement model fallback for resilience when primary model unavailable (#2046)
* fix: implement model fallback for resilience when primary model unavailable (fixes #2043)

- Add fallback_models parameter to Agent constructor for ordered fallback chain
- Implement _get_next_fallback_model() helper to retrieve next model from chain
- Replace TODO stubs with actual fallback logic in both sync and async paths
- Apply backoff before fallback and track fallback index across retries
- Update error messages to indicate when no fallback models are configured

The error classifier already routes transient/overloaded errors to model fallback,
but the runtime was not acting on it. This change completes the implementation
to automatically retry with the next model in the chain when the primary is unavailable.

Co-authored-by: Mervin Praison <[email protected]>

* fix: correct critical dispatcher caching bug in model fallback logic

- Clear _unified_dispatcher cache when switching models to ensure fallback actually uses the alternate model
- Fix log messages to show the failing model instead of the fallback model's provider
- Preserve _fallback_index across recursive retries to prevent chain resets
- Add exhausted fallback chain error messages for better user feedback
- Create defensive copy of fallback_models to prevent external mutations
- Add fallback_models to clone_kwargs to preserve resilience in cloned agents

These fixes make the fallback mechanism actually work instead of being a silent no-op.

Co-authored-by: Mervin Praison <[email protected]>

* feat: implement LLMConfig for cleaner API without SDK bloat

- Add LLMConfig class to group LLM-related parameters (model, fallback_models, base_url, api_key, auth)
- Update Agent.__init__ to accept llm_config parameter while maintaining backward compatibility
- Explicit parameters (model=, fallback_models=, etc.) still work and override llm_config values
- Verified all critical fixes from previous commits are still in place:
  - Dispatcher cache is properly cleared and restored during fallback
  - Log messages show correct failing model
  - Fallback index is preserved across recursive calls
  - Error messages distinguish between no fallback models vs exhausted fallbacks
  - Defensive copy of fallback_models prevents external mutations
  - clone_for_channel includes fallback_models

This provides a cleaner API for users while avoiding further bloat to Agent's 54+ parameters.

Co-authored-by: Mervin Praison <[email protected]>

* fix: remove backward-compatible fallback_models param to prevent SDK bloat

- Removed fallback_models parameter from Agent.__init__ signature
- LLMConfig is now the only way to configure fallback models
- Updated tests to remove backward compatibility tests
- Prevents further bloating of Agent class parameters (already has 50+ params)

This enforces cleaner API design where grouped configuration uses LLMConfig
rather than adding more individual parameters to Agent constructor.

* fix: refactor to accept LLMConfig via existing llm/model params instead of adding new param

- Remove llm_config parameter to prevent SDK bloat
- Make existing llm and model parameters accept LLMConfig objects directly
- Preserves clean API without adding more parameters
- Updated tests and examples to use new pattern
- Fallback models now only configurable via LLMConfig objects

This approach avoids parameter bloat while maintaining full functionality.

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-22 22:33:51 +01:00
b15c8a6611 fix: Wrapper gaps: async path skips observability/setup, sync vs async duplication, and sticky-None tool (#2079)
* fix: wrapper layer gaps - async path observability, sync/async duplication, tool caching (fixes #1860)

- Gap 1: Extract shared _prepare_adapter() method used by both sync/async paths
  Ensures observability init and adapter setup are consistent between code paths
- Gap 2: Remove duplicate arun() method definitions in Protocol and BaseFrameworkAdapter
- Gap 3: Fix ToolResolver to distinguish cacheable vs non-cacheable failures
  Transient import errors no longer permanently cached, allowing deps installed later

Co-authored-by: MervinPraison <[email protected]>

* fix: address final review feedback

- Fix tool resolver non-cacheable failure caching bug
- Remove duplicate AgentOps initialization
- Restore YAML autogen_version config support
- Fix PEP 8 missing blank line between methods

Co-authored-by: Mervin Praison <[email protected]>

* fix: remove unused _prepare_adapter method after rebase

The method is no longer needed as main uses _prepare_for_run

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 22:32:18 +01:00
192c21b59d fix: Runtime-scoped tool result middleware for plugin harnesses (#2077)
* feat: implement runtime-scoped tool result middleware for plugin harnesses (fixes #1944)

- Add RuntimeToolResultMiddleware protocol for normalizing tool results
- Implement RuntimeRegistry for per-runtime middleware management
- Integrate middleware into tool execution flow before hooks fire
- Support order: runtime middleware → global hooks → memory persistence
- Native 'praisonai' runtime bypasses middleware to avoid allocation overhead
- Add comprehensive documentation and tests for plugin authors
- Enable plugin harnesses to register vendor-specific result normalizers

Co-authored-by: MervinPraison <[email protected]>

* fix: properly propagate NormalizedToolResult fields to hooks and improve middleware implementation

- Fixed critical bug where success, error_message, and metadata fields were discarded
- Now properly propagates error context to AfterToolInput.tool_error field
- Fixed variable naming conflict (ctx reused for different purposes)
- Fixed test cleanup that was clearing entire global registry
- Added performance optimization to skip allocation when using PassThroughMiddleware
- Used module logger instead of root logger for consistency

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 22:31:53 +01:00
bd8ed31e00 fix: resolve top 3 architectural gaps in wrapper layer (fixes #1933) (#2070)
- Move AutoGen version selection entirely into adapter protocol, eliminating duplication
- Consolidate pattern recommendation and YAML serialization into BaseAutoGenerator
- Replace unsafe vector store globals with thread-safe centralized availability checking
- Remove environment mutation from vector store constructors

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 20:39:11 +01:00
7a8e11a2e2 fix: Dead code: orphaned _init_* methods in memory.py superseded by adapter registry (#2060)
* fix: remove dead code from memory.py - orphaned _init_* methods (fixes #1996)

- Remove _init_stm, _init_ltm, _init_mem0, _init_mongodb methods that were superseded by adapter registry
- Remove associated MongoDB helper methods _create_mongodb_indexes and _create_vector_search_indexes
- Keep _init_chroma as it's still used in the long-term reset path (line 1021)
- Total cleanup: ~113 lines of vestigial dead code removed
- No public API changes - all removed methods were private

Co-authored-by: MervinPraison <[email protected]>

* fix: Copy adapter attributes to self for backward compatibility

- Fixed AttributeError on self.use_vector_search (lines 579, 758, 857)
- MongoDB adapter's use_vector_search is now copied to Memory instance
- Fixed broken legacy compatibility paths by copying adapter attributes to self:
  - Mem0: mem0_client
  - ChromaDB: chroma_col, chroma_client
  - MongoDB: mongo_client, mongo_db, mongo_short_term, mongo_long_term
- Now hasattr(self, 'attribute') checks work correctly for legacy code
- Prevents incorrect SQLite fallback behavior

This ensures the dead code removal doesn't break existing functionality.

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 20:37:57 +01:00
MervinPraison ed9d7c2985 refactor(tests): rename test_redact_performance.py and optimize imports
Renamed the test file to reflect its new path and removed unnecessary
imports, streamlining the code for better readability.
2026-06-22 20:13:15 +01:00
MervinPraison fa45810bab Release v4.6.67 2026-06-22 20:10:26 +01:00
e2df0c7fd7 fix: remove orphaned register_legacy_reply helper (fixes #1959) (#2065)
The register_legacy_reply function in integration/_legacy_handlers.py was never
imported or used anywhere in the codebase. Legacy mode is actually handled by
the is_legacy_host() check in host_app.py that skips provider wiring.

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 20:03:45 +01:00
3c769d7fc5 fix: Add docstring to github_create_pull_request function (#2089)
* docs: enhance docstring for github_create_pull_request function

- Add detailed parameter descriptions for title, body, head_branch, and base_branch
- Include return type documentation and example usage
- Improve clarity and usefulness for AI agents consuming this tool

Fixes #1347

Co-authored-by: MervinPraison <[email protected]>

* fix: escape newline in docstring example

Fixes docstring rendering issue identified by Gemini Code Review.
The backslash before 'n' needs to be escaped to display correctly
in Python docstrings and prevent doctest parsing issues.

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 20:02:05 +01:00
0060da8ac4 fix: praisonai-platform[test] extras do not include required email-validator dependency (#2087)
* fix: add missing email-validator dependency to praisonai-platform test extras

Fixes #1584

- Added email-validator>=2.0.0 to [test] extra dependencies in src/praisonai-platform/pyproject.toml
- The EmailStr type from Pydantic requires email-validator but it was missing from test dependencies
- This caused ImportError during pytest collection in clean environments
- Verified fix: schemas import successfully and pytest collection works (113 tests found)

Co-authored-by: Mervin Praison <[email protected]>

* fix: move email-validator from test extras to core dependencies

Pydantic's EmailStr requires email-validator at runtime. The package is used
in schemas.py for RegisterRequest and LoginRequest models, making it a core
runtime requirement, not just a test dependency.

Without this fix, production installs fail on server startup with:
PydanticUserError: email-validator is not installed

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-22 20:01:48 +01:00
378f5a23f1 fix: JIRA Agent watch (#2078)
* feat: add JIRA Agent watch tools for monitoring issues and projects

- Add jira_tools.py with 4 comprehensive JIRA monitoring tools
- jira_watch_issue: Monitor specific issues for real-time changes
- jira_watch_project: Watch projects for new and updated issues
- jira_get_issue_info: Get detailed issue information
- jira_search_issues: Search using JQL queries
- Support both cloud and server JIRA authentication
- Include comprehensive unit tests and usage example
- Follow protocol-driven architecture with @tool decorator
- Implement lazy imports and proper error handling

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve critical JIRA tools issues - blocking sleep, data loss, injection, mocking

- Replace blocking time.sleep() polling with timestamp-based single-shot checks
- Fix silent comment data loss by tracking comments independently
- Add project key validation to prevent JQL injection attacks
- Fix test mocking to use correct patch target (jira.JIRA)
- Remove unused variables and improve code quality
- Update tests for new non-blocking API

Fixes blocking agent execution and security vulnerabilities identified by reviewers.

Co-authored-by: Mervin Praison <[email protected]>

* fix: resolve critical JIRA tools issues - string timestamp comparison, JQL injection, null safety

- Added _parse_timestamp() helper to properly parse ISO 8601 timestamps into datetime objects
- Added _validate_timestamp() to prevent JQL injection through timestamp parameters
- Fixed all timestamp comparisons to use datetime objects instead of string comparisons
- Added null safety checks for all JIRA API fields (author, creator, reporter)
- Removed unnecessary expand parameter from jira_get_issue_info
- Added comprehensive unit tests for timestamp parsing utilities

Addresses issues identified by Gemini, Greptile, and other reviewers

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 20:01:21 +01:00
77198f514f fix: Session runtime_state for native transcript mirroring (#2076)
* feat: add session runtime_state for native transcript mirroring (fixes #1943)

- Add runtime_state field to SessionData with structure {runtime_id: {turn_id: state}}
- Add SessionConfig.mirror_runtime_state option (default False, opt-in)
- Implement set_runtime_state(), get_runtime_state(), clear_runtime_state() methods
- Add RuntimeStateMirroringProtocol for type safety
- Include comprehensive unit tests for all functionality
- Ensure backward compatibility with existing sessions
- Document size limits and redaction policy for sensitive outputs

Co-authored-by: MervinPraison <[email protected]>

* fix: enforce mirror_runtime_state flag and harden state handling

- Fix P1: Added mirror_enabled parameter to set_runtime_state() to honor opt-in flag
- Fix null handling: Changed data.get('runtime_state', {}) to data.get('runtime_state') or {}
- Fix mutable state: Added copy.deepcopy() for storing and retrieving to prevent mutations
- Fix type annotation: Corrected Dict[str, Dict[str, Any]] to Dict[str, Dict[str, Dict[str, Any]]]
- Added comprehensive tests for all fixed issues

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 19:02:47 +01:00
392ac32ce5 fix: Sync streaming fails with reflection=True and AgentTeam; planning aborts non-interactive runs; web preset fetch on OpenA (#2069)
* fix: resolve sync streaming, planning approval, and web preset issues

- Fix reflection sync streaming fallback bypass in chat_mixin.py
  Always use stream=False during reflection to ensure compatibility with sync adapters

- Fix planning approval in non-interactive environments in approval.py
  Auto-approve plans when stdin is not a TTY to prevent silent aborts in CI/scripts

- Fix web preset fetch configuration in presets.py
  Disable fetch=False by default for all provider presets to ensure OpenAI model compatibility

Fixes #1931

Co-authored-by: MervinPraison <[email protected]>

* fix: add sys.stdin null check and move import to module level

- Add null check for sys.stdin to prevent AttributeError in GUI/daemon environments
- Move sys import to module level to follow best practices
- Fixes issue identified by Gemini and Greptile code reviews

Co-authored-by: Mervin Praison <[email protected]>

* Delete test_stdin_null.py

* Delete test_reproduction_cases.py

* Delete test_issue_1931_fixes.py

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 19:02:24 +01:00
b7b877c5f1 fix: Core SDK: EventBus does eager work (uuid4 + lock + history) on every publish even with zero subscribers (#2066)
* perf: optimize EventBus to skip expensive work when no subscribers

- Add fast-path check in publish_event() and publish_async() to skip lock,
  history append, and Event creation when no subscribers exist
- Add has_subscribers property for efficient lock-free checking
- Update memory _emit_memory_event() to conditionally build payload only
  when subscribers exist, avoiding datetime.now() and dict construction
- Preserve identical behavior when subscribers are present
- Add comprehensive tests for optimization behavior

Fixes hot-path performance issue in memory operations and sub-agent
lifecycle events where uuid4() + time.time() + lock + history append
occurred on every publish even with zero subscribers.

Fixes #1954

Co-authored-by: MervinPraison <[email protected]>

* fix: address reviewer feedback for EventBus optimization

- Move fast-path check before Event construction in publish() and publish_async() to avoid uuid4/time calls
- Eliminate event_data dict duplication in memory/core.py using lazy evaluation helper
- Remove unused mock patch from test
- Remove duplicate test file from repository root

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 19:00:21 +01:00
3c8531ec4f fix: Core SDK: trace redaction does a per-key O(N) scan despite an already-compiled regex sitting unused (#2064)
* fix: optimize trace redaction using compiled regex pattern (fixes #1960)

Replace O(56) per-key loop with single regex search using already-compiled
_REDACT_PATTERN. Preserves identical behavior while eliminating structural
inefficiency on trace-redaction hot path.

Co-authored-by: MervinPraison <[email protected]>

* fix: complete trace redaction optimization by pre-compiling all regex patterns

- Remove redundant re.IGNORECASE flag from _REDACT_PATTERN (already normalized to lowercase)
- Add pre-compiled _REDACT_KV_PATTERN for key=value matching in redact_string()
- Eliminate regex compilation on every redact_string() call (hot path optimization)
- Addresses performance issues identified by Greptile reviewer

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 18:59:36 +01:00
MervinPraison a5d8138a9a docs: auto-update feature parity trackers [skip ci] 2026-06-22 17:59:12 +00:00
56644ba7c1 fix: Core SDK: identical guardrail-validation and task-callback blocks duplicated between sync run_task() and async arun_task (#2063)
* fix: refactor duplicated guardrail and callback logic (fixes #1961)

Extract identical guardrail validation (~45 lines) and task callback logic (~13 lines)
that was duplicated between sync run_task() and async arun_task() into shared helper methods:

- _apply_task_guardrail(): Consolidates guardrail validation and retry logic
- _run_task_callback(): Consolidates task callback execution (sync/async)

This eliminates 58 lines of character-identical duplication and prevents future
guardrail/callback bugs from silently diverging between sync and async paths.

All existing behavior preserved, no breaking changes.

Co-authored-by: MervinPraison <[email protected]>

* fix: remove redundant callback execution in run_task/arun_task

The _run_task_callback helper was causing user callbacks to execute twice:
1. First in task.execute_callback via _execute_callback_with_metadata
2. Again in _run_task_callback

This commit removes the redundant _run_task_callback helper and its calls,
fixing the double execution bug identified during code review.

Additionally, the removed helper didn't support 2-parameter callbacks
that expect metadata, which the proper task.execute_callback does handle.

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 18:58:51 +01:00
8af852f79e fix: remove stale backup file agent_scheduler.py.bak (fixes #1964) (#2061)
Remove dead code - stale backup file that was tracked in git but
unreferenced. Also add *.bak to .gitignore to prevent recurrence.

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 18:58:24 +01:00
e76e3130c0 fix: Duplicate tool-name resolution paths in wrapper: resolve_tools() diverges from ToolResolver.resolve() (#2059)
* fix: consolidate duplicate tool resolution paths (fixes #2006)

- Modified resolve_tools() in templates/tool_override.py to delegate to ToolResolver
- Preserved all existing functionality: template-dir autoload, name variations, registry overrides
- Both resolution paths now share the same 5-source fallback chain
- Eliminates duplicate code and prevents future drift between implementations

Co-authored-by: MervinPraison <[email protected]>

* fix: address critical issues in tool resolution refactoring

- Fix double-execution: Remove manual template tools loading to avoid executing tools.py twice
- Fix security bypass: Only pass template path to ToolResolver when PRAISONAI_ALLOW_TEMPLATE_TOOLS=true
- Fix priority inversion: Registry overrides now properly take precedence over template tools
- Fix instantiation crashes: Handle class instantiation failures gracefully (backward compat)
- Add support for non-callable tools with run() method

Addresses feedback from Gemini, Greptile, and CodeRabbit reviews

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-22 18:58:03 +01:00
Wu Shuwen 5f39d667ee fix: remove unreferenced dead code in core SDK (#2058)
Remove two pockets of genuinely unused code confirmed by
repo-wide grep to have zero call sites:
- _config.missing_dependency_error() — private helper, no callers
- agent/summarization: SummarizationConfig, summarize_conversation(),
  summarize_conversation_sync() — unreferenced module-level symbols;
  SummarizationManager kept intact (tested public surface)

Fixes #2056
2026-06-19 23:35:52 +01:00
praisonai-triage-agent[bot]andMervin Praison b456e6b20f fix: remove three orphaned wrapper modules superseded by canonical equivalents (fixes #2053) (#2054)
- Remove src/praisonai/praisonai/bots/_delivery.py (UnifiedDelivery/create_delivery unused, superseded by bots/delivery.py)
- Remove src/praisonai/praisonai/persistence/conversation/mysql_new.py (abandoned refactor, superseded by conversation/mysql.py)
- Remove src/praisonai/praisonai/cli/legacy.py (route_to_legacy/main_with_legacy_support unused, superseded by __main__._run_legacy)

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-19 23:30:16 +01:00
MervinPraison affeb9e537 Release v4.6.65 2026-06-19 23:28:05 +01:00
praisonai-triage-agent[bot]andMervin Praison 5f2470b102 fix: extract warning filters to lightweight module to avoid heavy imports (#2055)
* fix: extract warning filters to lightweight module to avoid heavy imports (fixes #2052)

- Created new cli/_warnings.py module with stdlib-only dependencies
- Moved install_warning_filters, _uninstall_warning_filters, and _SUPPRESSED_PATTERNS
- Updated app.py to import from lightweight module instead of 7000-line main.py
- Added re-exports in main.py for backward compatibility
- Eliminates unnecessary yaml/rich/dotenv imports on every CLI invocation

* fix: add missing trailing newline to _warnings.py

Address review feedback from Greptile - POSIX requires files to end with a newline

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-19 23:26:21 +01:00
829e883e28 fix: critical correctness bugs in run control, compaction, sessions, and FileMemory (#1980)
* fix: critical correctness bugs in run control, compaction, sessions, and FileMemory

- Wire bot run control to agent.interrupt_controller (not _interrupt_controller)
- Drain queued pending messages after run completes instead of discarding them
- Mark ingress journal entries completed after successful chat()
- Break ContextCompactor _truncate infinite loop when only system messages exceed budget
- Use sanitised self.user_id for FileMemory storage paths
- Wire CLI session continuity to project session store via build/apply helpers

Co-authored-by: Mervin Praison <[email protected]>

* fix: critical issues identified by reviewers

- Fix preserve_system flag handling in compactor.py - respect flag when removing messages
- Fix auto-save regression in main.py - only apply session continuity for --continue/--session, not plain --auto-save
- Fix duplicate history injection in project.py - removed pre-population since mixin handles it

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
2026-06-19 23:25:22 +01:00
praisonai-triage-agent[bot]andMervin Praison 52636c2347 fix: consolidate tool registries, move framework dispatch to adapters, and fix lazy loading (#2003)
* fix: consolidate tool registries, move framework dispatch to adapters, and fix lazy loading (#2002)

- Gap 1: Replace if/elif dispatch with PluginRegistry pattern
  - Create CloudProviderRegistry for deploy/providers
  - Create EndpointProviderRegistry for endpoints
  - Create SearchProviderRegistry for web search
  - Fix SandboxRegistry to expose all 7 sandbox backends

- Gap 2: Consolidate duplicate KV persistence stacks (partial)
  - Make Redis StateStore wrap storage adapter to avoid duplication
  - Unify prefix handling between storage and persistence layers

- Gap 3: Extract UI bootstrap duplication into reusable factory
  - Add UIPreset dataclass and build_ui_app() factory function
  - Convert all 5 UI default_app.py files to use presets
  - Reduce each UI file from ~90 lines to ~30 lines

Fixes #2002

Co-authored-by: MervinPraison <[email protected]>

* fix: address critical issues from code review

- Fix host_app.py to not override existing provider (fixes cross-repo-integration tests)
- Fix Redis StateStore issues:
  - Handle bytes/string type mismatch in keys() and hgetall()
  - Fix connection pool leak for TTL writes using setex
  - Add data corruption fix with __wrapped__ marker
  - Mask password in logs
- Fix UI module regressions:
  - Add realtime_manager parameter to UIPreset for voice functionality
  - Add agent_loader parameter to UIPreset for YAML agent loading
  - Wire up OpenAIRealtimeManager for ui_realtime
  - Wire up YAML agent loading for ui_agents
- Fix Sandbox CLI logic error: update sandbox_type after fallback
- Fix env var consistency in ui_chat and ui_dashboard

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-19 23:24:37 +01:00
3230ea708a fix(cli): wire praison run session continuity to project session store (#1963)
* fix(cli): wire praison run session continuity to project session store

The CLI session continuity feature (#1929) discovered sessions in the
project-scoped store but agents saved/loaded from the global default
store. auto_save alone did not set session_id or enable history, so
prior messages were never restored and new messages were silently lost.

- Add build_cli_memory_config() and apply_cli_session_continuity()
- Wire project store in handle_direct_prompt and praison run actions mode
- Preserve session args across praison.run() arg re-parse for YAML runs
- Respect caller-injected session store in _init_session_store()
- Fix --output actions crash from invalid resume_session Agent kwarg

Co-authored-by: Mervin Praison <[email protected]>

* fix: resolve session continuity issues from CodeRabbit review

- Fix cli_project_sessions logic to check both session_id and auto_save_name
- Add hasattr(self, 'args') guard in main.py session continuity hook
- Remove auto-save re-enabling fallback in project_sessions.py
- Add session continuity support to profiled execution paths

Fixes issues identified in PR review to ensure --no-save and --continue
work correctly together without silently re-enabling persistence.

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
2026-06-19 23:23:23 +01:00
f689889f7a fix: four critical correctness bugs from recent refactors (#2021)
- Pass adapter.name (not adapter object) to cli_backend validation
- Fix AgentMailBot BotSessionManager init (BotConfig was used as max_history)
- Import ToolResolver from praisonai.tool_resolver in bot defaults
- Bridge bot streaming via stream_emitter; astart rejects stream_callback kwarg

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-19 23:22:56 +01:00
praisonai-triage-agent[bot]andMervin Praison e3ce707547 fix: add automatic session reset/expiry policies for bot sessions (#2031)
* fix: add automatic session reset/expiry policies for bot sessions (fixes #2019)

- Add SessionResetPolicy dataclass with idle and scheduled reset modes
- Integrate reset policy into BotSessionManager with automatic checks on load
- Update config schema to support session.reset YAML configuration
- Update all bot implementations to use reset policy from config
- Maintain backward compatibility with existing max_history config

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve critical bugs in session reset implementation

- Fix idle reset timing by moving _last_active update after history load
- Fix new session detection to properly initialize _last_reset
- Fix daily reset to check yesterday's schedule boundary
- Remove lock.drop() call that caused race conditions
- Fix max_history=0 configuration with explicit None checks
- Use model_dump() instead of __dict__ for Pydantic serialization

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-19 23:21:39 +01:00
praisonai-triage-agent[bot]andMervinPraison 978115e02d fix: add durable, idempotent outbound delivery for channel replies (fixes #2027) (#2036)
- Add OutboundDeliveryProtocol to core SDK for protocol definition
- Implement SQLite-backed OutboundQueue in wrapper for persistence
- Add DurableDelivery helper with retry logic and backoff
- Create DurableAdapterMixin for easy adapter integration
- Ensure crash-safe at-least-once delivery for computed replies

This addresses the gateway outbound durability gap where completed
agent replies could be lost during network failures or restarts.
Now replies are persisted before sending and retried on failure.

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-19 23:20:56 +01:00
praisonai-triage-agent[bot] f758467c1e fix: unify fragmented gateway config behind canonical schema (fixes #2025) (#2038)
- Create unified GatewayConfigSchema supporting all 3 config patterns
- Add shared env_utils.substitute_env_vars() helper (DRY)
- Implement gateway doctor checks for validation/migration/security
- Set secure defaults: group_policy='mention_only', warn empty allowed_users
- Remove deprecated marker from gateway command
- Update all loaders to use canonical schema

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
2026-06-19 23:20:04 +01:00
MervinPraison 3aa9cbc2bd fix: correct fork and merge-state detection in claude workflow
Use owner/repo for base repo comparison (context.repo.full_name is undefined)
and read mergeable_state from the REST API so DIRTY PRs get rebase instructions.
2026-06-19 17:05:14 +01:00
MervinPraison 345e47cab3 enhance: improve PR context and handling in GitHub Actions workflow
- Added outputs for head repository and merge state to enhance PR context.
- Updated conditional checks and instructions for handling merge conflicts and PR modifications.
- Streamlined messaging for clarity and better user guidance.

These changes aim to optimize the workflow's functionality while adhering to existing protocols.
2026-06-19 16:56:36 +01:00
MervinPraison d83427f9b2 refactor: enhance PR handling in GitHub Actions workflow
- Improved detection of pull requests to include review events.
- Added outputs for PR number and branch for better context.
- Streamlined conditional checks for forked repositories.
- Updated instructions for handling PRs and issues in the workflow.

This refactor aims to optimize the workflow's clarity and functionality while maintaining backward compatibility.
2026-06-19 16:38:18 +01:00
MervinPraison 1aa317ce6c docs: auto-update feature parity trackers [skip ci] 2026-06-19 14:58:09 +00:00
praisonai-triage-agent[bot]andMervin Praison 0115d51a2d fix: add persistent session support to gateway (#2040)
* fix: add persistent session support to gateway (fixes #2028)

- Gateway sessions now persist through restarts using SessionStoreProtocol
- Added session serialization/deserialization to GatewaySession
- Implemented session resumption on reconnect with event replay
- Added resume_window configuration for session TTL management
- Sessions are stored using existing DefaultSessionStore from core SDK
- WebSocket reconnect supports cursor-based event replay
- Added background cleanup task for expired sessions

Co-authored-by: MervinPraison <[email protected]>

* fix: critical bugs in gateway persistent session support

- Fix P0: Double message sending bug - messages were sent twice (once without cursor, once with)
- Fix P1: Session state never recovered - used get_chat_history() which strips metadata, switched to get_session()
- Fix P2: Ambiguous resume detection - added _was_resumed flag instead of time-based heuristic

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-19 15:57:49 +01:00
MervinPraison f5f5bd3f52 docs: auto-update api.md [skip ci] 2026-06-19 14:57:29 +00:00
praisonai-triage-agent[bot] 88cf0c2915 feat: file-based custom agents and reusable commands with auto-discovery (#2035)
- Add CustomDefinitionsDiscovery module for scanning project and user directories
- Support agents/*.md|*.yaml files with frontmatter (model, tools, role, instructions)
- Support commands/*.md files with template interpolation ($ARGUMENTS, @file)
- Add --agent and --command flags to 'praisonai run' CLI command
- Register discovered commands as slash commands (CommandKind.CUSTOM)
- Add 'praisonai agent list/show' and 'praisonai command list/show' management commands
- Implement project > user precedence for name collisions
- Add comprehensive unit tests for discovery, parsing, and interpolation

Fixes #2024

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
2026-06-19 15:57:10 +01:00
4f09886b51 fix: address security vulnerabilities in memory search refactoring (#2051)
* fix: address security vulnerabilities in memory search refactoring

- Add table name validation to prevent SQL injection in _search_sqlite()
- Remove hasattr guards to preserve original error behavior
- Add _ALLOWED_SQLITE_TABLES constant for allowed table names

This fixes the critical security issues identified in PR #2012 review.

Co-authored-by: Mervin Praison <[email protected]>

* fix: change mutable set to frozenset for SQL injection protection

- Changed _ALLOWED_SQLITE_TABLES from set to frozenset to prevent runtime mutation
- Improved error message format for consistency (sorted table list)
- Addresses critical security vulnerability identified by all reviewers

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-19 15:55:32 +01:00
MervinPraison 8ce5658d8b Release v4.6.64 2026-06-19 15:02:04 +01:00
praisonai-triage-agent[bot]andMervin Praison b7c8ab036b fix: add platform capability descriptors to bot/gateway layer (fixes #2044) (#2047)
- Add PlatformCapabilities dataclass in core protocols
- Enhance registry to store and retrieve capabilities
- Update Telegram and Discord adapters with specific capabilities
- Update chunking to respect platform-specific length units
- Add unified delivery module that uses capabilities

This enables uniform delivery features (streaming, rate limiting, chunking)
across all platforms without per-adapter special cases.

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-19 14:54:09 +01:00
MervinPraison e120bef8d3 docs: auto-update api.md [skip ci] 2026-06-19 13:53:36 +00:00
praisonai-triage-agent[bot] a743a3a507 feat: add interactive tool approval with persistence for CLI (#2037)
* feat: add interactive tool approval with persistence for CLI (fixes #2026)

- Create InteractiveCLIApprovalBackend with project-scoped rule persistence
- Add 'praisonai permissions' CLI command for rule management (list/allow/deny/reset)
- Wire PermissionManager into tool_execution approval flow
- Support permission modes: plan (read-only), accept-edits, bypass
- Add non-interactive mode for CI environments
- Enforce trust_level from tool registry as approval boundary

The CLI now prompts 'Allow once / Always allow / Deny' for sensitive tools,
persists 'always' decisions as rules, and remembers them for future runs.

* fix: address critical approval system issues (fixes #2037)

- Add thread safety to PermissionManager.save_rules() with lock
- Forward non_interactive parameter through resolve_approval_config()
- Apply permission modes before rule evaluation in all cases
- Normalize tool targets to always include colon for consistency
- Prevent ambiguous rule deletion with multi-match detection
- Rename list() to list_rules() to avoid shadowing builtin
- Remove redundant save_rules() calls (add_rule/remove_rule save internally)
- Replace deprecated asyncio.get_event_loop() with get_running_loop()
- Remove unused imports (sys, Path, json, PersistentApproval)

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
2026-06-19 14:53:18 +01:00
praisonai-triage-agent[bot]andMervin Praison 6da229e0e2 fix: consolidate lazy-cache, tool-loading and global async/registry state in wrapper (#2017)
* fix: consolidate lazy-cache, tool-loading and global async/registry state in wrapper (fixes #2016)

- Created unified _lazy_cache.py module with single LazyCache class
- Updated auto.py, llm/__init__.py, and inbuilt_tools/__init__.py to use lazy_get()
- Consolidated tool loading into ToolResolver with new public methods
- Removed duplicate tool loading methods from AgentsGenerator
- Made AsyncBridge public and instance-based for better multi-agent support
- Replaced static framework availability flags with on-demand is_available() checks

Co-authored-by: MervinPraison <[email protected]>

* fix: address all 8 critical issues from code review

- Restore tools.py/tools mutual exclusion (tools.py takes precedence)
- Remove underscore prefix filtering in load_functions_from_module
- Fix CLI availability constants to prevent NameError (define real module-level constants)
- Add lock protection to AsyncBridge.shutdown() to prevent race condition
- Add try/except guard for CALL_MODULE_AVAILABLE
- Handle both dict and list formats for tasks in YAML
- Raise AttributeError when lazy_get returns None in llm/__init__.py
- Narrow exception handling from Exception to specific types in agents_generator.py

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-19 14:52:03 +01:00
praisonai-triage-agent[bot]andMervinPraison 6377b84864 fix: defer context-trace event construction behind enabled check (fixes #2007) (#2014)
Add early guards to hot-path emit methods to avoid expensive event construction when tracing is disabled. This eliminates wasted CPU cycles on every tool call and LLM iteration in the default (tracing-disabled) configuration.

Changes:
- Add if not self._enabled: return guards at the top of all hot-path emit methods
- Affects: tool_call_start, tool_call_end, llm_request, llm_response, agent_start, agent_end, message_added
- Preserves exact behavior when enabled, skips all work when disabled
- Keeps existing _emit guard as safety backstop

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-19 14:51:44 +01:00
praisonai-triage-agent[bot]andMervinPraison ac8e4e4bbd fix: collapse identical STM/LTM search bodies in memory/search.py (fixes #2009) (#2012)
- Extract _search_mongodb() helper for MongoDB search logic
- Extract _search_sqlite() helper for SQLite search logic
- STM/LTM methods now delegate to shared helpers
- No behavior changes, just deduplication

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-19 14:50:44 +01:00
618d2142f3 fix(bots): prevent silent message loss in inbound journal redeliveries (#1989)
- Complete journal entries after successful BotSessionManager.chat()
- Return existing key for duplicate pending/stale-claimed messages
- Skip re-processing only for completed or actively-claimed entries
- Add unit tests covering redelivery and completion semantics

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-19 14:50:30 +01:00
9fa3e4803e fix: critical bugs in session continuity, file memory, and run control (#1973)
* fix: critical bugs in session continuity, file memory, and run control

- Wire praison run session save/load to ProjectSessionStore (#1929 follow-up)
- Fix FileMemory user_path bypassing _sanitise_user_id (path traversal)
- Fix chat_with_run_control attaching to wrong interrupt_controller attr
- Preserve injected session store in _init_session_store()
- Add unit tests for all three fixes

Co-authored-by: Mervin Praison <[email protected]>

* fix: CLI session continuity for resume-only runs

- Enable cli_project_sessions when session_id OR auto_save is set
- Display correct session ID in CLI messages for resume-only runs
- Fix session restore when agent has pre-seeded chat_history
- Add deduplication logic to prevent duplicate history entries

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
2026-06-19 14:47:46 +01:00
5b0154bdcd fix: add outbound message retry with exponential backoff and DLQ (#2048)
* fix: add outbound message retry with exponential backoff and DLQ (fixes #2045)

- Implement deliver_with_retry() wrapper for bounded exponential backoff on send failures
- Add OutboundDLQ class for parking undeliverable messages after max retries
- Update Telegram adapter to use retry wrapper for all send operations
- Add configuration schema for outbound resilience settings
- Ensure expensive LLM work is never lost due to transient platform errors

Co-authored-by: Mervin Praison <[email protected]>

* fix: resolve critical bugs in outbound retry implementation

- Made OutboundDLQ.enqueue_outbound async using asyncio.to_thread
- Fixed max_attempts=0 edge case with proper unlimited retry loop
- Added file seek(0) before each audio/voice retry attempt
- Added missing Any import to _resilience.py
- Resolved merge conflict by including all fields

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
Co-authored-by: MervinPraison <[email protected]>
2026-06-19 13:02:24 +01:00
praisonai-triage-agent[bot] 46f9e18828 fix: remove orphaned comments from PR #2013 dead code removal (#2049)
Removed two orphaned comments that were left behind after dead code removal.
These comments referenced _original_description reset blocks that no longer exist.

- Line 790: Removed orphaned comment in async workflow
- Line 1368: Removed orphaned comment in sync workflow

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
2026-06-19 13:01:44 +01:00
praisonai-triage-agent[bot]andMervinPraison 320eb74d2c fix: add proactive channel health monitoring and fleet auto-recovery (#2041)
* fix: add proactive channel health monitoring and fleet auto-recovery (fixes #2030)

- Add HealthReason enum and evaluate_channel_health() function to core SDK
- Implement ChannelHealthMonitor class with configurable policies and rate limiting
- Integrate health monitoring into gateway supervisor with restart capabilities
- Add supervision and health monitoring to BotOS for multi-bot recovery
- Support health config in gateway.yaml and BotOS YAML configuration
- Implement restart budgets with per-channel rate limiting (max N/hour)
- Add startup grace period to avoid false positives during initialization
- Track transport activity to detect stale sockets and stuck channels

Co-authored-by: MervinPraison <[email protected]>

* fix: address critical issues in health monitoring implementation

- Fix health reason immediately clobbered by reconnect() (P1)
- Fix reconnect() unable to interrupt running bot (P1)
- Fix null supervision config crashes from_yaml (P1)
- Fix wrong type annotation for restart_fn (P2)
- Fix unhandled async task at startup (P2)

The key fixes:
1. Moved reconnect() call before setting error fields so health reason persists
2. Made bot execution interruptible by wrapping in task and watching abort signal
3. Added null safety for supervision config in YAML parsing
4. Fixed restart_fn type annotation to be Awaitable
5. Changed health monitor startup to await instead of fire-and-forget

* fix: correct type annotation for async health_check_fn

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-19 13:00:25 +01:00
MervinPraison ee06c22f8a docs: auto-update api.md [skip ci] 2026-06-19 11:59:45 +00:00
praisonai-triage-agent[bot]andMervin Praison 8d1dfa1333 fix: unified, project-aware configuration hierarchy with persisted defaults (#2039)
* fix: unified, project-aware configuration hierarchy with persisted defaults (fixes #2022)

- Create unified config resolver with proper precedence (defaults → global → project → env → CLI)
- Implement walk-up discovery for project configs from subdirectories
- Extend configuration schema to include agent defaults (model, provider, tools, memory, etc.)
- Add backward compatibility for legacy paths (~/.praison/config.toml and ~/.praisonai/.env)
- Update llm/env.py to accept resolved configuration while maintaining env var precedence
- Update setup handler to write to unified config.yaml format
- Enhance config CLI commands with show, validate, and sources subcommands
- Add model resolution to run command from config when not explicitly provided

This consolidates the previously fragmented configuration system into a single,
well-structured hierarchy that enables drop-in project configs that 'just work'.

Co-authored-by: MervinPraison <[email protected]>

* fix: critical bugs in unified configuration hierarchy

- Fix walk-up discovery: search from current dir upward (nearest wins)
- Fix legacy .env crash: add missing _source key
- Fix typer.Exit double error messages: let exits propagate
- Fix unreachable vector_store_provider migration
- Fix silent config parse failures: catch specific exceptions
- Fix parameter shadowing and format validation in config_show
- Remove unused imports (lru_cache, replace)
- Improve error handling with proper logging

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-19 12:59:25 +01:00
praisonai-triage-agent[bot]andMervinPraison 7a9e53718c fix: remove duplicate resolve_guardrails helper (fixes #2008) (#2015)
- Removed redundant _resolver_helpers.py module
- Updated import in agent.py to use canonical param_resolver.resolve_guardrails
- Both implementations were functionally equivalent
- Reduces maintenance burden by having single source of truth

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-19 11:05:50 +01:00
5f44246030 fix: resolve three critical regressions from recent bot and framework changes (#2004)
- Pass adapter.name (not adapter object) to cli_backend validation so
  praisonai YAML configs with cli_backend/runtime no longer always fail
- Bridge bot streaming callbacks via stream_emitter and pass cancel_token
  to astart(), fixing TypeError on first streamed message and /stop handling
- Import ToolResolver from praisonai.tool_resolver in bot defaults so
  configured tools are not silently dropped to the fallback set

Adds regression tests covering all three fixes.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-19 10:59:05 +01:00
praisonai-triage-agent[bot]andMervinPraison 183b623800 fix: remove orphaned files - backup and unreferenced legacy handler (fixes #2011) (#2013)
- Remove unreachable loop_data blocks from process.py (async and sync versions)
- Remove _original_description restore blocks that never execute
- Remove unused deduplicate_topics function from context/manager.py

All removed code was verifiably unreachable with no behavior change.

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-19 10:57:50 +01:00
praisonai-triage-agent[bot] b7d21a91d2 fix: remove orphaned CLI helper builders - add_gateway_parser, _create_mcp_app, _create_tools_app (fixes #2005) (#2010)
Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
2026-06-19 10:56:55 +01:00
praisonai-triage-agent[bot]andMervinPraison d2091a7712 fix: make live-streamed responses, run-status reactions and typing portable across channels (#2042)
* fix: make live-streamed responses, run-status reactions and typing portable across channels (fixes #2032)

- Add ChannelCapabilities to core protocol for feature discovery
- Add RunStatus enum for progress lifecycle states
- Add reaction methods (add_reaction/remove_reaction) to BotProtocol
- Create shared StatusReactions engine for unified status feedback
- Create shared TypingManager with keepalive and circuit breaker
- Update DraftStreamer to check capabilities and degrade gracefully
- Add capabilities property to all bot adapters (Telegram, Slack, Discord, WhatsApp, Email)
- Add reaction method implementations to all adapters
- Enable live-edit streaming for Slack and Discord (not just Telegram)

Co-authored-by: MervinPraison <[email protected]>

* fix: address critical bugs in status reactions, streaming, and typing

- Fix Slack reactions: Map Unicode emojis to Slack text names (thinking_face, hourglass, etc.)
- Fix Telegram remove_reaction: Document known limitation that it clears all bot reactions
- Fix StatusReactions state desync: Clear current_emoji on remove, handle add_reaction failures
- Fix StatusReactions race condition: Capture pending status before await to avoid dropping updates
- Fix DraftStreamer rate limiting: Apply channel edit_rate_limit to config.min_interval
- Fix TypingManager duration: Reset start_time on stop() to avoid stale values

These fixes ensure status reactions work correctly on Slack, prevent state desync in StatusReactions, and properly apply rate limits from channel capabilities.

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-19 10:55:16 +01:00
praisonai-triage-agent[bot]andMervin Praison 688d770607 fix: add unified slash-command framework with per-command access control (#2029)
* fix: add unified slash-command framework with per-command access control (fixes #2018)

- Implement CommandRegistry for unified command management across all bot adapters
- Add CommandAccessPolicy for fine-grained per-command permissions
- Support admin_users and user_allowed_commands in bot configuration
- Add /whoami command to display user info and permissions
- Update Telegram adapter to use new command framework with access checks
- Maintain backward compatibility (no restrictions if fields not configured)

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve command framework issues found in review

- Fix custom commands missing from /help by adding _format_help_with_permissions method
- Remove unreachable whoami branch in handle_command
- Fix empty string config handling by filtering out empty strings after split
- Update docstring for unregister method to clarify return cases
- Keep module-level singleton as it's intentional design

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-19 10:52:36 +01:00
praisonai-triage-agent[bot]andMervin Praison 63ae440fcd feat: add portable interactive message presentation system for gateway/bots (#2034)
* feat: add portable interactive message presentation system for gateway/bots (fixes #2023)

- Added presentation protocol and dataclasses in core SDK (praisonaiagents)
- Created typed primitives for buttons, select menus, and text blocks
- Updated GatewayMessage to support optional presentations
- Implemented platform-specific renderers for Telegram, Slack, Discord
- Added presentation-based approval system to replace text classification
- Each channel can now render native widgets (inline keyboards, blocks, components)
- Supports channel capability limits and automatic truncation

Co-authored-by: MervinPraison <[email protected]>

* fix: address reviewer findings in presentation system

- Fix field/staticmethod collision by renaming factory methods (make_text, make_select, etc)
- Move SupportsPresentation protocol to protocols.py per architecture guidelines
- Fix presentation deserialization to handle null/non-dict values
- Replace deprecated asyncio.get_event_loop() with get_running_loop()
- Fix exception handling to not swallow CancelledError
- Replace unescaped zero-width space literals with \u200B
- Add 64-byte truncation for Telegram command-type callback_data
- Fix from_dict to preserve None for optional fields instead of empty lists

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-19 10:50:53 +01:00
praisonai-triage-agent[bot]andMervinPraison d87915e3de feat: add proactive outbound delivery routing and channel directory for bot gateway (#2033)
* feat: add proactive outbound delivery routing and channel directory for bot gateway

- Implement DeliveryRouter class for target resolution (origin, platform, platform:chat_id, aliases)
- Create ChannelDirectory for managing reachable channels with friendly aliases
- Update BotOS to integrate delivery routing for scheduled jobs
- Add deliver() method to BotOS for direct proactive messaging
- Extend config schema with home_channel and aliases support
- Update scheduled job delivery to use new routing system

Fixes #2020

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve YAML config, platform casing, and alias validation issues in delivery router

- Fix YAML home_channel/aliases configuration being silently ignored in from_config() path by checking both _config and _kwargs
- Normalize platform names to lowercase throughout ChannelDirectory for consistency
- Add validation to prevent silent alias overwriting - raises error on conflicting redefinitions
- Fix platform:channel_id format to validate empty channel_id and normalize platform casing
- Reorder resolve() logic to check platform names before aliases for deterministic precedence
- Prevent delivering literal 'None' for empty schedule results

Addresses issues raised by Greptile, CodeRabbit, and other reviewers.

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-19 10:49:58 +01:00
MervinPraison 863ade10c4 docs: auto-update api.md [skip ci] 2026-06-19 09:42:39 +00:00
22378ffb42 feat: implement CLI authentication system (fixes #1925) (#1928)
* feat: implement CLI authentication system (fixes #1925)

- Add praisonai auth command group with login/logout/list/status subcommands
- Implement secure credential storage with atomic writes and 0o600 permissions
- Store credentials in ~/.praison/credentials.json with JSON format
- Add LLM endpoint resolution with credential fallback (env > stored > fail)
- Add preflight checks to praisonai run to prevent raw API errors
- Extend config schema with [llm] section for default model/provider settings
- Support credential validation with format checks and optional live validation
- Backward compatible: stored credentials auto-inject into process env
- Add comprehensive test suite covering all auth functionality

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Claude <[email protected]>

* fix: resolve merge conflicts and fix critical auth bugs

- Merge configuration schema: combine LLMConfig from auth branch with RulesConfig from main
- Integrate auth preflight checks with rules auto-injection in run command
- Fix Windows file operations: os.rename → os.replace for atomic writes
- Fix missing Cohere provider in credential injection mapping
- Fix auth preflight to check all known provider env vars
- Fix double-close risk in credential file descriptor management

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Claude <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-19 10:42:21 +01:00
MervinPraison 9bcf9c6a00 docs: auto-update api.md [skip ci] 2026-06-19 09:42:03 +00:00
praisonai-triage-agent[bot]andMervin Praison 7814844961 feat: deprecate agent-level cli_backend and add doctor fix migration (#1946)
* feat: deprecate agent-level cli_backend and add doctor fix migration

- Add DeprecationWarning to core Agent when cli_backend used with model config
- Add DeprecationWarning to wrapper Agent when cli_backend used with model config
- Implement doctor fix command for automated config migration
- Add --fix, --execute, and --no-backup flags to doctor CLI
- Create migration tool that detects and annotates deprecated cli_backend usage
- Maintain backward compatibility during deprecation period

Fixes #1942

Co-authored-by: MervinPraison <[email protected]>

* fix: address critical deprecation and doctor fix issues

- Remove duplicate deprecation warnings between wrapper and core Agent
- Fix CLI command reference from 'praisonai doctor --fix' to 'praisonai doctor fix --execute'
- Remove unimplemented 'allow_legacy_runtime_pin=True' reference
- Fix duplicate file processing in doctor handler by deduplicating yaml_files
- Add proper error handling with exit codes in doctor fix command
- Remove unused imports and variables in doctor handler
- Fix CLI flag mapping from --config to --file for handler compatibility
- Remove test fixture from repository root

These fixes address all critical bugs identified by previous reviewers.

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-19 10:41:41 +01:00
MervinPraison c31e335770 docs: auto-update feature parity trackers [skip ci] 2026-06-19 09:41:12 +00:00
praisonai-triage-agent[bot]andMervin Praison 0698acbf00 fix: remove dead code - orphaned UnifiedChatMixin, broken filter_injected_from_schema, dead adapter method, and empty __init__new.py files (fixes #1983) (#1991)
Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-19 10:40:52 +01:00
praisonai-triage-agent[bot]andMervinPraison 1620b49f36 fix: remove orphaned files - backup and unreferenced legacy handler (fixes #1994) (#1998)
- Delete cli/features/agent_scheduler.py.bak (checked-in backup artifact)
- Delete integration/_legacy_handlers.py (unreferenced legacy handler module)

Both files were verified to have zero references in the codebase. The live implementations remain untouched:
- agent_scheduler.py continues to be the maintained scheduler
- PRAISONAI_HOST_LEGACY functionality remains properly implemented in host_app.py

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-18 11:48:58 +01:00
praisonai-triage-agent[bot]andMervin Praison a08752e528 fix: defer sandbox subsystem imports to avoid import bloat (#1992)
* fix: defer sandbox subsystem imports to avoid import bloat (fixes #1981)

- Move sandbox imports behind TYPE_CHECKING for type annotations only
- Defer SandboxConfig import to __init__ when sandbox=True
- Defer SandboxManager import to first use in get_sandbox_manager()
- Defer check_code_safety and format_warnings imports to method execution
- Quote return type annotations to prevent runtime imports

This change ensures the sandbox subsystem is only loaded when actually used,
following AGENTS.md §4.2 principle of no heavy initialization for opt-in features.
The sandbox functionality remains unchanged - only the import timing is optimized.

Co-authored-by: Mervin Praison <[email protected]>

* fix: normalize sandbox=False to None per agent parameter pattern

- Add explicit handling for sandbox=False to convert it to None
- Ensures has_sandbox returns False when sandbox is disabled
- Follows the pattern documented in agent.py:570
- Fixes regression identified in PR review

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-18 11:47:34 +01:00
MervinPraison 644721accb Release v4.6.63 2026-06-18 10:15:03 +01:00
praisonai-triage-agent[bot]andMervinPraison 9d5db0ccca fix: remove duplicate/dead config resolvers in feature_configs.py (fixes #1997) (#2000)
- Remove duplicate resolve_output and resolve_execution from feature_configs.py
- These functions are shadowed by param_resolver versions (public API uses param_resolver)
- The removed functions contained a latent bug calling non-existent from_preset methods
- Keep canonical implementations in param_resolver as single source of truth

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-18 10:13:07 +01:00
praisonai-triage-agent[bot]andMervinPraison f2b62978a2 fix: remove four empty, unreferenced __init__new.py files in core SDK (#1999)
Dead code cleanup - removed 0-byte files that were never imported or referenced:
- praisonaiagents/__init__new.py
- praisonaiagents/ui/__init__new.py
- praisonaiagents/planning/__init__new.py
- praisonaiagents/llm/__init__new.py

These files contained no code and were not part of any public API,
no import graph, no runtime behaviour.

Fixes #1995

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-18 10:12:48 +01:00
praisonai-triage-agent[bot]andMervin Praison 6cb97e26c0 fix: remove shadowed legacy session.py duplicating session/ package (fixes #1982) (#1990)
- Removes unreachable praisonaiagents/session.py (673 lines) that was shadowed by the session/ package
- Eliminates duplicate Session implementation and eager 'import requests' anti-pattern
- All imports already resolve to the package, not the file
- Tests confirm session functionality remains intact

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-18 10:11:19 +01:00
praisonai-triage-agent[bot]andMervin Praison ef63c233c0 fix(hooks): optimize execute_sync() to avoid event loop overhead when no hooks registered (#1978)
* fix(hooks): add fast-path to execute_sync() to avoid event loop creation when no hooks are registered (fixes #1977)

This optimization prevents unnecessary event loop creation/teardown overhead
on hot paths when no hooks are registered. Previously, execute_sync() would
always call asyncio.run() even when the fast-path check inside execute()
would immediately return [].

Performance improvement:
- No hooks: ~33,000x faster (5μs vs ~165ms)
- With hooks: identical behavior

The fix preserves all existing functionality:
- Running event loop safety check is unchanged
- All hook execution semantics remain identical
- All 57 hook unit tests continue to pass

Co-authored-by: Mervin Praison <[email protected]>

* perf(hooks): eliminate redundant get_hooks() call in execute_sync()

When hooks exist, execute_sync() was calling get_hooks() twice:
1. For the fast-path check (line 158)
2. Again inside execute() (line 99)

This adds an internal _hooks parameter to execute() to pass the
pre-fetched hooks list, eliminating the redundant registry call.

Backward compatibility: The _hooks parameter uses a private name
and defaults to None, so all existing public API calls are unaffected.

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-18 10:10:55 +01:00
a4f8d2050f fix: Remove redundant import causing UnboundLocalError in tool execution (fixes #1968) (#1974)
The conditional import of ToolExecutionError on line 248 created a local variable
scoping issue where isinstance(e, ToolExecutionError) would fail with UnboundLocalError
when the HALT branch was not taken. The module-level import on line 18 is sufficient.

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>
2026-06-18 10:10:27 +01:00
708b2de56f fix: implement lazy loading for CLI command modules (#2001)
* fix: implement lazy loading for CLI command modules (fixes #1993)

All ~85 CLI command modules are now loaded only when invoked, not eagerly on startup.
This reduces CLI cold-start latency by deferring imports until dispatch.

- Created LazyCommandGroup class extending TyperGroup
- Moved command registry to _LAZY_COMMANDS dictionary
- Commands listed in --help without importing modules
- Special handling for app, standardise, TUI, and retrieval commands
- No user-facing changes; all commands remain available

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve critical issues with lazy loading implementation

- Fix _get_app_command() to return click.Command instead of raw function
  - Creates local Typer instance to avoid mutating global app state
  - Uses typer_get_command() to properly convert to click.Command
  - Prevents duplicate command registration on repeated invocations

- Fix retrieval commands availability
  - Check parent's registered commands first via super().get_command()
  - Replace hardcoded 'delete' with correct 'search' command in list
  - Use set operations to avoid duplicate command listings

These fixes ensure all commands work properly with lazy loading while
maintaining backward compatibility.

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-18 10:09:37 +01:00
66c1c47efd fix: register audit command in both CLI entry points (fixes #1969) (#1975)
* fix: register audit command in both CLI entry points (fixes #1969)

- Add 'audit' to special_commands list in legacy CLI routing
- Register audit command in modern Typer app with Click wrapper
- Ensures praisonai audit agent-centric command is accessible via CLI

Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>

* fix: improve audit command implementation

- Remove unused audit_click_group import
- Add path existence validation to scan/fix/check options
- Move agent_centric import to module level for performance
- Use Typer Path type with exists=True validation

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-18 10:08:14 +01:00
praisonai-triage-agent[bot]andMervin Praison c175691991 fix: wrapper layer stale locks and dead imports (fixes #1971) (#1972)
* fix: wrapper layer stale locks and dead imports (fixes #1971)

- Remove dead imports from agents_generator.py (load_dotenv, AutoGenerator, PraisonAIModel)
- Replace BotSessionManager id()-based locks with WeakKeyDictionary to prevent corruption
- Create LockMap utility with LRU eviction and TTL cleanup
- Refactor duplicated lock patterns in _session, _debounce, and _run_control

Co-authored-by: MervinPraison <[email protected]>

* fix: address reviewer feedback on LockMap implementation

- Fix AttributeError: Change _session.py reset() to use .drop() instead of .pop()
- Fix LRU eviction logic: Continue past locked entries instead of breaking
- Update docstring: Clarify asyncio-safe vs thread-safe guarantees

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-18 10:07:49 +01:00
2ece9d59db fix: update MCP CLI commands in documentation (#1976)
* fix: update MCP CLI commands in documentation

Replace list-tools with tools command in README and examples
Update cli_tools_example.sh with correct CLI commands
Update test file comments to reflect actual CLI commands
Remove references to non-existent pagination flags and search subcommands

Fixes #1970

Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>

* fix: correct MCP CLI commands in documentation to match actual implementation

- Replace non-existent commands (describe, sync, list) with actual working commands (list-tools, tools search/info/schema)
- Fix README.md to document commands that exist in MCPServerCLI.handle()
- Fix cli_tools_example.sh to use working CLI commands
- Fix test docstrings to accurately describe what is being tested

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-18 10:07:24 +01:00
praisonai-triage-agent[bot] b025d199fd fix: consolidate tool registries, move framework dispatch to adapters, and fix lazy loading (#1988)
* fix: consolidate tool registries, move framework dispatch to adapters, and fix lazy loading

- Gap 1: Consolidated tool resolution into protocol-based ToolSource pattern
  - Removed dead BaseFrameworkAdapter._tool_registry code (zero call sites)
  - Added ToolResolver.invalidate() with cache management
  - Wired ToolRegistry to ToolResolver for cache invalidation on register

- Gap 2: Moved framework dispatch logic to adapter protocol
  - Added resolve_variant() method to FrameworkAdapter protocol
  - Moved AutoGen v0.2/v0.4 selection from AgentsGenerator to AutoGenAdapter
  - Extracted observability initialization to separate _init_observability method

- Gap 3: Fixed lazy loading in auto.py to honor its contract
  - Moved pydantic BaseModel imports to runtime functions
  - Replaced yaml.dump/load with lazy _yaml_dump/_yaml_safe_load wrappers
  - Replaced rich.print with lazy _rich_print wrapper
  - All Pydantic models now created on first use via _get_*_models() functions

These changes restore the architectural principles:
- DRY: Single tool resolution path
- Protocol-driven: ToolSource protocol, adapter.resolve_variant()
- Lazy loading: auto.py no longer imports heavy deps at module level
- No global singletons: Tool cache invalidation is scoped

Fixes #1987

* fix: resolve critical issues from code review

- Fix P0: NameError in agents_generator.py - changed undefined 'framework' to 'adapter'
- Fix P1: Overly broad exception handling in auto.py - now only catches YAML errors while re-raising OS-level errors like PermissionError
- Fix P2: Add clearer TODO for unused sources parameter in tool_resolver.py

These fixes ensure the PR is production-ready and maintains proper error handling semantics.

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
2026-06-18 07:50:49 +01:00
MervinPraison 6d7f1630b1 docs: auto-update api.md [skip ci] 2026-06-18 06:50:13 +00:00
praisonai-triage-agent[bot]andMervin Praison d0eb52156a fix: remove unreachable session.py, port TTL functionality to live package (#1966)
* fix: remove unreachable session.py, port TTL functionality to live package (fixes #1965)

- Port session_ttl parameter and TTL methods (is_expired, close, time_to_expiry) from unreachable session.py to session/api.py
- Remove praisonaiagents/session.py which was shadowed by session/ package and never imported
- Preserve all existing functionality while making TTL/expiry capability accessible
- Capability-preserving fix as per issue requirements

Co-authored-by: Mervin Praison <[email protected]>

* fix: address reviewer feedback for session TTL implementation

- Replace time.time() with time.monotonic() for reliable TTL calculations
- Add validation for negative session_ttl values with descriptive error
- Fix close() method to save chat histories before clearing agents
- Fix is_expired() boundary condition to use >= instead of >
- Remove silent exception swallowing in knowledge cleanup

Fixes identified issues from CodeRabbit and Greptile reviews.

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-18 07:49:48 +01:00
praisonai-triage-agent[bot]andMervinPraison 751de46794 refactor: remove dead/unreferenced code (fixes #1952) (#1958)
- Remove superseded unified_chat_mixin.py (151 lines)
- Remove 4 empty __init__new.py placeholder files
- Remove unused StepResult import from memory/workflows.py
- Remove unused _tools_lock and _tools_lazy_cache variables
- Clean up stale docstring references to UnifiedChatMixin

No runtime behavior changes. Pure cleanup for maintainability.

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-18 07:49:01 +01:00
praisonai-triage-agent[bot]andMervin Praison 8311acff9f refactor: consolidate triplicated CSV/text loop-task expansion in process.py (#1957)
* refactor: consolidate triplicated CSV/text loop-task expansion in process.py

This consolidates three near-identical implementations of CSV/text-file
loop-task expansion onto the existing _create_loop_subtasks() helper:

- Enhanced _create_loop_subtasks() with optional decision_mode parameter
- Replaced inline start-task logic (lines 1116-1205) with helper call
- Replaced inline mid-workflow logic (lines 1273-1330) with helper call

Removes ~300 lines of duplicated logic and fixes latent CSV-parsing
inconsistency where inline copies used plain csv.reader() instead of
csv.reader(f, quotechar='"', escapechar='\\').

All loop feature behavior preserved: CSV/text files, decision-task
creation with inherited next_tasks, and sync/async paths work identically.

Fixes #1953

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve critical issues in loop task consolidation

- Fix lexicographic sorting that breaks ordering with 10+ subtasks
- Retire parent loop task after expansion to prevent completion blocking
- Replace placeholder task names in decision conditions with concrete names
- Use is_start flag instead of name sorting for proper subtask selection

Addresses reviewer feedback from Greptile and CodeRabbit on PR #1957

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-18 07:48:09 +01:00
praisonai-triage-agent[bot]andMervinPraison 3015ce4387 fix: remove duplicate streaming accumulation in LLM.response() and aresponse() (fixes #1955) (#1956)
- Removed duplicate 'if content: response_text += content' lines in both sync and async methods
- Sync method: removed lines 5753-5754 duplicate
- Async method: removed lines 5850-5851 duplicate
- Streaming responses now accumulate content once instead of twice, fixing double-length output

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-18 07:47:26 +01:00
praisonai-triage-agent[bot]andMervin Praison 3a222a1cb8 feat: implement progressive streaming for channel bots (fixes #1912) (#1921)
* feat: implement progressive streaming for channel bots

Adds live token-by-token response delivery for Telegram, Discord, Slack, WhatsApp, Email, and AgentMail bots via progressive message editing.

Core Features:
- New BotConfig.streaming flag (default: False) enables progressive responses
- ChannelStreamConsumer bridges Agent.stream_emitter to edit_message APIs
- BotSessionManager.chat_with_streaming() extends session isolation to streaming
- 700ms edit interval respects platform rate limits
- Markdown-safe partial rendering prevents broken code fences
- Message chunking handles platform length limits
- Graceful fallback for platforms without edit support

Platform Integration:
- Telegram bot updated to use streaming when config.streaming=True
- Sends placeholder message, progressively edits during agent execution
- Preserves existing typing indicators, ack reactions, session management

CLI/YAML Support:
- praisonai bot telegram --stream --stream-edit-interval 1000
- YAML: streaming: true, stream_edit_interval_ms: 700

Fixes #1912

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve critical streaming issues in channel bots

- Fix threading bug: replace asyncio.create_task() with loop.call_soon_threadsafe()
  in _on_stream_event to properly schedule tasks from worker thread
- Fix hook bypassing: ensure fire_message_sending is called in streaming mode
- Handle orphaned placeholders: clean up '🤖 Thinking...' messages on agent errors
- Remove redundant nested task creation in _perform_edit method

Fixes critical P1 issues identified in code review that prevented
progressive streaming from working properly.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Mervin Praison <[email protected]>

* fix: resolve critical streaming issues in channel bots

- Fix P1 media handling bypass: Extract and send media files separately in streaming mode
- Fix P2 orphaned placeholder cleanup: Delete placeholder on agent errors and cancellation
- Fix P2 config compatibility: Auto-create StreamingConfig from BotConfig.streaming boolean
- Remove old _stream.py implementation to prevent confusion
- Ensure proper hook processing order: fire_message_sending before finalize()
- Add comprehensive error handling with graceful fallback

Addresses all critical issues identified in PR review:
- Media/audio output now works correctly in streaming mode
- No more orphaned 'Thinking...' messages on errors
- Backward compatible with existing BotConfig.streaming field
- Proper cleanup on cancellation and errors

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-18 07:47:06 +01:00
a6e4c26a24 fix: add bot run cancellation and timeout support (#1919)
* fix: add bot run cancellation and timeout support (fixes #1917)

- Add MESSAGE_ABORT event type to gateway protocols
- Enhance BotSessionManager with InterruptController integration
- Add configurable run_timeout parameter (default 300s)
- Implement cancel_run() method for user-initiated cancellation
- Add /stop command for cancelling active runs
- Add BotRunTimeout exception for timeout handling
- Maintain backward compatibility
- Follow protocol-driven architecture from AGENTS.md

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve critical bugs in bot run cancellation and timeout support

Fixes three critical issues identified by code review:

1. Fix parameter name mismatch: Agent.chat() expects 'cancel_token', not 'interrupt_controller'
   - Cooperative cancellation is now functional

2. Wire /stop command to all bot adapters (telegram, discord, slack, whatsapp)
   - Users can now actually cancel runs via /stop command

3. Prevent BotRunTimeout from being queued in DLQ to avoid infinite retry loops
   - Timeout exceptions are excluded from replay to prevent loops

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude <[email protected]>
2026-06-18 07:46:34 +01:00
e56456a3e1 feat: implement PreparedTurnContext for runtime plan standardization (fixes #1940) (#1948)
* feat: implement PreparedTurnContext for runtime plan standardization (fixes #1940)

- Add PreparedTurnContext dataclass to standardize runtime preparation
- Create DefaultTurnContextBuilder to consolidate scattered preparation logic
- Implement TurnRuntimeProtocol for standardized runtime execution
- Add PreparedTurnMixin to integrate with existing Agent class
- Create example harnesses demonstrating external usage
- Eliminate duplication between native loop and plugin harnesses
- Enable consistent metrics and tracing across harness types
- Multi-agent safe: context is per-turn, not stored on shared globals

🤖 Generated with Claude Code

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve critical architecture and runtime issues in PreparedTurnContext

- Fix event loop deadlocks in sync wrappers (detect running loops)
- Fix StreamEvent construction in example harness (proper import/usage)
- Fix runtime dispatch bugs (LLM client detection, duplicate params)
- Fix config merge order (kwargs now override agent defaults)
- Fix shallow immutability (convert to tuple/MappingProxyType)
- Fix ASYNC_STREAM routing (use streaming execution path)
- Move protocols to dedicated protocols.py per SDK contract
- Fix explicit tools=[] handling (respect empty tool lists)
- Fix shallow transcript copying (deep copy to prevent mutations)
- Fix async blocking (wrap sync calls in asyncio.to_thread)
- Fix test expectations and structure improvements

All critical P1 and major P2 issues identified by reviewers have been resolved.
The implementation now provides true immutability guarantees and follows
proper protocol-driven architecture patterns.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-18 07:45:32 +01:00
MervinPraison a160fa6de5 docs: auto-update feature parity trackers [skip ci] 2026-06-18 06:44:15 +00:00
praisonai-triage-agent[bot]andMervin Praison 17aca46f91 feat: implement model-scoped runtime selection policy (#1947)
* feat: implement model-scoped runtime selection policy

- Add AgentRuntimeConfig dataclass for model-scoped runtime configuration
- Implement RuntimeRegistry protocol and RuntimeResolver with resolution order:
  per-model → per-provider → auto → built-in default
- Add fail-closed behavior for unknown runtime IDs
- Integrate runtime resolution into Agent class and unified execution
- Add YAML support for models.<name>.runtime and providers.<name>.runtime_default
- Maintain backward compatibility with deprecation warnings for cli_backend
- Add comprehensive unit tests for configuration and resolution logic
- Update framework validation to support runtime features

Fixes #1935

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: MervinPraison <[email protected]>

* fix: critical runtime selection issues identified by reviewers

- Fix fail-closed behavior: Replace catch-all exception with specific handling
- Fix hasattr guard performance: Check for None instead of attribute existence
- Fix resolution order: Default runtime takes precedence over legacy cli_backend
- Fix concurrent access: Remove _cli_backend mutation in _chat_via_runtime
- Add deprecation warning for cli_backend parameter
- Fix to_dict() to return defensive copies
- Fix comment mislabeling in unified_execution_mixin
- Fix config mutation in resolver to avoid state leakage

Addresses critical P1 issues from Greptile and CodeRabbit reviews.

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-18 07:43:58 +01:00
MervinPraison 8b82df0065 docs: auto-update api.md [skip ci] 2026-06-18 06:38:43 +00:00
praisonai-triage-agent[bot]andMervin Praison 1e84466e46 feat: Multi-agent runtime preflight (doctor runtime --team) (#1945)
* feat: add doctor runtime command for multi-agent preflight validation

- Add 'praisonai doctor runtime --team' CLI subcommand
- Implement runtime compatibility checker for team YAML files
- Validate runtime availability, capabilities, and handoff compatibility
- Detect mixed-runtime setups and capability conflicts
- Support JSON output for CI integration
- Add comprehensive integration tests with fixture YAML files
- Export lint_runtime_team() API for programmatic use

Fixes #1937

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve critical bugs in doctor runtime command

- Fix registry registration silent failure due to wrong argument order
- Fix return type mismatch in runtime checks (aggregate results to single CheckResult)
- Fix handoff target normalization (string vs list iteration)
- Replace test silent returns with proper pytest.skip calls
- Tighten test return code assertions to catch failures
- Clean up unused imports (json, pathlib.Path)
- Improve error handling and logging

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-18 07:38:21 +01:00
MervinPraison 846568c7a5 chore: clean up redundant 'persist-credentials' entries in GitHub workflows
- Removed duplicate 'persist-credentials: false' lines from multiple workflow files for cleaner configuration.
- Updated version numbers in pyproject.toml and uv.lock to reflect recent changes.
- Added validation for issue references in workspace during issue creation and update processes.
2026-06-17 20:13:04 +01:00
MervinPraison 2c2678d192 docs: auto-update feature parity trackers [skip ci] 2026-06-17 10:10:32 +00:00
MervinPraison 9fbddf4105 docs: auto-update documentation parity trackers [skip ci] 2026-06-17 10:04:43 +00:00
MervinPraison 2a855c4700 Release v4.6.62 2026-06-17 11:04:06 +01:00
MervinPraison d51073710a docs: auto-update api.md [skip ci] 2026-06-17 07:57:50 +00:00
MervinPraison 6b4b59fbe7 chore: update performance metrics and agent instantiation details in documentation
- Revised agent instantiation time in README and benchmark results to reflect updated performance metrics.
- Added model specification in GitHub workflow configuration.
- Incremented version number in package.json for PraisonAI TypeScript framework.
- Enhanced tool approval handling in agent loop and updated utility tools for shell metacharacter validation.
- Added tests to block allowlist bypass and metacharacter chaining for commands.
2026-06-17 08:57:18 +01:00
MervinPraison 709a038785 Release v4.6.60 2026-06-17 08:42:21 +01:00
MervinPraison 64f7fdeb84 docs: auto-update api.md [skip ci] 2026-06-17 07:25:01 +00:00
MervinPraison f834d42448 Release v4.6.59 2026-06-17 08:24:34 +01:00
MervinPraison 705fae31f8 docs: auto-update feature parity trackers [skip ci] 2026-06-17 07:23:34 +00:00
MervinPraison b4270173d4 feat(auth): implement API key authentication middleware for AgentOS
- Added middleware to validate API key for incoming requests, enhancing security.
- Integrated environment variable support for API key retrieval.
- Ensured that health check endpoints remain accessible without authentication.
2026-06-17 08:23:02 +01:00
MervinPraison 6256744348 feat(tests): add integration tests for CLI session continuity and project session store
- Introduced integration tests for CLI session continuity, covering session argument handling and session list filtering.
- Added tests for project identification and project-scoped session store functionality.
- Ensured comprehensive coverage of session discovery and message management within the project context.
2026-06-17 07:43:10 +01:00
MervinPraisonandCursor 15d597da37 fix(ci): use SECRETS_ADMIN_PAT for PraisonAIUI secret sync
Co-authored-by: Cursor <[email protected]>
2026-06-15 13:31:59 +01:00
MervinPraisonandCursor 04d5e07e1b fix(ci): use GitHub App token for cross-repo secret sync to PraisonAIUI
Co-authored-by: Cursor <[email protected]>
2026-06-15 13:30:35 +01:00
MervinPraison 754386022c docs: auto-update feature parity trackers [skip ci] 2026-06-15 12:29:50 +00:00
MervinPraisonandCursor 1e27a73633 chore(ci): add workflow to sync Claude secrets to PraisonAIUI
Co-authored-by: Cursor <[email protected]>
2026-06-15 13:29:23 +01:00
MervinPraisonandCursor 2adfe7e832 refactor: harden input validation and access controls
Co-authored-by: Cursor <[email protected]>
2026-06-13 13:44:05 +01:00
MervinPraison 1ad58ca029 Release v4.6.58 2026-06-13 13:31:52 +01:00
MervinPraisonandCursor 2f9677abb2 refactor: harden input validation and query parameterization
Co-authored-by: Cursor <[email protected]>
2026-06-13 09:27:34 +01:00
9991e00f52 feat: CLI session continuity for praisonai run command (#1929)
* feat: add CLI session continuity for praisonai run command (fixes #1926)

- Add --continue/-c flag to resume most recent session for current project
- Add --session/-s flag to resume specific session ID
- Add --fork flag to fork from specified session
- Add --no-save flag to disable auto-save
- Implement project-scoped session storage in ~/.praisonai/sessions/projects/
- Add project identity detection via git root with fallback to cwd hash
- Update session list command with --all and --project filtering options
- Maintain backward compatibility with existing --resume functionality
- Leverage existing core SDK session stores and hierarchy

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

* fix: resolve critical session continuity and architecture issues

- Fix session flags ignored in YAML and profiling execution paths
- Remove sys.path manipulation across all files for better performance
- Fix autosave logic to work for all runs, not just session continuity
- Improve project-scoped session store with proper API
- Remove hardcoded session status, use actual data when available
- Update test files to remove fragile path manipulation

Addresses issues identified by CodeRabbit, Greptile, and Copilot reviews.
Follows AGENTS.md architecture principles for protocol-driven design.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Claude <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-13 09:25:38 +01:00
praisonai-triage-agent[bot]andMervin Praison 86aeaa4d89 feat: add in-flight run control for channel sessions (fixes #1914) (#1920)
* feat: add in-flight run control for channel sessions (fixes #1914)

- Add SessionRunControl class with busy feedback and message queuing
- Support for queue/interrupt/steer modes and /stop command
- Integrate InterruptController from core SDK for cancellation
- Add BotSessionManager.chat_with_run_control() method
- Extend BotConfig with busy_mode and busy_ack settings
- Add /stop command handler and help text updates
- Include comprehensive example and test coverage

Resolves silent blocking issue where mid-run messages queue
invisibly with no user feedback or cancellation option.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: MervinPraison <[email protected]>

* fix: resolve critical bugs in run control implementation

- Fix interrupt controller restoration to prevent deletion (issue #1920)
- Fix memory leak in cleanup_stale_sessions with last_activity tracking
- Add config field wiring in TelegramBot for busy_mode support
- Change steer mode fallback to WARNING level for visibility

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-13 09:25:24 +01:00
a66db1836a fix(examples): fix AgentFlow import in simple_workflow.py (fixes #1880) (#1906)
Changed import from Workflow to AgentFlow to match the usage on line 26.
This resolves the NameError: name 'AgentFlow' is not defined.

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>
2026-06-13 09:25:02 +01:00
MervinPraison e90d922318 Release v4.6.57 2026-06-13 09:20:37 +01:00
praisonai-triage-agent[bot]andMervin Praison 43b9552f9b feat: implement durable inbound message processing for bots (fixes #1915) (#1923)
* feat: implement durable inbound message processing for bots

- Add InboundJournal class with SQLite-backed message persistence
- Implement deduplication by (platform, account, channel_id, message_id) key
- Add claim/release mechanism for crash recovery with timeouts
- Integrate journal into BotSessionManager with async context managers
- Update all bot adapters (Telegram, Discord, Slack, WhatsApp, Email, AgentMail) to pass message_id and account
- Provide exactly-once processing for webhook redeliveries and crash scenarios
- Backward compatible - existing code works unchanged

Fixes #1915

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: MervinPraison <[email protected]>

* fix: critical bugs in durable inbound message processing

- Fix claim/release mechanism: context managers now properly release claims on exceptions
- Fix overflow cap enforcement: evict oldest pending entries when no completed entries available
- Fix early complete() timing: add delayed completion method for post-delivery completion
- Fix documentation example: correct parameter name from 'journal' to 'ingress_journal'

Fixes identified by Greptile code review.

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-13 09:09:04 +01:00
praisonai-triage-agent[bot]andMervin Praison d5c77c244b feat: auto-inject project instruction files into CLI runs (#1930)
* feat: auto-inject project instruction files into CLI runs (fixes #1927)

- Add --no-rules flag to run and chat commands for opt-out
- Auto-inject AGENTS.md, CLAUDE.md, PRAISON.md into agent instructions
- Add [rules] config section with auto=true and max_chars=32KB
- Integrate RulesManager with git root discovery for monorepo support
- Support size caps and verbose mode file listing
- Maintain backward compatibility with manual --include-rules

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve critical bugs in rules auto-injection system

- Fix config.rules.auto=false precedence bug that was being ignored
- Remove RULES_LOADED test artifact from AGENTS.md
- Fix duplicate '# Task:' headers when mentions + rules both present
- Add --no-rules support to direct prompt path
- Replace bare except blocks with Exception
- Enhance manual include_rules support for comma-separated names
- Improve error handling and precedence logic

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-13 09:08:15 +01:00
bcb6957dac feat: complete prompt cache optimization integration (#1932)
* feat: complete prompt cache optimization integration

- Integrate sorted tool schemas in agent pipeline for deterministic ordering
- Wire build_cache_optimized_context() into memory context retrieval across all major components
- Add cache boundary markers to system prompts when model supports prompt caching
- Update session.py, task.py, and agents.py to use cache-optimized context
- Ensure graceful fallback when cache optimization features unavailable

Resolves the dead code issue from issue #1808 by connecting:
- get_sorted_tool_schemas() -> _format_tools_for_completion()
- build_cache_optimized_context() -> memory context building pipeline
- Cache boundaries -> LLM prompt assembly

Co-authored-by: Mervin Praison <[email protected]>

* fix: address cache optimization integration issues

- Fix critical backward compatibility issue: preserve original include_in_output behavior
- Remove non-functional cache boundary marker (now empty string with docs)
- Add proper debug logging for cache optimization failures with context identifiers
- Improve exception handling specificity in chat_mixin.py
- Fix integration test to avoid database dependencies

Addresses issues identified by Greptile, CodeRabbit, and other reviewers.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Claude <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
Co-authored-by: Claude <[email protected]>
2026-06-13 08:26:07 +01:00
MervinPraison fe78664261 docs: auto-update feature parity trackers [skip ci] 2026-06-13 07:25:42 +00:00
d9a7c1a01e feat: ContextCompactor anti-thrashing, iterative summarisation, tool-result pruning (#1910)
* feat: enhance ContextCompactor with anti-thrashing protection, iterative summarization, and tool-result deduplication

- Add anti-thrashing protection with configurable min_savings_pct and max_consecutive_low_savings
- Implement iterative summary functionality that reuses previous summaries instead of starting from scratch
- Add tool-result deduplication pre-pass to reduce token usage by deduplicating repeated tool outputs
- Extend CompactionConfig with new fields for advanced compaction control
- Update CompactionResult to track savings percentage, tool pruning stats, and iterative summary usage
- Enhance LLM_SUMMARIZE strategy with focus_topic guided compression
- Maintain backward compatibility while adding powerful new compaction capabilities

Fixes #1832

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve critical ContextCompactor bugs per architecture review

- Fixed anti-thrashing permanently disabling compaction by removing premature early-exit
- Fixed iterative summary losing messages by using stable global message index
- Fixed tool result cache cross-run deduplication by using local cache per compaction
- Fixed config parameter conflicts by prioritizing config over constructor args
- Fixed hash collision vulnerability by using SHA256 instead of 8-char MD5
- Fixed min_savings_pct unit inconsistency (normalized to 0-100 scale)
- Fixed savings_pct initialization in CompactionResult
- Removed unused imports (json, Set)

All critical issues identified by Greptile, Gemini, and CodeRabbit reviewers resolved.

Co-authored-by: Mervin Praison <[email protected]>

* fix: resolve critical ContextCompactor bugs per architecture review

- Extract heavy implementations to protocols (ToolResultPrunerProtocol, etc)
- Core SDK now uses protocol-driven design per AGENTS.md §4.1
- Fix compact_async missing feature parity with compact()
- Fix hash collision vulnerability (MD5 -> SHA256, removed direct usage)
- Fix cross-run tool cache pollution (local cache per compaction)
- Fix iterative summary coordinate system bugs
- Fix config parameter conflicts and unit inconsistencies
- Add proper anti-thrashing state tracking
- Maintain backward compatibility with fallbacks

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-13 08:25:18 +01:00
MervinPraison 6a122c3671 docs: auto-update api.md [skip ci] 2026-06-13 07:22:48 +00:00
praisonai-triage-agent[bot]andMervin Praison 1f1cfa974b feat: implement streaming reply modes for channel bots (#1922)
* feat: implement streaming reply modes for channel bots (fixes #1916)

- Add DraftStreamer helper class for live draft updates with rate limiting
- Integrate streaming into BotSessionManager with stream_callback support
- Add StreamingConfigSchema with validation for mode, intervals, and deltas
- Update Telegram bot with full streaming support and proper control flow
- Support draft mode (progressive content) and progress mode (tool status)
- Maintain backward compatibility with existing non-streaming behavior

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve P1 critical issues in streaming bot implementation

- Fix message hooks bypass: Add fire_message_sending/fire_message_sent to streaming path
- Fix blocking asyncio.sleep: Replace inline sleep with background task scheduling
- Fix AutonomyResult handling: Extract output field when autonomy enabled
- Fix dead code: Properly assign _update_task for cancellation logic

Resolves critical issues identified in Greptile review for streaming functionality.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-13 08:22:23 +01:00
praisonai-triage-agent[bot]andMervin Praison 34d60ae8fa fix: Gateway channel supervision: unlimited retries with error classification and operator controls (#1918)
* feat: implement gateway channel supervision with resilient error handling

- Add ChannelSupervisor class for unlimited retries with error classification
- Integrate supervisor with WebSocketGateway to replace bounded retry logic
- Add operator controls for pause/resume/reconnect via CLI and REST API
- Update health endpoint to include supervision state and error details
- Use existing resilience primitives from bots/_resilience.py
- Ensures channels self-heal after network outages automatically

Fixes #1913

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve critical gateway channel supervision bugs

- Fix resume() method logic inversion that permanently froze paused channels
- Add missing authentication to channel control REST endpoints
- Sync total_recoveries metric from monitor to status object
- Fix CLI URL parsing to handle paths/queries correctly with urlparse
- Add proper exit codes to CLI commands on failure
- Preserve terminal FAILED states in supervision cleanup
- Clear manual_pause flag in reconnect() method
- Add supervisor state cleanup in stop_channels()

Fixes critical issues identified by Greptile, CodeRabbit, and Copilot reviewers.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-13 08:22:02 +01:00
095653d78a fix: wrapper layer gaps — sync/async convergence, kanban FD leak, exception handling (#1908)
* fix: wrapper layer gaps - InteractiveRuntime lifecycle, tool resolution, cli_backend support (fixes #1863)

* Fix sync/async path divergence in agents_generator.py
  - Extract shared _prepare_run() method consolidating ~150 lines of duplicate logic
  - Ensure both sync and async paths call adapter.resolve(), init_observability(), adapter.setup()
  - Add AgentOps initialization and AutoGen version selection to both paths
  - Remove behavioral drift between run() and arun()

* Fix file handle leak in kanban_dispatcher.py
  - Replace inline open() with proper context manager in _spawn_worker()
  - Parent FD now closes immediately after Popen(), preventing slow leaks

* Fix bare except clauses in kanban_dispatcher.py
  - Replace bare except: with specific exception handling
  - Add comprehensive error logging with task context
  - Allow KeyboardInterrupt/SystemExit/CancelledError to propagate for clean shutdown

Co-authored-by: MervinPraison <[email protected]>

* fix: wrapper layer gaps - CLI config regression, docstring mismatch, OSError handling

- Fix CLI config regression: _merge_cli_config now applies to both agent_yaml and agent_file paths (fixes #1863)
- Fix docstring mismatch: _prepare_run returns 4-tuple not 5-tuple
- Fix OSError handling: guard process.kill() calls in kanban dispatcher shutdown

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Claude <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude <[email protected]>
2026-06-13 01:00:41 +01:00
MervinPraison 7f6a483e62 update tests 2026-06-13 00:43:54 +01:00
MervinPraison c6883def69 test: add unit tests for multi-agent streaming and context assembly optimizations
- Introduced tests for multi-agent streaming to verify fix for issue #1882.
- Added tests for deterministic context assembly and cache optimization features, addressing issue #1808.
- Ensured tests validate memory context stability, tool schema sorting, and cache boundary markers.
2026-06-13 00:43:53 +01:00
MervinPraison d3c4a2afad Release v4.6.56 2026-06-13 00:42:25 +01:00
034138ebc9 fix(agents): multi-agent streaming failure with sync OpenAI adapter (#1911)
* fix(agents): resolve multi-agent streaming failure with sync OpenAI adapter

- Change MultiAgentOutputConfig.stream default from True to False
- Fixes #1882: multi-agent workflows now work with sync OpenAI adapter
- Aligns with safe defaults principle from AGENTS.md
- Single-agent retains fallback behavior, multi-agent now uses safe default
- Resolves benchmark regression where PraisonAI was only framework where multi-agent failed

Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>

* fix(agents): complete multi-agent streaming fix - address remaining stream=True paths

- Fix agents.py fallback path that sets _stream=True on import error
- Fix presets.py verbose/minimal multi-agent presets that force stream=True
- Fix test_multiagent_fix.py hardcoded path and error handling issues
- Ensures sync OpenAI adapter compatibility across all code paths

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-13 00:41:15 +01:00
MervinPraison 70b2df135b docs: auto-update api.md [skip ci] 2026-06-12 23:40:34 +00:00
3532600887 fix: deterministic context assembly for prompt caching (#1909)
* fix: implement deterministic context assembly for prompt caching optimization (fixes #1808)

- Add stable sorting for memory search results by timestamp desc then content hash
- Implement deterministic tool schema ordering alphabetically by function name
- Add cache boundary markers and build_cache_optimized_context method
- Include comprehensive test suite validating all cache optimization features

This resolves non-deterministic context assembly that was breaking prompt prefix
caching for providers like Anthropic and Google, preventing cache hits and
causing full input-token cost on every turn instead of only delta costs.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: MervinPraison <[email protected]>

* fix: improve deterministic context assembly for cache optimization

- Fix memory sorting to use full SHA256 hash instead of 50-char truncation
- Handle both created_at and timestamp fields with proper date parsing
- Fix build_cache_optimized_context to default include_in_output=True
- Remove unused STABLE_SECTION_ORDER constant
- Move json import to top level in tools/base.py
- Update tests to validate production code paths instead of duplicating logic
- Simplify conditional assignment for cache boundary

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-13 00:40:10 +01:00
13ba1ed954 fix: tool parameter schema generation for Optional, Union, Literal, and Enum (#1907)
* fix: tool parameter schema generation silently drops Optional, Union, Literal, and Enum types (fixes #1845)

- Created shared schema utility praisonaiagents/tools/schema.py with proper type handling
- Updated BaseTool._generate_parameters_schema() to use new schema utility
- Updated FunctionTool._generate_schema_from_func() to use new schema utility
- Updated LLM._generate_tool_definition() to use ToolRegistry and new schema utility
- Removed legacy globals().get() / getattr(__main__, ...) lookup in favor of ToolRegistry
- Fixed Optional[T] to generate {"anyOf": [T schema, {"type": "null"}]} instead of required string
- Fixed Literal["a", "b"] to generate {"type": "string", "enum": ["a", "b"]} instead of plain string
- Fixed Enum types to generate {"type": "string", "enum": [e.value for e in MyEnum]} instead of plain string
- Fixed List[T] and Dict[K,V] to include proper items/additionalProperties schemas
- Parameters with defaults or Optional types are now correctly excluded from required fields

Co-authored-by: MervinPraison <[email protected]>

* fix: address critical issues in schema generation

- Restore backward compatibility for string-name tool lookup in llm.py
- Fix Literal type inference to use actual value types instead of hardcoded string
- Fix Enum type inference to use actual enum value types
- Use consistent required parameter logic across all schema paths
- Add exception handling for get_type_hints() to prevent failures
- Apply schema normalization to registry-provided tools for consistency

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-13 00:39:22 +01:00
4a1fe173e8 fix: remove local 'from rich import print' causing UnboundLocalError (fixes #1879) (#1905)
Removes local import of 'from rich import print' at line 428 inside main() method.
Python scoping makes 'print' a local variable for the entire method when imported
locally, but execution paths that don't go through line 428 leave 'print' unbound,
causing UnboundLocalError at line 860. The module-level import at line 71 is sufficient.

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>
2026-06-13 00:38:20 +01:00
MervinPraison 147bcce448 docs: auto-update api.md [skip ci] 2026-06-12 23:38:05 +00:00
b5e8052809 fix: doctor env crashes on Windows with charmap UnicodeEncodeError (#1904)
* fix(doctor): add encoding-safe Unicode symbol fallback for Windows

- Add ASCII fallback symbols for systems that don't support UTF-8
- Implement _can_encode_unicode() to detect stdout encoding capability
- Fall back to [OK], [!], [X], [-] symbols on Windows cp1252 encoding
- Add tests for encoding safety with both UTF-8 and cp1252

Fixes #1878

Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>

* fix: optimize encoding detection and fix broken test

- Cache Unicode encoding detection in TextFormatter.__init__() to avoid repeated checks
- Remove invalid str.encode patch from cp1252 test that was causing TypeError
- Test now relies on natural cp1252 encoding failure for Unicode symbols
- Addresses performance concerns raised by CodeRabbit, Greptile, and Gemini

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-13 00:37:35 +01:00
8a93bc5096 fix: remove duplicate import time in _chat_completion except block (fixes #1901) (#1903)
The _chat_completion method in chat_mixin.py had 'import time' inside an
except block which shadowed the global time module import, causing
UnboundLocalError when trying to use time.time() at the start of the function.

Removed the redundant inner import since time is already imported at module level.

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-13 00:37:09 +01:00
MervinPraison f65b83614c docs: auto-update feature parity trackers [skip ci] 2026-06-12 23:37:01 +00:00
25000d9b26 fix: resolve wrapper layer architectural gaps (#1849)
* fix: resolve wrapper layer architectural gaps (fixes #1846)

- Remove dead deploy.py file shadowed by deploy/ package that contained stale version pin
- Replace module-level singleton with contextvars for multi-agent safety in host_app.py
- Remove duplicate exception handler and improve error logging
- Add cache invalidation method to BaseCLIIntegration
- Fix registry contract violation by adding try_create() method instead of overriding create()

Co-authored-by: MervinPraison <[email protected]>

* fix: address critical reviewer feedback

- Fix race condition in BaseCLIIntegration.is_available property by removing unlocked fast-path
- Add backward compatibility shim for _CONFIGURED to prevent test breakage
- Remove redundant default parameter in configure_host()
- Update stale documentation example in registry.py

Co-authored-by: Mervin Praison <[email protected]>

* fix: resolve remaining wrapper gaps

- Add praisonaiagents.__version__ lazy export to fix cross-repo CI
- Update version bump scripts to target deploy/docker.py instead of deleted deploy.py
- Add comprehensive unit tests for try_create, invalidate_availability, configure_host
- All functionality tested and verified working

Co-authored-by: Mervin Praison <[email protected]>

* fix: remove duplicate --timeout arguments from pytest.ini to fix CI conflicts

Both pytest.ini files had --timeout=60 in addopts while workflows passed
--timeout=300, causing pytest to reject duplicate arguments. Removed
pytest.ini timeouts to let workflows control timeout values explicitly.

Fixes cross-repo-integration CI failure.

* fix: remove timeout_method from pytest.ini files

Removed 'timeout_method = thread' configuration from both pytest.ini files.
The pytest-timeout plugin is not installed in CI, causing --timeout arguments
to be unrecognized. This removes all timeout-related pytest configuration,
allowing workflows to manage timeouts externally if needed.

Addresses cross-repo-integration CI failure with unrecognized --timeout argument.

* fix: remove --timeout arguments from cross-repo-integration workflow

The pytest-timeout plugin is not installed in CI, causing all --timeout
arguments to be rejected as unrecognized options. Removed all --timeout
flags from the workflow to allow tests to run without timeout constraints.

This completes the fix for the duplicate/unrecognized timeout arguments
that were blocking the cross-repo-integration CI workflow.

* fix: update integration tests to work with ContextVar-based _CONFIGURED

Added backward-compatible reset_configuration() and is_configured() functions
to host_app.py and updated all integration tests to use these instead of
directly manipulating host_app._CONFIGURED, which doesn't work properly
with the new ContextVar-based configuration tracking.

Fixes test failures related to the wrapper layer architectural changes.

* fix: improve session isolation for integration tests

- Add hybrid ContextVar + global flag approach for better test compatibility
- Enhanced reset_configuration() to clear both ContextVar and module-level state
- Fixed backward compatibility shim to handle both context-aware and global state
- Ensures test isolation works correctly across different execution contexts

This addresses the session leak failures in test_aiui_host_isolation.py where
provider._sessions was returning None instead of expected session data.

* fix: enhance test state cleanup for better isolation

- Clear additional cached state (_app, backends) during reset_configuration()
- Ensures complete cleanup between test runs to prevent interference
- Addresses potential auth/routing issues in integration tests

* fix: detect test environment and defer provider setup

- Skip default PraisonAIProvider setup when set_provider is monkeypatched
- Allows tests to have full control over provider configuration
- Should resolve 401 auth issues in SSE tests where provider override timing matters

* fix: resolve syntax error and remove debug file

- Fix SyntaxError in host_app.py by moving global _configured_global declaration to function top
- Remove debug_session_test.py from repository root
- Ensure proper global declaration order for cross-repo-integration compatibility

Co-authored-by: Mervin Praison <[email protected]>

* fix: resolve session isolation and provider setup issues

- Remove test environment detection that was skipping provider setup
- Enhanced reset_configuration() to handle different backend clearing methods
- Fix session isolation by ensuring provider setup works correctly in tests
- Addresses cross-repo-integration test failures:
  - test_configure_host_sets_provider (assert len(seen) == 1)
  - test_two_sessions_no_cross_leak (session isolation)
  - test_run_sse_stream (401 vs 200 auth issues)

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-13 00:36:38 +01:00
98cfdcc9ad fix: resolve three architectural gaps in wrapper layer (#1867)
* fix: resolve three architectural gaps in wrapper layer (fixes #1866)

Gap 1: resolve_llm_endpoint() now maps model prefixes to correct provider API keys
- Added provider mapping for anthropic/, groq/, google/, openrouter/, etc.
- Stops hardcoding OPENAI_API_KEY for all providers
- Maintains fallback to OPENAI_API_KEY for default OpenAI models

Gap 2: SubprocessSandbox now enforces SecurityPolicy and ResourceLimits
- Builds child environment from policy instead of copying host environment
- Applies resource limits via POSIX setrlimit where supported
- Enforces max_output_size to prevent OOM attacks
- Uses process groups for proper timeout cleanup
- Provides warnings when isolation is limited (Windows, missing modules)

Gap 3: Remove root logger mutation from inc/models.py
- Removed logging.basicConfig() call that violated project logging policy
- CLI logging configuration remains through _logging.py as intended
- Embedders no longer have their root logger hijacked at import time

Co-authored-by: Mervin Praison <[email protected]>

* fix: resolve critical bugs in architectural gaps implementation

- Fix inverted boolean condition in llm/env.py causing wrong API key fallback
- Add proper provider base URLs to prevent misrouting to OpenAI endpoints
- Add Windows compatibility for subprocess sandbox (preexec_fn, os.killpg)
- Remove unused variables and improve comments
- Add defensive documentation for hardcoded /tmp fallback

Co-authored-by: Mervin Praison <[email protected]>

* test: add comprehensive unit tests for security fixes

- Add provider mapping tests in test_env_resolver.py
- Add sandbox isolation tests in test_subprocess_security.py
- Add logging regression tests in test_logging_regression.py

Tests validate:
- Provider-specific API key routing (no cross-contamination)
- Environment isolation with SecurityPolicy
- Resource limits enforcement via setrlimit
- Cross-platform compatibility (Windows/POSIX)
- Logging.basicConfig() removal from inc/models.py

All critical security gaps now have test coverage.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Mervin Praison <[email protected]>

* fix: replace hardcoded secrets in tests with clearly fake placeholders

- Replace 'secret123', 'dbpass456', 'token789' with descriptive fake values
- Preserves test functionality while passing GitGuardian security checks
- Test logic checks for env var keys, not values, so functionality unchanged

Co-authored-by: Mervin Praison <[email protected]>

* fix: replace hardcoded secrets in tests with clearly fake placeholders

Replace test placeholders that GitGuardian was flagging:
- 'fake-aws-key-for-testing' → 'not-a-real-aws-secret'
- 'fake-db-password-for-testing' → 'not-a-real-db-password'
- 'fake-api-token-for-testing' → 'not-a-real-api-token'

Test functionality is preserved since the regression test checks for
environment variable keys, not their values.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

* fix: replace hardcoded secrets in tests with clearly fake placeholders

- Updated test values in test_subprocess_security.py to use obviously fake placeholders
- Changed 'not-a-real-*' format to 'FAKE_*_FOR_TESTING_ONLY' format
- Fixes GitGuardian security scanner detection of Generic Password patterns
- Test functionality preserved (tests environment variable keys, not values)

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
Co-authored-by: Claude <[email protected]>
2026-06-13 00:23:05 +01:00
AkhiChalasaniandAkhil Chalasani 6f2df7db2a docs: improve documentation (#1640)
Created CONTRIBUTING.md

Co-authored-by: Akhil Chalasani <[email protected]>
2026-06-12 23:44:01 +01:00
praisonai-triage-agent[bot]andMervin Praison 495d4604fe feat: remove deprecated tool_timeout and parallel_tool_calls parameters (#1902)
* feat: remove deprecated tool_timeout and parallel_tool_calls parameters

- Remove tool_timeout and parallel_tool_calls parameters from Agent constructor
- These are now fully consolidated into tool_config=ToolConfig() pattern
- Update tests to use new ToolConfig pattern
- Simplify _resolve_tool_config method
- Remove deprecation warning logic as parameters are fully removed

This completes the parameter consolidation effort following AGENTS.md §5.3

Co-authored-by: Mervin Praison <[email protected]>

* fix: address reviewer feedback - restore ExecutionConfig.parallel_tool_calls support and update docs

- Fix critical regression where ExecutionConfig.parallel_tool_calls was ignored
- Update ToolConfig documentation examples to use tool_config parameter
- Fix test expectations for Agent.__init__ parameters (tool_timeout -> tool_config)
- Consolidate ToolConfig imports to module level in test files

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-12 23:43:31 +01:00
MervinPraison a4386796c9 Release v4.6.55 2026-06-12 23:39:35 +01:00
e1f9ae5b6b fix: prevent duplicate session messages from persist+auto_save and save_state (#1897)
* fix: prevent duplicate session messages from persist+auto_save and save_state

When memory=history and auto_save share a session_id, per-turn _persist_message
wrote messages that _auto_save_session then re-appended because
_auto_save_last_index was not updated.

Session.save_state() also re-appended the full chat history on every call
instead of replacing it atomically.

- Sync _auto_save_last_index in _persist_message when sessions match
- Use set_chat_history() in Session._save_agent_chat_histories for idempotent saves
- Add regression tests for both duplicate paths

Co-authored-by: Mervin Praison <[email protected]>

* fix: address thread safety and fallback logging issues

- Wrap _auto_save_last_index increment in _history_lock for thread safety
- Add warning when session store lacks set_chat_history (duplicate risk)
- Add debug logging for empty message lists

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
2026-06-12 23:21:15 +01:00
aa5315b90e fix: implement retry policy for tool execution with exponential backoff (fixes #1840) (#1852)
* fix: implement retry policy for tool execution with exponential backoff (fixes #1840)

- Add RetryPolicy integration to tool execution pipeline
- Implement retry logic in _execute_tool_with_circuit_breaker() and execute_tool_async()
- Add tool-level and agent-level retry policy support to Agent constructor
- Extend OnRetryInput hook event with tool-specific fields (tool_name, attempt, delay_ms, error_type)
- Add helper methods for retry policy resolution and error classification
- Support exponential backoff with jitter for transient failures (timeout, rate_limit, connection_error)
- Emit ON_RETRY hook events during retry attempts for observability
- Maintain backward compatibility with existing OnRetryInput fields

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

* fix: add missing circuit_open guard and optimize retry policy caching

- Add missing circuit_open guard to async retry path (addresses Greptile P1 issue)
- Move inline import os to module level for consistency
- Cache default RetryPolicy instance to avoid repeated allocation

Co-authored-by: Mervin Praison <[email protected]>

* feat: comprehensive tool retry policy improvements and testing

- Fix RetryPolicy import in TYPE_CHECKING block
- Fix clone_for_channel to propagate tool_retry_policy
- Fix async hook emission to use executor instead of blocking event loop
- Add exception chaining (raise ... from e) for better error tracebacks
- Fix iterable guard in _get_tool_retry_policy for MCP tools
- Wire @tool(retry_policy=...) support on FunctionTool decorator
- Add comprehensive unit tests for sync/async retry functionality
- Add real agentic tests with flaky tools (satisfies AGENTS.md §9.4)
- Add CLI support with --tool-retry-* flags
- Add YAML configuration support for tool_retry_policy
- Test tool-level policy precedence and error classification

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Claude <[email protected]>

* feat: implement ToolConfig consolidation pattern to reduce Agent class bloat

- Add ToolConfig class following AGENTS.md §5.3 pattern (False=disabled, True=defaults, Config=custom)
- Consolidate tool_timeout, tool_retry_policy, parallel_tool_calls into single tool_config parameter
- Maintain full backward compatibility with deprecation warnings for legacy parameters
- Add ToolParam type alias and resolve_tools() function for type safety
- Update Agent constructor and clone_for_channel method to support consolidated config
- Tested with both new consolidated and legacy deprecated approaches

Follows AGENTS.md §5.3 parameter consolidation guidelines. Reduces Agent constructor bloat
while maintaining backward compatibility and following consistent SDK patterns.

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Claude <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-12 23:19:23 +01:00
b9d3ede110 fix: defer framework adapter creation until YAML is loaded (fixes #1877) (#1881)
* fix: defer framework adapter creation until YAML is loaded

- Fixes issue where AgentsGenerator crashes with empty framework before YAML is read
- Defers framework_adapter creation from __init__ to generate_crew_and_kickoff()
- Changes default YAML framework from 'crewai' to 'praisonai' for better onboarding
- Enables 'praisonai agents.yaml' to work without --framework flag

Fixes #1877

Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>

* test: add mandatory tests for framework default change and async path parity

- Add test verifying YAML files without framework key default to praisonai (fixes #1877)
- Add test ensuring async path has same adapter.resolve() + init_observability() pattern as sync
- Add FRAMEWORK_DEFAULTS_CHANGE.md documenting breaking change migration

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-12 23:12:36 +01:00
MervinPraison 441b428cc9 Release v4.6.54 2026-06-12 21:11:21 +01:00
praisonai-triage-agent[bot]andMervin Praison bcae07cf58 feat: implement CC/BCC support for email agents (#1876)
* feat: implement CC/BCC support for email agents

- Add CC and BCC parameters to all email functions in email_tools.py
- Support both comma-separated strings and list formats for recipients
- Update AgentMail and SMTP/IMAP backends to handle multiple recipients
- Enhance EmailBot.send_message() to support CC/BCC in content dict
- Add comprehensive tests for new functionality
- Maintain full backward compatibility
- Follow PraisonAI protocol-driven architecture

Fixes #1874

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve CC/BCC architecture issues identified in code review

- Fix critical race condition in EmailBot._send_smtp by passing recipients as parameter
- Add BCC header persistence in IMAP drafts to prevent loss on reopening
- Improve email list normalization to handle whitespace/empty entries in lists
- Add mandatory agentic tests with Agent.start() and real LLM interaction
- Remove duplicate test files (test_cc_functionality.py, simple_cc_test.py)
- Eliminate code duplication by reusing _parse_email_list in EmailBot

Addresses issues identified by CodeRabbit, Greptile, and Copilot reviewers.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-12 21:07:58 +01:00
praisonai-triage-agent[bot]andMervin Praison bf28787705 fix: add loop guardrails to prevent infinite tool calling (fixes #1886) (#1888)
* fix: add loop guardrails to prevent infinite tool calling in agent.chat() (fixes #1886)

- Added max_tool_calls_per_turn to ExecutionConfig (default: 10)
- Enhanced LLM.get_response() and get_response_async() with tool call counting
- Updated chat_mixin.py to pass guardrail limits to LLM methods
- Added clear error messages when tool call limit is exceeded
- Prevents broken tools from burning tokens with infinite loops

Co-authored-by: MervinPraison <[email protected]>

* fix: complete loop guardrail implementation

- Add max_tool_calls_per_turn to internal_params filter in llm.py
- Implement guardrail in get_response_stream method (was missing)
- Improve pre-batch check logic to be more precise with batch limiting
- Fix sys.exit(1) in test_loop_issue.py to use proper RuntimeError
- Fix API key placeholder in test_loop_guardrails.py for CI compatibility

Addresses all issues identified by Greptile review:
- Streaming path now has proper guardrails
- Parameter no longer leaks to LLM API
- Better batch size handling at limit boundaries
- Test improvements for CI/CD compatibility

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-12 21:06:04 +01:00
MervinPraison 5a8f4aedf8 docs: auto-update feature parity trackers [skip ci] 2026-06-12 20:05:39 +00:00
459a12a656 feat: implement real-time message steering for agents during execution (#1802)
* feat: implement real-time message steering for agents during execution

- Add MessageSteeringProtocol and AgentSteeringProtocol following protocol-driven architecture
- Implement MessageSteering class with priority-based message queuing
- Add SteeringMixin to Agent class with message_steering parameter
- Integrate steering checks into execution flow and tool execution
- Support mid-execution guidance with priority levels (normal, high, urgent, interrupt)
- Maintains backward compatibility with existing Agent API
- Zero overhead when message_steering=False (default)

Fixes #1799

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>

* fix: address critical bugs in message steering implementation

- Fix INTERRUPT detection in tool_execution.py (was using broken substring check)
- Fix rate limiter blocking urgent messages during tool execution
- Add INTERRUPT priority bypass in message queue (URGENT.value + 1)
- Add proper error handling in execution_mixin.py with graceful degradation
- Extend MessageSteeringProtocol with enabled property for contract compliance
- Move test file to proper tests/integration/ directory structure
- Add mandatory live agentic test with real LLM execution
- Improve prompt formatting with clear separators

Fixes critical bugs identified by Greptile, CodeRabbit reviewers.
All changes maintain backward compatibility.

Co-authored-by: Mervin Praison <[email protected]>

* fix: comprehensive message steering improvements

- Wire steering in ALL execution paths (run, chat, achat, streaming)
- Move protocols to agent/protocols.py per AGENTS.md convention
- Remove duplicate test file and enhance live test with actual validation
- Fix INTERRUPT marker mismatch between tool_execution and SteeringMixin
- Improve priority handling with distinct INTERRUPT formatting

Co-authored-by: Mervin Praison <[email protected]>

* feat: add CLI/YAML parity and examples for message steering

- Add --message-steering CLI flag support in wrapper package
- Add YAML agent config mapping (message_steering: true)
- Create comprehensive Python example showing all usage patterns
- Create YAML configuration example
- Implements AGENTS.md §7 requirement (Python + CLI + YAML support)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Claude <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
Co-authored-by: Claude <[email protected]>
2026-06-12 21:05:11 +01:00
MervinPraison d9224adc70 docs: auto-update api.md [skip ci] 2026-06-12 20:04:37 +00:00
82f1d8881b fix: resolve wrapper issues - duplicate arun definitions, ToolResolver cache bug, missing observability finalizer (#1896)
* fix: resolve wrapper issues - duplicate arun definitions, ToolResolver cache bug, missing observability finalizer (fixes #1895)

- Remove duplicate arun() definitions in framework_adapters/base.py
- Fix ToolResolver cache instantiation bug in tool_resolver.py
- Add finalize_observability() function to observability/hooks.py
- Update all framework adapters to use centralized observability finalizer
- Ensure AutoGen/AG2 adapters properly close AgentOps sessions

Co-authored-by: Mervin Praison <[email protected]>

* fix: ensure observability finalization on exception paths in all adapters

- Add try/except blocks to AutoGenV4Adapter.arun() and AG2Adapter.run()
- Call finalize_observability with status='Failure' on exceptions
- Update all adapters to pass explicit status='Success' parameter
- Rename unused framework_tag parameter to _framework_tag for clarity

Fixes critical telemetry issue where AgentOps sessions remained 'in progress'
when exceptions occurred during framework execution.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

* test: add regression tests for wrapper layer fixes in PR #1896

- Add unit tests for ToolResolver cache instantiate=True fast-path fix
- Add tests for observability finalization on adapter exception paths
- Validate framework adapter exception handling structure
- Test observability hooks error handling and status parameters
- Ensure comprehensive test coverage per AGENTS.md section 9 requirements

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
Co-authored-by: Claude <[email protected]>
2026-06-12 21:04:21 +01:00
ca07ebf813 fix: Agent handoff tool security boundary to prevent privilege escalation (#1848)
* fix: implement agent handoff tool security boundary to prevent privilege escalation (fixes #1842)

- Add HandoffToolPolicy dataclass with 'intersect' (secure by default) and 'passthrough' (legacy) modes
- Add blocked_tools list for tools that should never be passed through handoffs
- Update HandoffConfig to include tool_policy field with secure defaults
- Implement tool boundary enforcement in all handoff execution paths:
  - execute_programmatic(): programmatic handoffs
  - execute_async(): async handoffs with concurrency control
  - to_tool_function(): LLM-driven handoffs via tool calls
- Add convenience parameters to handoff() function: tool_policy_mode, blocked_tools
- Maintain full backward compatibility - existing code continues working unchanged
- Default mode is 'intersect' for security by default - sub-agents only get tools that both source and target agents have
- Optional 'passthrough' mode preserves legacy behavior when explicitly requested

Security impact:
- Prevents privilege escalation where restricted gatekeeper agents could accidentally grant dangerous tools to sub-agents
- Tool intersection computed at handoff boundary without mutating target agent's original tool list
- Blocked tools are always filtered regardless of intersection mode
- Safe by default - new tool policy applies automatically to protect against security gaps

Co-authored-by: MervinPraison <[email protected]>

* fix: critical handoff tool security bypass and blocked_tools logic

- Fix empty tools list security bypass in intersect mode
- Return None for unrestricted passthrough mode without blocked tools
- Return [] to enforce empty toolset (security boundary) in intersect mode
- Fix blocked_tools falsy logic to allow explicit empty list clearing

Security Impact:
- Prevents privilege escalation when orchestrator agent has no tools
- Ensures intersect mode properly restricts target agent capabilities
- Maintains backward compatibility with passthrough mode

Co-authored-by: Mervin Praison <[email protected]>

* fix: P0 HandoffToolPolicy security boundary enforcement

Critical security fix prevents privilege escalation during agent handoffs:

🔒 Security Fixes:
- Fix tools=[] vs tools=None distinction in chat_mixin.py (P0 vulnerability)
- Enforce HandoffToolPolicy intersect mode security boundaries
- Handle None tools attributes to prevent TypeError
- Fix blocked_tools=[] explicit empty list handling

✅ New Features:
- Add comprehensive HandoffToolPolicy unit tests (25 test cases)
- Export HandoffToolPolicy in __init__.py for public API
- Add security documentation and Mintlify docs stub

🛡️ Security Model:
- Default: intersect mode (secure by default - only shared tools)
- Legacy: passthrough mode (opt-in - full toolset with blocklist)
- Boundary: tools=[] enforces explicit tool denial
- Defense: Multi-layer privilege escalation prevention

Fixes #1842

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Mervin Praison <[email protected]>

* fix: Windows CI syntax error - replace Unicode characters with ASCII equivalents

Replace em-dash (—) and right arrow (→) characters in chat_mixin.py comments with ASCII equivalents to prevent Windows Python parser issues.

Fixes IndentationError on Windows CI around line 2296.

* Delete docs/handoff-security.mdx

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-12 21:04:02 +01:00
2b2443ff63 fix: resolve sync/async path drift, silent DB writes, and adapter inconsistency (#1884)
* fix: resolve sync/async path drift, silent DB writes, and adapter inconsistency (fixes #1883)

- Extract shared pipeline logic in AgentsGenerator to eliminate sync/async drift
- Add _init_stores() to all DB write hooks to prevent silent data loss
- Add _resolve_llm() to BaseFrameworkAdapter for consistent string/dict llm handling
- Fix CrewAI adapter to support string-form llm configuration like PraisonAI adapter

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: MervinPraison <[email protected]>

* fix: address IndexError, dead code, and missing store init issues

- Fix IndexError risk in BaseFrameworkAdapter._resolve_llm() by checking llm_config length
- Remove unused 'field' parameter from _resolve_llm() method signature
- Fix duplicate arun() method definition in BaseFrameworkAdapter
- Add missing _init_stores() calls to all tracing handlers (on_trace_start, on_trace_end, on_span_start, on_span_end)

Addresses feedback from CodeRabbit and Greptile reviews.

Co-authored-by: Mervin Praison <[email protected]>

* test: add unit tests for wrapper layer architectural fixes

- Add DB adapter tracing hooks initialization tests (7 tests)
  * Tests _init_stores() calls in on_trace_start/end, on_span_start/end
  * Validates prevention of silent data loss when stores uninitialized
  * Tests graceful degradation without state_url configuration

- Add framework adapter _resolve_llm() IndexError guard tests (5 tests)
  * Tests safe handling of empty llm_config lists and None values
  * Validates length checks before accessing llm_config[0]
  * Tests CrewAI adapter string LLM compatibility

Covers architectural fixes from issue #1883:
1. Silent DB writes prevention via store initialization
2. IndexError crashes prevention via safe access patterns
3. String LLM support via unified _resolve_llm() helper

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude <[email protected]>
2026-06-12 19:10:23 +01:00
praisonai-triage-agent[bot]andMervin Praison c8938c6aeb fix: persistence layer schema drift, async contract violations, and registry boilerplate (#1829)
* fix: persistence layer schema drift, async contract violations, and registry boilerplate (fixes #1822)

- Add AsyncConversationStore protocol to eliminate broken async-store contract
- Replace runtime introspection with type-based dispatch in orchestrator
- Create SQL conversation store base template to prevent schema drift
- Unify retry logic across all SQL backends (MySQL/Turso/SingleStore now get serverless retry)
- Add PluginRegistry.default() to eliminate copy-pasted singleton boilerplate
- Reduce postgres.py from 485→235 lines (~52% reduction)
- Ensure schema consistency between sync/async stores

Co-authored-by: MervinPraison <[email protected]>

🤖 Generated with [Claude Code](https://claude.ai/code)

* fix: critical bugs in persistence layer preventing PostgreSQL backend from working

- Fixed PostgreSQL property conflict by making sessions_table/messages_table properties in base class
- Fixed multi-statement DDL by separating schema creation in postgres.py
- Fixed broken connection retry logic by adding close parameter to _put_conn
- Fixed JSON handling to support psycopg2's automatic JSONB decoding
- Added SQL injection prevention with parameter validation for limit/offset
- Fixed empty message_ids handling in delete_messages
- Fixed registry lock initialization race condition with shared guard lock
- Fixed LLM registry reset to use __dict__ membership check
- Added concurrency protection for async SQLite initialization
- Removed artifact files: fix_async_postgres.py, postgres_backup.py, postgres_new.py

All critical issues identified by reviewers are now resolved.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-12 19:10:02 +01:00
MervinPraison fc072d37a3 Release v4.6.53 2026-06-12 19:04:44 +01:00
praisonai-triage-agent[bot]andMervin Praison ccfc66092b docs: document context_compaction deprecation cycle (#1900)
* docs: add context_compaction deprecation notice to AGENTS.md

- Add context_compaction parameter to ExecutionConfig table in §5.3
- Include deprecation notice for default changing from False to True
- Update example code to show explicit opt-out during deprecation period
- DeprecationWarning verified to fire correctly from user code

Co-authored-by: Mervin Praison <[email protected]>

* fix: correct documentation issues in context_compaction deprecation notice

- Change 'Breaking Change Notice' to 'Deprecation Notice' to match backward compatibility claim
- Update misleading comment about explicit opt-out - warning fires regardless during deprecation

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-12 18:47:11 +01:00
03e5f0b11a fix: pairing approval channel_type mismatch and UnifiedSessionStore message loss (#1892)
* fix: pairing approval channel_type mismatch and UnifiedSessionStore message loss

- Pass platform channel_type (not chat ID) into pairing callback payloads so
  verify_and_pair keys match is_paired lookups after owner approval
- Reload and merge messages under lock in UnifiedSessionStore.save() to
  prevent stale-cache overwrites losing concurrent CLI/TUI session writes
- Add regression tests for both failure modes

Co-authored-by: Mervin Praison <[email protected]>

* fix: improve Windows file locking and add missing durability in UnifiedSessionStore

- Fix Windows file locking to lock entire file instead of just 1 byte
- Add missing flush/fsync in fallback write path for consistency
- Remove redundant load() call in test_stale_cache_save_preserves_concurrent_messages

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
2026-06-12 18:47:01 +01:00
6b051dcafb fix: prevent CLI session message loss and Telegram policy bypass (#1885)
* fix: prevent CLI session message loss and Telegram policy bypass

- UnifiedSessionStore: merge messages under file lock on save to avoid
  clobbering concurrent writes from TUI/interactive processes; always reload
  from disk on load
- Telegram: align user allowlist check with Discord/Slack so empty
  allowed_users honours unknown_user_policy (default deny)
- Add regression tests for both issues

Co-authored-by: Mervin Praison <[email protected]>

* fix: improve session store concurrency safety

- Fix counter delta loss: Use delta tracking instead of max() to preserve concurrent increments
- Fix file stat race conditions: Handle FileNotFoundError when files are deleted between operations
- Add baseline stat tracking for proper merge semantics
- Maintain backward compatibility for existing sessions

Addresses CodeRabbit feedback on concurrent session safety issues.

Co-authored-by: Mervin Praison <[email protected]>

* fix: resolve syntax error in load() exception handling

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
2026-06-12 18:46:46 +01:00
ade64e2b9c fix: ToolResolver instantiate cache hit and CLI session concurrent write loss (#1858)
- Apply instantiate=True on ToolResolver cache fast path (fixes class tools
  returned after has_tool/validate_yaml_tools warmed the cache)
- UnifiedSessionStore save now read-merge-writes under file lock
- Invalidate CLI session cache when on-disk mtime changes

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-12 18:46:31 +01:00
praisonai-triage-agent[bot]andMervin Praison 3791c6ea64 fix: wrapper layer architectural violations (fixes #1890) (#1891)
* fix(wrapper): resolve three architectural violations (fixes #1890)

- Convert upload_vision.py and train/llm/trainer.py to lazy imports
  Following the pattern from train.py, moves heavy ML deps (torch, unsloth, transformers)
  inside _lazy_import_*_deps() functions called from constructors

- Convert endpoints/registry.py to use unified PluginRegistry
  Replaces bare module dict with thread-safe PluginRegistry[T] pattern
  Enables entry-points extensibility and consistent registry semantics

- Add async variants to blocking methods
  Adds DaemonManager.astop_daemon() and DeploymentScheduler.adeploy_with_retry()
  Uses asyncio.sleep() instead of time.sleep() for event-loop safety

All changes maintain backward compatibility while following wrapper layer philosophy:
lazy imports + optional deps, protocol-driven core, async-safe by default.

Co-authored-by: MervinPraison <[email protected]>

* fix: address reviewer feedback - async safety, backward compat, exception handling

- Fix adeploy_with_retry to use asyncio.to_thread for true async behavior
- Add missing backward compatibility methods (list_types, get_class) to ProviderRegistry
- Improve exception chaining in trainer.py ImportError handling
- Better exception specificity in deployment retry logic
- Distinguish between missing vs broken dependencies in registry

Co-authored-by: Mervin Praison <[email protected]>

* test: add comprehensive unit tests for architectural fixes

- Add test_lazy_imports.py for lazy loading and import performance
- Add test_provider_registry_compat.py for backward compatibility
- Add test_async_daemon_deployment.py for async-safe methods

Tests verify:
✓ Heavy ML deps not loaded during package import (< 200ms target)
✓ ProviderRegistry backward compatibility (list_types, get_class, etc)
✓ Async methods use asyncio.to_thread and never block event loop
✓ Exception chaining preserved in lazy import error handling

Per AGENTS.md §9 requirements for architectural changes.

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-12 18:45:57 +01:00
praisonai-triage-agent[bot]andMervin Praison ef8e1fa6b6 fix: critical security fixes - sandbox traversal, sync/async divergence, rate limiter (#1870)
* fix: critical security fixes - sandbox traversal, sync/async divergence, rate limiter (fixes #1869)

- Fixed sandbox path traversal vulnerability in subprocess.py and docker.py by adding safe_sandbox_path() validation
- Eliminated sync/async generator divergence by extracting shared _prepare() method in agents_generator.py
- Fixed rate limiter serialization bottleneck and memory leak with lock-free design and LRU channel tracking

Co-authored-by: MervinPraison <[email protected]>

* fix: address critical reviewer feedback - rate limiter concurrency, sync/async convergence

- Fix rate limiter timeline advancement to prevent concurrent burst past limits
- Consolidate sync path to use shared _prepare() method eliminating duplication
- Remove redundant AgentOps initialization in _prepare()
- Validates sandbox TOCTOU vulnerability as non-exploitable in current context

Addresses feedback from CodeRabbit, Greptile reviewers.

Co-authored-by: Mervin Praison <[email protected]>

* test: add comprehensive unit tests for security fixes (issue #1869)

- Add safe_sandbox_path() traversal rejection tests
- Add rate limiter concurrency timeline advancement tests
- Add agents_generator sync/async _prepare() parity tests
- Include real agentic tests per AGENTS.md §9.4 requirements
- Tests cover all three security fixes: sandbox traversal, rate limiting, sync/async convergence

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-12 18:44:20 +01:00
MervinPraison aa273b7c36 docs: auto-update api.md [skip ci] 2026-06-12 17:44:17 +00:00
praisonai-triage-agent[bot]andMervin Praison 98cf202178 fix: wrapper layer gaps - InteractiveRuntime lifecycle, tool resolution, cli_backend support (#1857)
* fix: wrapper layer gaps - InteractiveRuntime lifecycle, tool resolution, cli_backend support (fixes #1856)

- Remove duplicate arun() method definitions in framework_adapters/base.py
- Update CLI tool loading to use unified ToolResolver instead of bypassing to TOOL_MAPPINGS
- Fix async scheduler daemon state updates and consolidate ScheduleParser
- Create shared _BaseAgentScheduler class for sync/async scheduler logic
- Ensure CLI, YAML, and Python tool resolution behave consistently

Co-authored-by: MervinPraison <[email protected]>

* fix: critical scheduler bugs - missing _start_time init, blocking I/O, zero budget handling

- Initialize _start_time in AsyncAgentScheduler.__init__ to prevent AttributeError in get_stats()
- Set _start_time when scheduler starts to track runtime properly
- Wrap _update_state_if_daemon() calls with asyncio.to_thread() to avoid blocking event loop
- Fix max_cost=0.0 handling by using 'is not None' instead of truthiness check

Fixes critical issues identified in PR review by CodeRabbit, Greptile, and Copilot.

Co-authored-by: Mervin Praison <[email protected]>

* test: add comprehensive tests for PR #1857 scheduler fixes

- Add unit tests for AsyncAgentScheduler._start_time initialization
- Add tests for asyncio.to_thread daemon state update handling
- Add tests for max_cost=0.0 zero budget handling in _BaseAgentScheduler
- Add CLI smoke tests for ToolResolver integration
- Add integration tests combining all fixes
- Add framework adapter validation tests

Tests ensure critical bugs identified by CodeRabbit, Greptile, and other
reviewers are properly fixed and regression-tested per AGENTS.md §9.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-12 18:44:01 +01:00
praisonai-triage-agent[bot]andMervin Praison f49d5daf15 feat: comprehensive test coverage and fixes for PR #1853 (#1898)
* feat: comprehensive test coverage and fixes for PR #1853

- Add unit tests for error classification system (test_error_classification.py)
- Add unit tests for idle timeout breaker (test_idle_timeout_breaker.py)
- Add integration test for ExecutionConfig max_iter end-to-end (test_execution_config_max_iter.py)
- Deprecate dead _classify_error_and_should_retry code with fallback to new system
- Fix billing errors to surface immediately (non-retryable) in resolve_failover_decision
- Fix quota exceeded duplicate classification (removed from rate_limit, kept in billing)
- Align LLM default max_iter (20) with ExecutionConfig default (20)

Addresses test debt and remaining issues identified by reviewers:
- Greptile, CodeRabbit, Copilot feedback on dead code and billing error handling
- Ensures IdleTimeoutBreaker circuit breaker is functional
- Provides comprehensive test coverage for typed error classification

Co-authored-by: Mervin Praison <[email protected]>

* fix: critical test failures and error classification bugs

- Update test assertion for new max_iter default (20 vs 10)
- Fix error classification order: billing checked before rate_limit
- Add missing 'billing' to non-retryable error test list
- Fix backoff calculation test expectation (4000ms vs 2000ms)
- Add backward compatibility alias for renamed method

Fixes issues identified by Greptile, CodeRabbit, and other reviewers.

Co-authored-by: Mervin Praison <[email protected]>

* fix: add real agentic test per AGENTS.md §9.4 requirement

- Add test_real_agent_execution_with_max_iter() that calls real LLM
- Agent.start() with real prompt produces actual text response
- Validates max_iter enforcement end-to-end
- Prints output for developer verification per AGENTS.md

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-12 18:43:44 +01:00
011f54fbc5 feat: implement non-blocking spawn-and-announce pattern for multi-agent orchestration (fixes #1801) (#1803)
* feat: implement non-blocking spawn-and-announce pattern for multi-agent orchestration (fixes #1801)

- Add SpawnAnnounceProtocol, SpawnedSubAgent, and SubAgentCompletionEvent to agents/protocols.py
- Extend EventBus with new sub-agent event types: SUBAGENT_SPAWNED, SUBAGENT_COMPLETED, SUBAGENT_ERROR
- Enhance AgentTeam to implement SpawnAnnounceProtocol with:
  * spawn_sub_agent() - non-blocking sub-agent spawning
  * announce_completion() - push-based completion notifications
  * get_spawned_agents() - track active sub-agents
  * wait_for_completions() - optional blocking wait with timeout
- Add comprehensive test suite demonstrating the new capabilities
- Maintain full backward compatibility with existing AgentTeam usage

This enables efficient parallel sub-agent workflows with event-driven coordination
instead of blocking wait patterns, supporting sophisticated multi-agent orchestration.

Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>

* fix: resolve critical spawn-announce deadlock and race conditions (fixes #1801)

Critical fixes:
1. Replace threading.Lock with RLock to prevent deadlock in announce_completion
2. Fix race condition in wait_for_completions by reading _spawned_agents inside lock
3. Add async variants (aspawn_sub_agent, aannounce_completion, await_for_completions) per AGENTS.md
4. Maintain full backward compatibility with sync methods

🔧 Generated with Claude Code
Co-authored-by: Mervin Praison <[email protected]>

* fix: resolve critical spawn-announce deadlock and race conditions (fixes #1801)

- Fixed asyncio.Event.set() thread safety bug in await_for_completions using call_soon_threadsafe()
- Moved tests from repo root to proper pytest location tests/unit/agents/test_spawn_announce.py
- Removed CI skip guards and added real agentic tests with actual LLM execution per AGENTS.md §9.4
- Enhanced protocol-driven design documentation and integrated with handoff infrastructure per DRY principle
- Fixed race condition in wait_for_completions by reading _spawned_agents inside lock
- Maintained full backward compatibility while fixing critical deadlock and threading issues

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Claude <[email protected]>

* chore: remove root-level test artifacts from spawn-announce PR

- Deleted test_basic_syntax.py
- Deleted test_spawn_announce.py
- Deleted test_spawn_fix.py

Tests belong in tests/unit/agents/ per AGENTS.md structure.
Proper test exists at src/praisonai-agents/tests/unit/agents/test_spawn_announce.py

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
Co-authored-by: Claude <[email protected]>
2026-06-12 18:43:16 +01:00
89eefb87fe fix: implement proper LLM summarization for CompactionStrategy.LLM_SUMMARIZE (#1851)
* fix: implement proper LLM summarization for CompactionStrategy.LLM_SUMMARIZE (fixes #1843)

- Add real LLM integration to _llm_summarize_async() method in ContextCompactor
- Create _create_llm_summarize_function() in agent for proper LLM client access
- Add compaction_strategy parameter to ExecutionConfig for strategy selection
- Update all 3 compaction call sites in chat_mixin.py to support new strategy system
- Add robust error handling with fallback to naive summarization
- Ensure BEFORE_COMPACTION/AFTER_COMPACTION hooks fire consistently
- Use intelligent summarization prompt that preserves file paths, IDs, URLs, task references
- Support both sync and async compaction paths with proper strategy handling

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

* fix: resolve critical LLM summarization bugs found by reviewers

- Fix response content extraction to use existing _extract_llm_response_content method instead of str(response) fallback
- Fix sync call site to properly dispatch to async compaction for LLM_SUMMARIZE strategy
- Fix circular import using proper TYPE_CHECKING pattern and safe runtime access
- Fix hook error handling consistency across all compaction paths
- Fix deprecated asyncio.get_event_loop() to use asyncio.get_running_loop()

Co-authored-by: Mervin Praison <[email protected]>

* feat: enhance LLM_SUMMARIZE compaction with comprehensive tests and code deduplication

- Fix compaction_strategy default handling to prevent None.value crashes
- Add comprehensive unit tests for LLM_SUMMARIZE functionality
- Add end-to-end integration tests with real agent scenarios
- Deduplicate chat_mixin compaction code (3x copy reduced to helper methods)
- Add warning for asyncio.run fallback in sync context
- Add test coverage for ExecutionConfig.compaction_strategy serialization
- Improve error handling and fallback behavior

Fixes all critical bugs identified by previous reviewers while maintaining
backward compatibility and adding robust test coverage.

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Claude <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-12 18:42:57 +01:00
MervinPraison a14db7f39a docs: auto-update feature parity trackers [skip ci] 2026-06-12 17:42:27 +00:00
praisonai-triage-agent[bot]andMervin Praison ee126c60b4 fix: restore ConversationOptimizer and conversation compaction functionality (fixes P0 regression) (#1899)
* fix: restore ConversationOptimizer and conversation compaction functionality

- Add ConversationOptimizer class with lazy-loaded conversation components
- Update OPTIMIZER_REGISTRY to include OptimizerStrategy.CONVERSATION mapping
- Rename protocols to ConversationAnalyzerProtocol, ConversationCompactorProtocol
- Add backward compatibility aliases for existing imports
- Move root-level tests to proper tests/unit/context/ location
- Add real agentic test for ConversationOptimizer end-to-end functionality
- Fix manager.py kwargs compatibility with ConversationOptimizer constructor

Fixes the P0 regression where conversation compaction was lost during
the #1804→#1811 consolidation. Now get_optimizer(OptimizerStrategy.CONVERSATION)
works correctly without falling back to SmartOptimizer and causing TypeError.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Mervin Praison <[email protected]>

* fix: address reviewer feedback - logging, imports, assertions

- Add logging to ConversationOptimizer exception handling
- Remove redundant SmartOptimizer imports
- Fix missing sys import in test_context_compression.py
- Improve test assertions and remove unused variables
- Address all critical issues found by CodeRabbit, Greptile, and Copilot

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-12 18:41:55 +01:00
MervinPraison 4f8cfb783f docs: auto-update api.md [skip ci] 2026-06-12 13:02:42 +00:00
00872ee314 fix: implement proactive context overflow protection (fixes #1817) (#1828)
* fix: implement proactive context overflow protection (fixes #1817)

Replace opt-in reactive context compaction with default-on proactive budget checking

BREAKING CHANGE: ExecutionConfig.context_compaction now defaults to True (was False)

Changes:
- Add ContextCompactionPolicy class for intelligent routing decisions
- Replace reactive string-matching with proactive token budget analysis
- Update ExecutionConfig to enable context compaction by default
- Implement budget checking before LLM calls instead of after failures
- Add policy-based compaction strategies (truncate, summarise, drop_oldest_tools)
- Support both sync and async execution paths
- Maintain backward compatibility with explicit context_compaction=False

Fixes:
- Agents no longer fail silently at context limits
- Proactive protection triggers at 90% utilization by default
- Emergency recovery no longer relies on brittle error string matching
- Default safe behavior for all agents without configuration

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve architecture violations and reviewer issues (addresses #1817)

FIXES:
- Remove orphaned staging file (chat_mixin_update.py) with broken imports
- Fix mutable singleton in get_default_policy() using deepcopy
- Add sync custom-LLM path coverage for proactive context budget
- Fix double-counting system prompt tokens in all LLM paths
- Fix premature state mutation in async path (defer compaction commit)
- Add deprecation cycle for context_compaction default change
- Fix serialization round-trip for policy objects via __post_init__
- Implement protocol-driven design per AGENTS.md requirements

PROTOCOL-DRIVEN ARCHITECTURE:
- Move ContextCompactionPolicy implementation to adapters.py
- Create ContextCompactionPolicyProtocol interface in protocols.py
- Maintain backward compatibility via re-exports in policy.py
- Export protocol types in module __init__.py files

Breaking change reverted during deprecation period:
- context_compaction remains False by default with DeprecationWarning
- Will change to True in next release after warning period

Resolves architectural violations found by multiple reviewers.

Co-authored-by: Mervin Praison <[email protected]>

* fix: complete proactive context overflow protection implementation

All major reviewer issues resolved and functionality implemented:

🔧 **Critical Fixes**:
- Fix Agent.execution never being set (store ExecutionConfig as self.execution)
- Implement protocol-driven design (ContextCompactionPolicyProtocol)
- Fix mutable singleton in get_default_policy() (return fresh copies)
- Fix double token counting in system prompts (remove redundant parameter)
- Fix premature state mutation in async path (defer until LLM success)
- Fix sync custom-LLM path missing compaction (add proactive budget logic)
- Fix ExecutionConfig serialization round-trip (add from_dict method)

🏗️ **Architecture**:
- Deduplicate ~150 lines of sync/async helpers via shared core functions
- Protocol-driven design: heavy implementations in adapters.py
- Backward compatible: explicit opt-out still works during deprecation

✅ **Testing**:
- Unit tests for policy routing, ExecutionConfig round-trip, sync/async parity
- Real agentic end-to-end test with actual LLM calls near context limits
- Examples showing CLI/YAML/Python API parity

Fixes #1817

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude <[email protected]>
2026-06-12 14:02:11 +01:00
MervinPraison 4ee593cabb docs: auto-update feature parity trackers [skip ci] 2026-06-12 13:01:59 +00:00
4ed1412820 fix: add prompt injection protection for external tool results (#1824)
* fix: add prompt injection protection for external tool results (fixes #1820)

Implements security wrapper system to protect against indirect prompt injection
attacks from external sources (web search, scraping, MCP tools, etc).

Key changes:
- Created praisonaiagents/tools/trust.py with ToolTrustLevel enum and wrap_if_external()
- Updated tool_execution.py to apply wrapping after circuit breaker execution
- Enhanced tool registry with trust_level parameter for marking external tools
- Zero-cost for trusted tools, selective wrapping for external content ≥32 chars
- Security markers: <external_tool_result> tags instruct model to treat as factual data only

External tools protected: duckduckgo, tavily_search, web_search, scrape_page,
crawl4ai, spider_crawl, and any tools registered with trust_level='external'.

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve critical security vulnerabilities in prompt injection protection

- Fix double-wrapping bug: remove duplicate wrap_if_external call from timeout path
- Fix fence injection vulnerability: escape fence markers in untrusted content
- Fix missing system prompt integration: add trust instructions to agent system prompts
- Fix structured data bypass: serialize JSON/dict from external tools before wrapping
- Add trust level validation: validate trust_level parameter in registry
- Extend API coverage: add trust_level support to register_tool() and add_tool()

All critical issues identified by reviewers (CodeRabbit, Greptile) are now resolved.
Security boundary is properly established and enforced for external tool results.

Co-authored-by: Mervin Praison <[email protected]>

* fix: resolve merge conflict by integrating trust level system into ToolEntry architecture

- Combines prompt injection protection (trust levels) with new ToolEntry system
- Adds trust_level field to ToolEntry dataclass for unified storage
- Updates register() method to support both trust_level and dynamic_schema_overrides
- Maintains backward compatibility for all existing APIs
- Preserves all security functionality while adopting new architecture

Co-authored-by: Mervin Praison <[email protected]>

* feat: complete prompt injection protection implementation with comprehensive tests

- Move wrap_if_external import to module level in tool_execution.py for performance
- Add comprehensive unit test suite with 25+ test cases covering:
  * Trust level validation and registry integration
  * Fence marker escaping to prevent injection breakout
  * JSON serialization for external structured data
  * Edge cases and security scenarios
- Add end-to-end integration tests with real agent execution
- Verify system prompt integration includes security instructions
- Test both trusted and external tool handling in agent workflows

All critical security vulnerabilities identified by reviewers have been resolved:
✅ Double-wrapping bug fixed (already resolved in previous commits)
✅ Fence escaping implemented with HTML entity encoding
✅ System prompt integration working correctly
✅ Structured data serialization and wrapping
✅ Trust level validation with clear error messages
✅ Public API coverage for trust_level parameter

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-12 14:01:30 +01:00
MervinPraison f8031a4349 docs: auto-update api.md [skip ci] 2026-06-12 13:01:09 +00:00
b0a2aa35d4 feat: implement named toolset groups for PraisonAI tool registry (fixes #1841) (#1850)
* feat: implement named toolset groups for PraisonAI tool registry (fixes #1841)

This implements named toolset groups functionality to address the lack of
organized tool collections in PraisonAI's tool registry system.

Core SDK Changes (praisonaiagents):
- Add toolsets.py with ToolsetSpec dataclass and ToolsetRegistry
- Implement 8 prebuilt toolsets: web, files, code, system, scraping, research, safe, development
- Support toolset composition via includes with circular dependency detection
- Add TTL availability caching (30s) to ToolRegistry.list_available_tools()
- Update Agent.__init__ to accept toolsets parameter with automatic tool resolution
- Export toolsets functionality in main package __init__.py

Wrapper Layer Changes (praisonai):
- Extend ToolResolver with resolve_toolsets() and resolve_tools_and_toolsets()
- Add --toolset CLI flag to 'praisonai run' and 'praisonai chat' commands
- Integrate toolset resolution in CLI execution pipeline

Key Features:
- Named toolset groups for organized tool management
- Progressive disclosure (safe → research → development)
- Least-privilege tool surfaces for restricted environments
- Performance improvements via TTL caching
- Full backward compatibility with existing tools parameter
- Thread-safe operations for multi-agent scenarios

Usage:
- Python: Agent(role='researcher', toolsets=['research'])
- CLI: praisonai run 'task' --toolset research
- YAML: toolsets: [research] in agent configuration

Co-authored-by: MervinPraison <[email protected]>

* fix: address critical issues found in code review

- Fix CLI tools write-back regression in main.py (silent tool dropping)
- Improve error handling in Agent toolset resolution (fail fast on invalid toolsets)
- Fix mutable state leakage in ToolsetRegistry (defensive copying)
- Fix cache collision in ToolRegistry (use registry keys not tool.name)
- Fix NameError in CLI commands (missing toolset parameters)
- Add deduplication in tool resolution
- Improve test reliability with cleanup and assertions
- Filter empty toolset names from CLI input

Co-authored-by: Mervin Praison <[email protected]>

* fix: resolve final registry.py merge conflicts

- Fix missed conflict in list_available_tools method
- Proper TTL caching using registry keys to avoid collisions
- Clean up clear() method to remove _functions reference

Co-authored-by: Mervin Praison <[email protected]>

* feat: complete named toolsets YAML integration and testing

Wire YAML support for toolsets:
- Add 'toolsets' to known_fields in agents_generator.py
- Update validate_yaml_tools to validate toolset references
- Update praisonai_adapter to pass toolsets from YAML to Agent

Enhance testing:
- Move test_toolsets.py to tests/unit/test_toolsets.py
- Convert to pytest conventions with proper cleanup
- Add real agentic tests requiring LLM calls per AGENTS.md
- Add comprehensive test coverage including YAML validation

Fix error handling:
- Remove ImportError swallowing in Agent.__init__ for fail-fast behavior
- Add clear error messages for invalid/missing toolsets

Add documentation and examples:
- Add examples/python/toolsets_example.py with comprehensive demos
- Add examples/yaml/toolsets_workflow.yaml showing YAML usage
- Include both simple usage and complex multi-agent workflows

All changes maintain backward compatibility and follow AGENTS.md guidelines.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude <[email protected]>
2026-06-12 14:00:39 +01:00
74273677a8 fix: prevent CLI session message loss on concurrent saves (#1854)
* fix: prevent CLI session message loss on concurrent saves

UnifiedSessionStore.save() overwrote the session file without reloading
disk state first. Two CLI/TUI processes sharing a session could lose chat
history when one saved from a stale in-memory copy.

- Reload and merge messages under an exclusive file lock before writing
- Invalidate in-process cache when the on-disk file is newer
- Add regression tests for stale-save and concurrent writer scenarios

Co-authored-by: Mervin Praison <[email protected]>

* fix: prevent FileNotFoundError race condition in session delete

Use missing_ok=True to handle concurrent file deletion gracefully.
Fixes potential race where another process deletes file between
exists() check and unlink() call.

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
2026-06-12 10:56:02 +01:00
7c9899d0da fix: add typed error classification and fix uncoordinated iteration caps (#1853)
* fix: add typed error classification and fix uncoordinated iteration caps (fixes #1844)

Resolves three core issues with agent execution:

1. **Typed Error Classification**: Replace freeform error categories with
   closed AgentErrorKind Literal type including auth, rate_limit,
   context_overflow, model_not_found, empty_response, format_error,
   overloaded, billing, idle_timeout, unknown. Adds FailoverDecision
   dataclass for structured retry logic.

2. **Unified Iteration Authority**: Fix hardcoded >= 5 caps in llm.py
   that silently overrode ExecutionConfig.max_iter. Added max_iter parameter
   to LLM constructor and pass ExecutionConfig.max_iter from Agent class.
   Replaced 4 hardcoded iteration checks with configurable self.max_iter.

3. **Idle-Timeout Circuit Breaker**: Add IdleTimeoutBreaker to prevent
   runaway API costs from repeated provider stalls. Triggers after 3
   consecutive idle timeouts, resets on success.

Key changes:
- errors.py: Add AgentErrorKind, FailoverDecision, IdleTimeoutBreaker
- llm.py: Add classify_error_kind(), resolve_failover_decision(), max_iter param
- agent.py: Pass max_iter to LLM constructor in all instantiation sites

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

* fix: wire resolve_failover_decision into retry loops and fix critical issues

- Fix dead code: Wire resolve_failover_decision into _call_with_retry and _call_with_retry_async
- Fix backoff unit mixing in rate limit handling (prevent extreme delays)
- Fix quota exceeded duplicate classification (billing vs rate_limit)
- Add auth_permanent classification for non-retryable auth errors
- Fix IdleTimeoutBreaker _count field visibility with field(init=False)
- Add legacy error category mapping with deprecation warnings
- Update error subclass constructors to accept error_category parameter
- Fix remaining hardcoded iteration limits to use configurable max_iter

Addresses all critical issues identified by reviewers. The circuit breaker
is now functional and can properly trip to prevent runaway API costs.

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Claude <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-12 10:41:34 +01:00
praisonai-triage-agent[bot]andMervin Praison 5bff3e51bc feat: add provider-aware LLM error classifier with structured recovery routing (#1833)
* feat: add provider-aware LLM error classifier with structured recovery routing

- Replace ad-hoc string matching with structured LLMErrorClassification
- Add classify_llm_error() function with provider-specific recovery hints
- Implement jittered_backoff() utility for exponential backoff with jitter
- Update chat_mixin.py to use structured classification for recovery routing
- Support context compression, credential rotation, model fallback hints
- Maintain backward compatibility with existing classify_error() function

Fixes #1830

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve LLM error classifier issues identified in code review

- Fix auth errors marked retryable without credential rotation (P1 issue)
- Add jittered backoff with retry depth for progressive delays
- Remove unused asyncio import and document unreachable code
- Add async error handling parity with _handle_async_llm_error()
- Fix retry logic to skip retries when recovery actions needed
- Unify control flow to use structured classification for all errors

Addresses feedback from Greptile, CodeRabbit, Qodo reviews

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-12 10:40:56 +01:00
f55cc2c4ee fix: Add anti-injection framing and structured templates to context compaction (#1823)
* fix: Add anti-injection framing and structured templates to context compaction (fixes #1818)

- Added COMPACTION_PREFIX constant with anti-injection framing to prevent model from treating summarized content as active instructions
- Added SUMMARY_TEMPLATE with structured sections for better context organization
- Enhanced CompactionConfig with compaction_prefix, structured_template, and iterative_update fields
- Updated _llm_summarize method to use anti-injection prefix and structured templates
- Added _build_structured_summary method to extract and categorize conversation content
- Added _merge_summaries method for iterative update capability
- Enhanced get_stats to include compaction configuration metadata
- Added _previous_summary tracking for iterative re-compaction
- Maintains backward compatibility with existing configurations

This prevents silent behavioral correctness failures where models resume stale tasks,
re-execute completed actions, or follow outdated instructions from compacted context.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

* fix: Resolve critical context compaction issues

- Fix 'do' keyword false positives using word boundaries
- Make CompactionConfig authoritative over constructor parameters
- Prevent accumulation of old compacted summaries
- Fix ellipsis truncation logic to only show when needed
- Move import re to module level for better performance

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Claude <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-12 10:36:24 +01:00
0f9425010f feat: implement LLM-driven context compression with session lineage (fixes #1806) (#1811)
* feat: Add intelligent conversation compaction with continuity preservation (fixes #1800)

Implements intelligent message compaction that preserves conversation continuity through:
- Structured summarization preserving goals, progress, decisions, and critical context
- Conversation flow maintenance with narrative progression
- Critical context preservation including tool results and user preferences
- Configurable compaction strategies (hybrid, rule-based, LLM-powered)

Technical implementation:
- New ConversationAnalyzer protocol for extracting conversation context
- New ConversationCompactor protocol for intelligent compaction
- HybridConversationAnalyzer with LLM fallback to rule-based analysis
- IntelligentConversationCompactor creating structured summaries
- ConversationOptimizer integrating with existing optimization pipeline
- Protocol-driven design following AGENTS.md architecture

Features:
- Rule-based analysis extracts topics, decisions, facts, action items
- LLM-powered analysis for deeper understanding when available
- Structured summary messages maintaining conversation continuity
- Configurable minimum compaction ratio and preservation strategies
- Lazy loading for zero performance impact when not used

Configuration:
- conversation_compaction: bool - Enable intelligent compaction
- conversation_analyzer_strategy: str - Analysis strategy selection
- conversation_min_compaction_ratio: float - Minimum compression threshold

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>

* fix: ConversationOptimizer kwargs compatibility and error logging

- Fix P0 TypeError in ConversationOptimizer.__init__ by accepting additional kwargs from ContextManager
- Add proper error logging with debug messages for LLM analysis failures
- Fix lint warnings for unused variables in test files

Addresses critical issues identified by code reviewers for conversation compaction feature.

Co-authored-by: Mervin Praison <[email protected]>

* feat: implement LLM-driven context compression with session lineage (fixes #1806)

- Add ContextCompressor class with intelligent head/tail protection
- Implement LLMContextCompressorOptimizer for enhanced summarization
- Improve tokenization with model-aware estimation and fallbacks
- Add session lineage tracking for compression traceability
- Create structured summaries preserving tasks, decisions, and file refs
- Provide robust fallback to deterministic summaries on LLM failure
- Include comprehensive test suite demonstrating compression capabilities

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Claude <[email protected]>

* fix: resolve critical bugs in context compression implementation

- Fix comment leak in LLM prompts (compressor.py:351)
- Fix LLM path bypass in optimizer (use configured compressor)
- Remove _compression_metadata from message dicts (API validation)
- Add parent_session_id parameter for session lineage tracking
- Fix determinism in fallback summaries (sorted collections)
- Fix f-string literals without placeholders (F541 errors)
- Improve async/sync compatibility in _summarize_with_llm

Addresses critical issues identified by Greptile, CodeRabbit, and other reviewers.

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
Co-authored-by: Claude <[email protected]>
Co-authored-by: MervinPraison <[email protected]>
2026-06-12 10:35:40 +01:00
222d4f68eb fix: Windows compatibility for live AI code editor smoke harness (fixes #1839) (#1847)
* fix: make CLI session store cross-platform compatible (fixes #1839)

- Fix PYTHONPATH to use os.pathsep for Windows compatibility
- Add UTF-8 encoding for subprocess operations
- Add --no-acp flag for Windows automation
- Fix pytest environment isolation issues
- Fix over-broad test selectors (use node IDs vs -k patterns)
- Create missing ai_code_editor_fixture directory with complete mathlib project
- Update documentation with Windows compatibility notes

Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>

* fix: address critical Windows compatibility issues in live test harness

- Add pythonpath = ['src'] to fixture pyproject.toml to fix ModuleNotFoundError
- Replace hardcoded 'python' with sys.executable for consistent interpreter usage
- Add UTF-8 encoding to all read_text() calls for Windows compatibility
- Update run_pytest to handle node IDs consistently with check_pytest_passes

Fixes all P0-P2 issues identified in code review.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-12 10:29:30 +01:00
MervinPraison aad319c8d5 docs: auto-update api.md [skip ci] 2026-06-12 09:29:18 +00:00
d51191d073 feat: add idempotency-aware loop guard for tool execution safety (#1834)
* feat: add idempotency-aware loop guard for tool execution safety

- Implement LoopGuard class extending DoomLoopDetector with tool classification
- Add always-on protection for standard agent.chat() path (not just autonomous mode)
- Classify tools as idempotent (safe to repeat) vs mutating (dangerous)
- Provide graduated response: warn → block → halt with per-tool-type thresholds
- Wire into existing AFTER_TOOL hook infrastructure for minimal disruption
- Reset turn tracking at chat entry points for accurate per-turn detection

Fixes #1831

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

* fix: resolve 6 critical loop guard bugs for production safety

- Fix dead code: _check_no_progress() now called from check() method
- Fix off-by-one: thresholds now trigger at correct counts with pre/post-execution context
- Fix state leakage: DoomLoopDetector state properly reset between chat turns
- Fix teardown bypass: BLOCK action now flows through normal completion events
- Fix retryable flip: non-retryable HALT exceptions preserved unchanged
- Fix lost warnings: WARN guidance now injected into dict/list results, not just strings
- Remove unused Literal import and fix docstring example arity

Addresses all critical issues identified by Greptile, CodeRabbit, and Copilot reviewers.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Claude <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-12 10:28:48 +01:00
MervinPraison 8cb0e52a95 docs: auto-update feature parity trackers [skip ci] 2026-06-12 09:27:40 +00:00
praisonai-triage-agent[bot]andMervin Praison 744ed799af feat: implement typed validation outcomes and agent run results (#1827)
* feat: implement typed validation outcomes and agent run results

Replace freeform string-based validation outcomes with typed discriminant
union system enabling exhaustive error handling across process.py,
handoff.py, and scheduler components.

Key changes:
- Add AgentRunOutcome dataclass with RunStatus literal type
- Enhance HandoffResult with typed outcome field and backward compatibility
- Update Process class validation logic to use typed outcomes
- Add validate_decision_string() for legacy compatibility
- Export new types from main package for public API access
- Comprehensive test coverage for new functionality

Fixes #1819

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve critical validation feedback regression and type safety issues

- Fix P1 validation feedback regression: flatten context fields to top level for legacy compatibility in both async/sync workflows
- Add runtime validation for RunStatus to prevent invalid values at construction time
- Handle non-string inputs defensively in validate_decision_string with proper error messages
- Move test files to proper taxonomy (unit/ vs integration/)
- Improve handoff factory method consistency to use typed factories with proper error categories

Addresses critical backward compatibility issue identified by Greptile and CodeRabbit reviewers where retry prompts would silently lose validated task and rejected output context.

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-12 10:27:12 +01:00
MervinPraison c88bd1eeea docs: auto-update api.md [skip ci] 2026-06-12 09:26:09 +00:00
praisonai-triage-agent[bot]andMervinPraison 7890723aa6 feat: add memory provider lifecycle hooks for compression and session events (#1826)
* feat: add memory provider lifecycle hooks for compression and session events

- Add 4 new optional lifecycle hook methods to AgentMemoryProtocol:
  - on_pre_compress(): Called before context compaction discards messages
  - on_session_switch(): Called when active session ID changes
  - on_memory_write(): Called when auto_memory writes to built-in memory
  - on_delegation(): Called after subagent completes delegated task

- Integrate hooks into existing codebase:
  - Call on_pre_compress in _apply_context_management before compression
  - Call on_memory_write in store_memory method
  - Call on_delegation in all handoff success paths (sync, async, tool)

- All hooks have no-op defaults for backward compatibility
- Enables custom memory backends to maintain coherence across agent lifecycle events
- Addresses silent data-loss scenarios for production agents with long session lifetimes

Fixes #1821

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve memory provider lifecycle hooks issues

Fixes identified by code reviewers:

1. Fix on_pre_compress hook to only fire when compression occurs
   - Move hook call after checking compression threshold
   - Prevents unnecessary hook execution on every turn

2. Fix store_memory to call hook after successful storage only
   - Add action parameter to support replace/remove operations
   - Validate operations before calling hooks
   - Pass correct action types to hooks

3. Add async hook variants for async contexts
   - Add aon_* async versions of all lifecycle hooks
   - Support both sync and async execution contexts

4. Fix @runtime_checkable protocol compatibility breaks
   - Move lifecycle hooks to separate AgentMemoryLifecycleProtocol
   - Preserve backward compatibility for existing memory providers
   - Use hasattr checks in call sites for optional hooks

5. Add session switch hook documentation note
   - Document that call sites need implementation

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-12 10:25:36 +01:00
MervinPraison 8567f44a4c docs: auto-update api.md [skip ci] 2026-06-12 09:24:04 +00:00
6d26ff96a9 feat: implement dynamic tool schema override mechanism (fixes #1807) (#1813)
* feat: implement dynamic tool schema override mechanism (fixes #1807)

Implements dynamic tool schema override capability allowing tools to
update their JSON schemas at runtime based on current configuration.

Changes:
- Add ToolEntry dataclass to store tools with optional schema overrides
- Add get_tool_definitions() method to ToolRegistry that applies overrides
- Update BaseTool to accept and apply dynamic_schema_overrides
- Update @tool decorator to support dynamic_schema_overrides parameter
- Update ToolRegistry.register() to accept schema override functions
- Add comprehensive test demonstrating runtime schema updates

Example usage:
```python
@tool(dynamic_schema_overrides=lambda schema: {
    **schema,
    "function": {
        **schema["function"],
        "description": f"Current limit: {runtime_config.max_concurrent}"
    }
})
def delegate_task(task: str, max_concurrent: int = 1):
    return f"Delegated: {task}"
```

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

* fix: critical dynamic schema override bugs and cleanup debug artifacts

- Fix shallow copy bug causing permanent mutation of tool parameters in both BaseTool and FunctionTool
- Fix registry-level dynamic_schema_overrides being ignored for BaseTool instances
- Remove 9 debug test artifacts from repo root (test_*.py files)
- Fix f-string linting issues (Ruff F541 errors)
- Add comprehensive validation test for all fixes

Fixes address critical issues identified by CodeRabbit, Greptile, and other reviewers.

Co-authored-by: Mervin Praison <[email protected]>

* cleanup: remove temporary validation test file

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: Claude <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-12 10:23:31 +01:00
MervinPraison 2dbc1bcbdf docs: auto-update feature parity trackers [skip ci] 2026-06-12 09:23:24 +00:00
praisonai-triage-agent[bot]andMervin Praison 197f8d73ca feat: add TypedHandoff with Pydantic schema validation (fixes #1810) (#1812)
* feat: add TypedHandoff with Pydantic schema validation (fixes #1810)

- Add HandoffValidationError exception for schema validation failures
- Implement TypedHandoff[T] class with generic Pydantic schema validation
- Validate payloads at handoff boundary and raise errors on schema mismatch
- Convert validated payloads to structured JSON instead of string concatenation
- Maintain full backward compatibility with existing Handoff class
- Support both sync and async execution with proper error handling
- Add lazy imports for optional Pydantic dependency

This prevents silent data contract violations between agents by ensuring
type safety at inter-agent handoff boundaries. Schema mismatches now
raise Python exceptions instead of producing wrong LLM output.

Co-authored-by: MervinPraison <[email protected]>

* fix: resolve critical TypedHandoff issues identified by reviewers

- Fix input_filter silently ignored in TypedHandoff execution paths (Greptile P1)
- Fix stale context dict after error patching in HandoffValidationError (Greptile P2)
- Fix lazy loader recursion issue by removing 'handoff' from _handoff_names (CodeRabbit)
- Fix exception chaining for TimeoutError in async execution (CodeRabbit)
- Improve HandoffValidationError with remediation hints
- Use PYDANTIC_AVAILABLE flag for cleaner type handling

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com>
Co-authored-by: MervinPraison <[email protected]>
2026-06-12 10:22:55 +01:00
DhivyaandDhivya-Bharathy ce97667156 fix(examples): resolve syntax errors and align with consolidated-params API (#1855)
Co-authored-by: Dhivya-Bharathy <[email protected]>
2026-06-08 07:48:44 +01:00
602 changed files with 68572 additions and 12063 deletions

No files matched your search

+47 -36
View File
@@ -151,24 +151,40 @@ jobs:
id: check_fork
uses: actions/github-script@v7
with:
github-token: ${{ steps.app-token.outputs.token }}
script: |
const isPR = context.eventName === 'pull_request' || (context.payload.issue && context.payload.issue.pull_request);
if (isPR) {
const prNumber = context.payload.pull_request ? context.payload.pull_request.number : context.payload.issue.number;
const pr = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber
});
core.setOutput('pr_branch', pr.data.head.ref);
if (pr.data.head.repo && pr.data.head.repo.full_name !== context.repo.full_name) {
core.setOutput('is_fork', 'true');
core.setOutput('clone_url', pr.data.head.repo.clone_url);
core.setOutput('branch', pr.data.head.ref);
return;
}
const isPR = context.eventName === 'pull_request'
|| context.eventName === 'pull_request_review'
|| context.eventName === 'pull_request_review_comment'
|| (context.payload.issue && context.payload.issue.pull_request);
if (!isPR) {
core.setOutput('is_pr', 'false');
core.setOutput('is_fork', 'false');
return;
}
const prNumber = context.payload.pull_request
? context.payload.pull_request.number
: context.payload.issue.number;
const pr = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber
});
core.setOutput('is_pr', 'true');
core.setOutput('pr_number', String(prNumber));
core.setOutput('pr_branch', pr.data.head.ref);
core.setOutput('head_repo', pr.data.head.repo.full_name);
core.setOutput('merge_state', (pr.data.mergeable_state || pr.data.merge_state_status || '').toUpperCase());
const baseRepo = `${context.repo.owner}/${context.repo.repo}`;
if (pr.data.head.repo && pr.data.head.repo.full_name !== baseRepo) {
core.setOutput('is_fork', 'true');
core.setOutput('clone_url', pr.data.head.repo.clone_url);
core.setOutput('branch', pr.data.head.ref);
return;
}
core.setOutput('is_fork', 'false');
@@ -180,17 +196,19 @@ jobs:
token: ${{ steps.app-token.outputs.token }}
- name: Fetch PR branch and Setup Remote
if: github.event.issue.pull_request
if: steps.check_fork.outputs.is_pr == 'true'
env:
PR_BRANCH: ${{ steps.check_fork.outputs.pr_branch }}
PR_NUMBER: ${{ steps.check_fork.outputs.pr_number }}
IS_FORK: ${{ steps.check_fork.outputs.is_fork }}
run: |
# Fetch PR head from base repo and put it into local branch
git fetch origin pull/${{ github.event.issue.number }}/head:"${PR_BRANCH}"
git fetch origin "pull/${PR_NUMBER}/head:${PR_BRANCH}"
git checkout "${PR_BRANCH}"
git fetch origin main
# If it's a fork, make origin point to local to trick claude-code-action's `git fetch origin <branch>`
if [ "${IS_FORK}" = "true" ]; then
git remote set-url origin file://$(pwd)
git remote set-url origin "file://$(pwd)"
fi
- uses: anthropics/claude-code-action@beta
@@ -200,11 +218,13 @@ jobs:
allowed_bots: 'praisonai-triage-agent[bot]'
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
github_token: ${{ steps.app-token.outputs.token }}
model: opus
trigger_phrase: "@claude"
label_trigger: "claude"
direct_prompt: |
${{ steps.check_fork.outputs.is_fork == 'true' && 'CRITICAL: THIS IS A PULL REQUEST FROM A FORK. YOU DO NOT HAVE PUSH PERMISSIONS TO THIS REPOSITORY. ONLY READ FILES, VALIDATE CODE, AND PROVIDE YOUR COMMENTS/FEEDBACK DIRECTLY IN THIS PR VIA COMMENTS. DO NOT ATTEMPT TO PUSH OR CREATE PULL REQUESTS.\n\n' || 'NOTE: The branch is under MervinPraison/PraisonAI (not a fork). You are able to make modifications and push directly to this branch.\n\n' }}
${{ steps.check_fork.outputs.is_fork == 'true' && 'CRITICAL: THIS IS A PULL REQUEST FROM A FORK. YOU DO NOT HAVE PUSH PERMISSIONS TO THIS REPOSITORY. ONLY READ FILES, VALIDATE CODE, AND PROVIDE YOUR COMMENTS/FEEDBACK DIRECTLY IN THIS PR VIA COMMENTS. DO NOT ATTEMPT TO PUSH OR CREATE PULL REQUESTS.\n\n' || (steps.check_fork.outputs.is_pr == 'true' && 'NOTE: The branch is under MervinPraison/PraisonAI (not a fork). You are able to make modifications and push directly to this branch.\n\n' || 'NOTE: This is an issue (not a PR). Create a new fix branch per STEP 3 below.\n\n') }}
${{ steps.check_fork.outputs.is_pr == 'true' && format('CONTEXT: PR #{0} on branch {1}. head.repo={2}, is_fork={3}, merge_state={4}. NEVER treat cursor/*, claude/* branch names or bot authors as forks — only is_fork=true means fork.\n\n', steps.check_fork.outputs.pr_number, steps.check_fork.outputs.pr_branch, steps.check_fork.outputs.head_repo, steps.check_fork.outputs.is_fork, steps.check_fork.outputs.merge_state) || '' }}
You are working on the PraisonAI SDK. Follow AGENTS.md strictly.
STEP 0 — SETUP GIT IDENTITY & AUTH (GLOBAL — required for all repos):
@@ -233,15 +253,9 @@ jobs:
- Is there a name collision with existing exports in __init__.py?
If ANY of these conceptual checks fail (excluding routing), add a comment to the issue explaining why and close it. Do NOT create a PR.
STEP 3 — IMPLEMENT (for changes in THIS repo — PraisonAI):
- Create a fix branch: git checkout -b claude/issue-$ISSUE_NUMBER-$(date +%Y%m%d)
- Implement a minimal, focused fix
- Follow protocol-driven design: protocols in core SDK, heavy implementations in wrapper
- Keep changes small and backward-compatible
- Commit: git add -A && git commit -m "fix: <description> (fixes #$ISSUE_NUMBER)"
- Push: git push origin claude/issue-$ISSUE_NUMBER-$(date +%Y%m%d)
CRITICAL: After pushing, you MUST immediately run: gh pr create --title "fix: <title>" --body "Fixes #$ISSUE_NUMBER" --head claude/issue-$ISSUE_NUMBER-$(date +%Y%m%d)
Do NOT stop after pushing. Do NOT say "manually create a PR". Always create the PR automatically.
${{ steps.check_fork.outputs.is_pr == 'true' && steps.check_fork.outputs.is_fork != 'true' && steps.check_fork.outputs.merge_state == 'DIRTY' && format('STEP 3 — RESOLVE MERGE CONFLICTS ON PR (branch: {0}, PR #{1}):\n- DO NOT create a new branch or PR\n- git fetch origin main && git rebase origin/main\n- Resolve conflicts: keep this PR intent, merge in main newer logic\n- git add -A && git rebase --continue (repeat until done)\n- git push --force-with-lease origin {0}\n- Run targeted pytest on changed paths\n- Comment files resolved and what you skipped\n', steps.check_fork.outputs.pr_branch, steps.check_fork.outputs.pr_number) || '' }}
${{ steps.check_fork.outputs.is_pr == 'true' && steps.check_fork.outputs.is_fork != 'true' && steps.check_fork.outputs.merge_state != 'DIRTY' && format('STEP 3 — IMPLEMENT ON EXISTING PR (branch: {0}, PR #{1}):\n- DO NOT create a new branch or PR\n- git checkout {0}\n- Implement minimal, focused fixes from reviewer feedback\n- Follow protocol-driven design: protocols in core SDK, heavy implementations in wrapper\n- Keep changes small and backward-compatible\n- Commit: git add -A && git commit -m "fix: <description>"\n- Push: git push origin {0}\n- Comment a summary of exact files modified and what you skipped\n', steps.check_fork.outputs.pr_branch, steps.check_fork.outputs.pr_number) || '' }}
${{ steps.check_fork.outputs.is_pr != 'true' && 'STEP 3 — IMPLEMENT (for changes in THIS repo — PraisonAI):\n- Create a fix branch: git checkout -b claude/issue-$ISSUE_NUMBER-$(date +%Y%m%d)\n- Implement a minimal, focused fix\n- Follow protocol-driven design: protocols in core SDK, heavy implementations in wrapper\n- Keep changes small and backward-compatible\n- Commit: git add -A && git commit -m "fix: <description> (fixes #$ISSUE_NUMBER)"\n- Push: git push origin claude/issue-$ISSUE_NUMBER-$(date +%Y%m%d)\nCRITICAL: After pushing, you MUST immediately run: gh pr create --title "fix: <title>" --body "Fixes #$ISSUE_NUMBER" --head claude/issue-$ISSUE_NUMBER-$(date +%Y%m%d)\nDo NOT stop after pushing. Do NOT say "manually create a PR". Always create the PR automatically.\n' || '' }}
STEP 3-ALT — IMPLEMENT IN EXTERNAL REPO (for PraisonAI-Tools, PraisonAIDocs, PraisonAI-Plugins):
When work must happen in a different repository, follow these steps EXACTLY. Do NOT attempt to use `cd`, as directory state is not preserved. Use `git -C` for all commands.
@@ -267,10 +281,7 @@ jobs:
- For docs changes: verify files exist and are valid markdown/MDX
- Ensure no regressions
STEP 5 — CREATE PR:
- Commit with descriptive message, push, and create PR using `gh pr create`
- For external repos, use `gh pr create -R MervinPraison/<REPO_NAME>`
CRITICAL: You MUST create the PR automatically using `gh pr create`. Do NOT just provide a link or say "manual push required".
${{ steps.check_fork.outputs.is_pr == 'true' && 'STEP 5 — SKIP: Already on an open PR. Do NOT run gh pr create.\n' || 'STEP 5 — CREATE PR:\n- Commit with descriptive message, push, and create PR using `gh pr create`\n- For external repos, use `gh pr create -R MervinPraison/<REPO_NAME>`\nCRITICAL: You MUST create the PR automatically using `gh pr create`. Do NOT just provide a link or say "manual push required".\n' }}
NOTE: If you worked on an external repo, the wrapper action will report "No commits" for the main repo at the end. This is expected, do not worry about it.
allowed_tools: |
Bash(git:*)
-2
View File
@@ -22,8 +22,6 @@ jobs:
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
@@ -36,9 +36,6 @@ jobs:
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
token: ${{ steps.generate_token.outputs.token }}
- name: Run Gemini Issue Triage
@@ -37,9 +37,6 @@ jobs:
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
token: ${{ steps.generate_token.outputs.token }}
- name: Set up Git
-3
View File
@@ -48,9 +48,6 @@ jobs:
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
token: ${{ steps.generate_token.outputs.token }}
ref: ${{ steps.checkout_ref.outputs.ref }}
fetch-depth: 0
+4 -4
View File
@@ -60,16 +60,16 @@ jobs:
- name: Run PraisonAI integration tests
run: |
cd PraisonAI/src/praisonai
python -m pytest tests/integration/test_aiui_* -v --timeout=300
python -m pytest tests/integration/test_aiui_* -v
- name: Run PraisonAIUI integration tests
run: |
cd PraisonAIUI
if [ -f tests/integration/test_agentic_roundtrip.py ]; then
python -m pytest tests/integration/test_agentic_roundtrip.py -v --timeout=300
python -m pytest tests/integration/test_agentic_roundtrip.py -v
fi
if [ -f tests/test_feature_sdk_backends.py ]; then
python -m pytest tests/test_feature_sdk_backends.py -v --timeout=300
python -m pytest tests/test_feature_sdk_backends.py -v
fi
- name: Test Pattern C CLI
@@ -152,7 +152,7 @@ jobs:
run: |
cd PraisonAI/src/praisonai
if [ -n "$OPENAI_API_KEY" ]; then
python -m pytest tests/integration/test_aiui_host_agentic.py -v --timeout=600
python -m pytest tests/integration/test_aiui_host_agentic.py -v
else
echo "OPENAI_API_KEY not available, skipping agentic tests"
fi
+2 -2
View File
@@ -15,8 +15,8 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
+3 -12
View File
@@ -26,11 +26,8 @@ jobs:
# First checkout the upstream repo at the specified tag
- name: Checkout upstream repo
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
persist-credentials: false
repository: MervinPraison/PraisonAI
ref: ${{ github.event.inputs.tag }}
fetch-depth: 0
@@ -48,11 +45,8 @@ jobs:
# Now checkout the fork at main branch
- name: Checkout fork
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
persist-credentials: false
repository: ${{ github.event.inputs.fork_owner }}/PraisonAI
ref: main
fetch-depth: 0
@@ -449,11 +443,8 @@ jobs:
# First checkout the fork's main branch
- name: Checkout fork repo
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
persist-credentials: false
fetch-depth: 0
token: ${{ secrets.PAT_TOKEN }}
repository: ${{ github.event.inputs.fork_owner }}/PraisonAI
@@ -0,0 +1,31 @@
name: Sync Claude Secrets to PraisonAIUI
on:
workflow_dispatch:
permissions:
contents: read
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Sync secrets to PraisonAIUI
env:
GH_TOKEN: ${{ secrets.SECRETS_ADMIN_PAT }}
TARGET: MervinPraison/PraisonAIUI
CLAUDE_APP_ID: ${{ secrets.CLAUDE_APP_ID }}
CLAUDE_APP_PRIVATE_KEY: ${{ secrets.CLAUDE_APP_PRIVATE_KEY }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GH_TOKEN_VALUE: ${{ secrets.GH_TOKEN }}
run: |
set -euo pipefail
if [ -z "${GH_TOKEN:-}" ]; then
echo "SECRETS_ADMIN_PAT is not configured in this repository"
exit 1
fi
echo -n "$CLAUDE_APP_ID" | gh secret set CLAUDE_APP_ID --repo "$TARGET"
echo -n "$CLAUDE_APP_PRIVATE_KEY" | gh secret set CLAUDE_APP_PRIVATE_KEY --repo "$TARGET"
echo -n "$CLAUDE_CODE_OAUTH_TOKEN" | gh secret set CLAUDE_CODE_OAUTH_TOKEN --repo "$TARGET"
echo -n "$GH_TOKEN_VALUE" | gh secret set GH_TOKEN --repo "$TARGET"
echo "Synced CLAUDE_APP_ID, CLAUDE_APP_PRIVATE_KEY, CLAUDE_CODE_OAUTH_TOKEN, GH_TOKEN → $TARGET"
+2 -4
View File
@@ -37,10 +37,8 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
+2 -2
View File
@@ -22,8 +22,8 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
+4 -8
View File
@@ -13,10 +13,8 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v5
@@ -81,10 +79,8 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v5
+2 -4
View File
@@ -33,10 +33,8 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
+16 -16
View File
@@ -45,8 +45,8 @@ jobs:
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
@@ -105,8 +105,8 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
@@ -187,8 +187,8 @@ jobs:
fi
- uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
if: steps.check-key.outputs.has_key == 'true'
- name: Set up Python 3.11
@@ -259,8 +259,8 @@ jobs:
fi
- uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
if: steps.check-key.outputs.has_key == 'true'
- name: Set up Python 3.11
@@ -318,8 +318,8 @@ jobs:
fi
- uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
if: steps.check-key.outputs.has_key == 'true'
- name: Set up Python 3.11
@@ -377,8 +377,8 @@ jobs:
fi
- uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
if: steps.check-key.outputs.has_key == 'true'
- name: Set up Python 3.11
@@ -436,8 +436,8 @@ jobs:
fi
- uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
if: steps.check-key.outputs.has_key == 'true'
- name: Set up Python 3.11
@@ -495,8 +495,8 @@ jobs:
fi
- uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
if: steps.check-key.outputs.has_key == 'true'
- name: Set up Python 3.11
+2 -4
View File
@@ -45,10 +45,8 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
+3
View File
@@ -88,6 +88,9 @@ issue_details.json
.internal
.praisonai
# Backup files
*.bak
# Rust / Cargo
target/
Cargo.lock
+10
View File
@@ -0,0 +1,10 @@
# Agent Instructions
You are working on the PraisonAI project.
## Project Guidelines
- Follow the existing code style and conventions
- Be concise and helpful in responses
- Test implementation thoroughly
- Ensure backward compatibility with existing APIs
- Follow protocol-driven design: core protocols in praisonaiagents/, heavy implementations in praisonai/
+14
View File
@@ -0,0 +1,14 @@
# Contributing to PraisonAI
Thank you for your interest! Here's how to get started:
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Submit a pull request
## Guidelines
- Follow existing code style
- Add tests for new features
- Update documentation as needed
+2 -2
View File
@@ -770,11 +770,11 @@ agent.start('Write a movie script about a robot in Mars');
## ⚡ Performance
PraisonAI is built for speed, with agent instantiation in under 4μs. This reduces overhead, improves responsiveness, and helps multi-agent systems scale efficiently in real-world production workloads.
PraisonAI is built for speed, with agent instantiation in around 14μs. This reduces overhead, improves responsiveness, and helps multi-agent systems scale efficiently in real-world production workloads.
| Performance Metric | PraisonAI |
|--------------------|-----------|
| Avg Instantiation Time | **3.77 μs** |
| Avg Instantiation Time | **14 μs** |
---
+60 -12
View File
@@ -15,6 +15,7 @@ Methods:
* <code title="class GuardrailResult">GuardrailResult.<a href="./src/praisonai-agents/praisonaiagents/guardrails/guardrail_result.py">from_tuple</a>(result: Tuple[bool, Any]) -> 'GuardrailResult'</code>
* <code title="class HandoffConfig">HandoffConfig.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">from_dict</a>(data: Dict[str, Any]) -> 'HandoffConfig'</code>
* <code title="class HandoffConfig">HandoffConfig.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">to_dict</a>() -> Dict[str, Any]</code>
* <code title="class HandoffResult">HandoffResult.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">from_outcome</a>(outcome: AgentRunOutcome, target_agent: Optional[str] = None, source_agent: Optional[str] = None, handoff_depth: int = 0) -> 'HandoffResult'</code>
* <code title="class TaskOutput">TaskOutput.<a href="./src/praisonai-agents/praisonaiagents/main.py">json</a>() -> Optional[str]</code>
* <code title="class TaskOutput">TaskOutput.<a href="./src/praisonai-agents/praisonaiagents/main.py">to_dict</a>() -> dict</code>
* <code title="class ToolResult">ToolResult.<a href="./src/praisonai-agents/praisonaiagents/tools/base.py">to_dict</a>() -> Dict[str, Any]</code>
@@ -58,6 +59,8 @@ Methods:
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">handoff_to</a>(target_agent: 'Agent', prompt: str, context: Optional[Dict[str, Any]] = None, config: Optional['HandoffConfig'] = None) -> 'HandoffResult'</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">handoff_to_async</a>(target_agent: 'Agent', prompt: str, context: Optional[Dict[str, Any]] = None, config: Optional['HandoffConfig'] = None) -> 'HandoffResult'</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">is_closed</a>() -> bool</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">llm_instance</a>()</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">llm_instance</a>(value)</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">llm_model</a>() -> Optional[str]</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">output_style</a>() -> Optional[str]</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">output_style</a>(value: Optional[str]) -> None</code>
@@ -75,7 +78,7 @@ Methods:
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">run_until</a>(prompt: str, criteria: str, threshold: float = 8.0, max_iterations: int = 5, mode: str = 'optimize', on_iteration: Optional[Callable[[Any], None]] = None, verbose: bool = False) -> 'EvaluationLoopResult'</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">run_until_async</a>(prompt: str, criteria: str, threshold: float = 8.0, max_iterations: int = 5, mode: str = 'optimize', on_iteration: Optional[Callable[[Any], None]] = None, verbose: bool = False) -> 'EvaluationLoopResult'</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">skill_manager</a>() -> Optional[Any]</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">store_memory</a>(content: str, memory_type: str = 'short_term', **kwargs: Any) -> None</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">store_memory</a>(content: str, memory_type: str = 'short_term', action: str = 'add', **kwargs: Any) -> None</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">stream_emitter</a>() -> Optional[Any]</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">stream_emitter</a>(value: Optional[Any]) -> None</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">thinking_budget</a>() -> Optional[int]</code>
@@ -175,6 +178,7 @@ Methods:
* <code title="class BaseTool">BaseTool.<a href="./src/praisonai-agents/praisonaiagents/tools/base.py">validate_schema_roundtrip</a>() -> bool</code>
* <code title="class FunctionTool">FunctionTool.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">__call__</a>(*args, **kwargs) -> Any</code>
* <code title="class FunctionTool">FunctionTool.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">check_availability</a>() -> tuple[bool, str]</code>
* <code title="class FunctionTool">FunctionTool.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">get_schema</a>() -> Dict[str, Any]</code>
* <code title="class FunctionTool">FunctionTool.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">injected_params</a>() -> Dict[str, Any]</code>
* <code title="class FunctionTool">FunctionTool.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">run</a>(**kwargs) -> Any</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">clear</a>() -> None</code>
@@ -182,16 +186,18 @@ Methods:
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">discover_single_file_plugins</a>() -> int</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get</a>(name: str) -> Optional[Union[BaseTool, Callable]]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get_all</a>() -> Dict[str, Union[BaseTool, Callable]]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">list_available_tools</a>(context: Optional[Dict[str, Any]] = None) -> List[Union[BaseTool, Callable]]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get_tool_definitions</a>() -> List[Dict[str, Any]]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get_trust_level</a>(name: str) -> Optional[str]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">list_available_tools</a>(context: Optional[Dict[str, Any]] = None, ttl_seconds: Optional[float] = None) -> List[Union[BaseTool, Callable]]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">list_base_tools</a>() -> List[BaseTool]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">list_tools</a>() -> List[str]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">list_tools_with_allowed_filter</a>(context: Optional[Dict[str, Any]] = None) -> List[str]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">register</a>(tool: Union[BaseTool, Callable], name: Optional[str] = None, overwrite: bool = False) -> None</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">register</a>(tool: Union[BaseTool, Callable], name: Optional[str] = None, overwrite: bool = False, trust_level: Optional[str] = None, dynamic_schema_overrides: Optional[Callable[[Dict[str, Any]], Dict[str, Any]]] = None) -> None</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">unregister</a>(name: str) -> bool</code>
* <code title="class Tools">Tools.<a href="./src/praisonai-agents/praisonaiagents/tools/tools.py">internet_search</a>(*args, **kwargs)</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get_registry</a>() -> ToolRegistry</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get_tool</a>(name: str) -> Optional[Union[BaseTool, Callable]]</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">register_tool</a>(tool: Union[BaseTool, Callable], name: Optional[str] = None) -> None</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">register_tool</a>(tool: Union[BaseTool, Callable], name: Optional[str] = None, trust_level: Optional[str] = None, dynamic_schema_overrides: Optional[Callable[[Dict[str, Any]], Dict[str, Any]]] = None) -> None</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">tool</a>(func: Optional[Callable] = None) -> Union[FunctionTool, Callable[[Callable], FunctionTool]]</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/base.py">validate_tool</a>(tool: Any) -> bool</code>
@@ -236,6 +242,7 @@ from praisonaiagents import Memory, MemoryBackend, MemoryConfig
Methods:
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">build_cache_optimized_context</a>(task_descr: str, user_id: Optional[str] = None, additional: str = '', max_items: int = 3, include_cache_boundary: bool = True, include_in_output: Optional[bool] = None) -> Dict[str, str]</code>
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">build_context_for_task</a>(task_descr: str, user_id: Optional[str] = None, additional: str = '', max_items: int = 3, include_in_output: Optional[bool] = None) -> str</code>
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">calculate_quality_metrics</a>(output: str, expected_output: str, llm: Optional[str] = None, custom_prompt: Optional[str] = None) -> Dict[str, float]</code>
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">close_connections</a>()</code>
@@ -353,7 +360,7 @@ Methods:
* <code title="class Handoff">Handoff.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">to_tool_function</a>(source_agent: 'Agent') -> Callable</code>
* <code title="class Handoff">Handoff.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">tool_description</a>() -> str</code>
* <code title="class Handoff">Handoff.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">tool_name</a>() -> str</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">handoff</a>(agent: 'Agent', tool_name_override: Optional[str] = None, tool_description_override: Optional[str] = None, on_handoff: Optional[Callable] = None, input_type: Optional[type] = None, input_filter: Optional[Callable[[HandoffInputData], HandoffInputData]] = None, config: Optional[HandoffConfig] = None, context_policy: Optional[str] = None, timeout_seconds: Optional[float] = None, max_concurrent: Optional[int] = None, detect_cycles: Optional[bool] = None, max_depth: Optional[int] = None) -> Handoff</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">handoff</a>(agent: 'Agent', tool_name_override: Optional[str] = None, tool_description_override: Optional[str] = None, on_handoff: Optional[Callable] = None, input_type: Optional[type] = None, input_filter: Optional[Callable[[HandoffInputData], HandoffInputData]] = None, config: Optional[HandoffConfig] = None, context_policy: Optional[str] = None, timeout_seconds: Optional[float] = None, max_concurrent: Optional[int] = None, detect_cycles: Optional[bool] = None, max_depth: Optional[int] = None, tool_policy_mode: Optional[Literal['intersect', 'passthrough']] = None, blocked_tools: Optional[List[str]] = None) -> Handoff</code>
* <code title="class handoff_filters">handoff_filters.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">compress_history</a>(data: HandoffInputData) -> HandoffInputData</code>
* <code title="class handoff_filters">handoff_filters.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">keep_last_n_messages</a>(n: int) -> Callable[[HandoffInputData], HandoffInputData]</code>
* <code title="class handoff_filters">handoff_filters.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">remove_all_tools</a>(data: HandoffInputData) -> HandoffInputData</code>
@@ -511,10 +518,12 @@ Methods:
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">add_memory</a>(text: str, memory_type: str = 'long', **metadata) -> None</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">chat</a>(message: str, **kwargs) -> str</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">clear_memory</a>(memory_type: str = 'all') -> None</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">close</a>() -> None</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">create_agent</a>(*args, **kwargs) -> 'Agent'</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">get_context</a>(query: str, max_items: int = 3) -> str</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">get_state</a>(key: str, default: Any = None) -> Any</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">increment_state</a>(key: str, increment: int = 1, default: int = 0) -> None</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">is_expired</a>() -> bool</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">knowledge</a>() -> 'Knowledge'</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">memory</a>() -> 'Memory'</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">restore_state</a>() -> Dict[str, Any]</code>
@@ -523,6 +532,7 @@ Methods:
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">search_memory</a>(query: str, memory_type: str = 'long', limit: int = 5) -> List[Dict[str, Any]]</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">send_message</a>(message: str, **kwargs) -> str</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">set_state</a>(key: str, value: Any) -> None</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">time_to_expiry</a>() -> Optional[float]</code>
# MCP
@@ -605,7 +615,7 @@ Methods:
* <code title="class ContextManager">ContextManager.<a href="./src/praisonai-agents/praisonaiagents/context/manager.py">register_snapshot_callback</a>(callback: Callable[[SnapshotHookData], None]) -> None</code>
* <code title="class ContextManager">ContextManager.<a href="./src/praisonai-agents/praisonaiagents/context/manager.py">reset</a>() -> None</code>
* <code title="class ContextManager">ContextManager.<a href="./src/praisonai-agents/praisonaiagents/context/manager.py">set_tool_budget</a>(tool_name: str, max_tokens: int, protected: bool = False) -> None</code>
* <code title="class ContextManager">ContextManager.<a href="./src/praisonai-agents/praisonaiagents/context/manager.py">truncate_tool_output</a>(tool_name: str, output: str) -> str</code>
* <code title="class ContextManager">ContextManager.<a href="./src/praisonai-agents/praisonaiagents/context/manager.py">truncate_tool_output</a>(tool_name: str, output: str, tool_call_id: str = None, run_id: str = None) -> str</code>
# UI
@@ -626,6 +636,7 @@ Methods:
* <code title="class AutonomyConfig">AutonomyConfig.<a href="./src/praisonai-agents/praisonaiagents/agent/autonomy.py">effective_track_changes</a>() -> bool</code>
* <code title="class AutonomyConfig">AutonomyConfig.<a href="./src/praisonai-agents/praisonaiagents/agent/autonomy.py">from_dict</a>(data: Dict[str, Any]) -> 'AutonomyConfig'</code>
* <code title="class CachingConfig">CachingConfig.<a href="./src/praisonai-agents/praisonaiagents/config/feature_configs.py">to_dict</a>() -> Dict[str, Any]</code>
* <code title="class ExecutionConfig">ExecutionConfig.<a href="./src/praisonai-agents/praisonaiagents/config/feature_configs.py">from_dict</a>(data: Dict[str, Any]) -> 'ExecutionConfig'</code>
* <code title="class ExecutionConfig">ExecutionConfig.<a href="./src/praisonai-agents/praisonaiagents/config/feature_configs.py">to_dict</a>() -> Dict[str, Any]</code>
* <code title="class GuardrailConfig">GuardrailConfig.<a href="./src/praisonai-agents/praisonaiagents/config/feature_configs.py">to_dict</a>() -> Dict[str, Any]</code>
* <code title="class HooksConfig">HooksConfig.<a href="./src/praisonai-agents/praisonaiagents/config/feature_configs.py">to_dict</a>() -> Dict[str, Any]</code>
@@ -693,7 +704,7 @@ Methods:
Types:
```python
from praisonaiagents import AgentAppConfig, AgentAppProtocol, AgentFlow, AgentManager, AgentOSConfig, AgentOSProtocol, AgentTeam, AutoApproveBackend, EmbeddingResult, aembedding, aembeddings, embedding, embeddings, get_dimensions
from praisonaiagents import AgentAppConfig, AgentAppProtocol, AgentFlow, AgentManager, AgentOSConfig, AgentOSProtocol, AgentTeam, AutoApproveBackend, EmbeddingResult, RetryBackoffConfig, __version__, aembedding, aembeddings, embedding, embeddings, get_dimensions
```
Methods:
@@ -718,12 +729,16 @@ Methods:
* <code title="class AgentFlow">AgentFlow.<a href="./src/praisonai-agents/praisonaiagents/workflows/workflows.py">verbose</a>(value: bool)</code>
* <code title="class AgentOSProtocol">AgentOSProtocol.<a href="./src/praisonai-agents/praisonaiagents/app/protocols.py">get_app</a>() -> Any</code>
* <code title="class AgentOSProtocol">AgentOSProtocol.<a href="./src/praisonai-agents/praisonaiagents/app/protocols.py">serve</a>(host: Optional[str] = None, port: Optional[int] = None, reload: bool = False, **kwargs: Any) -> None</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">aannounce_completion</a>(agent_id: str, task_id: str, result: Any, success: bool = True, error: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None) -> None</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">add_task</a>(task)</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">aexecute_task</a>(task_id)</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">announce_completion</a>(agent_id: str, task_id: str, result: Any, success: bool = True, error: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None) -> None</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">append_to_state</a>(key: str, value: Any, max_length: Optional[int] = None) -> List[Any]</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">arun_all_tasks</a>()</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">arun_task</a>(task_id)</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">aspawn_sub_agent</a>(agent: Agent, task: Any, completion_callback: Optional[Callable[[SubAgentCompletionEvent], Any]] = None, metadata: Optional[Dict[str, Any]] = None) -> SpawnedSubAgent</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">astart</a>(content = None, return_dict = False, **kwargs)</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">await_for_completions</a>(timeout: Optional[float] = None, agent_ids: Optional[List[str]] = None) -> List[SubAgentCompletionEvent]</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">clean_json_output</a>(output: str) -> str</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">clear_state</a>() -> None</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">close</a>() -> None</code>
@@ -738,6 +753,7 @@ Methods:
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_all_tasks_status</a>()</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_detailed_token_report</a>() -> Dict[str, Any]</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_plan_markdown</a>() -> str</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_spawned_agents</a>() -> List[SpawnedSubAgent]</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_state</a>(key: str, default: Any = None) -> Any</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_task_details</a>(task_id)</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_task_result</a>(task_id)</code>
@@ -754,10 +770,12 @@ Methods:
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">save_output_to_file</a>(task, task_output)</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">save_session_state</a>(session_id: str, include_memory: bool = True) -> None</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">set_state</a>(key: str, value: Any) -> None</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">spawn_sub_agent</a>(agent: Agent, task: Any, completion_callback: Optional[Callable[[SubAgentCompletionEvent], Any]] = None, metadata: Optional[Dict[str, Any]] = None) -> SpawnedSubAgent</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">start</a>(content = None, return_dict = False, output = None, **kwargs)</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">todo_list</a>()</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">update_plan_step_status</a>(step_id: str, status: str) -> bool</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">update_state</a>(updates: Dict) -> None</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">wait_for_completions</a>(timeout: Optional[float] = None, agent_ids: Optional[List[str]] = None) -> List[SubAgentCompletionEvent]</code>
* <code title="class AutoApproveBackend">AutoApproveBackend.<a href="./src/praisonai-agents/praisonaiagents/approval/backends.py">request_approval</a>(request: ApprovalRequest) -> ApprovalDecision</code>
* <code title="class AutoApproveBackend">AutoApproveBackend.<a href="./src/praisonai-agents/praisonaiagents/approval/backends.py">request_approval_sync</a>(request: ApprovalRequest) -> ApprovalDecision</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/embedding/embed.py">aembedding</a>(input: Union[str, List[str]], model: str = 'text-embedding-3-small', dimensions: Optional[int] = None, encoding_format: str = 'float', timeout: float = 600.0, api_key: Optional[str] = None, api_base: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None, **kwargs) -> EmbeddingResult</code>
@@ -777,11 +795,17 @@ Methods:
* <code title="cli">praisonai <a href="./src/praisonai/praisonai/cli/main.py">--help</a></code>
* <code title="cli">praisonai acp acp-main <a href="./src/praisonai/praisonai/cli/commands/acp.py">--help</a></code>
* <code title="cli">praisonai agent list <a href="./src/praisonai/praisonai/cli/commands/agent.py">--help</a></code>
* <code title="cli">praisonai agent show <a href="./src/praisonai/praisonai/cli/commands/agent.py">--help</a></code>
* <code title="cli">praisonai agents create <a href="./src/praisonai/praisonai/cli/commands/agents.py">--help</a></code>
* <code title="cli">praisonai agents info <a href="./src/praisonai/praisonai/cli/commands/agents.py">--help</a></code>
* <code title="cli">praisonai agents list <a href="./src/praisonai/praisonai/cli/commands/agents.py">--help</a></code>
* <code title="cli">praisonai app app <a href="./src/praisonai/praisonai/cli/commands/app.py">--help</a></code>
* <code title="cli">praisonai audit agent-centric <a href="./src/praisonai/praisonai/cli/commands/audit.py">--help</a></code>
* <code title="cli">praisonai auth list <a href="./src/praisonai/praisonai/cli/commands/auth.py">--help</a></code>
* <code title="cli">praisonai auth login <a href="./src/praisonai/praisonai/cli/commands/auth.py">--help</a></code>
* <code title="cli">praisonai auth logout <a href="./src/praisonai/praisonai/cli/commands/auth.py">--help</a></code>
* <code title="cli">praisonai auth status <a href="./src/praisonai/praisonai/cli/commands/auth.py">--help</a></code>
* <code title="cli">praisonai batch batch-run <a href="./src/praisonai/praisonai/cli/commands/batch.py">--help</a></code>
* <code title="cli">praisonai batch list <a href="./src/praisonai/praisonai/cli/commands/batch.py">--help</a></code>
* <code title="cli">praisonai batch report <a href="./src/praisonai/praisonai/cli/commands/batch.py">--help</a></code>
@@ -822,6 +846,8 @@ Methods:
* <code title="cli">praisonai claw claw <a href="./src/praisonai/praisonai/cli/commands/claw.py">--help</a></code>
* <code title="cli">praisonai code <a href="./src/praisonai/praisonai/cli/main.py">--help</a></code>
* <code title="cli">praisonai code code-main <a href="./src/praisonai/praisonai/cli/commands/code.py">--help</a></code>
* <code title="cli">praisonai command list <a href="./src/praisonai/praisonai/cli/commands/command.py">--help</a></code>
* <code title="cli">praisonai command show <a href="./src/praisonai/praisonai/cli/commands/command.py">--help</a></code>
* <code title="cli">praisonai commit commit-main <a href="./src/praisonai/praisonai/cli/commands/commit.py">--help</a></code>
* <code title="cli">praisonai completion bash <a href="./src/praisonai/praisonai/cli/commands/completion.py">--help</a></code>
* <code title="cli">praisonai completion completion-callback <a href="./src/praisonai/praisonai/cli/commands/completion.py">--help</a></code>
@@ -834,6 +860,9 @@ Methods:
* <code title="cli">praisonai config path <a href="./src/praisonai/praisonai/cli/commands/config.py">--help</a></code>
* <code title="cli">praisonai config reset <a href="./src/praisonai/praisonai/cli/commands/config.py">--help</a></code>
* <code title="cli">praisonai config set <a href="./src/praisonai/praisonai/cli/commands/config.py">--help</a></code>
* <code title="cli">praisonai config show <a href="./src/praisonai/praisonai/cli/commands/config.py">--help</a></code>
* <code title="cli">praisonai config sources <a href="./src/praisonai/praisonai/cli/commands/config.py">--help</a></code>
* <code title="cli">praisonai config validate <a href="./src/praisonai/praisonai/cli/commands/config.py">--help</a></code>
* <code title="cli">praisonai context add <a href="./src/praisonai/praisonai/cli/commands/context.py">--help</a></code>
* <code title="cli">praisonai context clear <a href="./src/praisonai/praisonai/cli/commands/context.py">--help</a></code>
* <code title="cli">praisonai context compact <a href="./src/praisonai/praisonai/cli/commands/context.py">--help</a></code>
@@ -864,17 +893,18 @@ Methods:
* <code title="cli">praisonai docs serve <a href="./src/praisonai/praisonai/cli/commands/docs.py">--help</a></code>
* <code title="cli">praisonai docs stats <a href="./src/praisonai/praisonai/cli/commands/docs.py">--help</a></code>
* <code title="cli">praisonai doctor cleanup <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor config <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor db <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor doctor-callback <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor docker <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor env <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor llm-providers <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor mcp <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor memory-store <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor metadata-store <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor network <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor packaging <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor performance <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor runtime <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor selftest <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor skills <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor tools <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor troubleshoot <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai endpoints list <a href="./src/praisonai/praisonai/cli/commands/endpoints.py">--help</a></code>
* <code title="cli">praisonai endpoints test <a href="./src/praisonai/praisonai/cli/commands/endpoints.py">--help</a></code>
@@ -902,6 +932,9 @@ Methods:
* <code title="cli">praisonai gateway install <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway logs <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway mint-link <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway pause <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway reconnect <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway resume <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway send <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway start <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway status <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
@@ -975,6 +1008,9 @@ Methods:
* <code title="cli">praisonai memory search <a href="./src/praisonai/praisonai/cli/commands/memory.py">--help</a></code>
* <code title="cli">praisonai memory show <a href="./src/praisonai/praisonai/cli/commands/memory.py">--help</a></code>
* <code title="cli">praisonai memory status <a href="./src/praisonai/praisonai/cli/commands/memory.py">--help</a></code>
* <code title="cli">praisonai models describe <a href="./src/praisonai/praisonai/cli/commands/models.py">--help</a></code>
* <code title="cli">praisonai models list <a href="./src/praisonai/praisonai/cli/commands/models.py">--help</a></code>
* <code title="cli">praisonai models validate <a href="./src/praisonai/praisonai/cli/commands/models.py">--help</a></code>
* <code title="cli">praisonai n8n export <a href="./src/praisonai/praisonai/cli/commands/n8n.py">--help</a></code>
* <code title="cli">praisonai n8n import <a href="./src/praisonai/praisonai/cli/commands/n8n.py">--help</a></code>
* <code title="cli">praisonai n8n list <a href="./src/praisonai/praisonai/cli/commands/n8n.py">--help</a></code>
@@ -992,6 +1028,14 @@ Methods:
* <code title="cli">praisonai pairing list <a href="./src/praisonai/praisonai/cli/commands/pairing.py">--help</a></code>
* <code title="cli">praisonai pairing revoke <a href="./src/praisonai/praisonai/cli/commands/pairing.py">--help</a></code>
* <code title="cli">praisonai paths show <a href="./src/praisonai/praisonai/cli/commands/paths.py">--help</a></code>
* <code title="cli">praisonai permissions allow <a href="./src/praisonai/praisonai/cli/commands/permissions.py">--help</a></code>
* <code title="cli">praisonai permissions ask <a href="./src/praisonai/praisonai/cli/commands/permissions.py">--help</a></code>
* <code title="cli">praisonai permissions deny <a href="./src/praisonai/praisonai/cli/commands/permissions.py">--help</a></code>
* <code title="cli">praisonai permissions export <a href="./src/praisonai/praisonai/cli/commands/permissions.py">--help</a></code>
* <code title="cli">praisonai permissions import-rules <a href="./src/praisonai/praisonai/cli/commands/permissions.py">--help</a></code>
* <code title="cli">praisonai permissions list <a href="./src/praisonai/praisonai/cli/commands/permissions.py">--help</a></code>
* <code title="cli">praisonai permissions remove <a href="./src/praisonai/praisonai/cli/commands/permissions.py">--help</a></code>
* <code title="cli">praisonai permissions reset <a href="./src/praisonai/praisonai/cli/commands/permissions.py">--help</a></code>
* <code title="cli">praisonai plugins create <a href="./src/praisonai/praisonai/cli/commands/plugins.py">--help</a></code>
* <code title="cli">praisonai plugins disable <a href="./src/praisonai/praisonai/cli/commands/plugins.py">--help</a></code>
* <code title="cli">praisonai plugins discover <a href="./src/praisonai/praisonai/cli/commands/plugins.py">--help</a></code>
@@ -1071,6 +1115,7 @@ Methods:
* <code title="cli">praisonai serve gateway <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
* <code title="cli">praisonai serve lsp <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
* <code title="cli">praisonai serve mcp <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
* <code title="cli">praisonai serve openai <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
* <code title="cli">praisonai serve rag <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
* <code title="cli">praisonai serve recipe <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
* <code title="cli">praisonai serve registry <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
@@ -1138,6 +1183,9 @@ Methods:
* <code title="cli">praisonai up logs <a href="./src/praisonai/praisonai/cli/commands/up.py">--help</a></code>
* <code title="cli">praisonai up status <a href="./src/praisonai/praisonai/cli/commands/up.py">--help</a></code>
* <code title="cli">praisonai up up-start <a href="./src/praisonai/praisonai/cli/commands/up.py">--help</a></code>
* <code title="cli">praisonai validate check <a href="./src/praisonai/praisonai/cli/commands/validate.py">--help</a></code>
* <code title="cli">praisonai validate schema <a href="./src/praisonai/praisonai/cli/commands/validate.py">--help</a></code>
* <code title="cli">praisonai validate validate <a href="./src/praisonai/praisonai/cli/commands/validate.py">--help</a></code>
* <code title="cli">praisonai version check <a href="./src/praisonai/praisonai/cli/commands/version.py">--help</a></code>
* <code title="cli">praisonai version show <a href="./src/praisonai/praisonai/cli/commands/version.py">--help</a></code>
* <code title="cli">praisonai version version-callback <a href="./src/praisonai/praisonai/cli/commands/version.py">--help</a></code>
@@ -1290,7 +1338,7 @@ export { // Python parity additions
export { // Subagent Tool (agent-as-tool pattern)
SubagentTool, BaseTool, FunctionTool, ToolRegistry, ToolResult, ToolValidationError, createDelegator, createSubagentTool, createSubagentTools, createTool, getRegistry, getTool, registerTool, tool, validateTool } from "./tools";
export { airweaveSearch, bedrockBrowserClick, bedrockBrowserFill, bedrockBrowserNavigate, bedrockCodeInterpreter, codeExecution, codeMode, createCustomTool, exaSearch, firecrawlCrawl, firecrawlScrape, parallelSearch, perplexitySearch, registerCustomTool, registerLocalTool, registerNpmTool, superagentGuard, superagentRedact, superagentVerify, tavilyCrawl, tavilyExtract, tavilySearch, valyuBioSearch, valyuCompanyResearch, valyuEconomicsSearch, valyuFinanceSearch, valyuPaperSearch, valyuPatentSearch, valyuSecSearch, valyuWebSearch } from "./tools/builtins";
export { BudgetExceededError, MissingDependencyError, MissingEnvVarError, ToolsRegistry, composeMiddleware, createLoggingMiddleware, createRateLimitMiddleware, createRedactionMiddleware, createRetryMiddleware, createTimeoutMiddleware, createToolsRegistry, createTracingMiddleware, createValidationMiddleware, getRegistry, getTool, getToolsRegistry, get_registry, get_tool, registerTool, register_tool, resetToolsRegistry, validateTool, validate_tool } from "./tools/registry";
export { BudgetExceededError, MissingDependencyError, MissingEnvVarError, ToolsRegistry, composeMiddleware, createLoggingMiddleware, createRateLimitMiddleware, createRedactionMiddleware, createRetryMiddleware, createTimeoutMiddleware, createToolsRegistry, createTracingMiddleware, createValidationMiddleware, getToolsRegistry, get_registry, get_tool, register_tool, resetToolsRegistry, validate_tool } from "./tools/registry";
export type { InstallHints, PraisonTool, RedactionHooks, RegisteredTool, ToolCapabilities, ToolExecutionContext, ToolExecutionResult, ToolFactory, ToolHooks, ToolInstallStatus, ToolLimits, ToolLogger, ToolMetadata, ToolMiddleware, ToolParameterProperty, ToolParameterSchema } from "./tools/registry";
export { registerBuiltinTools, tools } from "./tools/tools";
export { // Classes
+1 -1
View File
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=4.6.52" \
"praisonai>=4.6.71" \
"praisonai[chat]" \
"embedchain[github,youtube]"
+1 -1
View File
@@ -20,7 +20,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=4.6.52" \
"praisonai>=4.6.71" \
"praisonai[ui]" \
"praisonai[chat]" \
"praisonai[realtime]" \
+1 -1
View File
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=4.6.52" \
"praisonai>=4.6.71" \
"praisonai[ui]" \
"praisonai[crewai]"
+6 -1
View File
@@ -12,10 +12,15 @@ from praisonaiagents import Agent
from praisonaiagents.approval import AgentApproval
from praisonaiagents.tools.shell_tools import execute_command
reviewer = AgentApproval(
reviewer_agent = Agent(
name="CommandReviewer",
instructions="Only approve read-only commands like 'ls' or 'cat'. Deny destructive commands.",
)
reviewer = AgentApproval(
approver_agent=reviewer_agent,
)
agent = Agent(
name="DevOps",
instructions="You are a DevOps assistant. Use shell tools when asked.",
+3 -3
View File
@@ -5,12 +5,12 @@ Demonstrates web search with consolidated params.
Presets: duckduckgo, tavily, google, bing, serper, search_only, fetch_only
"""
from praisonaiagents import Agent
from praisonaiagents import Agent, WebConfig
# Basic: Enable web search with preset
# Basic: Enable web search with preset (search only — fetch needs Claude)
agent = Agent(
instructions="You are a research assistant with web access.",
web="duckduckgo", # Presets: duckduckgo, tavily, google, bing, serper
web="search_only", # Compatible across models without web_fetch
)
if __name__ == "__main__":
@@ -30,5 +30,5 @@ if __name__ == "__main__":
sys.stdout.reconfigure(encoding="utf-8")
except Exception:
pass
result = workflow.run()
result = workflow.run("AI agents")
print(result)
@@ -0,0 +1,216 @@
# Industry Templates for PraisonAI
This directory contains industry-specific agent templates based on the SRAO Framework, providing pre-built workflows for rapid deployment of agent workforces across different industries.
## Overview
These templates demonstrate how PraisonAI can be used to create specialized agent teams for various industries, with **70% code reuse** across different sectors through shared patterns and abstractions.
## Available Templates
### 1. Manufacturing (`manufacturing_template.py`)
**Key Agents:**
- **ParseOrder**: Extracts and structures order information from various formats
- **CheckInventory**: Validates material availability and supply chain status
- **OptimizeSchedule**: Optimizes production scheduling based on constraints
- **DefectDetect**: Quality control and defect detection
**Features:**
- Order processing with multiple input formats
- Real-time inventory management
- Production optimization algorithms
- Quality inspection with defect detection
- Fallback strategies for each critical step
### 2. Energy (`energy_template.py`)
**Key Agents:**
- **SCADAReader**: Processes SCADA telemetry data from wind turbines
- **VibrationAnalyzer**: Analyzes vibration patterns for predictive maintenance
- **PowerForecaster**: Predicts power generation based on weather and history
- **MaintenanceScheduler**: Schedules predictive maintenance
**Features:**
- Real-time wind farm monitoring
- Vibration-based fault detection
- Power generation forecasting
- Grid integration and load balancing
- Predictive maintenance scheduling
### 3. Healthcare (`healthcare_template.py`)
**Key Agents:**
- **VitalSignsCapture**: Processes patient vital signs from monitoring devices
- **EMRRetrieval**: Retrieves electronic medical records with HIPAA compliance
- **TriageRecommendation**: Provides ESI-based triage recommendations
- **ResourceAllocator**: Manages hospital resource allocation
**Features:**
- Emergency triage workflow
- HIPAA-compliant data handling
- Patient safety protocols
- Resource optimization
- Cross-department coordination
### 4. Agriculture (`agriculture_template.py`)
**Key Agents:**
- **MultispectralAnalyzer**: Analyzes multispectral imagery for crop health
- **DiseaseIdentifier**: Identifies crop diseases and pests using AI
- **SprayRecommender**: Recommends targeted spraying strategies
- **YieldPredictor**: Predicts crop yield based on conditions
**Features:**
- Precision agriculture with drone/satellite imagery
- Pest and disease early warning system
- Optimized chemical application
- Yield forecasting
- Sustainable farming patterns
### 5. Transportation (`transportation_template.py`)
**Key Agents:**
- **LiDARFusion**: Processes LiDAR point cloud data for infrastructure
- **DisplacementCalculator**: Calculates structural displacement
- **HeatmapGenerator**: Generates safety visualization heatmaps
- **MaintenancePlanner**: Plans infrastructure maintenance
**Features:**
- Tunnel/bridge safety monitoring
- LiDAR-based structural analysis
- Real-time safety assessment
- Traffic flow optimization
- Predictive maintenance scheduling
## Key Features Across All Templates
### 1. I/O Schemas
Each template includes Pydantic models for structured data handling:
- Input validation
- Type safety
- Clear data contracts between agents
### 2. SLA Requirements
Every agent has defined Service Level Agreements:
- Processing time requirements
- Response time guarantees
- Performance benchmarks
### 3. Fallback Strategies
Robust error handling with fallback mechanisms:
- Graceful degradation
- Manual intervention queues
- Conservative estimates when systems fail
### 4. Cross-Industry Patterns (70% Reuse)
Reusable base patterns that work across industries:
- Data parsing agents
- Resource availability checkers
- Optimization engines
- Inspection/quality control agents
## Usage Example
```python
from manufacturing_template import (
order_parser,
inventory_checker,
schedule_optimizer,
quality_inspector,
manufacturing_workflow
)
# Process a manufacturing order
order_text = "Customer ABC needs 100 units of Product-XYZ by March 15th, high priority"
result = manufacturing_workflow(order_text)
print(result)
```
## Adapting Templates for Your Industry
### Using Base Patterns
Each template includes reusable pattern classes that can be adapted:
```python
from manufacturing_template import IndustryAgentPattern
# Create a custom data parser for any domain
custom_parser = IndustryAgentPattern.create_data_parser(
name="CustomParser",
domain="retail",
sla_seconds=20
)
# Create a resource checker for any resource type
resource_checker = IndustryAgentPattern.create_resource_checker(
name="StaffAvailability",
resource_type="customer_service_staff",
sla_seconds=3
)
```
### Creating Custom Workflows
Combine agents from different templates or create new ones:
```python
from praisonaiagents import Agent, tool
# Define custom tools
@tool
def custom_analysis(data: str) -> Dict:
"""Your custom analysis logic"""
return {"result": "analyzed"}
# Create custom agent
custom_agent = Agent(
name="CustomAnalyzer",
instructions="Your specific instructions",
tools=[custom_analysis]
)
```
## Integration with PraisonAI Core
These templates are built on top of PraisonAI's core Agent framework:
```python
from praisonaiagents import Agent, tool, AgentTeam, Task
# Combine multiple industry agents into a team
team = AgentTeam(
agents=[order_parser, inventory_checker, schedule_optimizer],
tasks=[
Task(name="parse", agent=order_parser),
Task(name="check", agent=inventory_checker),
Task(name="optimize", agent=schedule_optimizer)
]
)
```
## Best Practices
1. **Start with Templates**: Use these as starting points and customize for your specific needs
2. **Maintain SLAs**: Define clear performance requirements for each agent
3. **Implement Fallbacks**: Always have fallback strategies for critical operations
4. **Use Type Safety**: Leverage Pydantic models for data validation
5. **Monitor Performance**: Track agent performance against SLAs
6. **Iterate and Improve**: Continuously refine based on real-world usage
## Contributing
To add a new industry template:
1. Follow the existing template structure
2. Include all key components:
- I/O Schemas (Pydantic models)
- Agent definitions with SLAs
- Tools for agent capabilities
- Complete workflow function
- Fallback strategies
- Cross-industry patterns
3. Add comprehensive documentation
4. Include usage examples
## License
These templates are part of the PraisonAI project and follow the same licensing terms.
## References
Based on the [SRAO Framework](https://github.com/beixuan577/SRAO-Framework) (MIT License)
@@ -0,0 +1,38 @@
"""
Industry Templates for PraisonAI
================================
Pre-built agent templates for rapid deployment across different industries.
Based on SRAO Framework with 70% cross-industry code reuse.
Available templates:
- Manufacturing: Order processing, inventory, scheduling, quality control
- Energy: Wind farm monitoring, predictive maintenance, power forecasting
- Healthcare: Emergency triage, EMR retrieval, resource allocation
- Agriculture: Precision farming, pest detection, yield prediction
- Transportation: Infrastructure monitoring, safety assessment, maintenance
Example usage:
from Industry_Templates.manufacturing_template import manufacturing_workflow
result = manufacturing_workflow("Customer order text")
"""
__version__ = "1.0.0"
__author__ = "PraisonAI Team"
# Import the modules so they're accessible via the package
from . import manufacturing_template
from . import energy_template
from . import healthcare_template
from . import agriculture_template
from . import transportation_template
# Export commonly used items for convenience
__all__ = [
"manufacturing_template",
"energy_template",
"healthcare_template",
"agriculture_template",
"transportation_template"
]
@@ -0,0 +1,551 @@
"""
Agriculture Industry Template
=============================
Precision agriculture and pest management workflow
Based on SRAO Framework with IoT sensor integration
Key agents:
- MultispectralAnalyzer: Analyzes multispectral imagery
- DiseaseIdentifier: Identifies crop diseases and pests
- SprayRecommender: Recommends targeted spraying strategies
- YieldPredictor: Predicts crop yield based on conditions
"""
from praisonaiagents import Agent, tool
from typing import Dict, List, Any, Tuple, Optional
from pydantic import BaseModel
from datetime import datetime, timedelta
from enum import Enum
# Crop health levels
class CropHealthLevel(str, Enum):
EXCELLENT = "excellent"
GOOD = "good"
FAIR = "fair"
POOR = "poor"
CRITICAL = "critical"
# Pest/Disease severity
class SeverityLevel(str, Enum):
NONE = "none"
LOW = "low"
MODERATE = "moderate"
HIGH = "high"
SEVERE = "severe"
# I/O Schemas
class MultispectralData(BaseModel):
"""Multispectral imagery analysis data"""
field_id: str
capture_time: str
ndvi_index: float # Normalized Difference Vegetation Index
ndre_index: float # Normalized Difference Red Edge
ccci_index: float # Canopy Chlorophyll Content Index
moisture_level: float # Soil moisture percentage
temperature: float # Celsius
affected_area: float # Hectares
gps_coordinates: Tuple[float, float]
class PestDiseaseReport(BaseModel):
"""Pest and disease identification report"""
report_id: str
field_id: str
detection_time: str
pest_type: Optional[str]
disease_type: Optional[str]
severity: SeverityLevel
affected_crops: List[str]
spread_rate: float # % per day
environmental_factors: Dict[str, Any]
confidence_score: float
class SprayRecommendation(BaseModel):
"""Targeted spraying recommendation"""
recommendation_id: str
field_id: str
treatment_type: str # pesticide, fungicide, herbicide, fertilizer
product_name: str
application_rate: float # L/hectare
target_zones: List[Dict[str, Any]] # GPS zones
optimal_time: str
weather_window: Dict[str, Any]
cost_estimate: float
environmental_impact: str # low, medium, high
class YieldForecast(BaseModel):
"""Crop yield prediction"""
forecast_id: str
field_id: str
crop_type: str
predicted_yield: float # tons/hectare
confidence_interval: Tuple[float, float]
harvest_window: Tuple[str, str]
quality_grade: str # A, B, C
market_price_estimate: float
risk_factors: List[str]
# Agriculture-specific tools
@tool
def analyze_multispectral_imagery(field_id: str, image_data: Dict) -> Dict:
"""Analyze multispectral drone/satellite imagery for crop health"""
# Simulate multispectral analysis
ndvi = 0.75 # Healthy vegetation typically 0.6-0.9
# Determine health based on NDVI
if ndvi > 0.8:
health = CropHealthLevel.EXCELLENT
affected_area = 0.0
elif ndvi > 0.6:
health = CropHealthLevel.GOOD
affected_area = 2.5
elif ndvi > 0.4:
health = CropHealthLevel.FAIR
affected_area = 5.0
else:
health = CropHealthLevel.POOR
affected_area = 10.0
return {
"field_id": field_id,
"capture_time": datetime.now().isoformat(),
"ndvi_index": ndvi,
"ndre_index": 0.68,
"ccci_index": 0.72,
"moisture_level": 35.0,
"temperature": 24.5,
"affected_area": affected_area,
"gps_coordinates": (40.7128, -74.0060),
"health_level": health.value
}
@tool
def identify_pest_disease(spectral_data: Dict, historical_data: List) -> Dict:
"""Identify pests and diseases using AI image recognition"""
# Simulate pest/disease detection
ndvi = spectral_data.get("ndvi_index", 0.75)
affected_area = spectral_data.get("affected_area", 0)
if affected_area > 8.0:
pest_type = "aphids"
disease_type = "powdery_mildew"
severity = SeverityLevel.HIGH
spread_rate = 2.5
elif affected_area > 4.0:
pest_type = "whiteflies"
disease_type = "leaf_spot"
severity = SeverityLevel.MODERATE
spread_rate = 1.5
elif affected_area > 0:
pest_type = None
disease_type = "early_blight"
severity = SeverityLevel.LOW
spread_rate = 0.5
else:
pest_type = None
disease_type = None
severity = SeverityLevel.NONE
spread_rate = 0.0
return {
"report_id": f"PDR-{datetime.now().strftime('%Y%m%d%H%M')}",
"field_id": spectral_data["field_id"],
"detection_time": datetime.now().isoformat(),
"pest_type": pest_type,
"disease_type": disease_type,
"severity": severity.value,
"affected_crops": ["wheat", "corn"],
"spread_rate": spread_rate,
"environmental_factors": {
"humidity": 65,
"temperature": spectral_data.get("temperature", 25),
"rainfall_last_week": 15 # mm
},
"confidence_score": 0.85
}
@tool
def calculate_spray_recommendation(pest_report: Dict, field_data: Dict, weather: Dict) -> Dict:
"""Calculate optimal spraying strategy for pest/disease control"""
severity = pest_report.get("severity", "none")
if severity == "high" or severity == "severe":
treatment = "pesticide"
product = "Imidacloprid"
rate = 0.5
impact = "medium"
elif severity == "moderate":
treatment = "fungicide"
product = "Azoxystrobin"
rate = 0.3
impact = "low"
elif severity == "low":
treatment = "organic_treatment"
product = "Neem Oil"
rate = 0.2
impact = "minimal"
else:
treatment = "fertilizer"
product = "NPK 20-20-20"
rate = 0.4
impact = "positive"
# Calculate target zones based on affected area
affected_area = field_data.get("affected_area", 0)
if affected_area > 0:
target_zones = [
{
"zone_id": "A1",
"gps_boundary": [(40.71, -74.00), (40.72, -74.01)],
"priority": "high"
}
]
else:
target_zones = []
return {
"recommendation_id": f"SPR-{datetime.now().strftime('%Y%m%d%H%M')}",
"field_id": field_data["field_id"],
"treatment_type": treatment,
"product_name": product,
"application_rate": rate,
"affected_area": affected_area,
"target_zones": target_zones,
"optimal_time": (datetime.now() + timedelta(days=1)).isoformat(),
"weather_window": {
"wind_speed": weather.get("wind_speed", 5),
"precipitation": weather.get("precipitation", 0),
"suitable": True
},
"cost_estimate": rate * affected_area * 50, # $/hectare
"environmental_impact": impact
}
@tool
def predict_crop_yield(field_id: str, crop_type: str, current_health: Dict, weather_forecast: Dict) -> Dict:
"""Predict crop yield based on current conditions and forecasts"""
# Simulate yield prediction
health_level = current_health.get("health_level", "good")
base_yield = {
"wheat": 3.5,
"corn": 9.5,
"soybeans": 2.8,
"rice": 6.0
}.get(crop_type, 5.0)
# Adjust based on health
health_multiplier = {
"excellent": 1.2,
"good": 1.0,
"fair": 0.8,
"poor": 0.6,
"critical": 0.4
}.get(health_level, 1.0)
predicted_yield = base_yield * health_multiplier
# Quality grade based on conditions
if health_level in ["excellent", "good"]:
quality = "A"
price_multiplier = 1.2
elif health_level == "fair":
quality = "B"
price_multiplier = 1.0
else:
quality = "C"
price_multiplier = 0.8
return {
"forecast_id": f"YLD-{datetime.now().strftime('%Y%m%d%H%M')}",
"field_id": field_id,
"crop_type": crop_type,
"predicted_yield": predicted_yield,
"confidence_interval": (predicted_yield * 0.9, predicted_yield * 1.1),
"harvest_window": (
(datetime.now() + timedelta(days=60)).isoformat(),
(datetime.now() + timedelta(days=75)).isoformat()
),
"quality_grade": quality,
"market_price_estimate": predicted_yield * 250 * price_multiplier, # $/ton
"risk_factors": ["weather_variability", "pest_pressure", "market_volatility"]
}
# Agriculture agent definitions
multispectral_agent = Agent(
name="MultispectralAnalyzer",
instructions="""You are a remote sensing specialist for precision agriculture.
Analyze multispectral imagery from drones and satellites.
Calculate vegetation indices (NDVI, NDRE, CCCI) to assess crop health.
Identify stress zones and anomalies in field conditions.
SLA: Process imagery within 2 minutes per field.""",
tools=[analyze_multispectral_imagery]
)
disease_agent = Agent(
name="DiseaseIdentifier",
instructions="""You are a plant pathology expert with AI vision capabilities.
Identify crop diseases, pests, and nutrient deficiencies.
Assess severity levels and predict spread patterns.
Consider environmental factors affecting disease development.
SLA: Complete identification within 30 seconds.""",
tools=[identify_pest_disease]
)
spray_agent = Agent(
name="SprayRecommender",
instructions="""You are a precision application specialist.
Recommend targeted spraying strategies for pest and disease control.
Optimize chemical usage to minimize environmental impact.
Consider weather windows and application regulations.
SLA: Generate recommendations within 1 minute.""",
tools=[calculate_spray_recommendation]
)
yield_agent = Agent(
name="YieldPredictor",
instructions="""You are a crop yield forecasting specialist.
Predict harvest yields based on current conditions and historical data.
Assess crop quality grades and market value estimates.
Identify risk factors that could impact final yield.
SLA: Generate forecast within 45 seconds.""",
tools=[predict_crop_yield]
)
# Precision agriculture workflow
def precision_agriculture_workflow(field_ids: List[str], crop_type: str, weather_data: Dict):
"""
Complete precision agriculture workflow from monitoring to treatment
Includes early warning system and sustainable farming practices
"""
workflow_results = {
"timestamp": datetime.now().isoformat(),
"fields_monitored": len(field_ids),
"alerts": [],
"treatments_needed": [],
"yield_forecasts": [],
"sustainability_score": 0.0
}
for field_id in field_ids:
try:
# Step 1: Multispectral analysis
spectral_analysis = multispectral_agent.start(
f"Analyze multispectral imagery for field {field_id}"
)
# Step 2: Disease and pest detection
pest_report = disease_agent.start(
f"Identify pests/diseases from spectral data: {spectral_analysis}"
)
# Check for alerts
if pest_report.get("severity") in ["high", "severe"]:
workflow_results["alerts"].append({
"field_id": field_id,
"type": "pest_disease_alert",
"severity": pest_report["severity"],
"pest": pest_report.get("pest_type"),
"disease": pest_report.get("disease_type"),
"action": "immediate_treatment_required"
})
# Step 3: Generate spray recommendation
spray_recommendation = spray_agent.start(
f"Calculate spray strategy for pest report: {pest_report}, "
f"field: {spectral_analysis}, weather: {weather_data}"
)
workflow_results["treatments_needed"].append(spray_recommendation)
# Step 4: Yield prediction
yield_forecast = yield_agent.start(
f"Predict yield for field {field_id}, crop {crop_type}, "
f"health: {spectral_analysis}, weather: {weather_data}"
)
workflow_results["yield_forecasts"].append(yield_forecast)
except Exception as e:
# Fallback: Schedule manual inspection
workflow_results["alerts"].append({
"field_id": field_id,
"type": "monitoring_failure",
"error": str(e),
"action": "schedule_manual_inspection"
})
# Calculate sustainability score
total_chemical = sum(
t.get("application_rate", 0) * t.get("affected_area", 0)
for t in workflow_results["treatments_needed"]
)
workflow_results["sustainability_score"] = max(0, 100 - (total_chemical * 10))
return workflow_results
# Sustainable farming patterns
class SustainableFarmingPatterns:
"""
Patterns for sustainable and regenerative agriculture
"""
@staticmethod
def integrated_pest_management(pest_level: str) -> Dict:
"""IPM strategy based on pest pressure"""
strategies = {
"low": {
"method": "biological_control",
"actions": ["release_beneficial_insects", "plant_trap_crops"],
"chemical_use": 0
},
"moderate": {
"method": "targeted_intervention",
"actions": ["spot_treatment", "pheromone_traps", "minimal_spray"],
"chemical_use": 30
},
"high": {
"method": "integrated_approach",
"actions": ["systematic_spray", "crop_rotation", "resistant_varieties"],
"chemical_use": 70
}
}
return strategies.get(pest_level, strategies["moderate"])
@staticmethod
def precision_irrigation(moisture_level: float, crop_stage: str) -> Dict:
"""Calculate optimal irrigation based on soil moisture and crop stage"""
# Water requirements by growth stage (mm/day)
stage_requirements = {
"germination": 3.0,
"vegetative": 5.0,
"flowering": 7.0,
"grain_filling": 6.0,
"maturation": 2.0
}
required = stage_requirements.get(crop_stage, 5.0)
deficit = max(0, 40 - moisture_level) # Target 40% moisture
return {
"irrigation_needed": deficit > 10,
"amount_mm": deficit * 2,
"method": "drip" if deficit < 20 else "sprinkler",
"schedule": "night_irrigation" if deficit > 15 else "morning_irrigation",
"water_saved": (100 - deficit) / 100 * 1000 # Liters per hectare
}
@staticmethod
def crop_rotation_optimizer(current_crop: str, soil_data: Dict) -> str:
"""Recommend next crop for rotation based on soil health"""
rotations = {
"wheat": ["soybeans", "canola"], # Nitrogen fixation
"corn": ["soybeans", "alfalfa"], # Break disease cycle
"soybeans": ["wheat", "corn"], # Utilize fixed nitrogen
"rice": ["legumes", "vegetables"], # Soil structure improvement
}
return rotations.get(current_crop, ["cover_crops"])[0]
# IoT sensor integration patterns
class IoTSensorPatterns:
"""
Patterns for integrating IoT sensors in smart farming
"""
@staticmethod
def create_sensor_network_agent(sensor_types: List[str]) -> Agent:
"""Create agent for managing IoT sensor networks"""
return Agent(
name="SensorNetworkManager",
instructions=f"""Manage IoT sensor network with {', '.join(sensor_types)}.
Collect real-time data from field sensors.
Detect sensor anomalies and maintenance needs.
Aggregate data for precision agriculture decisions.
SLA: Data collection every 15 minutes."""
)
@staticmethod
def process_sensor_data(sensor_readings: List[Dict]) -> Dict:
"""Process and aggregate IoT sensor data"""
aggregated = {
"timestamp": datetime.now().isoformat(),
"sensor_count": len(sensor_readings),
"averages": {},
"alerts": []
}
# Calculate averages
for reading in sensor_readings:
for key, value in reading.items():
if isinstance(value, (int, float)):
if key not in aggregated["averages"]:
aggregated["averages"][key] = []
aggregated["averages"][key].append(value)
# Convert to means
for key in aggregated["averages"]:
values = aggregated["averages"][key]
aggregated["averages"][key] = sum(values) / len(values)
# Check for anomalies
if key == "soil_ph" and (aggregated["averages"][key] < 6.0 or aggregated["averages"][key] > 7.5):
aggregated["alerts"].append({
"type": "soil_ph_anomaly",
"value": aggregated["averages"][key],
"action": "adjust_soil_treatment"
})
return aggregated
# Example usage
if __name__ == "__main__":
# Monitor multiple fields
fields = ["FIELD-001", "FIELD-002", "FIELD-003"]
weather = {
"temperature": 25,
"humidity": 60,
"wind_speed": 8,
"precipitation": 0,
"forecast": "partly_cloudy"
}
result = precision_agriculture_workflow(fields, "wheat", weather)
print("Agriculture workflow result:", result)
# Sustainable farming example
ipm_strategy = SustainableFarmingPatterns.integrated_pest_management("moderate")
print("IPM Strategy:", ipm_strategy)
irrigation_plan = SustainableFarmingPatterns.precision_irrigation(25.0, "flowering")
print("Irrigation Plan:", irrigation_plan)
next_crop = SustainableFarmingPatterns.crop_rotation_optimizer("wheat", {})
print("Recommended next crop:", next_crop)
# IoT integration example
sensor_agent = IoTSensorPatterns.create_sensor_network_agent(
["soil_moisture", "temperature", "humidity", "light", "ph"]
)
sensor_data = [
{"sensor_id": "S001", "soil_moisture": 35, "temperature": 24, "soil_ph": 6.8},
{"sensor_id": "S002", "soil_moisture": 32, "temperature": 25, "soil_ph": 7.2},
{"sensor_id": "S003", "soil_moisture": 38, "temperature": 23, "soil_ph": 5.8}
]
processed_data = IoTSensorPatterns.process_sensor_data(sensor_data)
print("Processed sensor data:", processed_data)
@@ -0,0 +1,372 @@
"""
Energy Industry Template
========================
Wind farm monitoring and power optimization workflow
Based on SRAO Framework patterns with predictive maintenance
Key agents:
- SCADAReader: Processes SCADA telemetry data
- VibrationAnalyzer: Analyzes turbine vibration patterns
- PowerForecaster: Predicts power generation
- MaintenanceScheduler: Schedules predictive maintenance
"""
from praisonaiagents import Agent, tool
from typing import Dict, List, Any, Optional
from pydantic import BaseModel
from datetime import datetime, timedelta
# I/O Schemas
class TurbineData(BaseModel):
"""Wind turbine telemetry data"""
turbine_id: str
timestamp: str
wind_speed: float # m/s
power_output: float # MW
rotor_speed: float # RPM
blade_pitch: float # degrees
nacelle_temperature: float # Celsius
vibration_level: float # mm/s
operational_status: str # running, stopped, maintenance
class VibrationAnalysis(BaseModel):
"""Vibration analysis results"""
turbine_id: str
analysis_time: str
vibration_rms: float
frequency_peaks: List[float]
anomaly_detected: bool
failure_probability: float
recommended_action: str
class PowerForecast(BaseModel):
"""Power generation forecast"""
forecast_id: str
start_time: str
end_time: str
predicted_output: List[float] # MW per hour
confidence_interval: float
weather_factors: Dict[str, Any]
grid_demand: float
class MaintenancePlan(BaseModel):
"""Maintenance scheduling plan"""
plan_id: str
turbine_id: str
maintenance_type: str # preventive, predictive, corrective
scheduled_date: str
estimated_duration: int # hours
required_parts: List[str]
technician_team: str
production_loss: float # MW
# Tools for energy management
@tool
def read_scada_telemetry(turbine_id: str, time_range: str = "last_hour") -> Dict:
"""Read SCADA system telemetry for wind turbines"""
# Simulate SCADA data reading
return {
"turbine_id": turbine_id,
"timestamp": datetime.now().isoformat(),
"wind_speed": 12.5,
"power_output": 2.3,
"rotor_speed": 16.8,
"blade_pitch": 8.5,
"nacelle_temperature": 45.2,
"vibration_level": 3.2,
"operational_status": "running"
}
@tool
def analyze_vibration_patterns(turbine_data: Dict) -> Dict:
"""Analyze vibration patterns for early fault detection"""
# Simulate vibration analysis
vibration_level = turbine_data.get("vibration_level", 0)
anomaly = vibration_level > 5.0 # Threshold for anomaly
return {
"turbine_id": turbine_data["turbine_id"],
"analysis_time": datetime.now().isoformat(),
"vibration_rms": vibration_level,
"frequency_peaks": [10.5, 25.3, 50.0], # Hz
"anomaly_detected": anomaly,
"failure_probability": min(vibration_level / 10.0, 1.0),
"recommended_action": "immediate_inspection" if anomaly else "continue_monitoring"
}
@tool
def forecast_power_generation(weather_data: Dict, historical_data: List) -> Dict:
"""Forecast power generation based on weather and historical patterns"""
# Simulate power forecasting
base_forecast = [2.0 + (i * 0.1) for i in range(24)] # 24-hour forecast
return {
"forecast_id": f"FCST-{datetime.now().strftime('%Y%m%d%H%M')}",
"start_time": datetime.now().isoformat(),
"end_time": (datetime.now() + timedelta(hours=24)).isoformat(),
"predicted_output": base_forecast,
"confidence_interval": 0.85,
"weather_factors": weather_data,
"grid_demand": sum(base_forecast)
}
@tool
def schedule_maintenance(turbine_id: str, maintenance_type: str, urgency: str = "normal") -> Dict:
"""Schedule maintenance based on condition monitoring"""
# Calculate maintenance window
if urgency == "urgent":
scheduled_date = (datetime.now() + timedelta(days=1)).isoformat()
else:
scheduled_date = (datetime.now() + timedelta(days=7)).isoformat()
return {
"plan_id": f"MNT-{datetime.now().strftime('%Y%m%d%H%M')}",
"turbine_id": turbine_id,
"maintenance_type": maintenance_type,
"scheduled_date": scheduled_date,
"estimated_duration": 4,
"required_parts": ["bearing_set", "lubricant", "filter"],
"technician_team": "Team-A",
"production_loss": 8.0 # MW lost during maintenance
}
# Agent Definitions with SLA requirements
scada_reader = Agent(
name="SCADAReader",
instructions="""You are a SCADA system specialist for wind farms.
Process real-time telemetry data from wind turbines.
Identify operational anomalies and performance deviations.
SLA: Process telemetry within 1 second for real-time monitoring.""",
tools=[read_scada_telemetry]
)
vibration_analyzer = Agent(
name="VibrationAnalyzer",
instructions="""You are a vibration analysis expert for rotating machinery.
Analyze vibration patterns to detect early signs of mechanical failure.
Use FFT analysis and pattern recognition for fault diagnosis.
SLA: Complete analysis within 5 seconds per turbine.""",
tools=[analyze_vibration_patterns]
)
power_forecaster = Agent(
name="PowerForecaster",
instructions="""You are a power generation forecasting specialist.
Predict power output based on weather forecasts and historical patterns.
Consider grid demand and optimize generation schedules.
SLA: Generate 24-hour forecast within 30 seconds.""",
tools=[forecast_power_generation]
)
maintenance_scheduler = Agent(
name="MaintenanceScheduler",
instructions="""You are a predictive maintenance planning expert.
Schedule maintenance based on condition monitoring and failure predictions.
Minimize production loss while ensuring turbine reliability.
SLA: Create maintenance plan within 1 minute.""",
tools=[schedule_maintenance]
)
# Energy workflow with grid integration
def energy_monitoring_workflow(turbine_ids: List[str], weather_forecast: Dict):
"""
Complete energy monitoring and optimization workflow
Includes predictive maintenance and grid integration
"""
results = {
"timestamp": datetime.now().isoformat(),
"turbines_monitored": len(turbine_ids),
"alerts": [],
"maintenance_required": [],
"power_forecast": None
}
# Step 1: Monitor all turbines
for turbine_id in turbine_ids:
try:
# Read SCADA data
scada_data = scada_reader.start(
f"Read telemetry for turbine {turbine_id}"
)
# Analyze vibrations
vibration_result = vibration_analyzer.start(
f"Analyze vibrations for data: {scada_data}"
)
# Check for anomalies
if vibration_result.get("anomaly_detected"):
results["alerts"].append({
"turbine_id": turbine_id,
"type": "vibration_anomaly",
"severity": "high" if vibration_result.get("failure_probability", 0) > 0.7 else "medium"
})
# Schedule maintenance if needed
maintenance_plan = maintenance_scheduler.start(
f"Schedule maintenance for turbine {turbine_id} with urgency based on {vibration_result}"
)
results["maintenance_required"].append(maintenance_plan)
except Exception as e:
# Fallback: Mark turbine for manual inspection
results["alerts"].append({
"turbine_id": turbine_id,
"type": "monitoring_failure",
"error": str(e),
"action": "manual_inspection_required"
})
# Step 2: Power generation forecast
try:
historical_data = [] # Would be fetched from database
power_forecast = power_forecaster.start(
f"Forecast power with weather {weather_forecast} and history {historical_data}"
)
results["power_forecast"] = power_forecast
# Grid integration check
if power_forecast.get("predicted_output"):
total_predicted = sum(power_forecast["predicted_output"])
grid_demand = power_forecast.get("grid_demand", 0)
if total_predicted < grid_demand * 0.9:
results["alerts"].append({
"type": "supply_shortage",
"predicted": total_predicted,
"demand": grid_demand,
"action": "consider_backup_sources"
})
except Exception as e:
# Fallback: Use conservative forecast
results["power_forecast"] = {
"status": "fallback_mode",
"conservative_estimate": len(turbine_ids) * 1.5, # MW
"error": str(e)
}
return results
# Cross-industry pattern adaptation
class EnergyPatternAdapter:
"""
Adapts manufacturing patterns for energy sector
Demonstrates 70% code reuse across industries
"""
@staticmethod
def adapt_for_solar_farm():
"""Adapt wind farm agents for solar farm monitoring"""
return Agent(
name="SolarMonitor",
instructions="""Monitor solar panel efficiency and temperature.
Track irradiance levels and predict power output.
Detect panel degradation and soiling issues.
SLA: Real-time monitoring with 2-second updates."""
)
@staticmethod
def adapt_for_power_grid():
"""Adapt for power grid management"""
return Agent(
name="GridBalancer",
instructions="""Balance power supply and demand across the grid.
Manage load distribution and prevent blackouts.
Coordinate with renewable and traditional sources.
SLA: Sub-second response for grid stability."""
)
@staticmethod
def adapt_for_battery_storage():
"""Adapt for battery energy storage systems"""
return Agent(
name="BatteryOptimizer",
instructions="""Optimize battery charge/discharge cycles.
Predict battery degradation and schedule replacements.
Maximize energy arbitrage opportunities.
SLA: Optimization decision within 10 seconds."""
)
# Fallback strategies for critical operations
class EnergyFallbackStrategies:
"""
Fallback strategies for maintaining grid stability
"""
@staticmethod
def turbine_failure_fallback(failed_turbine_id: str, available_turbines: List[str]):
"""Handle turbine failure by redistributing load"""
return {
"strategy": "load_redistribution",
"failed_turbine": failed_turbine_id,
"compensating_turbines": available_turbines[:3], # Use top 3 turbines
"increase_output_by": 0.3, # MW per turbine
"duration": "until_maintenance_complete"
}
@staticmethod
def forecast_failure_fallback(last_known_forecast: Optional[Dict]):
"""Handle forecasting system failure"""
if last_known_forecast:
return {
"strategy": "use_last_known",
"forecast": last_known_forecast,
"confidence": 0.6,
"validity": "4_hours"
}
else:
return {
"strategy": "seasonal_average",
"estimated_output": 50.0, # MW based on seasonal average
"confidence": 0.4,
"recommendation": "increase_reserves"
}
@staticmethod
def communication_failure_fallback():
"""Handle SCADA communication failure"""
return {
"strategy": "local_control_mode",
"actions": [
"Switch turbines to autonomous mode",
"Use local safety thresholds",
"Log data locally for later sync",
"Alert maintenance teams"
],
"recovery_check_interval": 300 # seconds
}
# Example usage
if __name__ == "__main__":
# Monitor wind farm
turbine_list = [f"WT-{i:03d}" for i in range(1, 11)] # 10 turbines
weather = {"wind_speed": 14.0, "direction": "NW", "temperature": 15.0}
result = energy_monitoring_workflow(turbine_list, weather)
print("Energy monitoring result:", result)
# Demonstrate cross-industry adaptation
solar_monitor = EnergyPatternAdapter.adapt_for_solar_farm()
grid_balancer = EnergyPatternAdapter.adapt_for_power_grid()
battery_optimizer = EnergyPatternAdapter.adapt_for_battery_storage()
# Fallback examples
turbine_fallback = EnergyFallbackStrategies.turbine_failure_fallback(
"WT-003", ["WT-001", "WT-002", "WT-004", "WT-005"]
)
print("Turbine failure fallback:", turbine_fallback)
@@ -0,0 +1,478 @@
"""
Healthcare Industry Template
============================
Emergency triage and patient care coordination workflow
Based on SRAO Framework with HIPAA-compliant patterns
Key agents:
- VitalSignsCapture: Processes patient vital signs
- EMRRetrieval: Retrieves electronic medical records
- TriageRecommendation: Provides triage recommendations
- ResourceAllocator: Manages hospital resource allocation
"""
from praisonaiagents import Agent, tool
from typing import Dict, List, Any, Optional
from pydantic import BaseModel
from datetime import datetime
from enum import Enum
# Triage levels following Emergency Severity Index (ESI)
class TriageLevel(str, Enum):
RESUSCITATION = "1_resuscitation" # Immediate life-saving intervention
EMERGENT = "2_emergent" # High risk, severe pain/distress
URGENT = "3_urgent" # Stable but needs multiple resources
LESS_URGENT = "4_less_urgent" # Stable, one resource needed
NON_URGENT = "5_non_urgent" # Stable, no resources needed
# I/O Schemas
class VitalSigns(BaseModel):
"""Patient vital signs data"""
patient_id: str
timestamp: str
heart_rate: int # bpm
blood_pressure_systolic: int # mmHg
blood_pressure_diastolic: int # mmHg
respiratory_rate: int # breaths/min
temperature: float # Celsius
oxygen_saturation: int # percentage
pain_scale: int # 0-10
consciousness_level: str # alert, verbal, pain, unresponsive (AVPU)
class MedicalHistory(BaseModel):
"""Patient medical history from EMR"""
patient_id: str
allergies: List[str]
chronic_conditions: List[str]
current_medications: List[Dict[str, str]]
recent_visits: List[Dict[str, Any]]
emergency_contacts: List[Dict[str, str]]
insurance_status: str
class TriageAssessment(BaseModel):
"""Triage assessment and recommendations"""
assessment_id: str
patient_id: str
triage_level: TriageLevel
chief_complaint: str
recommended_department: str
estimated_wait_time: int # minutes
required_resources: List[str]
clinical_notes: str
red_flags: List[str]
class ResourceAllocation(BaseModel):
"""Hospital resource allocation"""
allocation_id: str
patient_id: str
assigned_bed: Optional[str]
assigned_staff: List[str]
equipment_needed: List[str]
department: str
priority_score: float
estimated_treatment_time: int # minutes
# Healthcare-specific tools
@tool
def capture_vital_signs(patient_id: str) -> Dict:
"""Capture patient vital signs from monitoring devices"""
# Simulate vital signs capture
return {
"patient_id": patient_id,
"timestamp": datetime.now().isoformat(),
"heart_rate": 78,
"blood_pressure_systolic": 130,
"blood_pressure_diastolic": 85,
"respiratory_rate": 16,
"temperature": 37.2,
"oxygen_saturation": 98,
"pain_scale": 6,
"consciousness_level": "alert"
}
@tool
def retrieve_medical_history(patient_id: str, consent_verified: bool = True) -> Dict:
"""Retrieve patient medical history from EMR system"""
if not consent_verified:
return {"error": "consent_required", "message": "Patient consent required for EMR access"}
# Simulate EMR retrieval
return {
"patient_id": patient_id,
"allergies": ["penicillin", "latex"],
"chronic_conditions": ["hypertension", "diabetes_type2"],
"current_medications": [
{"name": "metformin", "dose": "500mg", "frequency": "twice daily"},
{"name": "lisinopril", "dose": "10mg", "frequency": "once daily"}
],
"recent_visits": [
{"date": "2024-02-15", "reason": "routine_checkup", "department": "primary_care"}
],
"emergency_contacts": [
{"name": "Jane Doe", "relationship": "spouse", "phone": "555-0123"}
],
"insurance_status": "active"
}
@tool
def calculate_triage_priority(vital_signs: Dict, medical_history: Dict, chief_complaint: str) -> Dict:
"""Calculate triage priority based on clinical indicators"""
# Simulate triage algorithm
priority_score = 0
red_flags = []
# Check vital signs
if vital_signs["heart_rate"] > 120 or vital_signs["heart_rate"] < 50:
priority_score += 3
red_flags.append("abnormal_heart_rate")
if vital_signs["oxygen_saturation"] < 92:
priority_score += 4
red_flags.append("low_oxygen")
if vital_signs["consciousness_level"] != "alert":
priority_score += 5
red_flags.append("altered_consciousness")
# Determine triage level
if priority_score >= 8:
triage_level = TriageLevel.RESUSCITATION
department = "emergency"
wait_time = 0
elif priority_score >= 5:
triage_level = TriageLevel.EMERGENT
department = "emergency"
wait_time = 10
elif priority_score >= 3:
triage_level = TriageLevel.URGENT
department = "urgent_care"
wait_time = 30
else:
triage_level = TriageLevel.LESS_URGENT
department = "primary_care"
wait_time = 60
return {
"assessment_id": f"TRG-{datetime.now().strftime('%Y%m%d%H%M%S')}",
"patient_id": vital_signs["patient_id"],
"triage_level": triage_level.value,
"chief_complaint": chief_complaint,
"recommended_department": department,
"estimated_wait_time": wait_time,
"required_resources": ["physician", "nurse", "monitoring"],
"clinical_notes": f"Priority score: {priority_score}",
"red_flags": red_flags
}
@tool
def allocate_resources(patient_id: str, triage_level: str, department: str) -> Dict:
"""Allocate hospital resources based on triage assessment"""
# Simulate resource allocation
if "resuscitation" in triage_level:
bed = "ER-01"
staff = ["Dr. Smith", "Nurse Johnson", "Nurse Williams"]
equipment = ["cardiac_monitor", "defibrillator", "crash_cart"]
priority = 10.0
elif "emergent" in triage_level:
bed = "ER-05"
staff = ["Dr. Brown", "Nurse Davis"]
equipment = ["vital_signs_monitor", "IV_pump"]
priority = 8.0
else:
bed = "UC-12"
staff = ["PA Miller", "Nurse Garcia"]
equipment = ["basic_monitor"]
priority = 5.0
return {
"allocation_id": f"RES-{datetime.now().strftime('%Y%m%d%H%M%S')}",
"patient_id": patient_id,
"assigned_bed": bed,
"assigned_staff": staff,
"equipment_needed": equipment,
"department": department,
"priority_score": priority,
"estimated_treatment_time": 45
}
# Healthcare agent definitions with SLA requirements
vital_signs_agent = Agent(
name="VitalSignsCapture",
instructions="""You are a clinical vital signs specialist.
Capture and interpret patient vital signs from various monitoring devices.
Identify abnormal values and trends that require immediate attention.
Follow HIPAA guidelines for patient data handling.
SLA: Capture and process within 30 seconds.""",
tools=[capture_vital_signs]
)
emr_agent = Agent(
name="EMRRetrieval",
instructions="""You are an EMR system specialist with HIPAA compliance expertise.
Retrieve relevant patient medical history while ensuring data privacy.
Verify patient consent and maintain audit trails.
Highlight relevant allergies and contraindications.
SLA: Retrieve records within 5 seconds.""",
tools=[retrieve_medical_history]
)
triage_agent = Agent(
name="TriageRecommendation",
instructions="""You are an emergency triage specialist following ESI guidelines.
Assess patient severity and provide triage recommendations.
Consider vital signs, medical history, and presenting symptoms.
Identify red flags that require immediate intervention.
SLA: Complete assessment within 1 minute.""",
tools=[calculate_triage_priority]
)
resource_agent = Agent(
name="ResourceAllocator",
instructions="""You are a hospital resource management specialist.
Allocate beds, staff, and equipment based on patient needs and priority.
Optimize resource utilization while maintaining quality of care.
Coordinate between departments for efficient patient flow.
SLA: Allocate resources within 30 seconds.""",
tools=[allocate_resources]
)
# Healthcare workflow with safety protocols
def emergency_triage_workflow(patient_id: str, chief_complaint: str):
"""
Complete emergency triage workflow from arrival to resource allocation
Includes safety checks and fallback protocols
"""
workflow_result = {
"patient_id": patient_id,
"timestamp": datetime.now().isoformat(),
"status": "in_progress",
"safety_checks": [],
"clinical_pathway": []
}
# Step 1: Vital signs capture with validation
try:
vital_signs = vital_signs_agent.start(
f"Capture vital signs for patient {patient_id}"
)
workflow_result["clinical_pathway"].append({
"step": "vital_signs",
"status": "completed",
"data": vital_signs
})
# Safety check: Validate vital signs are within possible ranges
if vital_signs.get("heart_rate", 0) < 20 or vital_signs.get("heart_rate", 0) > 300:
workflow_result["safety_checks"].append({
"type": "vital_signs_validation",
"issue": "impossible_heart_rate",
"action": "manual_verification_required"
})
except Exception as e:
# Fallback: Manual vital signs entry
workflow_result["clinical_pathway"].append({
"step": "vital_signs",
"status": "manual_entry_required",
"error": str(e)
})
vital_signs = {"manual_entry": True}
# Step 2: EMR retrieval with consent check
try:
medical_history = emr_agent.start(
f"Retrieve medical history for patient {patient_id} with verified consent"
)
workflow_result["clinical_pathway"].append({
"step": "emr_retrieval",
"status": "completed",
"records_retrieved": True
})
# Safety check: Allergy alerts
if medical_history.get("allergies"):
workflow_result["safety_checks"].append({
"type": "allergy_alert",
"allergies": medical_history["allergies"],
"action": "notify_all_providers"
})
except Exception as e:
# Fallback: Proceed with limited information
medical_history = {
"patient_id": patient_id,
"allergies": [],
"note": "EMR_unavailable_proceed_with_caution"
}
workflow_result["safety_checks"].append({
"type": "emr_unavailable",
"action": "collect_history_manually"
})
# Step 3: Triage assessment
try:
triage_assessment = triage_agent.start(
f"Assess triage for patient with vitals {vital_signs}, "
f"history {medical_history}, complaint: {chief_complaint}"
)
workflow_result["clinical_pathway"].append({
"step": "triage_assessment",
"status": "completed",
"triage_level": triage_assessment.get("triage_level")
})
# Safety check: Red flags requiring immediate action
if triage_assessment.get("red_flags"):
workflow_result["safety_checks"].append({
"type": "clinical_red_flags",
"flags": triage_assessment["red_flags"],
"action": "immediate_physician_notification"
})
except Exception as e:
# Fallback: Conservative triage (assume urgent)
triage_assessment = {
"triage_level": TriageLevel.URGENT.value,
"recommended_department": "emergency",
"fallback": True
}
# Step 4: Resource allocation
try:
resource_allocation = resource_agent.start(
f"Allocate resources for patient {patient_id} "
f"with triage {triage_assessment.get('triage_level')} "
f"to {triage_assessment.get('recommended_department')}"
)
workflow_result["clinical_pathway"].append({
"step": "resource_allocation",
"status": "completed",
"bed_assigned": resource_allocation.get("assigned_bed"),
"staff_assigned": resource_allocation.get("assigned_staff")
})
except Exception as e:
# Fallback: Queue for next available
workflow_result["clinical_pathway"].append({
"step": "resource_allocation",
"status": "queued",
"queue_position": "next_available",
"error": str(e)
})
workflow_result["status"] = "completed"
return workflow_result
# HIPAA-compliant data handling patterns
class HIPAACompliancePatterns:
"""
Reusable patterns for HIPAA-compliant healthcare workflows
"""
@staticmethod
def anonymize_patient_data(data: Dict) -> Dict:
"""Remove PHI for analytics and reporting"""
safe_fields = ["triage_level", "department", "wait_time", "resource_type"]
return {k: v for k, v in data.items() if k in safe_fields}
@staticmethod
def audit_log_access(user_id: str, patient_id: str, action: str, reason: str):
"""Create audit trail for EMR access"""
return {
"timestamp": datetime.now().isoformat(),
"user_id": user_id,
"patient_id": patient_id,
"action": action,
"reason": reason,
"ip_address": "10.0.0.1", # Would be actual IP
"session_id": "SEC-" + datetime.now().strftime('%Y%m%d%H%M%S')
}
@staticmethod
def encrypt_sensitive_data(data: Dict) -> Dict:
"""Encrypt sensitive fields (placeholder for real encryption)"""
sensitive_fields = ["ssn", "dob", "address", "phone"]
encrypted = data.copy()
for field in sensitive_fields:
if field in encrypted:
encrypted[field] = f"ENCRYPTED_{field.upper()}"
return encrypted
# Cross-department coordination patterns
class HealthcareCoordinationPatterns:
"""
Patterns for coordinating across healthcare departments
"""
@staticmethod
def coordinate_lab_orders(patient_id: str, triage_level: str) -> Agent:
"""Create agent for lab order coordination"""
return Agent(
name="LabCoordinator",
instructions=f"""Coordinate laboratory orders for patient {patient_id}.
Priority level: {triage_level}.
Ensure STAT orders for critical patients.
Track specimen collection and result delivery.
SLA: Order placement within 2 minutes."""
)
@staticmethod
def coordinate_radiology(patient_id: str, imaging_type: str) -> Agent:
"""Create agent for radiology coordination"""
return Agent(
name="RadiologyCoordinator",
instructions=f"""Schedule {imaging_type} imaging for patient {patient_id}.
Check for contrast allergies and pregnancy status.
Coordinate with transport for patient movement.
SLA: Schedule within 5 minutes for urgent cases."""
)
@staticmethod
def coordinate_pharmacy(patient_id: str, medications: List[str]) -> Agent:
"""Create agent for pharmacy coordination"""
return Agent(
name="PharmacyCoordinator",
instructions=f"""Verify and dispense medications for patient {patient_id}.
Check for drug interactions and allergies.
Ensure proper dosing based on patient parameters.
SLA: Medication verification within 3 minutes."""
)
# Example usage
if __name__ == "__main__":
# Process emergency patient
patient_id = "PT-2024-00123"
chief_complaint = "severe chest pain radiating to left arm"
result = emergency_triage_workflow(patient_id, chief_complaint)
print("Triage workflow result:", result)
# Create specialized coordinators
lab_coordinator = HealthcareCoordinationPatterns.coordinate_lab_orders(
patient_id, TriageLevel.EMERGENT.value
)
radiology_coordinator = HealthcareCoordinationPatterns.coordinate_radiology(
patient_id, "chest_xray"
)
pharmacy_coordinator = HealthcareCoordinationPatterns.coordinate_pharmacy(
patient_id, ["aspirin", "nitroglycerin"]
)
# HIPAA compliance example
audit_log = HIPAACompliancePatterns.audit_log_access(
"DR-001", patient_id, "EMR_ACCESS", "emergency_triage"
)
print("Audit log created:", audit_log)
@@ -0,0 +1,298 @@
"""
Manufacturing Industry Template
================================
Order scheduling, inventory management, and quality inspection workflow
Based on SRAO Framework patterns with 70% cross-industry reuse
Key agents:
- ParseOrder: Extracts order details from various formats
- CheckInventory: Validates material availability
- OptimizeSchedule: Optimizes production scheduling
- DefectDetect: Quality control and defect detection
"""
from praisonaiagents import Agent, tool
from typing import Dict, List, Any
from pydantic import BaseModel
# I/O Schemas
class OrderDetails(BaseModel):
"""Structured order information"""
order_id: str
customer_id: str
product_id: str
quantity: int
priority: str = "normal" # normal, high, urgent
due_date: str
requirements: Dict[str, Any] = {}
class InventoryStatus(BaseModel):
"""Material availability status"""
material_id: str
available_quantity: int
reorder_level: int
supplier_lead_time: int
location: str
class ProductionSchedule(BaseModel):
"""Optimized production schedule"""
schedule_id: str
production_line: str
start_time: str
end_time: str
assigned_workers: List[str]
estimated_completion: str
bottleneck_analysis: Dict[str, Any] = {}
class QualityReport(BaseModel):
"""Quality inspection results"""
batch_id: str
inspection_time: str
defect_rate: float
defect_types: List[str]
passed: bool
recommendations: List[str]
# Tools for agents
@tool
def extract_order_details(text: str) -> Dict:
"""Extract structured order information from unstructured text"""
# Simulate order parsing logic
return {
"order_id": "ORD-2024-001",
"customer_id": "CUST-123",
"product_id": "PROD-456",
"quantity": 100,
"priority": "high",
"due_date": "2024-03-15",
"requirements": {"material": "steel", "finish": "polished"}
}
@tool
def check_material_availability(product_id: str, quantity: int) -> Dict:
"""Check if materials are available for production"""
# Simulate inventory check
return {
"material_id": "MAT-789",
"available_quantity": 500,
"reorder_level": 100,
"supplier_lead_time": 7,
"location": "Warehouse-A"
}
@tool
def calculate_optimal_schedule(order: Dict, inventory: Dict) -> Dict:
"""Calculate optimal production schedule based on constraints"""
# Simulate scheduling optimization
return {
"schedule_id": "SCH-2024-001",
"production_line": "Line-3",
"start_time": "2024-03-10T08:00:00",
"end_time": "2024-03-14T17:00:00",
"assigned_workers": ["Worker-001", "Worker-002"],
"estimated_completion": "2024-03-14T16:30:00",
"bottleneck_analysis": {
"critical_path": "Assembly",
"buffer_time": 2.5
}
}
@tool
def perform_quality_inspection(batch_id: str) -> Dict:
"""Perform automated quality inspection on production batch"""
# Simulate quality control
return {
"batch_id": batch_id,
"inspection_time": "2024-03-14T17:00:00",
"defect_rate": 0.02,
"defect_types": ["surface_scratch", "dimension_variance"],
"passed": True,
"recommendations": [
"Adjust machine calibration on Line-3",
"Increase inspection frequency for next batch"
]
}
# Agent Definitions with SLA requirements
order_parser = Agent(
name="ParseOrder",
instructions="""You are an order parsing specialist. Extract and structure order information
from various formats (email, PDF, API). Ensure all required fields are captured and validated.
SLA: Process within 30 seconds per order.""",
tools=[extract_order_details]
)
inventory_checker = Agent(
name="CheckInventory",
instructions="""You are an inventory management specialist. Verify material availability,
check reorder points, and identify potential supply chain issues.
SLA: Real-time response within 5 seconds.""",
tools=[check_material_availability]
)
schedule_optimizer = Agent(
name="OptimizeSchedule",
instructions="""You are a production scheduling expert. Optimize production schedules
based on order priority, resource availability, and constraints. Minimize idle time
and maximize throughput.
SLA: Generate schedule within 2 minutes for complex orders.""",
tools=[calculate_optimal_schedule]
)
quality_inspector = Agent(
name="DefectDetect",
instructions="""You are a quality control specialist. Perform automated inspections,
detect defects using vision analysis, and provide improvement recommendations.
SLA: Complete inspection within 1 minute per batch.""",
tools=[perform_quality_inspection]
)
# Workflow with fallback strategies
def manufacturing_workflow(order_text: str):
"""
Complete manufacturing workflow from order to quality control
Includes fallback strategies for each critical step
"""
# Step 1: Parse order with fallback
try:
order_details = order_parser.start(f"Parse this order: {order_text}")
if not order_details:
# Fallback: Manual review queue
return {"status": "manual_review_required", "reason": "order_parsing_failed"}
except Exception as e:
return {"status": "error", "stage": "order_parsing", "error": str(e)}
# Step 2: Check inventory with fallback
try:
inventory_status = inventory_checker.start(
f"Check materials for order: {order_details}"
)
if not inventory_status:
# Fallback: Alternative supplier check
return {"status": "alternative_supplier_needed", "order": order_details}
except Exception as e:
# Fallback: Conservative estimate with safety margin
inventory_status = {"estimated": True, "safety_margin": 1.5}
# Step 3: Optimize schedule with fallback
try:
schedule = schedule_optimizer.start(
f"Create schedule for order {order_details} with inventory {inventory_status}"
)
if not schedule:
# Fallback: Standard scheduling template
schedule = {"template": "standard", "priority": "normal"}
except Exception as e:
# Fallback: Queue for next available slot
schedule = {"status": "queued", "estimated_start": "next_available"}
# Step 4: Quality inspection with fallback
try:
quality_report = quality_inspector.start(
f"Inspect batch for schedule {schedule}"
)
if not quality_report:
# Fallback: Manual inspection required
quality_report = {"requires_manual": True, "inspector_assigned": "QC-Team-1"}
except Exception as e:
# Fallback: Quarantine for review
quality_report = {"status": "quarantined", "review_required": True}
return {
"status": "completed",
"order": order_details,
"inventory": inventory_status,
"schedule": schedule,
"quality": quality_report
}
# Cross-industry reusable patterns (70% reuse)
class IndustryAgentPattern:
"""
Base pattern reusable across industries
Provides common agent capabilities that can be specialized
"""
@staticmethod
def create_data_parser(name: str, domain: str, sla_seconds: int = 30):
"""Create a data parsing agent for any domain"""
return Agent(
name=name,
instructions=f"""You are a {domain} data parsing specialist.
Extract and structure information from various formats.
Validate all required fields according to {domain} standards.
SLA: Process within {sla_seconds} seconds."""
)
@staticmethod
def create_resource_checker(name: str, resource_type: str, sla_seconds: int = 5):
"""Create a resource availability checker for any resource type"""
return Agent(
name=name,
instructions=f"""You are a {resource_type} availability specialist.
Verify {resource_type} availability and identify constraints.
Provide real-time status and alternative options if needed.
SLA: Response within {sla_seconds} seconds."""
)
@staticmethod
def create_optimizer(name: str, optimization_target: str, sla_minutes: int = 2):
"""Create an optimization agent for any target metric"""
return Agent(
name=name,
instructions=f"""You are an optimization expert for {optimization_target}.
Find optimal solutions considering all constraints and priorities.
Minimize waste and maximize efficiency.
SLA: Generate solution within {sla_minutes} minutes."""
)
@staticmethod
def create_inspector(name: str, inspection_type: str, sla_minutes: int = 1):
"""Create an inspection agent for any quality metric"""
return Agent(
name=name,
instructions=f"""You are a {inspection_type} inspection specialist.
Perform automated analysis and detect anomalies.
Provide actionable recommendations for improvement.
SLA: Complete inspection within {sla_minutes} minute(s)."""
)
# Example usage
if __name__ == "__main__":
# Create a manufacturing workflow instance
order = "Customer ABC needs 100 units of Product-XYZ by March 15th, high priority"
# Option 1: Use specialized agents
result = manufacturing_workflow(order)
print("Manufacturing workflow result:", result)
# Option 2: Use reusable patterns for other industries
# These patterns can be adapted for Energy, Healthcare, Agriculture, Transportation
energy_parser = IndustryAgentPattern.create_data_parser(
"EnergyDataParser", "wind farm telemetry", sla_seconds=10
)
healthcare_checker = IndustryAgentPattern.create_resource_checker(
"BedAvailability", "hospital bed", sla_seconds=3
)
agriculture_optimizer = IndustryAgentPattern.create_optimizer(
"IrrigationOptimizer", "water usage", sla_minutes=5
)
transport_inspector = IndustryAgentPattern.create_inspector(
"TunnelSafety", "structural integrity", sla_minutes=2
)
@@ -0,0 +1,599 @@
"""
Transportation Industry Template
================================
Tunnel safety monitoring and infrastructure management workflow
Based on SRAO Framework with LiDAR and sensor fusion
Key agents:
- LiDARFusion: Processes LiDAR point cloud data
- DisplacementCalculator: Calculates structural displacement
- HeatmapGenerator: Generates safety heatmaps
- MaintenancePlanner: Plans infrastructure maintenance
"""
from praisonaiagents import Agent, tool
from typing import Dict, List, Any, Tuple, Optional
from pydantic import BaseModel
from datetime import datetime, timedelta
from enum import Enum
import math
# Safety levels for infrastructure
class SafetyLevel(str, Enum):
SAFE = "safe"
CAUTION = "caution"
WARNING = "warning"
DANGER = "danger"
CRITICAL = "critical"
# Infrastructure types
class InfrastructureType(str, Enum):
TUNNEL = "tunnel"
BRIDGE = "bridge"
HIGHWAY = "highway"
RAILWAY = "railway"
AIRPORT = "airport"
# I/O Schemas
class LiDARScan(BaseModel):
"""LiDAR scan data for infrastructure"""
scan_id: str
infrastructure_id: str
scan_time: str
point_cloud_size: int # Number of points
scan_resolution: float # Points per square meter
coverage_area: float # Square meters
anomaly_points: List[Tuple[float, float, float]] # XYZ coordinates
reference_baseline: Optional[Dict[str, Any]]
class DisplacementAnalysis(BaseModel):
"""Structural displacement analysis"""
analysis_id: str
infrastructure_id: str
analysis_time: str
max_displacement: float # millimeters
displacement_vector: Tuple[float, float, float] # XYZ displacement
critical_zones: List[Dict[str, Any]]
settlement_rate: float # mm/month
tilt_angle: float # degrees
safety_factor: float
class SafetyHeatmap(BaseModel):
"""Infrastructure safety heatmap"""
heatmap_id: str
infrastructure_id: str
generation_time: str
grid_resolution: float # meters
safety_scores: List[List[float]] # 2D grid of safety scores
critical_areas: List[Dict[str, Any]]
overall_safety: SafetyLevel
risk_trends: Dict[str, Any]
class MaintenanceSchedule(BaseModel):
"""Maintenance planning schedule"""
schedule_id: str
infrastructure_id: str
maintenance_type: str # preventive, corrective, emergency
priority_level: str # low, medium, high, critical
scheduled_date: str
estimated_duration: int # hours
required_resources: List[str]
traffic_impact: str
cost_estimate: float
# Transportation-specific tools
@tool
def process_lidar_scan(infrastructure_id: str, scan_type: str = "full") -> Dict:
"""Process LiDAR point cloud data for structural analysis"""
# Simulate LiDAR processing
return {
"scan_id": f"LIDAR-{datetime.now().strftime('%Y%m%d%H%M')}",
"infrastructure_id": infrastructure_id,
"scan_time": datetime.now().isoformat(),
"point_cloud_size": 5000000, # 5 million points
"scan_resolution": 100.0, # points/m²
"coverage_area": 50000.0, # m²
"anomaly_points": [
(10.5, 20.3, 5.1),
(15.2, 25.8, 5.3),
(20.1, 30.5, 5.5)
],
"reference_baseline": {
"date": "2024-01-01",
"baseline_id": "BSL-001"
}
}
@tool
def calculate_displacement(current_scan: Dict, baseline_scan: Dict) -> Dict:
"""Calculate structural displacement from LiDAR scans"""
# Simulate displacement calculation
anomalies = current_scan.get("anomaly_points", [])
if len(anomalies) > 5:
max_displacement = 15.0
safety_factor = 0.6
safety_level = SafetyLevel.WARNING
elif len(anomalies) > 2:
max_displacement = 8.0
safety_factor = 0.8
safety_level = SafetyLevel.CAUTION
else:
max_displacement = 3.0
safety_factor = 0.95
safety_level = SafetyLevel.SAFE
return {
"analysis_id": f"DISP-{datetime.now().strftime('%Y%m%d%H%M')}",
"infrastructure_id": current_scan["infrastructure_id"],
"analysis_time": datetime.now().isoformat(),
"max_displacement": max_displacement,
"displacement_vector": (2.5, 1.8, max_displacement),
"critical_zones": [
{
"zone_id": "CZ-001",
"location": "tunnel_crown",
"displacement": max_displacement * 0.8,
"risk": "high" if max_displacement > 10 else "medium"
}
],
"settlement_rate": max_displacement / 30, # mm/month
"tilt_angle": math.degrees(math.atan(max_displacement / 1000)),
"safety_factor": safety_factor
}
@tool
def generate_safety_heatmap(displacement_data: Dict, sensor_data: List[Dict]) -> Dict:
"""Generate safety heatmap visualization for infrastructure"""
# Simulate heatmap generation
safety_factor = displacement_data.get("safety_factor", 1.0)
# Create grid (simplified 10x10)
grid_size = 10
safety_grid = []
critical_areas = []
for i in range(grid_size):
row = []
for j in range(grid_size):
# Generate safety score based on position and displacement
base_score = safety_factor
distance_factor = math.sqrt((i-5)**2 + (j-5)**2) / 10
safety_score = min(1.0, base_score + (0.2 * distance_factor))
row.append(safety_score)
if safety_score < 0.7:
critical_areas.append({
"grid_position": (i, j),
"safety_score": safety_score,
"recommended_action": "immediate_inspection"
})
safety_grid.append(row)
# Determine overall safety
avg_safety = sum(sum(row) for row in safety_grid) / (grid_size * grid_size)
if avg_safety > 0.9:
overall = SafetyLevel.SAFE
elif avg_safety > 0.8:
overall = SafetyLevel.CAUTION
elif avg_safety > 0.7:
overall = SafetyLevel.WARNING
else:
overall = SafetyLevel.DANGER
return {
"heatmap_id": f"HEAT-{datetime.now().strftime('%Y%m%d%H%M')}",
"infrastructure_id": displacement_data["infrastructure_id"],
"generation_time": datetime.now().isoformat(),
"grid_resolution": 5.0, # meters
"safety_scores": safety_grid,
"critical_areas": critical_areas,
"overall_safety": overall.value,
"risk_trends": {
"trend": "deteriorating" if avg_safety < 0.8 else "stable",
"rate": -0.02 if avg_safety < 0.8 else 0.0
}
}
@tool
def plan_maintenance(infrastructure_id: str, safety_analysis: Dict, traffic_data: Dict) -> Dict:
"""Plan maintenance based on safety analysis and traffic patterns"""
safety_level = safety_analysis.get("overall_safety", "safe")
# Determine maintenance urgency
if safety_level in ["danger", "critical"]:
maintenance_type = "emergency"
priority = "critical"
days_until = 1
duration = 48
elif safety_level == "warning":
maintenance_type = "corrective"
priority = "high"
days_until = 7
duration = 24
elif safety_level == "caution":
maintenance_type = "preventive"
priority = "medium"
days_until = 30
duration = 12
else:
maintenance_type = "preventive"
priority = "low"
days_until = 90
duration = 8
# Traffic impact assessment
peak_traffic = traffic_data.get("peak_volume", 1000)
if peak_traffic > 5000:
traffic_impact = "severe"
cost_multiplier = 2.0
elif peak_traffic > 2000:
traffic_impact = "moderate"
cost_multiplier = 1.5
else:
traffic_impact = "minimal"
cost_multiplier = 1.0
return {
"schedule_id": f"MAINT-{datetime.now().strftime('%Y%m%d%H%M')}",
"infrastructure_id": infrastructure_id,
"maintenance_type": maintenance_type,
"priority_level": priority,
"scheduled_date": (datetime.now() + timedelta(days=days_until)).isoformat(),
"estimated_duration": duration,
"required_resources": [
"inspection_team",
"repair_crew",
"safety_equipment",
"traffic_control"
],
"traffic_impact": traffic_impact,
"cost_estimate": 50000 * cost_multiplier * (duration / 24)
}
# Transportation agent definitions
lidar_agent = Agent(
name="LiDARFusion",
instructions="""You are a LiDAR data processing specialist for infrastructure.
Process point cloud data from mobile and terrestrial LiDAR scanners.
Perform data fusion from multiple sensor sources.
Identify structural anomalies and deformations.
SLA: Process scan data within 5 minutes.""",
tools=[process_lidar_scan]
)
displacement_agent = Agent(
name="DisplacementCalculator",
instructions="""You are a structural displacement analysis expert.
Calculate precise displacement measurements from temporal scans.
Analyze settlement patterns and structural movements.
Predict future displacement trends using ML models.
SLA: Complete analysis within 2 minutes.""",
tools=[calculate_displacement]
)
heatmap_agent = Agent(
name="HeatmapGenerator",
instructions="""You are a safety visualization specialist.
Generate intuitive safety heatmaps for infrastructure monitoring.
Integrate multiple data sources for comprehensive risk assessment.
Highlight critical areas requiring immediate attention.
SLA: Generate heatmap within 1 minute.""",
tools=[generate_safety_heatmap]
)
maintenance_agent = Agent(
name="MaintenancePlanner",
instructions="""You are an infrastructure maintenance planning expert.
Schedule maintenance based on structural health and traffic patterns.
Optimize maintenance windows to minimize disruption.
Coordinate resources and estimate costs.
SLA: Create maintenance plan within 3 minutes.""",
tools=[plan_maintenance]
)
# Transportation infrastructure monitoring workflow
def infrastructure_monitoring_workflow(
infrastructure_ids: List[str],
infrastructure_type: InfrastructureType,
traffic_data: Dict
):
"""
Complete infrastructure monitoring workflow from scanning to maintenance
Includes safety assessment and predictive maintenance
"""
monitoring_results = {
"timestamp": datetime.now().isoformat(),
"infrastructure_type": infrastructure_type.value,
"structures_monitored": len(infrastructure_ids),
"safety_assessments": [],
"maintenance_required": [],
"emergency_alerts": [],
"network_health_score": 0.0
}
total_safety_score = 0.0
for infra_id in infrastructure_ids:
try:
# Step 1: LiDAR scanning
lidar_scan = lidar_agent.start(
f"Process LiDAR scan for {infrastructure_type.value} {infra_id}"
)
# Step 2: Displacement analysis
# Get baseline (would be from database in real scenario)
baseline = {"scan_id": "BSL-001", "point_cloud": []}
displacement_analysis = displacement_agent.start(
f"Calculate displacement for scan: {lidar_scan} against baseline: {baseline}"
)
# Step 3: Generate safety heatmap
sensor_data = [] # Would include IoT sensor readings
safety_heatmap = heatmap_agent.start(
f"Generate heatmap for displacement: {displacement_analysis} "
f"with sensors: {sensor_data}"
)
monitoring_results["safety_assessments"].append({
"infrastructure_id": infra_id,
"safety_level": safety_heatmap.get("overall_safety"),
"max_displacement": displacement_analysis.get("max_displacement"),
"critical_zones": len(safety_heatmap.get("critical_areas", []))
})
# Check for emergency conditions
if safety_heatmap.get("overall_safety") in ["danger", "critical"]:
monitoring_results["emergency_alerts"].append({
"infrastructure_id": infra_id,
"alert_level": "emergency",
"safety_status": safety_heatmap["overall_safety"],
"immediate_action": "close_to_traffic",
"notification_sent": True
})
# Step 4: Emergency maintenance planning
maintenance_plan = maintenance_agent.start(
f"Plan emergency maintenance for {infra_id} "
f"with safety: {safety_heatmap} and traffic: {traffic_data}"
)
monitoring_results["maintenance_required"].append(maintenance_plan)
elif safety_heatmap.get("overall_safety") in ["warning", "caution"]:
# Regular maintenance planning
maintenance_plan = maintenance_agent.start(
f"Plan maintenance for {infra_id} "
f"with safety: {safety_heatmap} and traffic: {traffic_data}"
)
monitoring_results["maintenance_required"].append(maintenance_plan)
# Calculate safety score contribution
safety_mapping = {
"safe": 1.0, "caution": 0.8, "warning": 0.6,
"danger": 0.3, "critical": 0.1
}
total_safety_score += safety_mapping.get(
safety_heatmap.get("overall_safety", "safe"), 0.5
)
except Exception as e:
# Fallback: Manual inspection
monitoring_results["emergency_alerts"].append({
"infrastructure_id": infra_id,
"alert_level": "system_failure",
"error": str(e),
"action": "dispatch_manual_inspection_team"
})
# Calculate network health score
if infrastructure_ids:
monitoring_results["network_health_score"] = (
total_safety_score / len(infrastructure_ids) * 100
)
return monitoring_results
# Multi-modal transportation patterns
class MultiModalTransportPatterns:
"""
Patterns for different transportation infrastructure types
Demonstrates cross-industry reuse for transportation sector
"""
@staticmethod
def adapt_for_bridge_monitoring() -> Agent:
"""Adapt tunnel monitoring for bridge structures"""
return Agent(
name="BridgeMonitor",
instructions="""Monitor bridge structural health using sensors.
Track deck deflection, cable tension, and vibration patterns.
Detect fatigue cracks and corrosion.
Consider wind and seismic loads.
SLA: Real-time monitoring with 10-second updates."""
)
@staticmethod
def adapt_for_railway_tracks() -> Agent:
"""Adapt for railway track monitoring"""
return Agent(
name="RailwayMonitor",
instructions="""Monitor railway track geometry and condition.
Detect rail defects, gauge variations, and ballast degradation.
Analyze wheel-rail interaction forces.
Predict track maintenance needs.
SLA: Process track inspection data within 3 minutes per km."""
)
@staticmethod
def adapt_for_airport_runways() -> Agent:
"""Adapt for airport runway monitoring"""
return Agent(
name="RunwayMonitor",
instructions="""Monitor airport runway surface conditions.
Detect FOD (Foreign Object Debris) and surface irregularities.
Assess pavement condition and friction levels.
Monitor drainage and lighting systems.
SLA: Complete runway scan within 10 minutes."""
)
# Intelligent traffic management integration
class TrafficManagementPatterns:
"""
Patterns for integrating with intelligent traffic systems
"""
@staticmethod
def optimize_traffic_flow(maintenance_window: Dict, traffic_patterns: Dict) -> Dict:
"""Optimize traffic flow during maintenance"""
peak_hours = traffic_patterns.get("peak_hours", [7, 9, 17, 19])
maintenance_start = maintenance_window.get("start_time", "02:00")
# Calculate optimal diversion routes
return {
"strategy": "dynamic_routing",
"diversion_routes": [
{"route_id": "ALT-1", "capacity": 0.6, "added_time": 15},
{"route_id": "ALT-2", "capacity": 0.3, "added_time": 20}
],
"lane_management": {
"reversible_lanes": True,
"peak_direction": "inbound" if int(maintenance_start[:2]) < 12 else "outbound"
},
"public_transport": {
"increase_frequency": True,
"additional_services": 4
},
"estimated_delay": 10 # minutes
}
@staticmethod
def incident_response_plan(incident_type: str, location: Dict) -> Dict:
"""Generate incident response plan"""
response_times = {
"accident": 5,
"breakdown": 10,
"infrastructure_failure": 2,
"weather_hazard": 15
}
return {
"incident_id": f"INC-{datetime.now().strftime('%Y%m%d%H%M')}",
"response_time": response_times.get(incident_type, 10),
"resources_dispatched": [
r for r in [
"emergency_response_team",
"traffic_control_unit",
"maintenance_crew" if "infrastructure" in incident_type else None
] if r is not None
],
"traffic_management": {
"lane_closures": 2,
"speed_reduction": 30, # km/h
"message_signs": ["Incident ahead", "Reduce speed", "Merge left"]
},
"estimated_clearance": 45 # minutes
}
# Predictive maintenance patterns
class PredictiveMaintenancePatterns:
"""
ML-based predictive maintenance for transportation infrastructure
"""
@staticmethod
def predict_failure_probability(
displacement_trend: List[float],
environmental_factors: Dict
) -> float:
"""Predict probability of structural failure"""
# Simplified prediction model
trend_slope = (displacement_trend[-1] - displacement_trend[0]) / len(displacement_trend)
temperature_factor = abs(environmental_factors.get("temp_variation", 0)) / 50
moisture_factor = environmental_factors.get("humidity", 50) / 100
base_probability = 0.1
trend_contribution = max(0, trend_slope * 10)
env_contribution = (temperature_factor + moisture_factor) * 0.2
return min(1.0, base_probability + trend_contribution + env_contribution)
@staticmethod
def optimize_maintenance_schedule(
infrastructure_conditions: List[Dict],
budget_constraint: float
) -> List[Dict]:
"""Optimize maintenance schedule across infrastructure network"""
# Sort by urgency (safety_factor * cost_efficiency)
prioritized = sorted(
infrastructure_conditions,
key=lambda x: (1 - x.get("safety_factor", 1)) * (1 / x.get("cost", 1)),
reverse=True
)
scheduled = []
total_cost = 0
for infra in prioritized:
if total_cost + infra.get("cost", 0) <= budget_constraint:
scheduled.append({
"infrastructure_id": infra["id"],
"scheduled_date": infra["recommended_date"],
"priority": "high" if infra["safety_factor"] < 0.7 else "medium"
})
total_cost += infra.get("cost", 0)
return scheduled
# Example usage
if __name__ == "__main__":
# Monitor tunnel network
tunnel_ids = [f"TUN-{i:03d}" for i in range(1, 6)]
traffic = {
"peak_volume": 3000,
"off_peak_volume": 800,
"peak_hours": [7, 9, 17, 19]
}
result = infrastructure_monitoring_workflow(
tunnel_ids,
InfrastructureType.TUNNEL,
traffic
)
print("Infrastructure monitoring result:", result)
# Multi-modal adaptation examples
bridge_monitor = MultiModalTransportPatterns.adapt_for_bridge_monitoring()
railway_monitor = MultiModalTransportPatterns.adapt_for_railway_tracks()
runway_monitor = MultiModalTransportPatterns.adapt_for_airport_runways()
# Traffic management example
maintenance = {"start_time": "22:00", "duration": 8}
traffic_plan = TrafficManagementPatterns.optimize_traffic_flow(maintenance, traffic)
print("Traffic optimization plan:", traffic_plan)
# Predictive maintenance example
displacement_history = [2.0, 2.3, 2.8, 3.5, 4.3] # mm over months
environment = {"temp_variation": 30, "humidity": 75}
failure_prob = PredictiveMaintenancePatterns.predict_failure_probability(
displacement_history, environment
)
print(f"Failure probability: {failure_prob:.2%}")
@@ -80,7 +80,7 @@ Be precise and quote exact values when asked about codes or numbers.""",
print(f"\nAnswer: {response}\n")
# Verify the answer contains the unique code
if "ZEBRA-71" in response.upper():
if "ZEBRA-71" in str(response or "").upper():
print("✅ VERIFIED: Agent correctly retrieved the unique code from context!\n")
else:
print("❌ WARNING: Agent did not find the code - check retrieval\n")
@@ -104,7 +104,7 @@ Be precise and quote exact values when asked about codes or numbers.""",
response = agent.chat("What is the security incident code?")
print(f"\nAnswer: {response}\n")
if "HAWK-88" in response.upper():
if "HAWK-88" in str(response or "").upper():
print("✅ VERIFIED: Agent correctly retrieved the security code!\n")
else:
print("❌ WARNING: Agent did not find the code - check retrieval\n")
+13 -3
View File
@@ -19,6 +19,16 @@ from praisonaiagents import Agent, AgentTeam
# Initialize observability
obs.init()
def set_span_attr(span, key, value):
"""Set span attribute safely across observability implementations."""
if hasattr(span, "set_attribute"):
span.set_attribute(key, value)
return
attrs = getattr(span, "attributes", None)
if attrs is not None:
attrs[key] = value
# Create agents
researcher = Agent(
name="Researcher",
@@ -37,13 +47,13 @@ with obs.trace("content-creation", metadata={"workflow": "research-write"}):
# Research phase
with obs.span("research-phase", kind=SpanKind.AGENT) as research_span:
research_span.attributes["agent"] = "Researcher"
set_span_attr(research_span, "agent", "Researcher")
research_result = researcher.chat("Research the benefits of AI in healthcare")
# Writing phase
with obs.span("writing-phase", kind=SpanKind.AGENT) as write_span:
write_span.attributes["agent"] = "Writer"
write_span.attributes["input_length"] = len(str(research_result))
set_span_attr(write_span, "agent", "Writer")
set_span_attr(write_span, "input_length", len(str(research_result)))
final_content = writer.chat(f"Write a blog post based on: {research_result}")
print("Final Content:")
+8 -5
View File
@@ -13,7 +13,8 @@ Expected output:
- Session can be resumed later with same session_id
"""
from praisonaiagents import Agent, db
from praisonaiagents import Agent
from praisonaiagents.db import db
# Create database connection (PostgreSQL + Redis)
my_db = db(
@@ -25,10 +26,12 @@ my_db = db(
agent = Agent(
name="Assistant",
instructions="You are a helpful assistant. Be concise.",
db=my_db,
# session_id is optional - defaults to per-hour ID (YYYYMMDDHH-hash)
# Set explicitly for session continuity across runs:
session_id="my-persistent-session"
memory={
"db": my_db,
# session_id is optional - defaults to per-hour ID (YYYYMMDDHH-hash)
# Set explicitly for session continuity across runs:
"session_id": "my-persistent-session",
},
)
# Chat - messages are automatically persisted
+6 -3
View File
@@ -12,7 +12,8 @@ Expected output:
- Export shows full session data
"""
from praisonaiagents import Agent, db
from praisonaiagents import Agent
from praisonaiagents.db import db
def get_weather(city: str) -> str:
"""Get weather for a city."""
@@ -29,8 +30,10 @@ agent = Agent(
name="WeatherBot",
instructions="You help with weather. Use the get_weather tool when asked.",
tools=[get_weather],
db=my_db,
session_id="weather-session-001"
memory={
"db": my_db,
"session_id": "weather-session-001",
},
)
# First chat - triggers run tracking
+6 -3
View File
@@ -13,7 +13,8 @@ Expected output:
Agent responds with state persisted to Redis
"""
from praisonaiagents import Agent, db
from praisonaiagents import Agent
from praisonaiagents.db import db
print("=== Redis State Store (Agent-First) ===")
@@ -26,8 +27,10 @@ my_db = db(
agent = Agent(
name="Assistant",
instructions="You are a helpful assistant.",
db=my_db,
session_id="redis-state-session"
memory={
"db": my_db,
"session_id": "redis-state-session",
},
)
# Chat - state is automatically managed via Redis
+6 -3
View File
@@ -11,7 +11,8 @@ Expected output:
- Second run: Resumes with history
"""
from praisonaiagents import Agent, db
from praisonaiagents import Agent
from praisonaiagents.db import db
# Create database
my_db = db(
@@ -25,8 +26,10 @@ SESSION_ID = "resume-demo-session"
agent = Agent(
name="MemoryBot",
instructions="You remember our conversation. Be helpful and concise.",
db=my_db,
session_id=SESSION_ID
memory={
"db": my_db,
"session_id": SESSION_ID,
},
)
# Check if this is a new or resumed session
+6 -3
View File
@@ -26,7 +26,8 @@ Expected Output:
Messages persisted: 2
"""
from praisonaiagents import Agent, db
from praisonaiagents import Agent
from praisonaiagents.db import db
# Create database adapter (simplified import)
db_instance = db(
@@ -37,8 +38,10 @@ db_instance = db(
agent = Agent(
name="Assistant",
instructions="You are a helpful assistant. Keep responses brief.",
db=db_instance,
session_id="demo-session-001", # Same session_id = resume conversation
memory={
"db": db_instance,
"session_id": "demo-session-001", # Same session_id = resume conversation
},
output="silent"
)
+6 -3
View File
@@ -11,7 +11,8 @@ Expected output:
- Data persisted to local SQLite file
"""
from praisonaiagents import Agent, db
from praisonaiagents import Agent
from praisonaiagents.db import db
import tempfile
import os
@@ -25,8 +26,10 @@ print(f"Database: {db_path}")
agent = Agent(
name="LocalBot",
instructions="You are a helpful assistant.",
db=my_db,
session_id="local-session"
memory={
"db": my_db,
"session_id": "local-session",
},
)
# Chat
@@ -60,9 +60,10 @@ agents_system = AgentTeam(
process="sequential", output="verbose"
)
print("Starting hierarchical agent generation...")
result = agents_system.start()
if __name__ == "__main__":
print("Starting hierarchical agent generation...")
result = agents_system.start()
print(f"\nHierarchical Result: {result[:200]}...")
print("\n✅ AutoAgents hierarchical generation complete!")
print("Demonstrated coordinated hierarchical workflow between manager and specialist agents.")
print(f"\nHierarchical Result: {str(result)[:200]}...")
print("\n✅ AutoAgents hierarchical generation complete!")
print("Demonstrated coordinated hierarchical workflow between manager and specialist agents.")
+16 -12
View File
@@ -6,10 +6,18 @@ It includes self-reflection for improving code quality and multiple specialized
"""
from praisonaiagents import Agent, Task, AgentTeam
from praisonaiagents.tools import CodeInterpreter
# Initialize code execution tool
code_interpreter = CodeInterpreter()
def run_python_code(code: str) -> str:
"""Execute Python code safely and return stdout or errors."""
import io
import contextlib
try:
buf = io.StringIO()
with contextlib.redirect_stdout(buf):
exec(code, {"__builtins__": __builtins__})
return buf.getvalue() or "Executed successfully (no output)"
except Exception as e:
return f"Execution error: {e}"
# Custom code analysis tool
def analyze_code_complexity(code: str) -> str:
@@ -32,9 +40,7 @@ code_writer = Agent(
goal="Write clean, efficient, and well-documented Python code",
backstory="You are a senior Python developer with 10+ years of experience in writing production-quality code.",
instructions="Write Python code that follows PEP 8 standards, includes proper error handling, and has clear documentation.",
reflection=True,
output="silent",
)
code_reviewer = Agent(
@@ -52,7 +58,7 @@ code_executor = Agent(
goal="Safely execute and test Python code",
backstory="You are responsible for running code and reporting results or errors.",
instructions="Execute the provided code safely and report the output or any errors encountered.",
tools=[code_interpreter]
tools=[run_python_code]
)
# Example tasks
@@ -87,7 +93,7 @@ def demonstrate_code_agent():
workflow = AgentTeam(
agents=[code_writer, code_reviewer, code_executor],
tasks=[write_task, review_task, execute_task],
process="sequential", output="verbose"
process="sequential", output="silent"
)
# Run the workflow
@@ -106,10 +112,8 @@ general_code_agent = Agent(
3. Explain code concepts
4. Refactor code for better performance
5. Add tests and documentation""",
tools=[code_interpreter, analyze_code_complexity],
reflection=True,
tools=[run_python_code, analyze_code_complexity],
output="silent",
)
if __name__ == "__main__":
+19 -16
View File
@@ -41,21 +41,25 @@ def calculate_statistics(numbers: list) -> str:
"""Calculate basic statistics for a list of numbers."""
if not numbers:
return "No numbers provided"
n = len(numbers)
mean = sum(numbers) / n
sorted_nums = sorted(numbers)
try:
nums = [float(x) for x in numbers]
except (TypeError, ValueError):
return "Error: all values must be numbers"
n = len(nums)
mean = sum(nums) / n
sorted_nums = sorted(nums)
median = sorted_nums[n//2] if n % 2 else (sorted_nums[n//2-1] + sorted_nums[n//2]) / 2
variance = sum((x - mean)**2 for x in numbers) / n
variance = sum((x - mean)**2 for x in nums) / n
std_dev = math.sqrt(variance)
return f"""Statistics:
- Count: {n}
- Mean: {mean:.4f}
- Median: {median:.4f}
- Standard Deviation: {std_dev:.4f}
- Min: {min(numbers)}
- Max: {max(numbers)}"""
- Min: {min(nums)}
- Max: {max(nums)}"""
# Create specialized math agents
calculation_agent = Agent(
@@ -66,8 +70,7 @@ calculation_agent = Agent(
instructions="Perform the requested calculations step by step, showing your work clearly.",
tools=[basic_calculator, solve_quadratic, calculate_statistics],
reflection=True,
output="silent",
)
problem_solver_agent = Agent(
@@ -82,8 +85,7 @@ problem_solver_agent = Agent(
4. Show each step of the solution
5. Verify the answer makes sense""",
reflection=True,
output="silent",
)
math_tutor_agent = Agent(
@@ -91,7 +93,8 @@ math_tutor_agent = Agent(
role="Mathematics tutor and explainer",
goal="Explain mathematical concepts clearly and help users understand math",
backstory="You are a patient math tutor who can explain concepts at any level from elementary to advanced.",
instructions="Explain mathematical concepts using simple language, provide examples, and check for understanding."
instructions="Explain mathematical concepts using simple language, provide examples, and check for understanding.",
output="silent",
)
# Example workflow for solving complex problems
@@ -126,7 +129,8 @@ def solve_math_problem_workflow():
workflow = AgentTeam(
agents=[problem_solver_agent, calculation_agent, math_tutor_agent],
tasks=[understand_task, solve_task, verify_task],
process="sequential", output="verbose"
process="sequential",
output="silent",
)
return workflow.start()
@@ -148,8 +152,7 @@ math_assistant = Agent(
Always show your work step by step.""",
tools=[basic_calculator, solve_quadratic, calculate_statistics],
reflection=True,
output="silent",
)
if __name__ == "__main__":
+3 -2
View File
@@ -1,4 +1,5 @@
from praisonaiagents import Agent
agent = Agent(name="TweetAgent", instructions="Create a Tweet based on the topic provided")
agent.launch(port=8080, protocol="mcp")
if __name__ == "__main__":
agent = Agent(name="TweetAgent", instructions="Create a Tweet based on the topic provided")
agent.launch(port=8080, protocol="mcp", path="/")
+238
View File
@@ -0,0 +1,238 @@
#!/usr/bin/env python3
"""
Example: Bot In-Flight Run Control
Demonstrates how to use SessionRunControl to provide better UX for bots
during long-running agent operations. Shows busy feedback, pending message
handling, and /stop command support.
This example shows the "before" and "after" behavior described in issue #1914.
"""
import asyncio
import time
from typing import Optional
# Mock agent for demonstration
class MockAgent:
"""Simple mock agent that simulates long-running tasks."""
def __init__(self, name="assistant"):
self.name = name
self.chat_history = []
self._interrupt_controller = None
def chat(self, prompt: str) -> str:
"""Simulate a long-running chat response."""
print(f"[Agent] Starting to process: '{prompt[:50]}...'")
# Simulate long processing with interrupt checking
for i in range(30): # 3 second task (30 x 0.1s)
time.sleep(0.1)
# Check for interruption
if (self._interrupt_controller and
hasattr(self._interrupt_controller, 'is_set') and
self._interrupt_controller.is_set()):
reason = getattr(self._interrupt_controller, 'reason', 'unknown')
print(f"[Agent] ⚠️ Interrupted: {reason}")
raise InterruptedError(f"Task cancelled: {reason}")
response = f"Completed analysis of: {prompt}"
print(f"[Agent] ✅ Finished: {response}")
return response
async def demo_without_run_control():
"""Demonstrate the old behavior (silent blocking)."""
print("\n" + "="*60)
print("🔴 BEFORE: Without Run Control (Silent Blocking)")
print("="*60)
from praisonai.bots._session import BotSessionManager
# Standard session manager (no run control)
session_mgr = BotSessionManager()
agent = MockAgent("research-agent")
user_id = "user123"
print("\n1. User sends long task:")
print(" User: 'research quantum computing trends'")
# Start first task (this will run for 3 seconds)
task1 = asyncio.create_task(
session_mgr.chat(agent, user_id, "research quantum computing trends")
)
# Simulate user sending follow-up messages during execution
await asyncio.sleep(0.5)
print("\n2. User sends follow-up (this will block silently):")
print(" User: 'actually focus on business impact'")
task2 = asyncio.create_task(
session_mgr.chat(agent, user_id, "actually focus on business impact")
)
await asyncio.sleep(0.5)
print("\n3. User tries to send /stop (this will also block):")
print(" User: '/stop'")
task3 = asyncio.create_task(
session_mgr.chat(agent, user_id, "/stop")
)
print("\n⏳ Waiting for all messages to complete...")
results = await asyncio.gather(task1, task2, task3)
print("\n📊 Results (all processed sequentially with no feedback):")
for i, result in enumerate(results, 1):
print(f" {i}. {result}")
print("\n❌ Problems with this approach:")
print(" - No immediate feedback on follow-up messages")
print(" - No way to cancel long-running tasks")
print(" - User doesn't know if bot is working or broken")
async def demo_with_run_control():
"""Demonstrate the new behavior with run control."""
print("\n" + "="*60)
print("🟢 AFTER: With Run Control (Better UX)")
print("="*60)
from praisonai.bots._run_control import SessionRunControl
from praisonai.bots._session import BotSessionManager
from praisonai.bots._commands import handle_stop_command
# Session manager with run control
run_control = SessionRunControl(
busy_mode="queue",
busy_ack_template="⏳ {action} — will process after current task finishes"
)
session_mgr = BotSessionManager(run_control=run_control)
agent = MockAgent("research-agent")
user_id = "user123"
print("\n1. User sends long task:")
print(" User: 'research quantum computing trends'")
# Start first task
task1 = asyncio.create_task(
session_mgr.chat_with_run_control(agent, user_id, "research quantum computing trends")
)
# Immediate follow-up messages get acknowledgments
await asyncio.sleep(0.5)
print("\n2. User sends follow-up (gets immediate feedback):")
print(" User: 'actually focus on business impact'")
result2 = await session_mgr.chat_with_run_control(
agent, user_id, "actually focus on business impact"
)
print(f" Bot: {result2['response']}")
print(f" Metadata: {result2['metadata']}")
await asyncio.sleep(0.5)
print("\n3. User decides to stop current task:")
print(" User: '/stop'")
stop_response = await handle_stop_command(user_id, run_control)
print(f" Bot: {stop_response}")
# Wait for first task to complete (it should be cancelled)
try:
result1 = await task1
print(f"\n📊 Task 1 result: {result1['response']}")
print(f" Metadata: {result1['metadata']}")
except asyncio.CancelledError:
print("\n📊 Task 1 was cancelled as expected")
print("\n4. User sends fresh request after stopping:")
print(" User: 'what's the weather like?'")
result4 = await session_mgr.chat_with_run_control(
agent, user_id, "what's the weather like?"
)
print(f" Bot: {result4['response']}")
print("\n✅ Benefits of this approach:")
print(" - Immediate feedback on all messages")
print(" - Can cancel long-running tasks with /stop")
print(" - Pending messages are queued and processed in order")
print(" - User always knows what's happening")
async def demo_interrupt_mode():
"""Demonstrate interrupt mode."""
print("\n" + "="*60)
print("⚡ BONUS: Interrupt Mode (Cancel and Restart)")
print("="*60)
from praisonai.bots._run_control import SessionRunControl
from praisonai.bots._session import BotSessionManager
# Use interrupt mode instead of queue mode
run_control = SessionRunControl(busy_mode="interrupt")
session_mgr = BotSessionManager(run_control=run_control)
agent = MockAgent("research-agent")
user_id = "user123"
print("\n1. User starts long task:")
print(" User: 'research renewable energy for 30 pages'")
task1 = asyncio.create_task(
session_mgr.chat_with_run_control(agent, user_id, "research renewable energy for 30 pages")
)
await asyncio.sleep(0.5)
print("\n2. User changes mind (interrupts and restarts):")
print(" User: 'actually just summarize solar panel efficiency'")
result2 = await session_mgr.chat_with_run_control(
agent, user_id, "actually just summarize solar panel efficiency"
)
print(f" Bot: {result2['response']}")
# Wait for original task (should be interrupted)
try:
result1 = await task1
print(f"\n📊 Original task result: {result1['response']}")
if result1['metadata'].get('interrupted'):
print(" ✅ Original task was properly interrupted")
except:
print("\n📊 Original task was cancelled")
print("\n✅ Interrupt mode is great for:")
print(" - When users frequently change direction")
print(" - Interactive sessions where latest intent matters most")
print(" - Real-time collaboration scenarios")
async def main():
"""Run all demonstrations."""
print("🚀 Bot In-Flight Run Control Demo")
print("Solving the silent lock problem described in issue #1914")
# Show the problem
await demo_without_run_control()
# Show the solution
await demo_with_run_control()
# Show advanced feature
await demo_interrupt_mode()
print("\n" + "="*60)
print("🎯 Summary")
print("="*60)
print("SessionRunControl solves the silent blocking problem by providing:")
print("1. Immediate feedback for mid-run messages")
print("2. /stop command support via InterruptController")
print("3. Configurable policies (queue/interrupt/steer)")
print("4. Run generation tracking to prevent race conditions")
print("5. Pending message merging and ordering")
print("\nThis makes bot interactions feel responsive and predictable!")
if __name__ == "__main__":
asyncio.run(main())
+11 -2
View File
@@ -90,9 +90,18 @@ def main():
if result:
print("\n🔍 Analysis Results:")
for task_id, task_result in result["task_results"].items():
task_results = result.get("task_results") if isinstance(result, dict) else result
if isinstance(task_results, dict):
iterable = task_results.items()
elif isinstance(task_results, list):
iterable = enumerate(task_results)
else:
iterable = [("result", task_results)]
for task_id, task_result in iterable:
print(f"\nTask {task_id}:")
print(task_result.raw)
raw_value = getattr(task_result, "raw", None)
print(raw_value if raw_value is not None else str(task_result))
else:
print("❌ Failed to capture and analyze camera feed")
+11 -2
View File
@@ -148,10 +148,19 @@ def main():
"scene_analysis": "🌍 SCENE ANALYSIS"
}
for task_id, task_result in result["task_results"].items():
task_results = result.get("task_results") if isinstance(result, dict) else result
if isinstance(task_results, dict):
iterable = task_results.items()
elif isinstance(task_results, list):
iterable = enumerate(task_results)
else:
iterable = [("result", task_results)]
for task_id, task_result in iterable:
print(f"\n{agent_names.get(task_id, f'Task {task_id}')}:")
print("-" * 40)
print(task_result.raw)
raw_value = getattr(task_result, "raw", None)
print(raw_value if raw_value is not None else str(task_result))
print("\n" + "="*60)
print("✅ Multi-agent analysis complete!")
@@ -34,5 +34,5 @@ print(f"\nChanged to: {handler.get_mode()}")
print("File edits now auto-approved, shell commands still require approval")
# Get statistics
stats = handler.get_stats()
stats = manager.get_stats()
print(f"\nStats: {stats}")
+1 -1
View File
@@ -31,4 +31,4 @@ print(f"Total cost: ${summary['total_cost']:.4f}")
print(f"Avg cost/request: ${summary['avg_cost_per_request']:.4f}")
# Display formatted (uses Rich if available)
handler.display_summary()
handler.print_summary()
@@ -41,9 +41,9 @@ def my_custom_handler(args, context):
custom_cmd = SlashCommand(
name="mycommand",
description="My custom command",
handler=my_custom_handler,
kind=CommandKind.ACTION,
aliases=["mc"]
action=my_custom_handler,
kind=CommandKind.CUSTOM,
alt_names=["mc"]
)
handler.register(custom_cmd)
@@ -1,8 +1,9 @@
import tempfile
from pathlib import Path
from praisonaiagents import Agent, Task, AgentTeam
import logging
import os
import sys
import logging
from praisonaiagents import Agent, Task, AgentTeam
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
@@ -19,11 +20,9 @@ config = {
}
}
# Create sample file in system temp directory
_sample_path = os.path.join(tempfile.gettempdir(), "sample_knowledge.txt")
if not os.path.exists(_sample_path):
with open(_sample_path, "w", encoding="utf-8") as f:
f.write("Mervin Praison is the creator of PraisonAI.\n")
_sample_path = Path(__file__).with_name("sample_knowledge.txt")
if not _sample_path.exists():
_sample_path.write_text("Mervin Praison is the creator of PraisonAI.\n", encoding="utf-8")
# Create an agent with knowledge capabilities
knowledge_agent = Agent(
@@ -31,7 +30,7 @@ knowledge_agent = Agent(
role="Information Specialist",
goal="Store and retrieve knowledge efficiently",
backstory="Expert in managing and utilizing stored knowledge",
knowledge={**config, "sources": [_sample_path]},
knowledge={**config, "sources": [str(_sample_path)]},
)
# Define a task for the agent
@@ -19,4 +19,5 @@ agents = AgentTeam(
execution={"max_iter": 30},
)
agents.start()
if __name__ == "__main__":
agents.start()
@@ -5,7 +5,8 @@ This example demonstrates self-reflection capabilities using PraisonAI's
built-in reflection features for iterative improvement and quality optimization.
"""
from praisonaiagents import Agent, Task, AgentTeam
from praisonaiagents import Agent, Task
from praisonaiagents.agents import AgentTeam
from praisonaiagents.tools import internet_search
print("=== Self-Reflection Optimization Patterns Example ===\n")
@@ -33,9 +34,16 @@ reflection_task = Task(
)
# Run with self-reflection optimization
print("Starting self-reflection optimization demonstration...")
result = reflection_agent.execute_task(reflection_task)
if __name__ == "__main__":
print("Starting self-reflection optimization demonstration...")
agents = AgentTeam(
agents=[reflection_agent],
tasks=[reflection_task],
process="sequential",
output="verbose",
)
result = agents.start()
print(f"\nSelf-Reflection Result: {result[:200]}...")
print("\n✅ Self-reflection optimization complete!")
print("Agent demonstrated iterative improvement through built-in self-reflection capabilities.")
print(f"\nSelf-Reflection Result: {str(result)[:200]}...")
print("\n✅ Self-reflection optimization complete!")
print("Agent demonstrated iterative improvement through built-in self-reflection capabilities.")
+2 -1
View File
@@ -136,4 +136,5 @@ agents = AgentTeam(
output="minimal",
)
agents.start()
if __name__ == "__main__":
agents.start()
+201
View File
@@ -0,0 +1,201 @@
"""
Prototype implementation of Gateway Server with backpressure and flow control.
This demonstrates how the bounded queues and slow consumer detection would be
implemented in the wrapper layer (praisonai package) using the configuration
from the core SDK (praisonaiagents).
"""
from __future__ import annotations
import asyncio
import logging
from dataclasses import dataclass, field
from typing import Any, Dict, Optional
# For the prototype, we'll define minimal config inline
# from praisonaiagents.gateway import GatewayConfig
@dataclass
class SessionConfig:
max_inbox: int = 256
@dataclass
class GatewayConfig:
session_config: SessionConfig = field(default_factory=SessionConfig)
max_buffered_bytes: int = 1024 * 1024 # 1MB default
logger = logging.getLogger(__name__)
@dataclass
class GatewaySession:
"""Gateway session with bounded inbox for backpressure."""
_session_id: str
_agent_id: str
_client_id: Optional[str] = None
_inbox: asyncio.Queue = field(default_factory=asyncio.Queue)
_max_inbox: int = 256
_is_executing: bool = False
def setup_inbox(self, max_inbox: int = 256) -> None:
"""Set up the inbox with proper bounds."""
self._max_inbox = max_inbox
if max_inbox > 0:
self._inbox = asyncio.Queue(maxsize=max_inbox)
else:
self._inbox = asyncio.Queue() # Unbounded for backward compat if 0
async def queue_message(self, message: str) -> bool:
"""Queue a user message with backpressure handling.
Returns:
True if message was queued, False if inbox is full.
"""
if self._max_inbox > 0:
# Bounded queue - check if full
try:
self._inbox.put_nowait(message)
return True
except asyncio.QueueFull:
return False
else:
# Unbounded queue for backward compatibility
await self._inbox.put(message)
return True
class GatewayServer:
"""Gateway server with flow control."""
def __init__(self, config: GatewayConfig):
self.config = config
self._sessions: Dict[str, GatewaySession] = {}
self._clients: Dict[str, Any] = {} # client_id -> WebSocket
def create_session(self, session_id: str, agent_id: str, client_id: str) -> GatewaySession:
"""Create a new session with bounded inbox."""
session = GatewaySession(
_session_id=session_id,
_agent_id=agent_id,
_client_id=client_id,
)
# Set up inbox with configured bounds
session.setup_inbox(self.config.session_config.max_inbox)
self._sessions[session_id] = session
return session
async def handle_message(self, session: GatewaySession, message: str, client_id: str) -> str:
"""Handle incoming message with inbox overflow protection."""
# Try to queue the message
queued = await session.queue_message(message)
if not queued:
# Inbox is full, send error response
await self._send_to_client(
client_id,
{
"type": "error",
"code": "inbox_full",
"message": "Message queue is full. Please wait for current messages to be processed."
}
)
return "Inbox full - message rejected."
# Start processing if not already running
if not session._is_executing:
session._is_executing = True
asyncio.create_task(self._drain_session_queue(session, client_id))
return "Processing started."
return "Message queued."
async def _drain_session_queue(self, session: GatewaySession, client_id: str) -> None:
"""Process all messages from the session's inbox queue."""
try:
while not session._inbox.empty():
msg = await session._inbox.get()
# Process message here (simplified for prototype)
logger.info(f"Processing message for session {session._session_id}: {msg}")
# Simulate processing delay
await asyncio.sleep(0.1)
# Send response to client (simplified)
await self._send_to_client(
client_id,
{
"type": "response",
"message": f"Processed: {msg}"
}
)
except Exception as e:
logger.error(f"Error processing queue for session {session._session_id}: {e}")
finally:
session._is_executing = False
async def _send_to_client(self, client_id: str, data: Dict[str, Any]) -> None:
"""Send data to client with slow consumer detection."""
ws = self._clients.get(client_id)
if ws:
try:
# Check for slow consumer (buffered bytes)
if self.config.max_buffered_bytes > 0:
# Try to get transport buffer size
transport = getattr(ws, 'transport', None) or getattr(ws, '_transport', None)
if transport and hasattr(transport, 'get_write_buffer_size'):
buffered_bytes = transport.get_write_buffer_size()
if buffered_bytes > self.config.max_buffered_bytes:
# Check if this is a droppable event type
event_type = data.get("type", "")
droppable_types = {"presence", "typing", "status"}
if event_type in droppable_types:
# Drop the message for slow consumers
logger.warning(f"Dropping {event_type} event for slow consumer {client_id} (buffered: {buffered_bytes} bytes)")
return
else:
# Critical event - close the connection
logger.warning(f"Closing slow consumer {client_id} (buffered: {buffered_bytes} bytes > {self.config.max_buffered_bytes})")
await ws.close(code=1013, reason="slow consumer")
return
# Send the message
await ws.send_json(data)
except Exception as e:
logger.error(f"Error sending to client {client_id}: {e}", exc_info=True)
# Example usage demonstrating the flow control
async def demo():
"""Demonstrate the flow control implementation."""
# Create config with bounded inbox and slow consumer detection
config = GatewayConfig()
config.session_config.max_inbox = 10 # Small inbox for demo
config.max_buffered_bytes = 1024 # 1KB for demo
# Create server and session
server = GatewayServer(config)
session = server.create_session("sess1", "agent1", "client1")
# Simulate filling up the inbox
print(f"Inbox max size: {session._inbox.maxsize}")
# Fill the inbox
for i in range(10):
success = await session.queue_message(f"Message {i}")
print(f"Queued message {i}: {success}")
# Try to add one more - should fail
success = await session.queue_message("Overflow message")
print(f"Overflow message queued: {success}")
# Process messages
while not session._inbox.empty():
msg = await session._inbox.get()
print(f"Processing: {msg}")
if __name__ == "__main__":
asyncio.run(demo())
+16 -6
View File
@@ -49,10 +49,20 @@ agents = AgentTeam(
process="sequential"
)
# Run all tasks
result = agents.start()
if __name__ == "__main__":
# Run all tasks
result = agents.start()
# Print results
for task_id, task_result in result["task_results"].items():
print(f"\nTask {task_id} Result:")
print(task_result.raw)
# Print results
task_results = result.get("task_results") if isinstance(result, dict) else result
if isinstance(task_results, dict):
iterable = task_results.items()
elif isinstance(task_results, list):
iterable = enumerate(task_results)
else:
iterable = [("result", task_results)]
for task_id, task_result in iterable:
print(f"\nTask {task_id} Result:")
raw_value = getattr(task_result, "raw", None)
print(raw_value if raw_value is not None else str(task_result))
@@ -14,30 +14,33 @@ agent = Agent(name="env-mgmt", backend=managed)
# First call creates agent + environment + session
agent.start("Say hello briefly")
# Access the underlying Anthropic client for environment management
client = managed._get_client()
env_id = managed.environment_id
print(f"Environment ID: {env_id}")
if hasattr(managed, "_get_client"):
# Access the underlying Anthropic client for environment management
client = managed._get_client()
env_id = managed.environment_id
print(f"Environment ID: {env_id}")
# List all environments
environments = client.beta.environments.list()
print(f"\nTotal environments: {len(environments.data)}")
for env in environments.data[:5]:
print(f" {env.id} | {env.name} | {getattr(env, 'status', 'active')}")
# List all environments
environments = client.beta.environments.list()
print(f"\nTotal environments: {len(environments.data)}")
for env in environments.data[:5]:
print(f" {env.id} | {env.name} | {getattr(env, 'status', 'active')}")
# Retrieve a specific environment
env = client.beta.environments.retrieve(env_id)
print(f"\nRetrieved: {env.id} | {env.name}")
# Retrieve a specific environment
env = client.beta.environments.retrieve(env_id)
print(f"\nRetrieved: {env.id} | {env.name}")
# Archive (read-only, existing sessions continue)
archived = client.beta.environments.archive(env_id)
print(f"Archived: {archived.id} | status: {getattr(archived, 'status', 'archived')}")
# Archive (read-only, existing sessions continue)
archived = client.beta.environments.archive(env_id)
print(f"Archived: {archived.id} | status: {getattr(archived, 'status', 'archived')}")
# Delete (only if no sessions reference it — may fail if sessions exist)
try:
client.beta.environments.delete(env_id)
print(f"Deleted: {env_id}")
except Exception as e:
print(f"Delete skipped (expected if sessions exist): {e}")
# Delete (only if no sessions reference it — may fail if sessions exist)
try:
client.beta.environments.delete(env_id)
print(f"Deleted: {env_id}")
except Exception as e:
print(f"Delete skipped (expected if sessions exist): {e}")
else:
print("Environment management demo skipped: backend does not expose _get_client().")
print("\nEnvironment management complete.")
+6 -7
View File
@@ -15,13 +15,6 @@ Protocol: MCP 2025-11-25
from praisonaiagents import Agent, MCP
# Agent with resumability support (automatic)
agent = Agent(
name="Resumable Assistant",
instructions="You are a helpful assistant with resumable connections.",
tools=MCP("https://api.example.com/mcp")
)
# Resumability is automatic - the MCP client:
# 1. Tracks event IDs from SSE streams (id: field)
# 2. Stores last event ID per stream
@@ -29,6 +22,12 @@ agent = Agent(
# 4. Respects retry delay from server (retry: field)
if __name__ == "__main__":
agent = Agent(
name="Resumable Assistant",
instructions="You are a helpful assistant with resumable connections.",
tools=MCP("https://api.example.com/mcp"),
)
print("MCP Resumability Example")
print("=" * 40)
print()
@@ -15,13 +15,6 @@ Protocol: MCP 2025-11-25
from praisonaiagents import Agent, MCP
# Basic agent with automatic session management
agent = Agent(
name="Session Assistant",
instructions="You are a helpful assistant with session support.",
tools=MCP("https://api.example.com/mcp")
)
# Session management is automatic - the MCP client:
# 1. Receives Mcp-Session-Id from server during initialization
# 2. Includes session ID in all subsequent requests
@@ -29,6 +22,12 @@ agent = Agent(
# 4. Can terminate session explicitly when done
if __name__ == "__main__":
agent = Agent(
name="Session Assistant",
instructions="You are a helpful assistant with session support.",
tools=MCP("https://api.example.com/mcp"),
)
print("MCP Session Management Example")
print("=" * 40)
print()
+24 -27
View File
@@ -16,34 +16,31 @@ Protocol: MCP 2025-11-25
from praisonaiagents import Agent, MCP
# Basic Streamable HTTP connection
agent = Agent(
name="HTTP Stream Assistant",
instructions="You are a helpful assistant connected via Streamable HTTP.",
tools=MCP("https://api.example.com/mcp")
)
# With authentication headers
agent_auth = Agent(
name="Authenticated Assistant",
instructions="You are a helpful assistant with authenticated connection.",
tools=MCP(
"https://api.example.com/mcp",
headers={"Authorization": "Bearer your-token"}
)
)
# With timeout and debug
agent_debug = Agent(
name="Debug Assistant",
tools=MCP(
"https://api.example.com/mcp",
timeout=120,
debug=True
)
)
if __name__ == "__main__":
agent = Agent(
name="HTTP Stream Assistant",
instructions="You are a helpful assistant connected via Streamable HTTP.",
tools=MCP("https://api.example.com/mcp"),
)
agent_auth = Agent(
name="Authenticated Assistant",
instructions="You are a helpful assistant with authenticated connection.",
tools=MCP(
"https://api.example.com/mcp",
headers={"Authorization": "Bearer your-token"},
),
)
agent_debug = Agent(
name="Debug Assistant",
tools=MCP(
"https://api.example.com/mcp",
timeout=120,
debug=True,
),
)
print("Streamable HTTP MCP Transport Example")
print("=" * 40)
print("This example requires a Streamable HTTP MCP server running.")
+26 -29
View File
@@ -16,36 +16,33 @@ Protocol: SEP-1288 (WebSocket Transport)
from praisonaiagents import Agent, MCP
# Basic WebSocket connection
agent = Agent(
name="WebSocket Assistant",
instructions="You are a helpful assistant connected via WebSocket.",
tools=MCP("ws://localhost:8080/mcp")
)
# Secure WebSocket with authentication
agent_secure = Agent(
name="Secure WebSocket Assistant",
instructions="You are a helpful assistant with secure WebSocket connection.",
tools=MCP(
"wss://api.example.com/mcp",
auth_token="Bearer your-secret-token",
timeout=60
)
)
# WebSocket with custom options
agent_custom = Agent(
name="Custom WebSocket Assistant",
tools=MCP(
"wss://api.example.com/mcp",
auth_token="your-token",
timeout=120,
debug=True
)
)
if __name__ == "__main__":
agent = Agent(
name="WebSocket Assistant",
instructions="You are a helpful assistant connected via WebSocket.",
tools=MCP("ws://localhost:8080/mcp"),
)
agent_secure = Agent(
name="Secure WebSocket Assistant",
instructions="You are a helpful assistant with secure WebSocket connection.",
tools=MCP(
"wss://api.example.com/mcp",
auth_token="Bearer your-secret-token",
timeout=60,
),
)
agent_custom = Agent(
name="Custom WebSocket Assistant",
tools=MCP(
"wss://api.example.com/mcp",
auth_token="your-token",
timeout=120,
debug=True,
),
)
# Note: Requires a running WebSocket MCP server
# Example server: https://github.com/modelcontextprotocol/servers
+173
View File
@@ -0,0 +1,173 @@
#!/usr/bin/env python3
"""
Message Steering Example
Demonstrates real-time message steering for agents during execution.
Shows how to send guidance messages to agents while they're running tasks.
Usage:
# Python API
python message_steering_example.py
# CLI with steering enabled
praisonai --message-steering "Write a detailed report about AI trends"
# YAML (add message_steering: true to agent config)
"""
import asyncio
import threading
import time
from praisonaiagents import Agent
def basic_message_steering():
"""Basic message steering example - enable steering and send guidance."""
print("🎯 Basic Message Steering Example")
print("=" * 50)
# Create agent with message steering enabled
agent = Agent(
name="researcher",
instructions="You are a helpful research assistant",
message_steering=True, # Enable real-time message steering
llm="gpt-4o-mini"
)
print("✅ Agent created with message steering enabled")
print(f"📊 Steering status: {agent.get_steering_status()}")
# Send some guidance
msg_id_1 = agent.steer("Focus on recent developments from 2024")
msg_id_2 = agent.steer("Keep response under 200 words", priority=10)
print(f"📨 Sent steering messages: {msg_id_1}, {msg_id_2}")
print(f"📊 Steering status: {agent.get_steering_status()}")
# Execute task - steering will be automatically injected
result = agent.start("Summarize the latest AI trends")
print("\n📝 Result:")
print(result)
def threaded_message_steering():
"""Advanced example - send steering messages while agent is running."""
print("\n🚀 Threaded Message Steering Example")
print("=" * 50)
agent = Agent(
name="writer",
instructions="You are a content writer",
message_steering=True,
llm="gpt-4o-mini"
)
result_holder = [None]
def run_agent():
"""Run agent in background thread."""
result = agent.start("Write a comprehensive article about quantum computing")
result_holder[0] = result
# Start agent in background
thread = threading.Thread(target=run_agent)
thread.start()
# Send steering messages while it's running
time.sleep(0.5) # Let execution start
agent.steer("Make it accessible to beginners")
time.sleep(1.0)
agent.steer("Include practical applications", priority=15)
time.sleep(1.5)
agent.steer("Keep it under 300 words total", priority=20)
# Wait for completion
thread.join(timeout=60)
if result_holder[0]:
print("\n📝 Final Result:")
print(result_holder[0])
else:
print("⚠️ Agent execution timed out")
async def async_message_steering():
"""Async example showing message steering with async execution."""
print("\n⚡ Async Message Steering Example")
print("=" * 50)
agent = Agent(
name="analyst",
instructions="You are a data analyst",
message_steering=True,
llm="gpt-4o-mini"
)
# Start async task
async def run_analysis():
return await agent.astart("Analyze the impact of AI on job markets")
# Create task
task = asyncio.create_task(run_analysis())
# Send steering while running
await asyncio.sleep(0.3)
agent.steer("Focus on positive opportunities")
await asyncio.sleep(0.7)
agent.steer("Include specific examples", priority=12)
# Wait for result
result = await task
print("\n📝 Analysis Result:")
print(result)
def cli_yaml_examples():
"""Show CLI and YAML usage examples."""
print("\n💻 CLI & YAML Examples")
print("=" * 50)
print("🔧 CLI Usage:")
print(" praisonai --message-steering 'Your task here'")
print(" # Then in another terminal:")
print(" # agent.steer('Additional guidance')")
print("\n📄 YAML Configuration:")
yaml_example = """
# agents.yaml
framework: praisonai
roles:
researcher:
role: Research Assistant
goal: Conduct thorough research
backstory: Expert researcher with steering support
message_steering: true # Enable steering for this agent
tools:
- internet_search
- write_file
tasks:
research_task:
description: Research AI trends and write report
expected_output: Comprehensive research report
"""
print(yaml_example.strip())
if __name__ == "__main__":
# Run examples
basic_message_steering()
threaded_message_steering()
# Run async example
asyncio.run(async_message_steering())
# Show CLI/YAML usage
cli_yaml_examples()
print("\n✨ Message steering examples completed!")
print("📚 See docs for more advanced patterns and priority levels")
+2 -2
View File
@@ -12,7 +12,6 @@ Environment:
"""
import os
import tempfile
from praisonaiagents import Agent, AutoRagAgent
# Ensure API key is set
@@ -21,7 +20,8 @@ if not os.environ.get("OPENAI_API_KEY"):
exit(1)
# Create a sample document for testing
sample_doc = os.path.join(tempfile.gettempdir(), "sample_doc.txt")
from pathlib import Path
sample_doc = str(Path(__file__).with_name("sample_doc.txt"))
with open(sample_doc, "w", encoding="utf-8") as f:
f.write("""
PraisonAI Framework Documentation
@@ -4,7 +4,7 @@ Example 3: Save Agent Output Using Workflow output_file
Workflow step output is automatically saved with variable substitution.
"""
from praisonaiagents import AgentFlow
from praisonaiagents import Agent, AgentFlow, Task
# Define workflow with output_file
workflow_config = {
@@ -33,9 +33,30 @@ workflow_config = {
]
}
# Run workflow
workflow = AgentFlow(config=workflow_config)
result = workflow.run()
# Build agent and workflow from config dict
writer_cfg = workflow_config["agents"]["writer"]
writer = Agent(
name="writer",
role=writer_cfg["role"],
goal=writer_cfg["goal"],
llm=writer_cfg["llm"],
)
step_cfg = workflow_config["steps"][0]
workflow = AgentFlow(
name=workflow_config["metadata"]["name"],
variables=workflow_config["variables"],
steps=[
Task(
name="write_tutorial",
description=step_cfg["action"],
expected_output=step_cfg["expected_output"],
output_file=step_cfg["output_file"],
agent=writer,
)
],
)
result = workflow.start()
print("✅ Workflow completed!")
print("Output saved to: generated/tutorial.md")
@@ -69,9 +69,10 @@ agents_system = AgentTeam(
memory=True, output="verbose"
)
print("Starting state management demonstration...")
result = agents_system.start()
if __name__ == "__main__":
print("Starting state management demonstration...")
result = agents_system.start()
print(f"\nState Management Result: {result[:200]}...")
print("\n✅ State management patterns complete!")
print("Agent demonstrated persistent state tracking across multiple tasks.")
print(f"\nState Management Result: {str(result)[:200]}...")
print("\n✅ State management patterns complete!")
print("Agent demonstrated persistent state tracking across multiple tasks.")
@@ -179,7 +179,7 @@ def main():
context = memory.build_context_for_task(
task_descr="Explain AI alignment challenges",
user_id="quality_demo_user",
memory={"user_id": "quality_demo_user"},
max_items=3
)
@@ -1,284 +1,55 @@
"""
Production Telemetry Example
This example demonstrates comprehensive telemetry and monitoring setup for
production AI agent systems, including metrics collection, performance tracking,
and observability.
Features demonstrated:
- OpenTelemetry integration for distributed tracing
- Custom metrics collection and monitoring
- Performance tracking and analytics
- Error tracking and alerting
- Usage analytics and reporting
Demonstrates compatible telemetry setup with `enable_telemetry()` and
`TelemetryCollector` patterns.
"""
from contextlib import nullcontext
from praisonaiagents import Agent, Task, AgentTeam
from praisonaiagents.tools import duckduckgo
from praisonaiagents.telemetry import enable_telemetry
import time
import random
from datetime import datetime
from praisonaiagents.telemetry import enable_telemetry, TelemetryCollector
# Initialize telemetry for production monitoring
print("="*80)
print("PRODUCTION TELEMETRY SETUP")
print("="*80)
# Configure telemetry (in production, you'd use actual telemetry backends)
telemetry = enable_telemetry(
backend="opentelemetry",
config={
"service_name": "praisonai_production_agents",
"service_version": "1.0.0",
"environment": "production",
"export_console": True, # For demo purposes
"export_file": True, # Export to file for analysis
"sample_rate": 1.0, # 100% sampling for demo
"attributes": {
"deployment": "cloud",
"region": "us-east-1",
"cluster": "main"
}
}
)
def main() -> None:
print("=" * 80)
print("PRODUCTION TELEMETRY SETUP")
print("=" * 80)
# Create agents for production workflow with telemetry
customer_service_agent = Agent(
name="CustomerServiceAgent",
role="Customer Service Representative",
goal="Provide excellent customer service with fast response times",
backstory="You are a customer service representative focused on resolving customer issues quickly and effectively.",
tools=[duckduckgo],
instructions="Provide helpful, accurate customer service responses. Research solutions when needed."
)
# New API: no args
enable_telemetry()
collector = TelemetryCollector()
technical_support_agent = Agent(
name="TechnicalSupportAgent",
role="Technical Support Specialist",
goal="Resolve technical issues with detailed solutions",
backstory="You are a technical support specialist who provides detailed technical solutions and troubleshooting.",
instructions="Provide detailed technical solutions with step-by-step instructions."
)
customer_service_agent = Agent(
name="CustomerServiceAgent",
role="Customer Service Representative",
goal="Provide excellent customer service with fast response times",
backstory="Customer service expert focused on quick issue resolution.",
tools=[duckduckgo],
instructions="Provide accurate customer-service responses.",
)
quality_assurance_agent = Agent(
name="QualityAssuranceAgent",
role="Quality Assurance Specialist",
goal="Ensure all customer interactions meet quality standards",
backstory="You review and assess the quality of customer service interactions to ensure high standards.",
instructions="Review customer service interactions and provide quality scores with detailed feedback."
)
# Production workflow simulation with telemetry tracking
def simulate_customer_requests():
"""Simulate various customer requests for telemetry demonstration"""
customer_requests = [
{
"type": "billing_inquiry",
"complexity": "low",
"description": "Customer asking about their monthly bill charges",
"expected_duration": 30
},
{
"type": "technical_issue",
"complexity": "high",
"description": "Customer experiencing connectivity issues with the service",
"expected_duration": 120
},
{
"type": "account_setup",
"complexity": "medium",
"description": "New customer needs help setting up their account",
"expected_duration": 60
},
{
"type": "feature_question",
"complexity": "low",
"description": "Customer asking about product features and capabilities",
"expected_duration": 45
}
requests = [
"Customer asking about monthly bill charges",
"Customer experiencing connectivity issues",
]
return customer_requests
# Execute production workflow with comprehensive telemetry
print("Starting production workflow with telemetry tracking...")
for idx, description in enumerate(requests, start=1):
trace_ctx = getattr(collector, "trace", None)
trace_cm = trace_ctx(f"customer_request_{idx}") if callable(trace_ctx) else nullcontext()
with trace_cm:
task = Task(
name=f"customer_request_{idx}",
description=description,
expected_output="Professional customer service response with solution",
agent=customer_service_agent,
)
agents = AgentTeam(agents=[customer_service_agent], tasks=[task], output="minimal")
result = agents.start()
print(f"✅ Request {idx} completed, response length={len(str(result))}")
customer_requests = simulate_customer_requests()
print("\nTelemetry demonstration completed.")
for i, request in enumerate(customer_requests):
print(f"\n{'='*60}")
print(f"PROCESSING CUSTOMER REQUEST {i+1}: {request['type'].upper()}")
print(f"Complexity: {request['complexity']} | Expected Duration: {request['expected_duration']}s")
print(f"{'='*60}")
# Start telemetry trace for this customer interaction
with telemetry.trace_operation(f"customer_request_{request['type']}") as trace:
# Add custom attributes to the trace
trace.set_attributes({
"request.type": request['type'],
"request.complexity": request['complexity'],
"request.expected_duration": request['expected_duration'],
"customer.id": f"customer_{random.randint(1000, 9999)}",
"agent.assigned": "CustomerServiceAgent"
})
start_time = time.time()
# Create customer service task with telemetry
customer_task = Task(
name=f"customer_request_{i+1}",
description=request['description'],
expected_output="Professional customer service response with solution",
agent=customer_service_agent
)
# Execute with telemetry tracking
try:
# Customer service response
with telemetry.trace_agent_execution("CustomerServiceAgent") as agent_trace:
agents = AgentTeam(
agents=[customer_service_agent],
tasks=[customer_task],
output="minimal" # Reduce verbosity for telemetry demo
)
cs_result = agents.start()
agent_trace.set_attributes({
"agent.response_length": len(str(cs_result)),
"agent.tools_used": len(customer_service_agent.tools) if customer_service_agent.tools else 0
})
# Technical support (if needed for high complexity)
if request['complexity'] == 'high':
with telemetry.trace_agent_execution("TechnicalSupportAgent") as tech_trace:
tech_task = Task(
name=f"technical_support_{i+1}",
description=f"Provide detailed technical support for: {request['description']}",
expected_output="Detailed technical solution with troubleshooting steps",
agent=technical_support_agent,
context=[customer_task]
)
tech_agents = AgentTeam(
agents=[technical_support_agent],
tasks=[tech_task], output="minimal"
)
tech_result = tech_agents.start()
tech_trace.set_attributes({
"agent.response_length": len(str(tech_result)),
"agent.escalation": True
})
# Quality assurance check
with telemetry.trace_agent_execution("QualityAssuranceAgent") as qa_trace:
qa_task = Task(
name=f"quality_check_{i+1}",
description=f"Review the quality of customer service provided for: {request['description']}",
expected_output="Quality assessment score and feedback",
agent=quality_assurance_agent
)
qa_agents = AgentTeam(
agents=[quality_assurance_agent],
tasks=[qa_task], output="minimal"
)
qa_result = qa_agents.start()
# Extract quality score (simulated)
quality_score = random.uniform(0.8, 1.0) # Simulated score
qa_trace.set_attributes({
"qa.score": quality_score,
"qa.passed": quality_score >= 0.85
})
# Calculate performance metrics
end_time = time.time()
duration = end_time - start_time
# Record custom metrics
telemetry.record_metric("request_duration", duration, {
"request_type": request['type'],
"complexity": request['complexity']
})
telemetry.record_metric("request_success", 1, {
"request_type": request['type']
})
# Set final trace attributes
trace.set_attributes({
"request.duration_seconds": duration,
"request.status": "completed",
"request.quality_score": quality_score,
"request.escalated": request['complexity'] == 'high'
})
print(f"✅ Request completed successfully in {duration:.2f}s (Quality: {quality_score:.2f})")
except Exception as e:
# Record error metrics
telemetry.record_metric("request_error", 1, {
"request_type": request['type'],
"error_type": type(e).__name__
})
trace.set_attributes({
"request.status": "error",
"request.error": str(e)
})
print(f"❌ Request failed: {str(e)}")
# Add some delay between requests
time.sleep(2)
# Generate telemetry summary
print(f"\n{'='*80}")
print("TELEMETRY SUMMARY AND ANALYTICS")
print(f"{'='*80}")
# Simulated telemetry analytics (in production, this would come from your telemetry backend)
print("📊 Performance Metrics:")
print("- Total Requests Processed: 4")
print("- Average Response Time: 3.2s")
print("- Success Rate: 100%")
print("- Quality Score Average: 0.91")
print("- High Complexity Requests: 25%")
print("\n📈 Usage Analytics:")
print("- Most Common Request Type: technical_issue")
print("- Peak Performance Window: All requests within SLA")
print("- Agent Utilization: CustomerService (100%), TechnicalSupport (25%), QA (100%)")
print("\n🔍 Observability Features Demonstrated:")
print("- Distributed tracing across agent interactions")
print("- Custom metrics for business KPIs")
print("- Performance monitoring and SLA tracking")
print("- Error tracking and alerting")
print("- Quality assurance metrics")
print("- User journey tracking")
print("- Resource utilization monitoring")
print("\n⚙️ Production Monitoring Setup:")
print("- OpenTelemetry integration for standardized observability")
print("- Custom metrics for business-specific KPIs")
print("- Trace correlation across multi-agent workflows")
print("- Performance benchmarking against expected durations")
print("- Quality scoring and automated validation")
print("- Error categorization and alerting")
print(f"\n{'='*80}")
print("TELEMETRY DEMONSTRATION COMPLETED")
print(f"{'='*80}")
print("This example demonstrated comprehensive production telemetry including:")
print("- Request tracing and performance monitoring")
print("- Custom business metrics collection")
print("- Quality assurance tracking")
print("- Error handling and alerting")
print("- Multi-agent workflow observability")
print("- Production-ready monitoring patterns")
if __name__ == "__main__":
main()
+115
View File
@@ -0,0 +1,115 @@
#!/usr/bin/env python3
"""
JIRA Agent Watch Example
This example demonstrates how to use the JIRA watch tools with PraisonAI agents.
The agent can monitor JIRA issues and projects for changes, search for issues,
and get detailed issue information.
Setup:
1. Install JIRA library: pip install jira
2. Set environment variables:
- [email protected] (for cloud JIRA)
- JIRA_API_TOKEN=your_api_token
- Or use JIRA_USERNAME + JIRA_API_TOKEN for server JIRA
Usage:
python jira_agent_example.py
"""
import os
from praisonaiagents import Agent
from praisonaiagents.tools import jira_tools
def main():
"""Demonstrate JIRA agent watch capabilities."""
# Check if JIRA credentials are available
jira_email = os.getenv('JIRA_EMAIL')
jira_token = os.getenv('JIRA_API_TOKEN')
jira_username = os.getenv('JIRA_USERNAME')
if not jira_token:
print("❌ JIRA_API_TOKEN environment variable not set")
print("Please set your JIRA API token:")
print("export JIRA_API_TOKEN='your_token_here'")
return
if not (jira_email or jira_username):
print("❌ JIRA credentials incomplete")
print("Please set either:")
print("- JIRA_EMAIL (for cloud JIRA)")
print("- JIRA_USERNAME (for server JIRA)")
return
# Create agent with JIRA tools
agent = Agent(
name="JIRA Monitor Agent",
instructions="""
You are a JIRA monitoring agent. You can:
1. Watch specific JIRA issues for changes
2. Monitor JIRA projects for new issues and updates
3. Search for issues using JQL queries
4. Get detailed information about specific issues
When using JIRA tools, always provide the full JIRA URL
(e.g., https://yourcompany.atlassian.net).
Be helpful and provide clear summaries of JIRA activity.
""",
tools=jira_tools(), # Add all JIRA tools
llm="gpt-4o-mini"
)
print("🎯 JIRA Agent Watch Example")
print("=" * 50)
print()
print("Available JIRA tools:")
print("- jira_watch_issue: Monitor a specific issue for changes")
print("- jira_watch_project: Monitor a project for new/updated issues")
print("- jira_get_issue_info: Get detailed info about an issue")
print("- jira_search_issues: Search issues using JQL")
print()
# Example interactions
example_prompts = [
"Get information about JIRA issue DEMO-1 from https://praisonai.atlassian.net",
"Search for open issues in project DEMO from https://praisonai.atlassian.net using JQL: 'project = DEMO AND status = Open'",
"Watch JIRA issue DEMO-1 from https://praisonai.atlassian.net for 2 minutes (check every 60 seconds, max 2 checks)",
]
print("📝 Example prompts you can try:")
for i, prompt in enumerate(example_prompts, 1):
print(f"{i}. {prompt}")
print()
# Interactive mode
print("💬 Interactive JIRA Agent (type 'quit' to exit)")
print("-" * 50)
while True:
try:
user_input = input("\n🧑 You: ").strip()
if user_input.lower() in ['quit', 'exit', 'q']:
print("👋 Goodbye!")
break
if not user_input:
continue
print("🤖 Agent: ", end="", flush=True)
response = agent.start(user_input)
print(response)
except KeyboardInterrupt:
print("\n👋 Goodbye!")
break
except Exception as e:
print(f"❌ Error: {e}")
if __name__ == "__main__":
main()
@@ -5,11 +5,17 @@ from dotenv import load_dotenv
load_dotenv()
os.environ['SERPER_API_KEY'] = os.getenv('SERPER_API_KEY')
serper_api_key = os.getenv("SERPER_API_KEY")
if serper_api_key is not None:
os.environ["SERPER_API_KEY"] = serper_api_key
search = GoogleSerperAPIWrapper()
data_agent = Agent(instructions="Suggest me top 5 most visited websites for Dosa Recipe", tools=[search])
editor_agent = Agent(instructions="List out the websites with their url and a short description")
agents = AgentTeam(agents=[data_agent, editor_agent])
agents.start()
if __name__ == "__main__":
if not serper_api_key:
print("SERPER_API_KEY is not set. Skipping Google Serper example.")
else:
search = GoogleSerperAPIWrapper()
data_agent = Agent(instructions="Suggest me top 5 most visited websites for Dosa Recipe", tools=[search])
editor_agent = Agent(instructions="List out the websites with their url and a short description")
agents = AgentTeam(agents=[data_agent, editor_agent])
agents.start()
+227
View File
@@ -0,0 +1,227 @@
"""
Named Toolsets Example
This example demonstrates how to use named toolsets to organize and reuse
groups of tools across agents and workflows.
Toolsets provide:
- Organized tool collections (web, research, development, etc.)
- Progressive disclosure (safe → research → development)
- Composable design (toolsets can include other toolsets)
- YAML configuration support
"""
import os
from praisonaiagents import Agent, Task, AgentTeam
from praisonaiagents.toolsets import (
list_toolsets,
resolve_toolset,
register_toolset
)
# Set up environment (replace with your actual API key)
os.environ.setdefault("OPENAI_API_KEY", "your-openai-api-key-here")
def demo_basic_toolsets():
"""Demonstrate basic toolset functionality."""
print("=== Basic Toolsets Demo ===")
# List available toolsets
available_toolsets = list_toolsets()
print(f"Available toolsets: {available_toolsets}")
# Show what tools are in each prebuilt toolset
for toolset_name in ["safe", "web", "research"]:
tools = resolve_toolset(toolset_name)
print(f"\n{toolset_name.upper()} toolset contains: {tools}")
def demo_agent_with_toolsets():
"""Demonstrate agents using toolsets."""
print("\n=== Agent with Toolsets Demo ===")
# Create a research agent with research toolset
researcher = Agent(
name="researcher",
role="Research Specialist",
goal="Research topics thoroughly using web tools",
instructions="You are a research specialist with access to web search and file tools.",
toolsets=["research"], # includes web tools + file tools
llm="gpt-4o-mini"
)
print(f"Research agent has {len(researcher.tools)} tools available")
print(f"Tools: {[getattr(t, '__name__', str(t)) for t in researcher.tools]}")
# Create a safe agent with minimal tools
safe_agent = Agent(
name="safe_assistant",
role="Safe Assistant",
goal="Help users safely without risky operations",
instructions="You are a helpful assistant with access to only safe, read-only tools.",
toolsets=["safe"],
llm="gpt-4o-mini"
)
print(f"\nSafe agent has {len(safe_agent.tools)} tools available")
print(f"Safe tools: {[getattr(t, '__name__', str(t)) for t in safe_agent.tools]}")
def demo_custom_toolsets():
"""Demonstrate creating and using custom toolsets."""
print("\n=== Custom Toolsets Demo ===")
# Register a custom toolset for data analysis
register_toolset(
"data_analysis",
tools=["read_file", "write_file"],
includes=["safe"], # Include safe tools too
description="Tools for data analysis workflows"
)
# Create an agent with the custom toolset
analyst = Agent(
name="data_analyst",
role="Data Analyst",
goal="Analyze data files and generate reports",
instructions="You specialize in data analysis. Read files, process data, and write reports.",
toolsets=["data_analysis"],
llm="gpt-4o-mini"
)
print(f"Data analyst has {len(analyst.tools)} tools available")
print(f"Analysis tools: {[getattr(t, '__name__', str(t)) for t in analyst.tools]}")
def demo_mixed_tools_and_toolsets():
"""Demonstrate mixing explicit tools with toolsets."""
print("\n=== Mixed Tools and Toolsets Demo ===")
# Create agent with both explicit tools and toolsets
hybrid_agent = Agent(
name="hybrid_assistant",
role="Hybrid Assistant",
goal="Assist with both research and custom tasks",
instructions="You can research topics and perform custom analysis tasks.",
tools=["write_file"], # Explicit tool
toolsets=["research"], # Toolset with web + file tools
llm="gpt-4o-mini"
)
print(f"Hybrid agent has {len(hybrid_agent.tools)} tools available")
tool_names = [getattr(t, '__name__', str(t)) for t in hybrid_agent.tools]
print(f"Hybrid tools: {tool_names}")
# Note: write_file might appear twice (once explicit, once from toolset)
# The agent handles deduplication automatically
def demo_multi_agent_with_toolsets():
"""Demonstrate multi-agent workflows with different toolsets."""
print("\n=== Multi-Agent with Toolsets Demo ===")
# Create agents with different toolsets for different roles
researcher = Agent(
name="researcher",
role="Research Specialist",
goal="Research topics and gather information",
toolsets=["research"],
llm="gpt-4o-mini"
)
writer = Agent(
name="writer",
role="Content Writer",
goal="Write content based on research",
toolsets=["safe"], # Safe tools for writing
llm="gpt-4o-mini"
)
# Create tasks for each agent
research_task = Task(
name="research_task",
description="Research the benefits of renewable energy",
expected_output="A summary of key benefits of renewable energy",
agent=researcher
)
writing_task = Task(
name="writing_task",
description="Write a blog post about renewable energy based on the research",
expected_output="A well-written blog post about renewable energy benefits",
agent=writer
)
# Create team with different toolsets per agent
team = AgentTeam(
agents=[researcher, writer],
tasks=[research_task, writing_task],
process="sequential"
)
print(f"Team created with {len(team.agents)} agents using different toolsets")
print(f"Research agent tools: {len(researcher.tools)}")
print(f"Writer agent tools: {len(writer.tools)}")
def demo_yaml_config():
"""Show how toolsets work in YAML configuration."""
print("\n=== YAML Configuration Example ===")
yaml_config = """
# agents.yaml - Example using toolsets
agents:
researcher:
role: Research Specialist
goal: Research topics thoroughly
toolsets:
- research # Includes web search, file tools
llm: gpt-4o-mini
analyst:
role: Data Analyst
goal: Analyze research data
tools:
- write_file # Explicit tool
toolsets:
- safe # Safe toolset
llm: gpt-4o-mini
tasks:
research_task:
description: Research renewable energy trends
agent: researcher
expected_output: Research summary
analysis_task:
description: Analyze the research data
agent: analyst
expected_output: Analysis report
"""
print("Example YAML configuration with toolsets:")
print(yaml_config)
print("Use: praisonai run 'Research renewable energy' --toolset research")
print("Or: praisonai chat --toolset web,files")
if __name__ == "__main__":
print("🛠️ PraisonAI Named Toolsets Example")
print("=" * 50)
# Run all demos
demo_basic_toolsets()
demo_agent_with_toolsets()
demo_custom_toolsets()
demo_mixed_tools_and_toolsets()
demo_multi_agent_with_toolsets()
demo_yaml_config()
print("\n✅ Toolsets example completed!")
print("\n💡 Key Benefits:")
print(" • Organized tool collections")
print(" • Progressive disclosure (safe → research → development)")
print(" • Reusable across agents and workflows")
print(" • YAML configuration support")
print(" • Composable design with includes")
@@ -2,6 +2,7 @@ from praisonaiagents import Agent, Task, AgentTeam
from pydantic import BaseModel
from typing import List, Dict
from gitingest import ingest
import sys
class CodeMetrics(BaseModel):
category: str
@@ -60,7 +61,10 @@ def analyze_code(code_source: str) -> CodeAnalysisReport:
Analyze code from directory path or GitHub URL
"""
# Ingest code content
summary, tree, content = ingest(code_source)
try:
summary, tree, content = ingest(code_source)
except Exception as exc:
raise RuntimeError(f"Failed to ingest code source: {exc}") from exc
# Concatenate context into structured format
context_text = f"""
@@ -86,10 +90,22 @@ def analyze_code(code_source: str) -> CodeAnalysisReport:
tasks=[code_analysis_task]
)
return agents.start(context_text)
result = agents.start(context_text)
if result is None:
raise RuntimeError("No analysis result returned by AgentTeam.start()")
return result
if __name__ == "__main__":
def main() -> None:
# Example usage
code_source = "https://github.com/openai/openai-python/tree/main/src/openai/cli/_api/chat" # GitHub URL or local directory
result = analyze_code(code_source)
print(result)
try:
result = analyze_code(code_source)
except RuntimeError as exc:
print(f"Code analysis failed: {exc}")
sys.exit(1)
print(result)
if __name__ == "__main__":
main()
@@ -77,25 +77,44 @@ agents = AgentTeam(
process="sequential", output="verbose"
)
def _extract_pydantic_result(result_obj):
"""Extract pydantic payload from AgentTeam.start() output shapes."""
if hasattr(result_obj, "pydantic"):
return result_obj.pydantic
if isinstance(result_obj, dict):
task_results = result_obj.get("task_results")
if isinstance(task_results, dict):
values = list(task_results.values())
if values and hasattr(values[-1], "pydantic"):
return values[-1].pydantic
if isinstance(task_results, list) and task_results and hasattr(task_results[-1], "pydantic"):
return task_results[-1].pydantic
return None
# Start the analysis
result = agents.start()
pydantic_result = _extract_pydantic_result(result)
if pydantic_result is None:
raise ValueError("Could not extract structured CVAnalysisReport from workflow result")
# Access the structured results
print("\nCV Analysis Results:")
print(f"\nOverall Score: {result.pydantic.overall_score}/100")
print(f"\nOverall Score: {pydantic_result.overall_score}/100")
print("\nKey Strengths:")
for strength in result.pydantic.key_strengths:
for strength in pydantic_result.key_strengths:
print(f"- {strength}")
print("\nSkill Analysis:")
print("\nTechnical Skills:")
for skill in result.pydantic.skill_analysis.technical_skills:
for skill in pydantic_result.skill_analysis.technical_skills:
print(f"- {skill}")
print("\nExperience Highlights:")
for achievement in result.pydantic.experience_analysis.key_achievements:
for achievement in pydantic_result.experience_analysis.key_achievements:
print(f"- {achievement}")
print("\nRecommendations:")
for recommendation in result.pydantic.recommendations:
for recommendation in pydantic_result.recommendations:
print(f"- {recommendation}")
@@ -100,6 +100,8 @@ def evaluate_project(video_path: str) -> ProjectEvaluation:
evaluation_data = response
print(f"Debug - Parsed evaluation_data: {evaluation_data}")
if not evaluation_data or not isinstance(evaluation_data, dict):
raise ValueError("No structured evaluation data returned by agent")
# Create and return ProjectEvaluation instance
return ProjectEvaluation(
+9 -3
View File
@@ -1,5 +1,11 @@
from praisonaiagents import VideoAgent
import os
agent = VideoAgent(llm="azure/sora-2")
video = agent.generate("Mountain landscape")
print(video.id)
if __name__ == "__main__":
if not os.getenv("AZURE_API_BASE"):
print("AZURE_API_BASE is not configured. Skipping Azure video example.")
else:
agent = VideoAgent(llm="azure/sora-2")
video = agent.generate("Mountain landscape")
print(video.id)
+1 -1
View File
@@ -5,7 +5,7 @@ Demonstrates using Agent instances directly as workflow steps.
Each agent processes the input and passes output to the next agent.
"""
from praisonaiagents import Agent, Workflow
from praisonaiagents import Agent, AgentFlow
# Create agents with specific roles
researcher = Agent(
+5 -2
View File
@@ -6,6 +6,7 @@ with Agents for workflow monitoring and logging.
"""
from praisonaiagents import Agent, Task, AgentTeam
from praisonaiagents.config.feature_configs import MultiAgentHooksConfig
# Create an agent
researcher = Agent(
@@ -47,8 +48,10 @@ if __name__ == "__main__":
agents=[researcher],
tasks=[task1, task2],
process="workflow",
on_task_start=on_start,
on_task_complete=on_complete,
hooks=MultiAgentHooksConfig(
on_task_start=on_start,
on_task_complete=on_complete,
),
variables={"global_var": "shared_value"} # Global variables
)
@@ -6,7 +6,6 @@ to different next steps based on output content.
"""
from praisonaiagents import AgentFlow, Task
from praisonaiagents import AgentFlowManager
# Create a workflow with branching
workflow = AgentFlow(
@@ -36,16 +35,9 @@ workflow = AgentFlow(
)
if __name__ == "__main__":
# Create manager and register workflow
manager = WorkflowManager()
manager.workflows["Decision Workflow"] = workflow
# Execute - will branch to success_handler since 42 is positive
result = manager.execute(
"Decision Workflow",
default_llm="gpt-4o-mini"
)
result = workflow.run("", llm="gpt-4o-mini", verbose=True)
print("Workflow completed!")
for step_result in result["results"]:
print(f" {step_result['step']}: {step_result['status']}")
for step in result.get("steps", []):
name = step.get("step", step.get("name", "unknown"))
status = step.get("status", "completed")
print(f" {name}: {status}")
@@ -54,11 +54,13 @@ workflow = AgentFlow(
analyzer, # Always runs
Task(
name="tech_review",
description="Review technical aspects when technical keywords are detected.",
agent=tech_reviewer,
should_run=is_technical # Only runs for technical content
),
Task(
name="creative_enhance",
description="Enhance narrative quality when creative keywords are detected.",
agent=creative_enhancer,
should_run=needs_creative # Only runs for creative content
),
@@ -6,9 +6,8 @@ functions that return StepResult with stop_workflow=True.
"""
from praisonaiagents import (
Workflow, Task, WorkflowContext, StepResult
AgentFlow, Task, WorkflowContext, StepResult
)
from praisonaiagents import AgentFlowManager
# Custom validator that can stop the workflow
def validate_data(context: WorkflowContext) -> StepResult:
@@ -50,20 +49,13 @@ workflow = AgentFlow(
)
if __name__ == "__main__":
manager = WorkflowManager()
manager.workflows["Data Processing"] = workflow
# Test with invalid data (will stop early)
print("=== Testing with invalid data ===")
result = manager.execute("Data Processing", default_llm="gpt-4o-mini")
for step_result in result["results"]:
print(f" {step_result['step']}: {step_result['output']}")
# Test with valid data (will complete)
result = workflow.run("", llm="gpt-4o-mini", verbose=True)
for step in result.get("steps", []):
print(f" {step.get('step', 'step')}: {step.get('output', step.get('status'))}")
print("\n=== Testing with valid data ===")
workflow.variables["data"] = {"value": 42}
result = manager.execute("Data Processing", default_llm="gpt-4o-mini")
for step_result in result["results"]:
print(f" {step_result['step']}: {step_result['status']}")
result = workflow.run("", llm="gpt-4o-mini", verbose=True)
for step in result.get("steps", []):
print(f" {step.get('step', 'step')}: {step.get('status', 'completed')}")
+8 -15
View File
@@ -6,7 +6,6 @@ Each item is processed individually with the loop variable.
"""
from praisonaiagents import AgentFlow, Task
from praisonaiagents import AgentFlowManager
# Create a workflow with loop
workflow = AgentFlow(
@@ -30,18 +29,12 @@ workflow = AgentFlow(
)
if __name__ == "__main__":
# Create manager and register workflow
manager = WorkflowManager()
manager.workflows["Process Items"] = workflow
# Execute - will process each fruit in the list
result = manager.execute(
"Process Items",
default_llm="gpt-4o-mini"
)
result = workflow.run("", llm="gpt-4o-mini", verbose=True)
print("Workflow completed!")
for step_result in result["results"]:
print(f" {step_result['step']}: {step_result['status']}")
if isinstance(step_result.get("output"), list):
print(f" Loop processed {len(step_result['output'])} items")
for step in result.get("steps", []):
name = step.get("step", step.get("name", "unknown"))
status = step.get("status", "completed")
print(f" {name}: {status}")
out = step.get("output")
if isinstance(out, list):
print(f" Loop processed {len(out)} items")
@@ -5,7 +5,7 @@ Demonstrates running multiple agents concurrently and
combining their results with an aggregator agent.
"""
from praisonaiagents import Agent, Workflow
from praisonaiagents import Agent, AgentFlow
from praisonaiagents.workflows import parallel
# Create parallel research agents
@@ -42,6 +42,7 @@ def main():
# Create tasks with robustness features
research_task = Task(
name="research",
description="Research the latest trends in AI agents for 2025",
expected_output="A summary of key AI agent trends",
agent=researcher,
@@ -51,6 +52,7 @@ def main():
# Optional enrichment step - workflow continues even if this fails
enrich_task = Task(
name="enrich",
description="Enrich the research with real-world examples",
expected_output="Research with added examples",
agent=enricher,
@@ -60,6 +62,7 @@ def main():
)
write_task = Task(
name="write",
description="Write a brief summary based on the research",
expected_output="A well-written summary paragraph",
agent=writer,
@@ -5,7 +5,7 @@ Demonstrates decision-based routing where an agent classifier
routes to specialized handler agents based on the request type.
"""
from praisonaiagents import Agent, Workflow
from praisonaiagents import Agent, AgentFlow
from praisonaiagents.workflows import route
# Create classifier agent
+1 -1
View File
@@ -190,7 +190,7 @@ def example_run_history_with_backend():
# Use with RunHistory
history = RunHistory(backend=backend)
runs = history.list()
runs = history.list_runs()
print(f"RunHistory created with SQLite backend, {len(runs)} runs")
print("Recipe runs will be stored in database instead of files")
+1 -1
View File
@@ -257,7 +257,7 @@ if __name__ == "__main__":
# Check dependencies
try:
import praisonaiagents
print(f"✅ PraisonAI version: {praisonaiagents.__version__}")
print(f"✅ PraisonAI version: {getattr(praisonaiagents, '__version__', None)}")
except ImportError:
print("❌ PraisonAI not installed: pip install praisonaiagents")
sys.exit(1)
+1 -1
View File
@@ -12,7 +12,7 @@ assert os.getenv("OPENAI_API_KEY"), "OPENAI_API_KEY must be set"
# Enable web search and fetch
agent = Agent(
instructions="You are a research assistant with web access.",
web=True,
web="search_only",
)
# Disable web (default)
+3 -11
View File
@@ -4,7 +4,7 @@ WebConfig Example
Demonstrates using WebConfig for fine-grained control.
"""
import os
from praisonaiagents import Agent, WebConfig
from praisonaiagents import Agent
# Ensure API key is set from environment
assert os.getenv("OPENAI_API_KEY"), "OPENAI_API_KEY must be set"
@@ -12,21 +12,13 @@ assert os.getenv("OPENAI_API_KEY"), "OPENAI_API_KEY must be set"
# Custom web configuration
agent = Agent(
instructions="You are a research assistant.",
web=WebConfig(
search=True,
fetch=True,
search_provider="duckduckgo",
max_results=5,
),
web="search_only",
)
# Search only (no fetch)
agent_search_only = Agent(
instructions="You are a research assistant.",
web=WebConfig(
search=True,
fetch=False,
),
web="search_only",
)
if __name__ == "__main__":
@@ -0,0 +1,47 @@
framework: praisonai
topic: AI Research with Message Steering
roles:
researcher:
role: AI Research Analyst
goal: Conduct comprehensive AI research with real-time guidance
backstory: You are an expert AI researcher who can receive and incorporate real-time guidance during your work
message_steering: true # Enable real-time message steering
tools:
- internet_search
- write_file
tasks:
research_task:
description: |
Research the latest developments in AI and write a comprehensive report.
You may receive additional guidance during execution via message steering.
expected_output: |
A well-structured research report covering recent AI developments,
incorporating any real-time guidance received during execution.
writer:
role: Technical Writer
goal: Write clear, engaging content based on research
backstory: You are a skilled technical writer who can adapt content based on real-time feedback
message_steering: true # Enable steering for dynamic content adjustment
tools:
- read_file
- write_file
tasks:
writing_task:
description: |
Take the research findings and create an accessible article for a general audience.
Adjust style and focus based on any steering messages received.
expected_output: |
A polished article that transforms technical research into accessible content,
refined based on real-time steering guidance.
dependencies:
- writing_task: [research_task]
# Usage Instructions:
# 1. Run: praisonai --agent-file message_steering_example.yaml
# 2. While running, use steering API to send guidance:
# agent.steer("Focus more on practical applications")
# agent.steer("Keep it under 500 words", priority=15)
# agent.steer("Add more examples", priority=10)
+148
View File
@@ -0,0 +1,148 @@
# Named Toolsets YAML Example
# This example demonstrates using toolsets in YAML configuration
# Global configuration
config:
llm: gpt-4o-mini
memory: true
# Define agents with different toolsets
agents:
# Research agent with research toolset (includes web tools)
researcher:
role: Senior Research Analyst
goal: Research topics thoroughly using web search and analysis tools
instructions: |
You are a senior research analyst with access to web search and file tools.
Research topics comprehensively and provide detailed findings.
toolsets:
- research # Includes: web search, file tools, basic analysis
# Writer agent with safe toolset for writing tasks
writer:
role: Content Writer
goal: Create engaging content based on research
instructions: |
You are a skilled content writer. Transform research into clear,
engaging content for the target audience.
toolsets:
- safe # Safe, read-only tools for writing
# Developer agent with development toolset
developer:
role: Software Developer
goal: Create and modify code based on requirements
instructions: |
You are a software developer with access to development tools.
Write, test, and debug code according to specifications.
toolsets:
- development # Includes: code tools, file operations, testing
# Data analyst with mixed tools and toolsets
analyst:
role: Data Analyst
goal: Analyze data and generate insights
instructions: |
You are a data analyst. Process data files and generate
actionable insights and visualizations.
tools:
- read_file # Explicit tool
- write_file # Explicit tool
toolsets:
- safe # Safe toolset for analysis
# Security-focused agent with minimal toolset
security_auditor:
role: Security Auditor
goal: Review and audit for security issues
instructions: |
You are a security auditor with minimal tool access.
Focus on reading and analyzing without making changes.
toolsets:
- safe # Minimal, safe tools only
# Define tasks for the workflow
tasks:
# Research task using research toolset
research_phase:
description: |
Research the latest trends in AI agent frameworks.
Focus on:
- Popular frameworks and their features
- Industry adoption patterns
- Security considerations
- Performance benchmarks
expected_output: |
A comprehensive research report covering AI agent frameworks,
including trends, adoption, security, and performance data.
agent: researcher
# Content creation using safe toolset
content_creation:
description: |
Create a technical blog post based on the research findings.
The post should be:
- Well-structured with clear sections
- Accessible to technical and non-technical audiences
- Include actionable insights
- 1000-1500 words
expected_output: |
A polished blog post about AI agent frameworks suitable
for publication on a technical blog.
agent: writer
# Code development using development toolset
code_examples:
description: |
Create code examples demonstrating key concepts from the research.
Include:
- Simple "hello world" style examples
- Intermediate use cases
- Best practices examples
- Proper documentation
expected_output: |
A collection of well-documented code examples that illustrate
the concepts discussed in the research and blog post.
agent: developer
# Data analysis using mixed tools/toolsets
trend_analysis:
description: |
Analyze the research data to identify trends and patterns.
Create:
- Summary statistics
- Trend analysis
- Recommendations based on findings
expected_output: |
A data analysis report with trends, patterns, and
actionable recommendations for AI framework adoption.
agent: analyst
# Security review using minimal toolset
security_review:
description: |
Review all outputs for security considerations.
Check for:
- Potential security vulnerabilities in code examples
- Security best practices in recommendations
- Privacy considerations in data handling
expected_output: |
A security review report identifying any security concerns
and recommendations for secure implementation.
agent: security_auditor
# Workflow process (sequential execution)
process: sequential
# Optional: Define workflow dependencies
# tasks:
# research_phase:
# dependencies: []
# content_creation:
# dependencies: [research_phase]
# code_examples:
# dependencies: [research_phase]
# trend_analysis:
# dependencies: [research_phase]
# security_review:
# dependencies: [content_creation, code_examples, trend_analysis]
+4 -1
View File
@@ -378,7 +378,7 @@ Agent parameters are consolidated into Config objects following the pattern:
| Config Object | Replaces (Deprecated) |
|--------------|----------------------|
| `execution=ExecutionConfig(code_execution=True, code_mode="safe", rate_limiter=obj)` | `allow_code_execution`, `code_execution_mode`, `rate_limiter` |
| `execution=ExecutionConfig(code_execution=True, code_mode="safe", rate_limiter=obj, context_compaction=False)` | `allow_code_execution`, `code_execution_mode`, `rate_limiter` |
| `memory=MemoryConfig(auto_save="session_name")` | `auto_save` |
| `autonomy=AutonomyConfig(verification_hooks=[...])` | `verification_hooks` |
| `handoffs=[other_agent]` | `allow_delegation` |
@@ -390,6 +390,8 @@ Agent parameters are consolidated into Config objects following the pattern:
**Deprecated standalone params still work** (backward compatible) but emit `DeprecationWarning`.
> **📢 Deprecation Notice:** `ExecutionConfig.context_compaction` currently defaults to `False` during a deprecation period. In the next release, it will default to `True` for automatic proactive context overflow protection. To opt out explicitly, set `context_compaction=False`. To use the new behavior early, set `context_compaction=True`.
```python
# ❌ Old way (deprecated)
agent = Agent(
@@ -413,6 +415,7 @@ agent = Agent(
code_execution=True,
code_mode="safe",
rate_limiter=my_limiter,
context_compaction=False, # prepare for next release when default becomes True; warning still fires during deprecation period
),
memory=MemoryConfig(auto_save="my_session"),
autonomy=AutonomyConfig(verification_hooks=[my_hook]),
+60 -59
View File
@@ -1,6 +1,6 @@
# Documentation Parity Tracker (Python)
> **Categories:** 60 | **Documented:** 57 | **Parity:** 95.0%
> **Categories:** 64 | **Documented:** 61 | **Parity:** 95.3%
This report compares **Python SDK feature categories** against **Python documentation** (docs/concepts, docs/features, etc.).
@@ -8,72 +8,76 @@ This report compares **Python SDK feature categories** against **Python document
| Metric | Count |
|--------|-------|
| Feature Categories | 60 |
| **Documented Categories** | **57** |
| Feature Categories | 64 |
| **Documented Categories** | **61** |
| **Undocumented Categories** | **3** |
| **Parity** | **95.0%** |
| **Parity** | **95.3%** |
## Documented Categories
| Category | Features | Docs | Lines |
|----------|----------|------|-------|
| ✅ AGUI | 1 | 2 | 420 |
| ✅ Agent | 19 | 30 | 8367 |
| ✅ Agent-to-Agent (A2A) | 1 | 3 | 625 |
| ✅ Approval | 1 | 2 | 889 |
| ✅ Agent | 21 | 34 | 9360 |
| ✅ Agent-to-Agent (A2A) | 1 | 7 | 2880 |
| ✅ Agent-to-User (A2U) | 1 | 3 | 547 |
| ✅ Approval | 1 | 3 | 1283 |
| ✅ Audio | 2 | 10 | 649 |
| ✅ Auto Generation | 3 | 6 | 1386 |
| ✅ Autonomy | 3 | 4 | 1146 |
| ✅ Bots | 5 | 2 | 573 |
| ✅ Auto Generation | 4 | 6 | 1724 |
| ✅ Autonomy | 3 | 5 | 1919 |
| ✅ Bots | 7 | 14 | 5228 |
| ✅ Budget | 1 | 1 | 287 |
| ✅ CLI | 5 | 98 | 24103 |
| ✅ Chunking | 2 | 2 | 285 |
| ✅ Citations | 2 | 1 | 202 |
| ✅ Code Execution | 3 | 13 | 3395 |
| ✅ Code Execution | 3 | 13 | 3551 |
| ✅ Conditions | 1 | 3 | 1086 |
| ✅ Configuration | 2 | 7 | 1753 |
| ✅ Context Management | 13 | 29 | 7576 |
| ✅ Database | 1 | 31 | 3037 |
| ✅ Configuration | 3 | 7 | 1812 |
| ✅ Context Management | 16 | 31 | 9294 |
| ✅ Database | 1 | 34 | 4339 |
| ✅ Display | 6 | 2 | 1175 |
| ✅ Embeddings | 6 | 23 | 1966 |
| ✅ Evaluation | 1 | 6 | 2538 |
| ✅ Events | 1 | 2 | 699 |
| ✅ Execution | 3 | 2 | 515 |
| ✅ Files | 2 | 5 | 863 |
| ✅ Flow | 1 | 1 | 284 |
| ✅ Gateway | 6 | 1 | 256 |
| ✅ Guardrails | 4 | 4 | 2279 |
| ✅ Handoffs | 10 | 4 | 1674 |
| ✅ Hooks | 2 | 6 | 1606 |
| ✅ Evaluation | 1 | 6 | 2539 |
| ✅ Events | 1 | 2 | 704 |
| ✅ Execution | 3 | 3 | 1059 |
| ✅ Files | 2 | 6 | 1100 |
| ✅ Flow | 1 | 2 | 435 |
| ✅ Gateway | 7 | 10 | 3924 |
| ✅ Guardrails | 4 | 4 | 2008 |
| ✅ Handoffs | 11 | 5 | 2155 |
| ✅ Hooks | 2 | 6 | 1916 |
| ✅ Image | 1 | 11 | 1096 |
| ✅ Knowledge | 3 | 12 | 3262 |
| ✅ LLM | 2 | 2 | 1049 |
| ✅ Loops | 4 | 1 | 250 |
| ✅ MCP | 2 | 47 | 9754 |
| ✅ Memory | 6 | 12 | 4474 |
| ✅ Output | 3 | 4 | 868 |
| ✅ Parallel Execution | 2 | 2 | 347 |
| ✅ Planning | 6 | 5 | 1506 |
| ✅ Plugins | 8 | 3 | 1494 |
| ✅ Prompts | 2 | 4 | 796 |
| ✅ Providers | 2 | 50 | 5396 |
| ✅ Knowledge | 3 | 12 | 3466 |
| ✅ LLM | 3 | 5 | 2251 |
| ✅ Loops | 4 | 3 | 774 |
| ✅ MCP | 2 | 51 | 10960 |
| ✅ Memory | 6 | 16 | 5855 |
| ✅ Observability | 2 | 23 | 2590 |
| ✅ Output | 3 | 4 | 903 |
| ✅ Parallel Execution | 3 | 2 | 399 |
| ✅ Planning | 6 | 5 | 1519 |
| ✅ Plugins | 8 | 3 | 1502 |
| ✅ Prompts | 2 | 7 | 1570 |
| ✅ Providers | 2 | 51 | 5911 |
| ✅ Query | 1 | 2 | 616 |
| ✅ RAG | 5 | 14 | 2910 |
| ✅ RAG | 5 | 15 | 3190 |
| ✅ Realtime | 2 | 5 | 584 |
| ✅ Reflection | 3 | 2 | 469 |
| ✅ Retrieval | 2 | 5 | 941 |
| ✅ Retrieval | 2 | 5 | 944 |
| ✅ Routing | 1 | 2 | 597 |
| ✅ Sandbox | 5 | 3 | 1002 |
| ✅ Security | 1 | 1 | 875 |
| ✅ Sessions | 2 | 8 | 2128 |
| ✅ Skills | 5 | 5 | 2002 |
| ✅ Tasks | 2 | 5 | 2590 |
| ✅ Telemetry | 1 | 2 | 541 |
| ✅ Sandbox | 5 | 6 | 2369 |
| ✅ Security | 1 | 3 | 2518 |
| ✅ Sessions | 4 | 9 | 3826 |
| ✅ Skills | 5 | 9 | 3415 |
| ✅ Tasks | 2 | 6 | 3064 |
| ✅ Telemetry | 1 | 2 | 560 |
| ✅ Templates | 1 | 7 | 1411 |
| ✅ Tools | 4 | 97 | 17827 |
| ✅ Tools | 12 | 113 | 23809 |
| ✅ Tracing | 3 | 2 | 139 |
| ✅ Video | 2 | 5 | 460 |
| ✅ Video | 2 | 6 | 510 |
| ✅ Vision | 2 | 1 | 283 |
| ✅ Web | 4 | 6 | 1267 |
| ✅ Workflows | 4 | 9 | 3579 |
| ✅ Web | 4 | 6 | 1297 |
| ✅ Workflows | 4 | 11 | 4302 |
## Stub Documentation (Need Content)
@@ -94,21 +98,18 @@ These categories have documentation files but < 50 lines (stubs):
These docs exist but don't match any implemented feature category:
- ℹ️ AI SDK (1 docs, 112 lines)
- ℹ️ Agent-to-User (A2U) (3 docs, 770 lines)
- ℹ️ CLI (91 docs, 21386 lines)
- ℹ️ Callbacks (1 docs, 985 lines)
- ℹ️ Chat (3 docs, 546 lines)
- ℹ️ AI SDK (2 docs, 527 lines)
- ℹ️ Callbacks (1 docs, 1122 lines)
- ℹ️ Chat (3 docs, 634 lines)
- ℹ️ Deep Research (2 docs, 437 lines)
- ℹ️ Deployment (29 docs, 5931 lines)
- ℹ️ Jobs (1 docs, 358 lines)
- ℹ️ Deployment (29 docs, 6013 lines)
- ℹ️ Jobs (2 docs, 961 lines)
- ℹ️ Middleware (1 docs, 60 lines)
- ℹ️ Observability (21 docs, 1580 lines)
- ℹ️ Process (1 docs, 506 lines)
- ℹ️ Scheduler (2 docs, 858 lines)
- ℹ️ Storage (4 docs, 722 lines)
- ℹ️ Streaming (3 docs, 485 lines)
- ℹ️ Token Management (3 docs, 1020 lines)
- ℹ️ Process (1 docs, 545 lines)
- ℹ️ Scheduler (2 docs, 1084 lines)
- ℹ️ Storage (5 docs, 957 lines)
- ℹ️ Streaming (4 docs, 1163 lines)
- ℹ️ Token Management (4 docs, 1383 lines)
- ℹ️ Vector Store (11 docs, 733 lines)
---
@@ -1,35 +1,70 @@
# PraisonAI Agents - Benchmark Results
**Generated:** 2025-12-18 14:40:22
**Iterations:** 100
**Test:** Agent instantiation (without tools)
**Generated:** 2026-06-17
**Methodology:** 10 warmup runs + 1000 measured iterations (`performance_benchmark.py` config)
**Config:** `Agent(name=..., instructions=..., model="gpt-4o-mini", tools=[get_weather], output="silent")`
## Results
## Agent Instantiation Time
| Framework | Avg Time (μs) | Relative |
|-----------|---------------|----------|
| **PraisonAI** | **3.77** | **1.00x (fastest)** |
| OpenAI Agents SDK | 5.26 | 1.39x |
| Agno | 5.64 | 1.49x |
| PraisonAI (LiteLLM) | 7.56 | 2.00x |
| PydanticAI | 226.94 | 60.16x |
| LangGraph | 4,558.71 | 1,209x |
| CrewAI | 15,607.92 | 4,138x |
| Framework | Avg Time (μs) | Min (μs) | Median (μs) | Relative |
|-----------|---------------|----------|-------------|----------|
| OpenAI Agents SDK | 5.05 | — | — | 1.00x (fastest) |
| Agno | 5.27 | — | — | 1.04x |
| **PraisonAI** | **13.62** | **12.58** | **12.96** | **2.69x** |
| PraisonAI (LiteLLM) | 14.49 | 13.37 | 13.71 | 2.86x |
### PraisonAI detail (1000 iter)
| Variant | Avg (μs) | Min (μs) | Median (μs) | Std (μs) |
|---------|----------|----------|-------------|----------|
| With tools (`gpt-4o-mini`) | 13.62 | 12.58 | 12.96 | 2.73 |
| With tools (`openai/gpt-4o-mini`) | 14.49 | 13.37 | 13.71 | 4.14 |
| Minimal, no tools | 13.40 | — | — | — |
## Historical baseline
| Source | Config | Avg (μs) | Notes |
|--------|--------|----------|-------|
| `performance_benchmark.py` (Dec 2025) | tools + 1000 iter | **3.20** | Pre-init-bloat baseline |
| `simple_benchmark.py` (Dec 2025) | no tools, 100 iter | **3.77** | Saved in prior `BENCHMARK_RESULTS.md` |
| Current (Jun 2026) | tools + 1000 iter | **13.62** | After lazy-init optimisations (was ~31 μs pre-fix) |
## Package Versions
| Package | Version |
|---------|--------|
| PraisonAI | 0.1.5 |
| Agno | 2.3.14 |
| PydanticAI | 1.35.0 |
| OpenAI Agents SDK | 0.6.3 |
| LangGraph | 1.0.5 |
| CrewAI | 1.6.1 |
|---------|---------|
| PraisonAI | 1.6.60 |
| Agno | 2.4.2 |
| OpenAI Agents SDK | 0.7.0 |
## How to Reproduce
PraisonAI-only (fast; avoids CrewAI/LangGraph import hang):
```bash
cd praisonai-agents
python benchmarks/simple_benchmark.py
PYTHONWARNINGS='ignore::DeprecationWarning' python -c "
import time, statistics
from typing import Literal
from praisonaiagents import Agent
def get_weather(city: Literal['nyc', 'sf']):
return 'cloudy' if city == 'nyc' else 'sunny'
def bench(fn, n=1000, w=10):
for _ in range(w): fn()
times = []
for _ in range(n):
s = time.perf_counter(); fn(); times.append(time.perf_counter()-s)
print(f'avg={statistics.mean(times)*1e6:.2f} μs')
bench(lambda: Agent(name='Test', instructions='Hi', model='gpt-4o-mini', tools=[get_weather], output='silent'))
"
```
Full multi-framework benchmark:
```bash
cd praisonai-agents
PYTHONWARNINGS='ignore::DeprecationWarning' python benchmarks/performance_benchmark.py --save
```
@@ -0,0 +1,62 @@
# Agent Instantiation — Root Cause Analysis
**Date:** 2026-06-17
**Version:** praisonaiagents 1.6.58 (dev)
**Methodology:** 10 warmup + 1000 measured iterations, `time.perf_counter()`
## Summary
Instantiation regressed from **~3.2 μs** (Dec 2025) to **~31 μs** (~10×). After fixes, steady-state is **~16 μs** (~2× historical). PraisonAI is competitive with Agno (~5 μs) on warmed benchmarks using `model=`.
## Before vs After Fixes
| Config | Historical | Pre-fix | Post-fix |
|--------|------------|---------|----------|
| Minimal (`name`, `output="silent"`) | 3.20 μs | 31.36 μs | **16.35 μs** |
| `model="gpt-4o-mini"` | 3.77 μs | 32.19 μs | **16.86 μs** |
| `llm="gpt-4o-mini"` (deprecated) | 3.77 μs | 46.48 μs | **~18 μs** (warn once) |
| LiteLLM `openai/gpt-4o-mini` cold | 7.31 μs | **3.7 s** | **53.63 μs** |
| LiteLLM warmed instantiate | — | 60 μs | **19.83 μs** |
| Import `Agent` | — | 20.8 ms | **29.6 ms** |
## Root Causes (validated)
| ID | Cause | Status | Fix applied |
|----|-------|--------|-------------|
| H1 | Config loader cold start (+9 ms first init) | CONFIRMED | Fast path when no config defaults (`_defaults_has_any_values`) |
| H2 | LoopGuard on every init (~2 μs) | CONFIRMED | Lazy `_ensure_loop_guard()` on first tool call |
| H3 | Approval presets | REJECTED | No change needed |
| H4 | `llm=` deprecation every init (+16 μs) | CONFIRMED | Warn once per process in `warn_deprecated_param` |
| H5 | Init bloat / config resolution (~25 μs) | CONFIRMED | Partially addressed via loader fast path |
| H6 | LiteLLM eager `LLM()` import (+3.7 s) | CONFIRMED | Deferred `_llm_init_params` + lazy `llm_instance` property |
| H7 | `ExecutionConfig` deprecation every init | CONFIRMED | Fixed stack walk; internal SDK path cached |
## cProfile — Post-fix (500 warmed `model=` inits)
| Function | Time (ms) | Per call |
|----------|-----------|----------|
| `Agent.__init__` | 8 | ~16 μs |
| `apply_config_defaults` | 1 | ~2 μs |
| `ExecutionConfig.__post_init__` | 1 | ~2 μs (first call only walks stack) |
| `_warnings.warn` | **0** | removed from hot path |
## Fixes implemented
1. **`praisonaiagents/config/feature_configs.py`** — Fix `ExecutionConfig.__post_init__` deprecation guard; cache internal SDK detection.
2. **`praisonaiagents/utils/deprecation.py`** — Warn once per process for deprecated params.
3. **`praisonaiagents/config/loader.py`** — Skip `apply_config_defaults` lookups when config has no defaults.
4. **`praisonaiagents/agent/agent.py`** — Lazy LoopGuard, lazy LLM instance, deferred LiteLLM params.
5. **`benchmarks/simple_benchmark.py`** — Use `model=`, add 10-iteration warmup.
6. **`benchmarks/performance_benchmark.py`** — Use `model=` instead of deprecated `llm=`.
## Remaining gap (~16 μs vs ~3 μs)
The residual ~13 μs is primarily **`Agent.__init__` size** (~1200 lines of consolidated config resolution added since v0.1.5). Further optimisation requires a dedicated slim fast-path for `Agent(name=..., output="silent")` without full config object churn.
## How to reproduce
```bash
cd praisonai-agents
PYTHONWARNINGS='ignore::DeprecationWarning' python benchmarks/simple_benchmark.py
PYTHONWARNINGS='ignore::DeprecationWarning' python benchmarks/performance_benchmark.py
```
@@ -120,8 +120,8 @@ def run_benchmark(iterations: int = 100) -> List[ParamResult]:
('backstory', 'I am an AI assistant.', 'backstory="..."'),
# LLM configuration
('llm', 'gpt-4o-mini', 'llm="gpt-4o-mini"'),
('llm', 'openai/gpt-4o-mini', 'llm="openai/gpt-4o-mini" (LiteLLM)'),
('model', 'gpt-4o-mini', 'model="gpt-4o-mini"'),
('model', 'openai/gpt-4o-mini', 'model="openai/gpt-4o-mini" (LiteLLM)'),
# Tools
('tools', [sample_tool], 'tools=[sample_tool]'),
@@ -298,7 +298,7 @@ def save_results(results: List[ParamResult], filename: str = 'PARAM_IMPACT_RESUL
f.write('### Best Practices\n\n')
f.write('1. Use `output="silent"` for production workloads\n')
f.write('2. Only enable `context=True` when needed for long conversations\n')
f.write('3. Use `llm="gpt-4o-mini"` (OpenAI SDK) for fastest instantiation\n')
f.write('3. Use `model="gpt-4o-mini"` for fastest instantiation\n')
f.write('4. Avoid `memory=True` unless persistent memory is required\n')
f.write('\n## How to Reproduce\n\n')
@@ -185,7 +185,7 @@ def instantiate_praisonai_agent():
return PraisonAgent(
name="Test Agent",
instructions="Be concise, reply with one sentence.",
llm="gpt-4o-mini",
model="gpt-4o-mini",
tools=tools,
output="silent"
)
@@ -196,7 +196,7 @@ def instantiate_praisonai_litellm_agent():
return PraisonAgent(
name="Test Agent",
instructions="Be concise, reply with one sentence.",
llm="openai/gpt-4o-mini",
model="openai/gpt-4o-mini",
tools=tools,
output="silent"
)
@@ -14,6 +14,7 @@ import argparse
ITERATIONS = 100
WARMUP = 10
def sample_tool(city: Literal['nyc', 'sf']):
@@ -23,8 +24,10 @@ def sample_tool(city: Literal['nyc', 'sf']):
return 'sunny'
def measure_instantiation(create_fn, iterations=ITERATIONS):
def measure_instantiation(create_fn, iterations=ITERATIONS, warmup=WARMUP):
"""Measure average instantiation time in microseconds."""
for _ in range(warmup):
create_fn()
times = []
for _ in range(iterations):
start = time.perf_counter()
@@ -48,11 +51,11 @@ def run_benchmark():
from praisonaiagents import Agent as PraisonAgent
results['PraisonAI'] = measure_instantiation(
lambda: PraisonAgent(name='Test', llm='gpt-4o-mini', output="silent")
lambda: PraisonAgent(name='Test', model='gpt-4o-mini', output="silent")
)
results['PraisonAI (LiteLLM)'] = measure_instantiation(
lambda: PraisonAgent(name='Test', llm='openai/gpt-4o-mini', output="silent")
lambda: PraisonAgent(name='Test', model='openai/gpt-4o-mini', output="silent")
)
# Other frameworks for comparison
@@ -0,0 +1,229 @@
#!/usr/bin/env python3
"""
Context Compaction Example - Python API
Demonstrates how to configure context compaction policies in PraisonAI agents.
This provides examples for CLI/YAML parity.
"""
from praisonaiagents import Agent
from praisonaiagents.config.feature_configs import ExecutionConfig
from praisonaiagents.context.adapters import ContextCompactionPolicyAdapter
from praisonaiagents.context.protocols import CompactionStrategy
def example_default_behavior():
"""Example 1: Default behavior (during deprecation period)."""
print("=== Example 1: Default Behavior ===")
# Currently defaults to False with deprecation warning
agent = Agent(
name="default_agent",
instructions="You are a helpful assistant."
)
print(f"Context compaction enabled: {agent.execution.context_compaction}")
print("Note: Currently False during deprecation period, will change to True in next release\n")
def example_explicit_disable():
"""Example 2: Explicitly disable context compaction."""
print("=== Example 2: Explicitly Disabled ===")
agent = Agent(
name="no_compaction_agent",
instructions="You are a helpful assistant.",
execution=ExecutionConfig(context_compaction=False)
)
print(f"Context compaction enabled: {agent.execution.context_compaction}")
print("No automatic compaction will occur\n")
def example_default_policy():
"""Example 3: Use default compaction policy."""
print("=== Example 3: Default Policy ===")
agent = Agent(
name="default_policy_agent",
instructions="You are a helpful assistant.",
execution=ExecutionConfig(context_compaction=True)
)
print(f"Context compaction enabled: {agent.execution.context_compaction}")
print("Will use balanced default policy (trigger at 90%, preserve 5 recent turns)\n")
def example_conservative_policy():
"""Example 4: Conservative compaction policy."""
print("=== Example 4: Conservative Policy ===")
conservative_policy = ContextCompactionPolicyAdapter(
trigger_at=0.80, # Trigger at 80% capacity
strategy=CompactionStrategy.DROP_OLDEST_TOOLS,
preserve_last_n_turns=8, # Keep more conversation history
target_utilization=0.60, # Target 60% utilization after compaction
aggressive_tool_truncation=False
)
agent = Agent(
name="conservative_agent",
instructions="You are a helpful assistant.",
execution=ExecutionConfig(context_compaction=conservative_policy)
)
print(f"Policy trigger: {conservative_policy.trigger_at}")
print(f"Strategy: {conservative_policy.strategy}")
print(f"Preserve turns: {conservative_policy.preserve_last_n_turns}")
print(f"Target utilization: {conservative_policy.target_utilization}\n")
def example_aggressive_policy():
"""Example 5: Aggressive compaction policy."""
print("=== Example 5: Aggressive Policy ===")
aggressive_policy = ContextCompactionPolicyAdapter(
trigger_at=0.95, # Wait until 95% capacity
strategy=CompactionStrategy.SUMMARISE, # Use LLM summarization
preserve_last_n_turns=3, # Keep only recent exchanges
target_utilization=0.75, # Target higher utilization
aggressive_tool_truncation=True, # Truncate large tool outputs
model_overrides={
"gpt-4": {"trigger_at": 0.92}, # Different threshold for gpt-4
"claude-3": {"trigger_at": 0.88}
}
)
agent = Agent(
name="aggressive_agent",
instructions="You are a helpful assistant.",
execution=ExecutionConfig(context_compaction=aggressive_policy)
)
print(f"Policy trigger: {aggressive_policy.trigger_at}")
print(f"Strategy: {aggressive_policy.strategy}")
print(f"Preserve turns: {aggressive_policy.preserve_last_n_turns}")
print(f"Model overrides: {aggressive_policy.model_overrides}\n")
def example_yaml_config():
"""Example 6: YAML configuration equivalent."""
print("=== Example 6: YAML Configuration Equivalent ===")
print("""
# agent_config.yaml
name: "helpful_agent"
instructions: "You are a helpful assistant."
llm: "gpt-4o-mini"
execution:
context_compaction:
trigger_at: 0.85
strategy: "drop_oldest_tools"
preserve_last_n_turns: 6
target_utilization: 0.65
aggressive_tool_truncation: true
model_overrides:
"gpt-4":
trigger_at: 0.90
strategy: "summarise"
""")
# Python equivalent:
policy = ContextCompactionPolicyAdapter(
trigger_at=0.85,
strategy="drop_oldest_tools",
preserve_last_n_turns=6,
target_utilization=0.65,
aggressive_tool_truncation=True,
model_overrides={
"gpt-4": {
"trigger_at": 0.90,
"strategy": "summarise"
}
}
)
agent = Agent(
name="helpful_agent",
instructions="You are a helpful assistant.",
llm="gpt-4o-mini",
execution=ExecutionConfig(context_compaction=policy)
)
print(f"Loaded policy trigger: {policy.trigger_at}")
print(f"Loaded strategy: {policy.strategy}\n")
def example_cli_equivalent():
"""Example 7: CLI equivalent commands."""
print("=== Example 7: CLI Equivalent ===")
print("""
# Basic usage (will use default policy when enabled)
praisonai agent run --name "assistant" --instructions "Be helpful"
# Disable compaction explicitly
praisonai agent run --name "assistant" --execution '{"context_compaction": false}'
# Use conservative policy
praisonai agent run --name "assistant" \\
--execution '{
"context_compaction": {
"trigger_at": 0.80,
"strategy": "drop_oldest_tools",
"preserve_last_n_turns": 8
}
}'
# Load from YAML config file
praisonai agent run --config agent_config.yaml
""")
def example_serialization_round_trip():
"""Example 8: Serialization round-trip."""
print("=== Example 8: Serialization Round-Trip ===")
# Create agent with custom policy
original_policy = ContextCompactionPolicyAdapter(
trigger_at=0.88,
strategy=CompactionStrategy.SUMMARISE
)
config = ExecutionConfig(context_compaction=original_policy)
# Serialize to dict (for JSON/YAML storage)
config_dict = config.to_dict()
print(f"Serialized context_compaction type: {type(config_dict['context_compaction'])}")
# Deserialize back to object
restored_config = ExecutionConfig.from_dict(config_dict)
restored_policy = restored_config.context_compaction
print(f"Restored policy type: {type(restored_policy)}")
print(f"Attributes preserved: trigger_at={restored_policy.trigger_at}, strategy={restored_policy.strategy}")
print("✓ Round-trip serialization successful\n")
if __name__ == "__main__":
print("Context Compaction Configuration Examples\n")
print("=" * 50)
try:
example_default_behavior()
example_explicit_disable()
example_default_policy()
example_conservative_policy()
example_aggressive_policy()
example_yaml_config()
example_cli_equivalent()
example_serialization_round_trip()
print("=" * 50)
print("✅ All examples completed successfully!")
print("\nFor more information, see:")
print("- Documentation: https://docs.praisonai.com/context-compaction")
print("- Examples: examples/context_compaction_example.py")
except Exception as e:
print(f"❌ Example failed: {e}")
import traceback
traceback.print_exc()
@@ -0,0 +1,75 @@
#!/usr/bin/env python3
"""Example of using LLMConfig with Agent for model fallback configuration."""
from praisonaiagents import Agent
from praisonaiagents.config import LLMConfig
# Example 1: Pass LLMConfig via llm parameter
def example_llm_param():
"""Using LLMConfig via the llm parameter."""
config = LLMConfig(
model="gpt-4o",
fallback_models=["claude-3-5-sonnet", "gpt-4o-mini"],
base_url="https://api.example.com",
api_key="test-key"
)
agent = Agent(
name="Assistant",
instructions="You are a helpful assistant",
llm=config # Pass LLMConfig object directly
)
print(f"Primary model: {agent.llm}")
print(f"Fallback models: {agent.fallback_models}")
print(f"Base URL: {agent.base_url}")
print(f"API Key: {agent.api_key}")
# Example 2: Pass LLMConfig via model parameter
def example_model_param():
"""Using LLMConfig via the model parameter (preferred)."""
config = LLMConfig(
model="gpt-4o",
fallback_models=["claude-3-5-sonnet", "gpt-4o-mini"],
base_url="https://api.example.com",
api_key="test-key"
)
agent = Agent(
name="Assistant",
instructions="You are a helpful assistant",
model=config # Pass LLMConfig via model parameter
)
print(f"Primary model: {agent.llm}")
print(f"Fallback models: {agent.fallback_models}")
print(f"Base URL: {agent.base_url}")
print(f"API Key: {agent.api_key}")
# Example 3: Traditional usage still works
def example_traditional():
"""Traditional usage without LLMConfig (no fallback support)."""
agent = Agent(
name="Assistant",
instructions="You are a helpful assistant",
model="gpt-4o",
base_url="https://api.example.com",
api_key="test-key"
)
print(f"Primary model: {agent.llm}")
print(f"Fallback models: {agent.fallback_models}") # Will be empty
print(f"Base URL: {agent.base_url}")
print(f"API Key: {agent.api_key}")
if __name__ == "__main__":
print("=== Example 1: LLMConfig via llm parameter ===")
example_llm_param()
print()
print("=== Example 2: LLMConfig via model parameter ===")
example_model_param()
print()
print("=== Example 3: Traditional usage (no fallback) ===")
example_traditional()
@@ -114,6 +114,9 @@ def _get_lazy_cache():
# ============================================================================
_LAZY_IMPORTS = {
# Version information (lazy loaded to avoid file I/O on import)
'__version__': ('praisonaiagents._version', '__version__'),
# Tools (moved from eager imports for lazy loading)
'Tools': ('praisonaiagents.tools.tools', 'Tools'),
'BaseTool': ('praisonaiagents.tools.base', 'BaseTool'),
@@ -127,6 +130,23 @@ _LAZY_IMPORTS = {
'get_tool': ('praisonaiagents.tools.registry', 'get_tool'),
'ToolRegistry': ('praisonaiagents.tools.registry', 'ToolRegistry'),
# Toolsets (named toolset groups)
'ToolsetSpec': ('praisonaiagents.toolsets', 'ToolsetSpec'),
'ToolsetRegistry': ('praisonaiagents.toolsets', 'ToolsetRegistry'),
'get_toolset_registry': ('praisonaiagents.toolsets', 'get_toolset_registry'),
'register_toolset': ('praisonaiagents.toolsets', 'register_toolset'),
'resolve_toolset': ('praisonaiagents.toolsets', 'resolve_toolset'),
'resolve_toolsets': ('praisonaiagents.toolsets', 'resolve_toolsets'),
'list_toolsets': ('praisonaiagents.toolsets', 'list_toolsets'),
'get_toolset': ('praisonaiagents.toolsets', 'get_toolset'),
'unregister_toolset': ('praisonaiagents.toolsets', 'unregister_toolset'),
'has_toolset': ('praisonaiagents.toolsets', 'has_toolset'),
# Run outcomes - typed validation and agent execution results
'AgentRunOutcome': ('praisonaiagents.run_outcome', 'AgentRunOutcome'),
'RunStatus': ('praisonaiagents.run_outcome', 'RunStatus'),
'validate_decision_string': ('praisonaiagents.run_outcome', 'validate_decision_string'),
# Main display utilities (imports rich)
'TaskOutput': ('praisonaiagents.main', 'TaskOutput'),
'ReflectionOutput': ('praisonaiagents.main', 'ReflectionOutput'),
@@ -174,6 +194,7 @@ _LAZY_IMPORTS = {
'RECOMMENDED_PROMPT_PREFIX': ('praisonaiagents.agent.handoff', 'RECOMMENDED_PROMPT_PREFIX'),
'prompt_with_handoff_instructions': ('praisonaiagents.agent.handoff', 'prompt_with_handoff_instructions'),
'HandoffConfig': ('praisonaiagents.agent.handoff', 'HandoffConfig'),
'HandoffToolPolicy': ('praisonaiagents.agent.handoff', 'HandoffToolPolicy'),
'HandoffResult': ('praisonaiagents.agent.handoff', 'HandoffResult'),
'HandoffInputData': ('praisonaiagents.agent.handoff', 'HandoffInputData'),
'ContextPolicy': ('praisonaiagents.agent.handoff', 'ContextPolicy'),
@@ -211,6 +232,7 @@ _LAZY_IMPORTS = {
# Agent classes
'Agent': ('praisonaiagents.agent.agent', 'Agent'),
'RetryBackoffConfig': ('praisonaiagents.agent.retry_utils', 'RetryBackoffConfig'),
'BudgetExceededError': ('praisonaiagents.errors', 'BudgetExceededError'),
# Error hierarchy - structured exception handling
@@ -386,6 +408,17 @@ _LAZY_IMPORTS = {
'MultiAgentMemoryConfig': ('praisonaiagents.config.feature_configs', 'MultiAgentMemoryConfig'),
'ToolSearchConfig': ('praisonaiagents.config.feature_configs', 'ToolSearchConfig'),
# Context compaction policies
'ContextCompactionPolicy': ('praisonaiagents.context.policy', 'ContextCompactionPolicy'),
'ContextCompactionPolicyProtocol': ('praisonaiagents.context.policy', 'ContextCompactionPolicyProtocol'),
'CompactionRoute': ('praisonaiagents.context.policy', 'CompactionRoute'),
'CompactionStrategy': ('praisonaiagents.context.policy', 'CompactionStrategy'),
'ContextBudgetResult': ('praisonaiagents.context.policy', 'ContextBudgetResult'),
'get_default_policy': ('praisonaiagents.context.policy', 'get_default_policy'),
'CONSERVATIVE_POLICY': ('praisonaiagents.context.policy', 'CONSERVATIVE_POLICY'),
'BALANCED_POLICY': ('praisonaiagents.context.policy', 'BALANCED_POLICY'),
'AGGRESSIVE_POLICY': ('praisonaiagents.context.policy', 'AGGRESSIVE_POLICY'),
# Parameter resolver
'resolve': ('praisonaiagents.config.param_resolver', 'resolve'),
'ArrayMode': ('praisonaiagents.config.param_resolver', 'ArrayMode'),
@@ -444,7 +477,7 @@ _LAZY_IMPORTS = {
'GatewayClientProtocol': ('praisonaiagents.gateway.protocols', 'GatewayClientProtocol'),
'GatewayEvent': ('praisonaiagents.gateway.protocols', 'GatewayEvent'),
'GatewayMessage': ('praisonaiagents.gateway.protocols', 'GatewayMessage'),
'EventType': ('praisonaiagents.gateway.protocols', 'EventType'),
'GatewayEventType': ('praisonaiagents.gateway.protocols', 'EventType'),
'GatewayConfig': ('praisonaiagents.gateway.config', 'GatewayConfig'),
'SessionConfig': ('praisonaiagents.gateway.config', 'SessionConfig'),
@@ -626,6 +659,11 @@ def _custom_handler(name, cache):
mod = importlib.import_module('.db', 'praisonaiagents')
cache['db'] = mod
return mod
if name == 'toolsets':
import importlib
mod = importlib.import_module('.toolsets', 'praisonaiagents')
cache['toolsets'] = mod
return mod
raise AttributeError(f"Not handled by custom_handler: {name}")
@@ -741,8 +779,12 @@ def warmup(include_litellm: bool = False, include_openai: bool = True) -> dict:
# ============================================================================
__all__ = [
# Version information
'__version__',
# Core classes - the essentials
'Agent',
'RetryBackoffConfig',
'AgentTeam', # Primary class for multi-agent coordination (v1.0+)
'AgentManager', # Silent alias for AgentTeam
'Agents', # Deprecated alias for AgentTeam (emits warning)
Loaded 100 of 602 files, more files were not shown because too many files have changed in this diff. Show more