praisonai-code 0.0.4__tar.gz → 0.0.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/PKG-INFO +29 -6
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/README.md +28 -5
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/__init__.py +1 -1
- praisonai_code-0.0.5/praisonai_code/__main__.py +19 -0
- praisonai_code-0.0.5/praisonai_code/_framework_availability.py +134 -0
- praisonai_code-0.0.5/praisonai_code/_logging.py +24 -0
- praisonai_code-0.0.5/praisonai_code/_registry.py +343 -0
- praisonai_code-0.0.5/praisonai_code/_safe_loader.py +124 -0
- praisonai_code-0.0.5/praisonai_code/_version.py +32 -0
- praisonai_code-0.0.5/praisonai_code/_wrapper_bridge.py +48 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/app.py +22 -11
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/branding.py +4 -7
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/acp.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/agent.py +2 -2
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/agents.py +3 -3
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/attach.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/benchmark.py +7 -7
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/call.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/chat.py +7 -4
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/checkpoint.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/code.py +7 -7
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/command.py +2 -2
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/commit.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/context.py +3 -3
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/daemon.py +5 -5
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/debug.py +4 -4
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/deploy.py +4 -4
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/diag.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/docs.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/doctor.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/endpoints.py +2 -2
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/environment.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/eval.py +2 -2
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/github.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/hooks.py +3 -3
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/init.py +2 -2
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/lsp.py +4 -4
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/memory.py +4 -4
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/models.py +3 -3
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/package.py +3 -3
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/profile.py +3 -3
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/recipe.py +5 -5
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/registry.py +2 -2
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/research.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/rules.py +3 -3
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/run.py +24 -27
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/schedule.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/serve.py +3 -3
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/session.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/setup.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/skills.py +3 -3
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/standardise.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/templates.py +2 -2
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/test.py +2 -2
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/todo.py +3 -3
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/tools.py +4 -4
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/tracker.py +2 -2
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/train.py +2 -2
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/version.py +24 -19
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/workflow.py +3 -3
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/configuration/model_resolver.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/execution/profiler.py +2 -2
- praisonai_code-0.0.5/praisonai_code/cli/features/_approval_bridge.py +144 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/diag.py +2 -2
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/checks/acp_checks.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/checks/env_checks.py +4 -3
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/checks/lsp_checks.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/checks/serve_checks.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/engine.py +2 -2
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/examples.py +2 -2
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/interactive_core_headless.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/interactive_tools.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/job_workflow.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/profiler/core.py +2 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/profiler/suite.py +2 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/interactive/async_tui.py +3 -3
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/interactive/split_tui.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/interactive/tui_app.py +1 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/main.py +35 -27
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli_backends/registry.py +12 -21
- praisonai_code-0.0.5/praisonai_code/llm/__init__.py +28 -0
- praisonai_code-0.0.5/praisonai_code/llm/catalogue.py +472 -0
- praisonai_code-0.0.5/praisonai_code/llm/config.py +35 -0
- praisonai_code-0.0.5/praisonai_code/llm/credentials.py +200 -0
- praisonai_code-0.0.5/praisonai_code/llm/env.py +233 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/runtime/descriptor.py +3 -12
- praisonai_code-0.0.5/praisonai_code/tool_registry.py +123 -0
- praisonai_code-0.0.5/praisonai_code/tool_resolver.py +1251 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code.egg-info/PKG-INFO +29 -6
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code.egg-info/SOURCES.txt +16 -0
- praisonai_code-0.0.5/praisonai_code.egg-info/entry_points.txt +2 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/pyproject.toml +4 -1
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/__init__.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/_forward_shim.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/_paths.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/_warnings.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/approval_backend.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/__init__.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/app.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/audit.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/auth.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/batch.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/browser.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/completion.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/config.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/examples.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/flow.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/knowledge.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/langextract.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/langfuse.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/loop.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/managed.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/mcp.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/n8n.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/obs.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/paths.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/permissions.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/plugins.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/port.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/publish.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/rag.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/realtime.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/replay.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/retrieval.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/sandbox.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/traces.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/ui.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/up.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/commands/validate.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/config_loader.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/configuration/__init__.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/configuration/config.schema.json +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/configuration/credentials.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/configuration/loader.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/configuration/oauth.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/configuration/paths.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/configuration/resolver.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/configuration/schema.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/execution/__init__.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/execution/core.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/execution/request.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/execution/result.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/fallback_schema.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/__init__.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/_endpoint_registry.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/_search_registry.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/acp.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/action_orchestrator.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/agent_scheduler.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/agent_tools.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/agents.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/at_mentions.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/audit_cli.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/auto_memory.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/auto_mode.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/autonomy_mode.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/background.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/base.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/benchmark.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/capabilities.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/checkpoints.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/cli_profiler.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/code_intelligence.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/compaction.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/compare.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/config_hierarchy.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/context_manager.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/cost_tracker.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/csv_test_runner.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/custom_definitions.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/debug.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/deploy.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/display_jsonl.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/__init__.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/checks/__init__.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/checks/bot_checks.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/checks/config_checks.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/checks/db_checks.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/checks/gateway_checks.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/checks/mcp_checks.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/checks/memory_checks.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/checks/network_checks.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/checks/obs_checks.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/checks/packaging_checks.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/checks/performance_checks.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/checks/permissions_checks.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/checks/runtime_checks.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/checks/runtime_migration_checks.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/checks/selftest_checks.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/checks/skills_checks.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/checks/tools_checks.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/formatters.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/handler.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/models.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/doctor/registry.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/endpoints.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/eval.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/external_agents.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/fast_context.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/file_history.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/flow_display.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/git_attribution.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/git_integration.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/guardrail.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/handoff.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/hooks.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/hybrid_workflow.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/image.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/interactive_runtime.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/interactive_test_harness.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/interactive_tui.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/jobs.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/knowledge.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/knowledge_cli.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/lite.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/logs.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/lsp_cli.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/lsp_diagnostics.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/mcp.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/message_queue.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/metrics.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/migrate.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/migration_flow.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/migration_spec.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/n8n.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/observability.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/ollama.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/output_modes.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/output_style.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/package.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/performance.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/persistence.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/profiler/__init__.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/profiler/optimizations.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/queue/__init__.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/queue/manager.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/queue/models.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/queue/persistence.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/queue/scheduler.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/queue/worker.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/recipe.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/recipe_creator.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/recipe_optimizer.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/recipe_prompts.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/registry.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/repo_map.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/router.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/safe_shell.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/sandbox_cli.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/sandbox_executor.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/sdk_knowledge.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/session.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/session_checkpoints.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/setup/__init__.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/setup/handler.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/setup/templates.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/skills.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/slash_commands.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/telemetry.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/templates.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/thinking.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/todo.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tools.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tui/__init__.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tui/app.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tui/cli.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tui/config.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tui/debug.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tui/events.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tui/mock_provider.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tui/orchestrator.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tui/screens/__init__.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tui/screens/help.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tui/screens/main.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tui/screens/queue.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tui/screens/session.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tui/screens/settings.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tui/session_store.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tui/widgets/__init__.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tui/widgets/chat.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tui/widgets/command_popup.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tui/widgets/composer.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tui/widgets/file_popup.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tui/widgets/queue_panel.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tui/widgets/status.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/tui/widgets/tool_panel.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/wizard.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/workflow.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/features/yaml_utils.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/interactive/__init__.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/interactive/config.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/interactive/core.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/interactive/events.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/interactive/frontends/__init__.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/interactive/frontends/rich_frontend.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/interactive/frontends/textual_frontend.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/interactive/praison_io.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/interactive/repl.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/langfuse_client.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/output/__init__.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/output/console.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/output/event_bridge.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/schedule_cli.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/schema_provider.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/session/__init__.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/session/resume.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/session/unified.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/state/__init__.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/state/identifiers.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/state/project_sessions.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/state/sessions.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/ui/__init__.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/ui/config.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/ui/events.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/ui/mg_backend.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/ui/plain.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/ui/rich_backend.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/unified_schema.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/utils/env_utils.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli/utils/project.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli_backends/__init__.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/cli_backends/claude.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/runtime/__init__.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/runtime/__main__.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/runtime/client.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code/runtime/server.py +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code.egg-info/dependency_links.txt +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code.egg-info/requires.txt +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/praisonai_code.egg-info/top_level.txt +0 -0
- {praisonai_code-0.0.4 → praisonai_code-0.0.5}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: praisonai-code
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.5
|
|
4
4
|
Summary: Agentic terminal CLI for PraisonAI — the terminal-native agent (run, chat, code, runtime, cli backends) extracted from the praisonai wrapper.
|
|
5
5
|
Author: Mervin Praison
|
|
6
6
|
License: MIT
|
|
@@ -42,9 +42,14 @@ Agentic terminal CLI for PraisonAI — the terminal-native agent product
|
|
|
42
42
|
| C4 | Agentic features | Done |
|
|
43
43
|
| C5 | `main.py`, `app.py`, config/session/utils + shims | Done |
|
|
44
44
|
| C6 | Integration gate + sign-off | Done |
|
|
45
|
+
| C7 | Hot-path standalone (agentic CLI without wrapper import) | Done |
|
|
45
46
|
|
|
46
47
|
- `praisonai_code.runtime` — warm local runtime (daemon + thin client).
|
|
47
48
|
- `praisonai_code.cli_backends` — CLI backend implementations (e.g. Claude Code).
|
|
49
|
+
- `praisonai_code.llm` — endpoint resolution, credentials, model catalogue.
|
|
50
|
+
- `praisonai_code.tool_resolver` — YAML tool name resolution.
|
|
51
|
+
|
|
52
|
+
See `src/praisonai/tests/C7_VERIFICATION.md` for the hot-path sign-off checklist.
|
|
48
53
|
|
|
49
54
|
## Dependency rules
|
|
50
55
|
|
|
@@ -57,11 +62,21 @@ praisonai-code → depends on praisonaiagents (core SDK)
|
|
|
57
62
|
click, textual, PyYAML, python-dotenv, litellm, mcp, pydantic — see
|
|
58
63
|
`pyproject.toml`). The rules above govern the **inter-package** direction.
|
|
59
64
|
|
|
60
|
-
> **C7
|
|
61
|
-
>
|
|
62
|
-
>
|
|
63
|
-
>
|
|
64
|
-
>
|
|
65
|
+
> **C7 (hot path complete):** Standalone `pip install praisonai-code` supports
|
|
66
|
+
> agentic terminal commands (`run`, `chat`, `code`, warm runtime) without importing
|
|
67
|
+
> the wrapper on the hot path. Approval backends resolve locally via
|
|
68
|
+
> `praisonai_code.cli.features._approval_bridge` (channel bots delegate to the
|
|
69
|
+
> wrapper). Optional features (observability sinks, framework adapters,
|
|
70
|
+
> bots/gateway) remain wrapper-only via `praisonai_code._wrapper_bridge`.
|
|
71
|
+
|
|
72
|
+
Completed C7 steps:
|
|
73
|
+
- `praisonai_code._registry` — vendored plugin registry (no wrapper import)
|
|
74
|
+
- `praisonai_code._version` / `runtime/descriptor.py` — version from `praisonai-code`
|
|
75
|
+
- `praisonai_code.__main__` + `praisonai-code` console script — standalone entry
|
|
76
|
+
- `praisonai_code._logging` — CLI logging without wrapper dependency
|
|
77
|
+
- `praisonai_code.llm.*` — env, credentials, catalogue, config
|
|
78
|
+
- `praisonai_code._framework_availability`, `_safe_loader`, `tool_resolver` — execution helpers
|
|
79
|
+
- Namespace cleanup — `praisonai_code.cli.features.*` local imports on agentic path
|
|
65
80
|
|
|
66
81
|
Backward compatibility is preserved via PEP 562 shims at the old
|
|
67
82
|
`praisonai.*` import paths, so `pip install praisonai` and
|
|
@@ -75,6 +90,14 @@ Backward compatibility is preserved via PEP 562 shims at the old
|
|
|
75
90
|
pip install praisonai
|
|
76
91
|
```
|
|
77
92
|
|
|
93
|
+
**Standalone code package (agentic CLI only):**
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
pip install praisonai-code
|
|
97
|
+
praisonai-code --version
|
|
98
|
+
python -m praisonai_code --help
|
|
99
|
+
```
|
|
100
|
+
|
|
78
101
|
**Development / monorepo:**
|
|
79
102
|
|
|
80
103
|
```bash
|
|
@@ -20,9 +20,14 @@ Agentic terminal CLI for PraisonAI — the terminal-native agent product
|
|
|
20
20
|
| C4 | Agentic features | Done |
|
|
21
21
|
| C5 | `main.py`, `app.py`, config/session/utils + shims | Done |
|
|
22
22
|
| C6 | Integration gate + sign-off | Done |
|
|
23
|
+
| C7 | Hot-path standalone (agentic CLI without wrapper import) | Done |
|
|
23
24
|
|
|
24
25
|
- `praisonai_code.runtime` — warm local runtime (daemon + thin client).
|
|
25
26
|
- `praisonai_code.cli_backends` — CLI backend implementations (e.g. Claude Code).
|
|
27
|
+
- `praisonai_code.llm` — endpoint resolution, credentials, model catalogue.
|
|
28
|
+
- `praisonai_code.tool_resolver` — YAML tool name resolution.
|
|
29
|
+
|
|
30
|
+
See `src/praisonai/tests/C7_VERIFICATION.md` for the hot-path sign-off checklist.
|
|
26
31
|
|
|
27
32
|
## Dependency rules
|
|
28
33
|
|
|
@@ -35,11 +40,21 @@ praisonai-code → depends on praisonaiagents (core SDK)
|
|
|
35
40
|
click, textual, PyYAML, python-dotenv, litellm, mcp, pydantic — see
|
|
36
41
|
`pyproject.toml`). The rules above govern the **inter-package** direction.
|
|
37
42
|
|
|
38
|
-
> **C7
|
|
39
|
-
>
|
|
40
|
-
>
|
|
41
|
-
>
|
|
42
|
-
>
|
|
43
|
+
> **C7 (hot path complete):** Standalone `pip install praisonai-code` supports
|
|
44
|
+
> agentic terminal commands (`run`, `chat`, `code`, warm runtime) without importing
|
|
45
|
+
> the wrapper on the hot path. Approval backends resolve locally via
|
|
46
|
+
> `praisonai_code.cli.features._approval_bridge` (channel bots delegate to the
|
|
47
|
+
> wrapper). Optional features (observability sinks, framework adapters,
|
|
48
|
+
> bots/gateway) remain wrapper-only via `praisonai_code._wrapper_bridge`.
|
|
49
|
+
|
|
50
|
+
Completed C7 steps:
|
|
51
|
+
- `praisonai_code._registry` — vendored plugin registry (no wrapper import)
|
|
52
|
+
- `praisonai_code._version` / `runtime/descriptor.py` — version from `praisonai-code`
|
|
53
|
+
- `praisonai_code.__main__` + `praisonai-code` console script — standalone entry
|
|
54
|
+
- `praisonai_code._logging` — CLI logging without wrapper dependency
|
|
55
|
+
- `praisonai_code.llm.*` — env, credentials, catalogue, config
|
|
56
|
+
- `praisonai_code._framework_availability`, `_safe_loader`, `tool_resolver` — execution helpers
|
|
57
|
+
- Namespace cleanup — `praisonai_code.cli.features.*` local imports on agentic path
|
|
43
58
|
|
|
44
59
|
Backward compatibility is preserved via PEP 562 shims at the old
|
|
45
60
|
`praisonai.*` import paths, so `pip install praisonai` and
|
|
@@ -53,6 +68,14 @@ Backward compatibility is preserved via PEP 562 shims at the old
|
|
|
53
68
|
pip install praisonai
|
|
54
69
|
```
|
|
55
70
|
|
|
71
|
+
**Standalone code package (agentic CLI only):**
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pip install praisonai-code
|
|
75
|
+
praisonai-code --version
|
|
76
|
+
python -m praisonai_code --help
|
|
77
|
+
```
|
|
78
|
+
|
|
56
79
|
**Development / monorepo:**
|
|
57
80
|
|
|
58
81
|
```bash
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Console entry for ``python -m praisonai_code`` and ``praisonai-code`` script."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main() -> None:
|
|
9
|
+
"""Run the Typer CLI (agentic terminal product)."""
|
|
10
|
+
from praisonai_code._logging import configure_cli_logging
|
|
11
|
+
from praisonai_code.cli.app import app, register_commands
|
|
12
|
+
|
|
13
|
+
configure_cli_logging(os.environ.get("LOGLEVEL", "WARNING") or "WARNING")
|
|
14
|
+
register_commands()
|
|
15
|
+
app()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
if __name__ == "__main__":
|
|
19
|
+
main()
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Single source of truth for optional-framework availability."""
|
|
2
|
+
import importlib.util
|
|
3
|
+
import importlib.metadata
|
|
4
|
+
import threading
|
|
5
|
+
from typing import Callable
|
|
6
|
+
|
|
7
|
+
_cache: dict[str, bool] = {}
|
|
8
|
+
_lock = threading.Lock()
|
|
9
|
+
|
|
10
|
+
def _ag2_probe() -> bool:
|
|
11
|
+
# AG2 ships under the `autogen` namespace; presence of the dist is
|
|
12
|
+
# the only reliable signal.
|
|
13
|
+
try:
|
|
14
|
+
importlib.metadata.distribution('ag2')
|
|
15
|
+
except importlib.metadata.PackageNotFoundError:
|
|
16
|
+
return False
|
|
17
|
+
return importlib.util.find_spec("autogen") is not None
|
|
18
|
+
|
|
19
|
+
def _autogen_v4_probe() -> bool:
|
|
20
|
+
return (importlib.util.find_spec("autogen_agentchat") is not None
|
|
21
|
+
and importlib.util.find_spec("autogen_ext") is not None)
|
|
22
|
+
|
|
23
|
+
def _langgraph_probe() -> bool:
|
|
24
|
+
# find_spec on a submodule imports the parent package; guard against
|
|
25
|
+
# import-time errors in langgraph/its deps crashing the availability check.
|
|
26
|
+
try:
|
|
27
|
+
if importlib.util.find_spec("langgraph") is None:
|
|
28
|
+
return False
|
|
29
|
+
return importlib.util.find_spec("langgraph.prebuilt") is not None
|
|
30
|
+
except Exception:
|
|
31
|
+
return False
|
|
32
|
+
|
|
33
|
+
def _openai_agents_probe() -> bool:
|
|
34
|
+
try:
|
|
35
|
+
importlib.metadata.distribution("openai-agents")
|
|
36
|
+
except importlib.metadata.PackageNotFoundError:
|
|
37
|
+
return False
|
|
38
|
+
if importlib.util.find_spec("agents") is None:
|
|
39
|
+
return False
|
|
40
|
+
try:
|
|
41
|
+
from agents import Runner # noqa: F401
|
|
42
|
+
return True
|
|
43
|
+
except Exception:
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
def _agno_probe() -> bool:
|
|
47
|
+
try:
|
|
48
|
+
importlib.metadata.distribution("agno")
|
|
49
|
+
except importlib.metadata.PackageNotFoundError:
|
|
50
|
+
return False
|
|
51
|
+
if importlib.util.find_spec("agno") is None:
|
|
52
|
+
return False
|
|
53
|
+
try:
|
|
54
|
+
from agno.agent import Agent # noqa: F401
|
|
55
|
+
return True
|
|
56
|
+
except Exception:
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
def _google_adk_probe() -> bool:
|
|
60
|
+
try:
|
|
61
|
+
importlib.metadata.distribution("google-adk")
|
|
62
|
+
except importlib.metadata.PackageNotFoundError:
|
|
63
|
+
return False
|
|
64
|
+
if importlib.util.find_spec("google.adk") is None:
|
|
65
|
+
return False
|
|
66
|
+
try:
|
|
67
|
+
from google.adk.agents import Agent # noqa: F401
|
|
68
|
+
return True
|
|
69
|
+
except Exception:
|
|
70
|
+
try:
|
|
71
|
+
from google.adk import Agent # noqa: F401
|
|
72
|
+
return True
|
|
73
|
+
except Exception:
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
def _pydantic_ai_probe() -> bool:
|
|
77
|
+
try:
|
|
78
|
+
importlib.metadata.distribution("pydantic-ai")
|
|
79
|
+
except importlib.metadata.PackageNotFoundError:
|
|
80
|
+
return False
|
|
81
|
+
if importlib.util.find_spec("pydantic_ai") is None:
|
|
82
|
+
return False
|
|
83
|
+
try:
|
|
84
|
+
from pydantic_ai import Agent # noqa: F401
|
|
85
|
+
return True
|
|
86
|
+
except Exception:
|
|
87
|
+
return False
|
|
88
|
+
|
|
89
|
+
_PROBES: dict[str, Callable[[], bool]] = {
|
|
90
|
+
"crewai": lambda: importlib.util.find_spec("crewai") is not None,
|
|
91
|
+
"autogen": lambda: importlib.util.find_spec("autogen") is not None,
|
|
92
|
+
"autogen_v4": _autogen_v4_probe,
|
|
93
|
+
"ag2": _ag2_probe,
|
|
94
|
+
"praisonaiagents": lambda: importlib.util.find_spec("praisonaiagents") is not None,
|
|
95
|
+
"praisonai_tools": lambda: importlib.util.find_spec("praisonai_tools") is not None,
|
|
96
|
+
"agentops": lambda: importlib.util.find_spec("agentops") is not None,
|
|
97
|
+
"litellm": lambda: importlib.util.find_spec("litellm") is not None,
|
|
98
|
+
"openai": lambda: importlib.util.find_spec("openai") is not None,
|
|
99
|
+
# Additional probes for Gap 2 consolidation
|
|
100
|
+
"acp": lambda: importlib.util.find_spec("acp") is not None,
|
|
101
|
+
"rich": lambda: importlib.util.find_spec("rich") is not None,
|
|
102
|
+
"gradio": lambda: importlib.util.find_spec("gradio") is not None,
|
|
103
|
+
"unsloth": lambda: importlib.util.find_spec("unsloth") is not None,
|
|
104
|
+
# Vector store probes for Gap 3 fix
|
|
105
|
+
"chromadb": lambda: importlib.util.find_spec("chromadb") is not None,
|
|
106
|
+
"pinecone": lambda: importlib.util.find_spec("pinecone") is not None,
|
|
107
|
+
"qdrant_client": lambda: importlib.util.find_spec("qdrant_client") is not None,
|
|
108
|
+
"weaviate": lambda: importlib.util.find_spec("weaviate") is not None,
|
|
109
|
+
"langgraph": _langgraph_probe,
|
|
110
|
+
"openai_agents": _openai_agents_probe,
|
|
111
|
+
"agno": _agno_probe,
|
|
112
|
+
"google_adk": _google_adk_probe,
|
|
113
|
+
"pydantic_ai": _pydantic_ai_probe,
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
def is_available(name: str) -> bool:
|
|
117
|
+
if name not in _PROBES:
|
|
118
|
+
raise ValueError(f"unknown framework name: {name!r}")
|
|
119
|
+
cached = _cache.get(name)
|
|
120
|
+
if cached is not None:
|
|
121
|
+
return cached
|
|
122
|
+
with _lock:
|
|
123
|
+
cached = _cache.get(name)
|
|
124
|
+
if cached is None:
|
|
125
|
+
cached = bool(_PROBES[name]())
|
|
126
|
+
_cache[name] = cached
|
|
127
|
+
return cached
|
|
128
|
+
|
|
129
|
+
def invalidate(name: str | None = None) -> None:
|
|
130
|
+
with _lock:
|
|
131
|
+
if name is None:
|
|
132
|
+
_cache.clear()
|
|
133
|
+
else:
|
|
134
|
+
_cache.pop(name, None)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Logging helpers for praisonai-code (no praisonai wrapper dependency)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
_PKG_LOGGER = "praisonai_code"
|
|
9
|
+
_configured = False
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def configure_cli_logging(level: str | int | None = None) -> None:
|
|
13
|
+
"""Configure root logging once from the CLI entrypoint."""
|
|
14
|
+
global _configured
|
|
15
|
+
if _configured:
|
|
16
|
+
return
|
|
17
|
+
lvl = level or os.environ.get("LOGLEVEL", "WARNING")
|
|
18
|
+
logging.basicConfig(level=lvl, format="%(asctime)s - %(levelname)s - %(message)s")
|
|
19
|
+
_configured = True
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_logger(name: str | None = None) -> logging.Logger:
|
|
23
|
+
"""Return a namespaced logger under ``praisonai_code``."""
|
|
24
|
+
return logging.getLogger(f"{_PKG_LOGGER}.{name}" if name else _PKG_LOGGER)
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
"""Generic plugin registry implementation for PraisonAI.
|
|
2
|
+
|
|
3
|
+
This module provides a unified registry pattern to replace the divergent singleton
|
|
4
|
+
implementations across the codebase. It supports both built-in and entry-point
|
|
5
|
+
based plugin discovery with thread-safe registration and dependency injection.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import threading
|
|
11
|
+
from importlib.metadata import entry_points
|
|
12
|
+
from typing import Callable, Dict, Generic, Optional, Type, TypeVar
|
|
13
|
+
import logging
|
|
14
|
+
|
|
15
|
+
T = TypeVar("T")
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PluginRegistry(Generic[T]):
|
|
20
|
+
# Guards creation of per-subclass default-instance locks
|
|
21
|
+
_default_locks_guard = threading.Lock()
|
|
22
|
+
"""Generic plugin registry: builtins + entry points + runtime register().
|
|
23
|
+
|
|
24
|
+
This replaces the singleton pattern with dependency injection while maintaining
|
|
25
|
+
the same functionality. Supports:
|
|
26
|
+
- Built-in plugins with lazy loading
|
|
27
|
+
- Entry points discovery
|
|
28
|
+
- Runtime registration
|
|
29
|
+
- Alias support for alternative names
|
|
30
|
+
- Thread-safe operations
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
*,
|
|
36
|
+
entry_point_group: str,
|
|
37
|
+
builtins: Optional[Dict[str, Callable[[], Type[T]]]] = None,
|
|
38
|
+
discover_entry_points: bool = True
|
|
39
|
+
) -> None:
|
|
40
|
+
"""Initialize the registry.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
entry_point_group: Entry points group name for plugin discovery
|
|
44
|
+
builtins: Dict of name -> loader function for built-in plugins
|
|
45
|
+
discover_entry_points: When True (default) the registry scans the
|
|
46
|
+
``entry_point_group`` for third-party plugins on construction.
|
|
47
|
+
Pass False to build an isolated registry that only contains the
|
|
48
|
+
supplied builtins / explicitly registered plugins (useful for
|
|
49
|
+
opt-out and deterministic tests).
|
|
50
|
+
"""
|
|
51
|
+
self._entry_point_group = entry_point_group
|
|
52
|
+
self._loaders: Dict[str, Callable[[], Type[T]]] = {} # lazy loaders
|
|
53
|
+
self._items: Dict[str, Type[T]] = {} # resolved cache
|
|
54
|
+
self._aliases: Dict[str, str] = {} # alias -> canonical name
|
|
55
|
+
self._lock = threading.RLock() # Use RLock for re-entrant access
|
|
56
|
+
|
|
57
|
+
# Store built-in loaders (normalized) without calling them
|
|
58
|
+
if builtins:
|
|
59
|
+
for name, loader in builtins.items():
|
|
60
|
+
self._add_loader(name, loader)
|
|
61
|
+
|
|
62
|
+
if discover_entry_points:
|
|
63
|
+
self._discover_entry_points()
|
|
64
|
+
|
|
65
|
+
def _add_loader(self, name: str, loader: Callable[[], Type[T]]) -> None:
|
|
66
|
+
"""Add a loader with normalized name."""
|
|
67
|
+
normalized_name = name.lower()
|
|
68
|
+
self._loaders[normalized_name] = loader
|
|
69
|
+
|
|
70
|
+
def _discover_entry_points(self) -> None:
|
|
71
|
+
"""Discover entry point loaders without loading them."""
|
|
72
|
+
try:
|
|
73
|
+
for ep in entry_points(group=self._entry_point_group):
|
|
74
|
+
self._add_loader(ep.name, ep.load)
|
|
75
|
+
except Exception:
|
|
76
|
+
# entry_points() might not be available in older Python versions
|
|
77
|
+
logger.debug("Entry points not available for group %s", self._entry_point_group)
|
|
78
|
+
|
|
79
|
+
def register(
|
|
80
|
+
self,
|
|
81
|
+
name: str,
|
|
82
|
+
cls: Type[T],
|
|
83
|
+
*,
|
|
84
|
+
aliases: Optional[list[str]] = None
|
|
85
|
+
) -> None:
|
|
86
|
+
"""Register a plugin at runtime.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
name: Plugin name
|
|
90
|
+
cls: Plugin class
|
|
91
|
+
aliases: Optional list of alias names for this plugin
|
|
92
|
+
"""
|
|
93
|
+
with self._lock:
|
|
94
|
+
canonical_name = name.lower()
|
|
95
|
+
# Store both in loaders (for consistency with discovery) and items (for immediate access)
|
|
96
|
+
self._loaders[canonical_name] = lambda: cls
|
|
97
|
+
self._items[canonical_name] = cls
|
|
98
|
+
|
|
99
|
+
# Register aliases
|
|
100
|
+
if aliases:
|
|
101
|
+
for alias in aliases:
|
|
102
|
+
normalized_alias = alias.lower()
|
|
103
|
+
self._aliases[normalized_alias] = canonical_name
|
|
104
|
+
|
|
105
|
+
def unregister(self, name: str) -> bool:
|
|
106
|
+
"""Unregister a plugin.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
name: Plugin name
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
True if plugin was found and removed, False otherwise
|
|
113
|
+
"""
|
|
114
|
+
with self._lock:
|
|
115
|
+
normalized_name = name.lower()
|
|
116
|
+
|
|
117
|
+
# Check if it's an alias
|
|
118
|
+
if normalized_name in self._aliases:
|
|
119
|
+
del self._aliases[normalized_name]
|
|
120
|
+
return True
|
|
121
|
+
|
|
122
|
+
# Check if it's a canonical name
|
|
123
|
+
if normalized_name in self._loaders:
|
|
124
|
+
# Remove all aliases pointing to this plugin
|
|
125
|
+
aliases_to_remove = [
|
|
126
|
+
alias for alias, canonical in self._aliases.items()
|
|
127
|
+
if canonical == normalized_name
|
|
128
|
+
]
|
|
129
|
+
for alias in aliases_to_remove:
|
|
130
|
+
del self._aliases[alias]
|
|
131
|
+
|
|
132
|
+
# Remove from both loaders and items cache
|
|
133
|
+
del self._loaders[normalized_name]
|
|
134
|
+
self._items.pop(normalized_name, None)
|
|
135
|
+
return True
|
|
136
|
+
|
|
137
|
+
return False
|
|
138
|
+
|
|
139
|
+
def resolve(self, name: str) -> Type[T]:
|
|
140
|
+
"""Resolve a plugin name to its class.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
name: Plugin name
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
Plugin class
|
|
147
|
+
|
|
148
|
+
Raises:
|
|
149
|
+
ValueError: If plugin is not found
|
|
150
|
+
"""
|
|
151
|
+
normalized_name = name.lower()
|
|
152
|
+
|
|
153
|
+
with self._lock:
|
|
154
|
+
# Check cache first
|
|
155
|
+
canonical_name = self._aliases.get(normalized_name, normalized_name)
|
|
156
|
+
cls = self._items.get(canonical_name)
|
|
157
|
+
|
|
158
|
+
if cls is not None:
|
|
159
|
+
return cls
|
|
160
|
+
|
|
161
|
+
# Try to load from loaders
|
|
162
|
+
loader = self._loaders.get(canonical_name)
|
|
163
|
+
if loader is None:
|
|
164
|
+
# Capture available plugins snapshot while holding lock
|
|
165
|
+
available_loaders = sorted(self._loaders.keys())
|
|
166
|
+
available_aliases = sorted(self._aliases.keys())
|
|
167
|
+
available_snapshot = available_loaders + available_aliases
|
|
168
|
+
raise ValueError(
|
|
169
|
+
f"Unknown {self._entry_point_group} plugin: {name!r}. "
|
|
170
|
+
f"Available: {available_snapshot}"
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# Load and cache the plugin (release lock during loading to prevent deadlock)
|
|
174
|
+
pass
|
|
175
|
+
|
|
176
|
+
# Load outside the lock to prevent deadlock if loader calls back into registry
|
|
177
|
+
try:
|
|
178
|
+
cls = loader()
|
|
179
|
+
except ImportError as e:
|
|
180
|
+
raise ValueError(
|
|
181
|
+
f"Plugin {name!r} is registered but its dependencies "
|
|
182
|
+
f"are not installed: {e}"
|
|
183
|
+
) from e
|
|
184
|
+
|
|
185
|
+
# Re-acquire lock to cache the result. Guard against the loader being
|
|
186
|
+
# replaced or removed by another thread while we loaded outside the
|
|
187
|
+
# lock, so we never resurrect an unregistered plugin or cache a stale
|
|
188
|
+
# class over a newer registration.
|
|
189
|
+
with self._lock:
|
|
190
|
+
if self._loaders.get(canonical_name) is loader:
|
|
191
|
+
self._items[canonical_name] = cls
|
|
192
|
+
return cls
|
|
193
|
+
|
|
194
|
+
def create(self, name: str, *args, **kwargs) -> T:
|
|
195
|
+
"""Create an instance of the specified plugin.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
name: Plugin name
|
|
199
|
+
*args, **kwargs: Arguments to pass to plugin constructor
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
Plugin instance
|
|
203
|
+
|
|
204
|
+
Raises:
|
|
205
|
+
ValueError: If plugin is not found
|
|
206
|
+
"""
|
|
207
|
+
cls = self.resolve(name)
|
|
208
|
+
return cls(*args, **kwargs)
|
|
209
|
+
|
|
210
|
+
def list_names(self) -> list[str]:
|
|
211
|
+
"""List all registered plugin names.
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
Sorted list of plugin names
|
|
215
|
+
"""
|
|
216
|
+
with self._lock:
|
|
217
|
+
return sorted(self._loaders.keys())
|
|
218
|
+
|
|
219
|
+
def is_available(self, name: str) -> bool:
|
|
220
|
+
"""Check if a plugin is available.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
name: Plugin name
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
True if plugin exists and can be created
|
|
227
|
+
"""
|
|
228
|
+
try:
|
|
229
|
+
self.resolve(name)
|
|
230
|
+
return True
|
|
231
|
+
except ValueError:
|
|
232
|
+
return False
|
|
233
|
+
|
|
234
|
+
def list_aliases(self) -> Dict[str, str]:
|
|
235
|
+
"""List all aliases and their target plugins.
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
Dict mapping alias -> canonical name
|
|
239
|
+
"""
|
|
240
|
+
with self._lock:
|
|
241
|
+
return dict(self._aliases)
|
|
242
|
+
|
|
243
|
+
def list_all_names(self) -> list[str]:
|
|
244
|
+
"""List all names including aliases.
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
Sorted list of all registered names and aliases
|
|
248
|
+
"""
|
|
249
|
+
with self._lock:
|
|
250
|
+
return sorted(list(self._loaders.keys()) + list(self._aliases.keys()))
|
|
251
|
+
|
|
252
|
+
def get_by_attr(self, module_name: str, attr_name: str) -> Type[T]:
|
|
253
|
+
"""Get a plugin by attribute name for __getattr__ dispatch.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
module_name: Module name requesting the attribute (for error messages)
|
|
257
|
+
attr_name: Attribute name to resolve
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
Plugin class
|
|
261
|
+
|
|
262
|
+
Raises:
|
|
263
|
+
AttributeError: If plugin is not found
|
|
264
|
+
"""
|
|
265
|
+
try:
|
|
266
|
+
return self.resolve(attr_name)
|
|
267
|
+
except ValueError:
|
|
268
|
+
raise AttributeError(f"module {module_name!r} has no attribute {attr_name!r}")
|
|
269
|
+
|
|
270
|
+
@classmethod
|
|
271
|
+
def default(cls) -> "PluginRegistry[T]":
|
|
272
|
+
"""
|
|
273
|
+
Process-default registry. Prefer DI; use this only at the edge.
|
|
274
|
+
|
|
275
|
+
Provides thread-safe lazy initialization of default instances per-subclass.
|
|
276
|
+
Each subclass gets its own default instance cache.
|
|
277
|
+
"""
|
|
278
|
+
# Use a class-level cache stored in the subclass's __dict__
|
|
279
|
+
cache_key = "_default_instance"
|
|
280
|
+
lock_key = "_default_instance_lock"
|
|
281
|
+
|
|
282
|
+
# Get or create lock in subclass __dict__ (not shared across inheritance hierarchy)
|
|
283
|
+
if lock_key not in cls.__dict__:
|
|
284
|
+
# Thread-safe initialization of the per-subclass lock itself
|
|
285
|
+
with PluginRegistry._default_locks_guard:
|
|
286
|
+
if lock_key not in cls.__dict__:
|
|
287
|
+
# Store directly in the class __dict__ to avoid inheritance sharing
|
|
288
|
+
setattr(cls, lock_key, threading.Lock())
|
|
289
|
+
|
|
290
|
+
lock = getattr(cls, lock_key)
|
|
291
|
+
|
|
292
|
+
# Check cache first (without lock for performance)
|
|
293
|
+
cache = cls.__dict__.get(cache_key)
|
|
294
|
+
if cache is not None:
|
|
295
|
+
return cache
|
|
296
|
+
|
|
297
|
+
# Double-checked locking pattern
|
|
298
|
+
with lock:
|
|
299
|
+
cache = cls.__dict__.get(cache_key)
|
|
300
|
+
if cache is None:
|
|
301
|
+
cache = cls()
|
|
302
|
+
# Store directly in the class __dict__ to ensure per-subclass isolation
|
|
303
|
+
setattr(cls, cache_key, cache)
|
|
304
|
+
return cache
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def create_lazy_getattr(registry: PluginRegistry[T]) -> Callable[[str], T]:
|
|
308
|
+
"""Create a __getattr__ function backed by a PluginRegistry.
|
|
309
|
+
|
|
310
|
+
This replaces manual if/elif ladders in __init__.py files with a data-driven
|
|
311
|
+
approach using the plugin registry.
|
|
312
|
+
|
|
313
|
+
Args:
|
|
314
|
+
registry: The plugin registry to use for resolution
|
|
315
|
+
|
|
316
|
+
Returns:
|
|
317
|
+
Function that can be used as __getattr__ in a module
|
|
318
|
+
|
|
319
|
+
Example:
|
|
320
|
+
# In __init__.py:
|
|
321
|
+
from ._registry import create_lazy_getattr
|
|
322
|
+
|
|
323
|
+
# Assuming you have a registry instance
|
|
324
|
+
__getattr__ = create_lazy_getattr(my_registry)
|
|
325
|
+
"""
|
|
326
|
+
# Capture the *defining* module's name once, so the AttributeError reports
|
|
327
|
+
# the module that actually lacks the attribute rather than the caller, and
|
|
328
|
+
# we avoid inspecting the call stack on every failed lookup.
|
|
329
|
+
import inspect
|
|
330
|
+
|
|
331
|
+
frame = inspect.currentframe()
|
|
332
|
+
if frame and frame.f_back:
|
|
333
|
+
module_name = frame.f_back.f_globals.get('__name__', 'unknown')
|
|
334
|
+
else:
|
|
335
|
+
module_name = 'unknown'
|
|
336
|
+
|
|
337
|
+
def __getattr__(name: str) -> T:
|
|
338
|
+
try:
|
|
339
|
+
return registry.resolve(name)
|
|
340
|
+
except ValueError:
|
|
341
|
+
raise AttributeError(f"module {module_name!r} has no attribute {name!r}")
|
|
342
|
+
|
|
343
|
+
return __getattr__
|