fix: async-safe sync hooks/compaction + bounded session cache #10801
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Core Tests | |
| on: | |
| push: | |
| branches: [ main, develop ] | |
| paths: | |
| - 'src/**' | |
| - 'src/praisonai-deploy/infra/**' | |
| - 'src/praisonai-bot/infra/**' | |
| - 'scripts/check_helm_charts.sh' | |
| - 'pyproject.toml' | |
| - '.github/workflows/test-core.yml' | |
| - '.github/actions/install-monorepo-packages/**' | |
| pull_request: | |
| branches: [ main, develop ] | |
| paths: | |
| - 'src/**' | |
| - 'src/praisonai-deploy/infra/**' | |
| - 'src/praisonai-bot/infra/**' | |
| - 'scripts/check_helm_charts.sh' | |
| - 'pyproject.toml' | |
| - '.github/workflows/test-core.yml' | |
| - '.github/actions/install-monorepo-packages/**' | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| env: | |
| PYTHONPATH: ${{ github.workspace }}/src/praisonai-agents | |
| PRAISONAI_ALLOW_NETWORK: '0' | |
| PRAISONAI_LIVE_TESTS: '0' | |
| PRAISONAI_TEST_TIER: main | |
| # litellm reads this at import and, without it, downloads its model-price | |
| # table from raw.githubusercontent.com before a single test runs -- which | |
| # made the suite depend on that host being reachable. It has to be set in | |
| # the environment rather than a conftest, because pytest imports plugins | |
| # (and litellm with them) before any conftest is loaded. | |
| LITELLM_LOCAL_MODEL_COST_MAP: 'True' | |
| HF_HUB_OFFLINE: '1' | |
| jobs: | |
| test-core-collect: | |
| name: test-core-collect | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Python 3.11 | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install UV | |
| run: | | |
| curl -LsSf https://astral.sh/uv/install.sh | sh | |
| echo "$HOME/.local/bin" >> $GITHUB_PATH | |
| - name: Install dependencies | |
| run: | | |
| cd src/praisonai | |
| uv pip install --system duckduckgo_search | |
| uv pip install --system pytest pytest-asyncio pytest-timeout pytest-xdist | |
| uv pip install --system "praisonaiagents[knowledge]" | |
| - uses: ./.github/actions/install-monorepo-packages | |
| with: | |
| use-uv: "true" | |
| wrapper-extras: ".[ui,gradio,api,agentops,google,openai,anthropic,cohere,chat,code,realtime,call,crewai,autogen]" | |
| - name: Set environment variables | |
| run: | | |
| echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY || 'sk-test-key-for-github-actions-testing-only-not-real' }}" >> $GITHUB_ENV | |
| echo "OPENAI_API_BASE=${{ secrets.OPENAI_API_BASE || 'https://api.openai.com/v1' }}" >> $GITHUB_ENV | |
| echo "OPENAI_MODEL_NAME=gpt-4o-mini" >> $GITHUB_ENV | |
| echo "PYTHONPATH=${{ github.workspace }}/src/praisonai-agents:$PYTHONPATH" >> $GITHUB_ENV | |
| - name: Verify unit test collection | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| cd src/praisonai | |
| python -m pytest tests/unit/ \ | |
| -m "not provider_anthropic and not provider_google and not provider_ollama and not provider_grok_xai and not provider_groq and not provider_cohere" \ | |
| --ignore=tests/fixtures \ | |
| --collect-only -q --disable-warnings | |
| test-core-unit: | |
| name: test-core (${{ matrix.shard }}) | |
| needs: test-core-collect | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - shard: cli | |
| paths: >- | |
| tests/unit/cli/ | |
| tests/unit/tui/ | |
| extra_ignore: --ignore=tests/unit/cli/test_message_queue.py | |
| - shard: bots-gateway | |
| workdir: src/praisonai-bot | |
| # tests/unit/test_bots_cli.py sat at the top level and was collected | |
| # by no workflow at all, which is why a TypeError that made | |
| # `praisonai bot slack` unstartable shipped unnoticed. Named | |
| # explicitly rather than adding the whole top-level directory, which | |
| # carries suites with pre-existing env-dependent failures. | |
| # tests/unit/kanban/ was likewise collected by no workflow, which | |
| # is how six declared KANBAN_TASK_* hook events shipped with no | |
| # emitter at all. | |
| paths: >- | |
| tests/unit/bots/ | |
| tests/unit/gateway/ | |
| tests/unit/cli/ | |
| tests/unit/daemon/ | |
| tests/unit/kanban/ | |
| tests/unit/test_bots_cli.py | |
| extra_ignore: "" | |
| pythonpath: >- | |
| ${{ github.workspace }}/src/praisonai-agents:${{ github.workspace }}/src/praisonai-code | |
| - shard: agents-core | |
| workdir: src/praisonai-agents | |
| # src/praisonai-agents/tests holds 727 files and 11,437 test | |
| # functions, and until now NO workflow referenced the directory at | |
| # all -- so the LLM layer, the provider adapters, the local-model | |
| # package and the streaming paths were guarded by tests that never | |
| # ran. Nearly every defect found in the recent local-model audits | |
| # had a working test sitting unused beside it. | |
| # | |
| # The ten directories originally left out of this shard (knowledge, | |
| # runtime, session, tools, bus, plugins, skills, agents, rag, | |
| # streaming -- about 52 failures) have since been repaired, so they | |
| # are all listed below: 1,567 tests in the seven added here alone. | |
| # Leaving them out would have meant the repairs were never exercised. | |
| # | |
| # The whole tree in one invocation was hanging. The cause was NOT | |
| # the pytest-rerunfailures run_server thread guessed at here | |
| # earlier: it is test_botos_integration.py, where a supervised | |
| # Bot.start() deliberately never returns and the mock adapter | |
| # reports is_running=True forever (fixed on this branch). Naming | |
| # directories explicitly is still the safer shape for a shard. | |
| paths: >- | |
| tests/unit/agent/ tests/unit/app/ tests/unit/approval/ | |
| tests/unit/auth/ tests/unit/background/ tests/unit/checkpoints/ | |
| tests/unit/cli/ tests/unit/compaction/ tests/unit/conditions/ | |
| tests/unit/config/ tests/unit/context/ tests/unit/embedding/ | |
| tests/unit/eval/ tests/unit/gateway/ tests/unit/goal/ | |
| tests/unit/hooks/ tests/unit/llm/ tests/unit/lsp/ tests/unit/mcp/ | |
| tests/unit/memory/ tests/unit/permissions/ tests/unit/policy/ | |
| tests/unit/process/ tests/unit/runs/ tests/unit/sandbox/ | |
| tests/unit/security/ tests/unit/server/ tests/unit/snapshot/ | |
| tests/unit/storage/ tests/unit/task/ tests/unit/telemetry/ | |
| tests/unit/trace/ tests/unit/utils/ tests/unit/workflows/ | |
| tests/unit/workspace/ tests/unit/knowledge/ tests/unit/session/ | |
| tests/unit/plugins/ tests/unit/runtime/ tests/unit/tools/ | |
| tests/unit/bus/ tests/unit/skills/ tests/unit/agents/ | |
| tests/unit/rag/ tests/unit/streaming/ | |
| tests/unit/test_adapter_registry.py | |
| # Both tests previously deselected here now pass in combination. | |
| # test_lazy_litellm_import was looking the loader module up by | |
| # name and could get a different object than the one get_litellm() | |
| # mutates; it reads the function's own __globals__ now. | |
| # test_skills_discovery_uses_paths_module read the real home | |
| # directory and asserted every discovered dir was named "skills", | |
| # which is false once a remote skill has ever been cached; it runs | |
| # against a temp HOME and asserts the paths module is used. | |
| extra_ignore: "" | |
| pythonpath: >- | |
| ${{ github.workspace }}/src/praisonai-agents | |
| - shard: train | |
| workdir: src/praisonai-train | |
| # tests/unit/data/ (96 tests) was collected by no workflow at all. | |
| paths: >- | |
| tests/unit/train/ tests/unit/data/ | |
| extra_ignore: "" | |
| pythonpath: >- | |
| ${{ github.workspace }}/src/praisonai-agents:${{ github.workspace }}/src/praisonai-code | |
| - shard: mcp | |
| workdir: src/praisonai-mcp | |
| paths: >- | |
| tests/mcp_server/ | |
| extra_ignore: "" | |
| pythonpath: >- | |
| ${{ github.workspace }}/src/praisonai-agents | |
| - shard: sandbox | |
| workdir: src/praisonai-sandbox | |
| paths: >- | |
| tests/ | |
| extra_ignore: "" | |
| pythonpath: >- | |
| ${{ github.workspace }}/src/praisonai-agents | |
| - shard: deploy | |
| workdir: src/praisonai-deploy | |
| paths: >- | |
| tests/ | |
| extra_ignore: "" | |
| pythonpath: >- | |
| ${{ github.workspace }}/src/praisonai-agents | |
| - shard: subdirs | |
| paths: >- | |
| tests/unit/scheduler/ | |
| tests/unit/integrations/ | |
| tests/unit/mcp/ | |
| tests/unit/knowledge/ | |
| tests/unit/llm/ | |
| tests/unit/doctor/ | |
| tests/unit/code/ | |
| tests/unit/acp/ | |
| tests/unit/agent/ | |
| tests/unit/tools/ | |
| tests/unit/persistence/ | |
| tests/unit/security/ | |
| tests/unit/recipe/ | |
| tests/unit/storage/ | |
| tests/unit/rag/ | |
| tests/unit/replay/ | |
| tests/unit/escalation/ | |
| tests/unit/orchestration/ | |
| tests/unit/dev/ | |
| tests/unit/_dev/ | |
| tests/unit/context/ | |
| tests/unit/adapters/ | |
| tests/unit/templates/ | |
| tests/unit/suite_runner/ | |
| tests/unit/standardise/ | |
| extra_ignore: "" | |
| - shard: root | |
| paths: "" | |
| extra_ignore: "" | |
| root_glob: "true" | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Python 3.11 | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install UV | |
| run: | | |
| curl -LsSf https://astral.sh/uv/install.sh | sh | |
| echo "$HOME/.local/bin" >> $GITHUB_PATH | |
| - name: Install dependencies | |
| run: | | |
| cd src/praisonai | |
| uv pip install --system duckduckgo_search | |
| uv pip install --system pytest pytest-asyncio pytest-timeout pytest-xdist | |
| uv pip install --system "praisonaiagents[knowledge]" | |
| - uses: ./.github/actions/install-monorepo-packages | |
| with: | |
| use-uv: "true" | |
| wrapper-extras: ".[ui,gradio,api,agentops,google,openai,anthropic,cohere,chat,code,realtime,call,crewai,autogen]" | |
| - name: Set environment variables | |
| run: | | |
| echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY || 'sk-test-key-for-github-actions-testing-only-not-real' }}" >> $GITHUB_ENV | |
| echo "OPENAI_API_BASE=${{ secrets.OPENAI_API_BASE || 'https://api.openai.com/v1' }}" >> $GITHUB_ENV | |
| echo "OPENAI_MODEL_NAME=gpt-4o-mini" >> $GITHUB_ENV | |
| echo "PYTHONPATH=${{ github.workspace }}/src/praisonai-agents:$PYTHONPATH" >> $GITHUB_ENV | |
| - name: Run unit shard | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| WORKDIR="${{ matrix.workdir || 'src/praisonai' }}" | |
| if [ "$WORKDIR" = "src/praisonai-sandbox" ]; then | |
| bash "$GITHUB_WORKSPACE/scripts/check_c13_sandbox_imports.sh" | |
| fi | |
| if [ "$WORKDIR" = "src/praisonai-deploy" ]; then | |
| bash "$GITHUB_WORKSPACE/scripts/check_c14_deploy_imports.sh" | |
| fi | |
| if [ "$WORKDIR" = "src/praisonai-mcp" ]; then | |
| bash "$GITHUB_WORKSPACE/scripts/check_c12_mcp_imports.sh" | |
| fi | |
| cd "$WORKDIR" | |
| if [ -n "${{ matrix.pythonpath || '' }}" ]; then | |
| export PYTHONPATH="${{ matrix.pythonpath }}:${PYTHONPATH:-}" | |
| fi | |
| PYTEST_ARGS=( | |
| -m "not slow and not network and not local_service and not provider_anthropic and not provider_google and not provider_ollama and not provider_grok_xai and not provider_groq and not provider_cohere" | |
| -q --tb=line --disable-warnings | |
| --timeout=60 -n 2 --dist loadfile | |
| --ignore=tests/fixtures | |
| --ignore=tests/e2e | |
| --ignore=tests/live | |
| --ignore=tests/unit/test_langflow_components.py | |
| ) | |
| if [ "${{ matrix.root_glob || 'false' }}" = "true" ]; then | |
| shopt -s nullglob | |
| ROOT_TESTS=(tests/unit/test_*.py) | |
| shopt -u nullglob | |
| EXCLUDE=(tests/unit/test_langflow_components.py) | |
| FILTERED=() | |
| for t in "${ROOT_TESTS[@]}"; do | |
| skip=false | |
| for e in "${EXCLUDE[@]}"; do | |
| if [ "$t" = "$e" ]; then skip=true; break; fi | |
| done | |
| if [ "$skip" = false ]; then FILTERED+=("$t"); fi | |
| done | |
| if [ ${#FILTERED[@]} -eq 0 ]; then | |
| echo "No root unit tests matched" | |
| exit 0 | |
| fi | |
| python -m pytest "${FILTERED[@]}" "${PYTEST_ARGS[@]}" | |
| else | |
| python -m pytest ${{ matrix.paths }} "${PYTEST_ARGS[@]}" ${{ matrix.extra_ignore }} | |
| fi | |
| test-core-integration: | |
| name: test-core-integration | |
| needs: test-core-unit | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 25 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Python 3.11 | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install UV | |
| run: | | |
| curl -LsSf https://astral.sh/uv/install.sh | sh | |
| echo "$HOME/.local/bin" >> $GITHUB_PATH | |
| - name: Install dependencies | |
| run: | | |
| uv pip install --system duckduckgo_search | |
| uv pip install --system pytest pytest-asyncio pytest-timeout | |
| uv pip install --system "praisonaiagents[knowledge]" | |
| - uses: ./.github/actions/install-monorepo-packages | |
| with: | |
| use-uv: "true" | |
| wrapper-extras: ".[ui,gradio,api,agentops,google,openai,anthropic,cohere,chat,code,realtime,call,crewai,autogen]" | |
| - name: Set environment variables | |
| run: | | |
| echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY || 'sk-test-key-for-github-actions-testing-only-not-real' }}" >> $GITHUB_ENV | |
| echo "OPENAI_API_BASE=${{ secrets.OPENAI_API_BASE || 'https://api.openai.com/v1' }}" >> $GITHUB_ENV | |
| echo "OPENAI_MODEL_NAME=gpt-4o-mini" >> $GITHUB_ENV | |
| echo "PYTHONPATH=${{ github.workspace }}/src/praisonai-agents:$PYTHONPATH" >> $GITHUB_ENV | |
| - name: Run integration tests | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| cd src/praisonai | |
| python -m pytest tests/integration/ \ | |
| -m "not slow and not network and not local_service and not provider_anthropic and not provider_google and not provider_ollama and not provider_grok_xai and not provider_groq and not provider_cohere" \ | |
| -q --tb=line --disable-warnings \ | |
| --timeout=120 \ | |
| --ignore=tests/fixtures \ | |
| --ignore=tests/e2e \ | |
| --ignore=tests/live \ | |
| --ignore=tests/integration/test_tracker_complex_tasks.py \ | |
| --ignore=tests/integration/test_serve_integration.py | |
| helm-charts: | |
| name: helm-charts | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Setup Helm | |
| uses: azure/setup-helm@v3 | |
| with: | |
| version: v3.14.4 | |
| - name: Lint and template Helm charts | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bash "$GITHUB_WORKSPACE/scripts/check_helm_charts.sh" | |
| test-core: | |
| name: test-core | |
| needs: [test-core-collect, test-core-unit, test-core-integration, helm-charts] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Report Core Tests result | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| collect='${{ needs.test-core-collect.result }}' | |
| unit='${{ needs.test-core-unit.result }}' | |
| integration='${{ needs.test-core-integration.result }}' | |
| helm='${{ needs.helm-charts.result }}' | |
| echo "collect=$collect unit=$unit integration=$integration helm=$helm" | |
| if [ "$collect" != "success" ] || [ "$unit" != "success" ] || [ "$integration" != "success" ] || [ "$helm" != "success" ]; then | |
| echo "Core Tests failed" | |
| exit 1 | |
| fi | |
| echo "Core Tests passed" |