Generator looked for docs.json at repo root; PraisonAIDocs uses
docs/docs.json so nav was never updated after auto-gen. Also run
sync_sdk_reference_nav.py after generation as a safety net.
Legacy layout strips /index and /__init__ before writing package_group['pages'],
so logging len(unique_pages) overstated the count. Count the final navigation
entries instead (granular sums sub-group pages).
Co-authored-by: Mervin Praison <[email protected]>
Track generated MDX paths relative to the passed --docs-root instead of a
hardcoded local PraisonAIDocs path so generate-docs runs in GitHub Actions
can commit navigation updates.
NaN/inf prices passed float() but made both per-call and session-spend
comparisons evaluate false, bypassing the configured guard and poisoning
_session_spend. Fail closed on non-finite/negative prices in _check_budget
and validate the response charge in spend recording so a malformed response
cannot corrupt cumulative spend.
Co-authored-by: Mervin Praison <[email protected]>
Both _extract_method and _extract_price now unwrap the live registry's
`endpoint` envelope before reading fields, and _extract_price prefers the
structured cost object's `usd` value (guard budgets are USD). Flat forms
and fail-closed behaviour are preserved. Fixtures mirror the live schema.
Co-authored-by: Mervin Praison <[email protected]>
Address reviewer findings on PR #84:
- describe_path template validated (must contain {tool_id}; malformed templates
return a tool-result error instead of raising, honoring the error contract)
- tool_id URL-encoded on both describe and /call routes (prevents path escape)
- explicit method stripped/upper-cased and validated against an allowed set
- deny-path spend-guard tests assert client.request (not client.post) not called
Co-authored-by: Mervin Praison <[email protected]>
- describe now uses /catalog/endpoints/{tool_id} (configurable via
TOOL_PROXY_DESCRIBE_PATH); fixes 404s that also broke spend-guarded calls
- registry_call derives the endpoint's HTTP method from describe (reusing the
guard's describe fetch); GET sends query params, POST/PUT send a JSON body;
method can be passed explicitly to skip the lookup
- docs: describe-path env var, HTTP-method behaviour, passthrough-URL/SSRF note
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-authored-by: Mervin Praison <[email protected]>
Recognise MervinPraison Claude finished replies in stale-FINAL detection,
re-post FINAL immediately after conflict rebase pushes, broaden generate-docs
triggers to all praisonai_tools changes plus post-main CI, and sync pipeline
labels on push/PR events.
Address Greptile finding: langextract integration/render tests wrote
generated HTML into the working directory. Pass temporary output_path
in test_agent_usage_pattern, test_contract_analysis_scenario, and
test_render_file_success so tests no longer modify the checkout or
collide across runs.
Also remove committed test artifacts (contract-001.html,
financial-analysis.html, tmpc8_hvm6m.html) that leaked from these
CWD writes in the original langextract PR.
Co-authored-by: Mervin Praison <[email protected]>
CI runs `pytest tests/unit --ignore=tests/unit/video`, so the parse-failure
and collision guards in tests/test_tool_discovery.py (at tests/ root) never
executed in CI. Adds a thin bridge under tests/unit/tools/ so a syntactically
broken shipped tool module fails loudly at CI time instead of silently
dropping out of discovery and surfacing later as an AttributeError.
Closes the remaining Greptile P1 (parse test misses CI).
Co-authored-by: Mervin Praison <[email protected]>
Main (#74) added ToolCatalogueEntry/list_tools/get_tool_names to the
top-level __all__. The top-level surface now equals the tools-package
surface plus those catalogue helpers, so test_all_matches_manifest is
updated accordingly.
Co-authored-by: Mervin Praison <[email protected]>
Discovery intentionally swallows a per-module SyntaxError so one broken
module cannot break the whole package import, but that meant a broken
shipped module silently dropped from the manifest (resolving as
AttributeError instead of the real parse error). The existing coverage
test shared the same swallowing helper, so it could not catch it.
Add test_every_shipped_module_parses which ast.parse()s each module
directly, surfacing the pointing SyntaxError at CI time. Addresses the
re-raised Greptile P1.
Co-authored-by: Mervin Praison <[email protected]>
Address reviewer feedback on PR #53:
- Honour an explicit module-level __all__ (CodeRabbit/Gemini): modules that
expose tools via aliases can opt them in; bare aliases are otherwise not
auto-exported to avoid cross-module name collisions.
- Skip underscore-prefixed modules so internal code cannot leak into the
public surface (Greptile).
- Warn instead of silently dropping tools when a module fails to parse
(Greptile).
- Guard the drop-in test for read-only installs and use a non-underscore
probe module (Gemini); add an n8n external-symbol resolution test
(CodeRabbit) and an explicit-__all__ test.
- Memoise the AST scan in a fingerprinted on-disk cache, cutting import
time from ~260ms to ~70ms (AGENTS.md <200ms budget) while keeping
discovery automatic; add cache round-trip/corruption tests and gitignore.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-authored-by: Mervin Praison <[email protected]>
Adding a tool now means adding one file under praisonai_tools/tools/.
An AST-based scan builds the name->module manifest without importing any
tool module, preserving per-symbol lazy import. __all__ at both package
levels is derived from the same manifest so they cannot drift, and
collisions are detected at discovery time. README now documents that this
package uses in-package discovery while external packages use the
praisonaiagents.tools entry-point group.
Co-authored-by: Mervin Praison <[email protected]>
The logging-suppression filter must run before the @tool re-exports are
imported, requiring those imports to sit after module-level code. Mark them
with noqa: E402 to satisfy ruff while preserving the required ordering.
Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>
Importing praisonai_tools tools (e.g. registry_proxy) outside a running
agent context emitted one WARNING per @tool-decorated function from
praisonaiagents.tools.decorator ("Registry is None for tool …"). On a bare
import that is the normal state, not an anomaly, so it polluted logs/CI for
every library consumer and scaled with the tool catalogue.
Attach a targeted logging.Filter (to the root logger and its handlers, where
praisonaiagents routes the record) that drops only that specific message and
passes all other logging through untouched. Registration behaviour is
unchanged; the root fix (defer/downgrade) belongs in praisonaiagents core.
Co-authored-by: Mervin Praison <[email protected]>
Addresses reviewer findings on PR #78:
- Do not attach the ambient TOOL_PROXY_TOKEN when an explicit proxy_url is
supplied without a matching token, preventing token exfiltration to an
attacker-controlled endpoint (Critical).
- Persist cumulative spend per (proxy_url, token) via a module-level store so
max_session_spend enforces across separate registry_call invocations (Major).
- Fail closed in _check_budget when a guard is configured but price cannot be
validated; record spend deterministically from the quoted price (Major).
- Add regression tests + autouse spend-state reset fixture; update docs.
Co-authored-by: Mervin Praison <[email protected]>
Add a bring-your-own-account connector mirroring PraisonAI's deferred
tool-search bridge: registry_search / registry_describe / registry_call
over a registry's plain-HTTP proxy (token header, configurable base URL).
Credentials are injected server-side by the registry, never held by the
agent. Connector is disabled unless TOOL_PROXY_URL is set; httpx is
lazy-imported; errors surface as {"error": ...} tool results. Includes
optional per-call/per-session spend guards, docs (Tiers 0/1/2) and mocked
HTTP unit tests plus a skills-dir compatibility test.
Co-authored-by: MervinPraison <[email protected]>
Address reviewer findings on PR #77:
- _scan_module now also reads annotated `description: str = "..."` class
attributes (AnnAssign), so CapsuleTool/NexusPredictionMarketTool surface
their real descriptions instead of name-synthesised summaries.
- Restrict the _TOOL_MAP catalogue fallback to nested (absolute-module) tools
only; flat *_tool.py classes stay fully source-derived, preserving the
no-hand-maintained-bookkeeping guarantee.
- Fix phantom-guard test to resolve absolute tool modules correctly so a
missing nested first-party module is flagged as a phantom, not tolerated as
an optional dependency.
- Tighten CalculatorTool summary assertion and add regression coverage for
annotated descriptions.
Co-authored-by: Mervin Praison <[email protected]>
Derive the first-party tool catalogue from the source tree instead of the
hand-maintained _TOOL_MAP, so adding a tool becomes a single-file change and
phantom names (advertised tools with no file) are impossible by construction.
- catalogue.py: AST-scan praisonai_tools/tools/*_tool.py for *Tool classes and
their literal description (cheap, import-free, no optional deps). Summaries now
come from each tool's real description rather than name-synthesis.
- tools/__init__.py: extend lazy __getattr__ to fall back to the source-derived
scan, so dropping a *_tool.py makes its class importable with no _TOOL_MAP edit.
Auto-discovers previously-unmapped ClaudeMemoryTool/CrowPayTool/NightmarketTool.
- tests/test_catalogue_no_phantoms.py: CI guard asserting every catalogue name
resolves (tolerating only missing optional deps), maps to a real file, and that
auto-discovery + real-description summaries work.
Lazy-import behaviour and cheap `import praisonai_tools` are preserved; existing
public import names remain stable.
Co-authored-by: Mervin Praison <[email protected]>
Expose a programmatic catalogue of shipped tools plus a `praisonai.tools`
entry-point discovery contract so validators/CLI/docs/third-party packages
resolve against the live catalogue instead of a hand-maintained allowlist
that drifts.
- praisonai_tools.catalogue: ToolCatalogueEntry, list_tools(), get_tool_names()
- Derive catalogue from the existing _TOOL_MAP source of truth (no optional deps)
- Discover third-party tools via `praisonai.tools` entry points
- Add `praisonai-tools list [--extras]` CLI subcommand
- Document the entry-point group in pyproject.toml
- Tests in tests/test_catalogue.py
Co-authored-by: Mervin Praison <[email protected]>
Replace stale team.leader.name access with team.agents[0].name to match
the hierarchical AgentTeam API (no static leader in this mode).
Co-authored-by: Mervin Praison <[email protected]>
The integration tests asserted `team.leader` and an exact `kwargs` dict,
but the preset uses `process="hierarchical"` where the coordinator is the
first agent (`team.agents[0]`) and `process`/`manager_llm` are forwarded.
Update tests to the hierarchical semantics and document the pattern in the
`motion_graphics_team` docstring.
Co-authored-by: Mervin Praison <[email protected]>
HTML tags stay case-insensitive (<div>/<DIV>) but JSX components are now
matched exactly, so a mis-cased component such as <badge> is escaped as
prose instead of being emitted as a broken component. escape_mdx and
validate_mdx share a single _is_valid_mdx_tag helper so they stay in
lockstep. Resolves the outstanding Greptile P1 tag-casing finding.
Co-authored-by: Mervin Praison <[email protected]>
Clears this PR's lint contribution (video dir 13 -> 0 ruff errors):
sort imports and combine nested `with` statements. No behaviour change;
video tests still pass (54 passed with deps). Repo-wide ruff debt in
praisonai_tools/ (~5321 pre-existing errors) is out of scope for this
video-test-deps PR.
Co-authored-by: Mervin Praison <[email protected]>
Replace the directory-wide pytest.importorskip in tests/unit/video/conftest.py
with per-test skipif guards on only the tests that build a real
HtmlRenderBackend. Dependency-independent protocol, mock-backend and retry
tests now run on a minimal `pip install -e '.[dev]'` (40 passed / 14 skipped
without video-motion; 54 passed with it), addressing Greptile's feedback that
the broad skip dropped useful coverage.
Also make test_import_error_handling deterministic by patching async_playwright
so the imageio-ffmpeg guard is what's under test regardless of installed deps.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-authored-by: Mervin Praison <[email protected]>
- Add playwright and imageio-ffmpeg to the [dev] extra so the full pytest
suite runs after `pip install -e ".[dev]"` (Option A).
- Add tests/unit/video/conftest.py with pytest.importorskip guards so
minimal installs skip video tests gracefully instead of erroring (Option C).
- Document the video-motion render deps and `playwright install chromium`
step in README Testing section.
Co-authored-by: Mervin Praison <[email protected]>
The CI `ruff check praisonai_tools tests` step had no ruff config, so a
newer ruff release pulled in strict opinionated defaults (UP/I/W/SIM/RUF)
flagging 5000+ pre-existing, non-functional findings across untouched
files. This pre-dated PR #62 (failing on main at b96bfbc).
Add [tool.ruff.lint] selecting the historical E/F baseline that the
existing `--ignore E…,F…` CI flags already imply, restoring green
without editing unrelated files or weakening any tests.
Co-authored-by: Mervin Praison <[email protected]>