failproofai 1.0.1 → 1.0.2-beta.0
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.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +6 -6
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js +2 -2
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js +2 -2
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/audit/invite/route.js +3 -2
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js +5 -4
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js +2 -2
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +4 -3
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js +4 -4
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js +4 -4
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page.js +2 -2
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js +2 -2
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +66 -22
- package/.next/standalone/.next/server/app/policies/page.js +5 -4
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js +2 -2
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js +2 -2
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
- package/.next/standalone/.next/server/app/settings/page.js +1 -1
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__1qzqcio._.js → [externals]__0e32-7c._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[externals]__1s462yl._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__01eqny5._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0xu5z._.js +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1p8poxd._.js → [root-of-the-server]__0igxt32._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0neqlu-._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t9oqnn._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0bs-v8p._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0tovk6q._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0trp3yc._.js +3 -0
- package/.next/standalone/.next/server/chunks/_185gyk9._.js +3 -0
- package/.next/standalone/.next/server/chunks/_1ek68ln._.js +5 -6
- package/.next/standalone/.next/server/chunks/lib_telemetry-id_ts_1k8gj9o._.js +3 -0
- package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_09d5c79._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_04op_ou._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_1d2g73o._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_0dr20uz._.js} +1 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_0xbw3bu._.js +12 -0
- package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0spkm68._.js → [root-of-the-server]__07gm3zl._.js} +21 -20
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +6 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__07tbbf3._.js → [root-of-the-server]__0zig5sv._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__192nhvh._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{_0lvyuv4._.js → _00e9tnf._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_0u5zauv._.js → _12l4a_k._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +13 -13
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_08o7bqo._.js +10 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware.js +1 -1
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +71 -27
- package/.next/standalone/.next/static/chunks/{37fcpc36x9gpe.js → 0eqky-7n7dgdm.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1ide1wl2f7zi1.js → 0sa9v5mhc8861.js} +13 -13
- package/.next/standalone/.next/static/chunks/1mfzsja6ftim5.css +2 -0
- package/.next/standalone/.next/static/chunks/{3k7mxbp9hiom0.js → 1nuqytbta44af.js} +1 -1
- package/.next/standalone/.next/static/chunks/1sl2xmn9inx3y.js +6 -0
- package/.next/standalone/.next/static/chunks/{2_2lhuojq2xop.js → 2aawsjld9ied_.js} +1 -1
- package/.next/standalone/.next/static/chunks/2gn7ut4svofm2.js +1 -0
- package/.next/standalone/.next/static/chunks/{31s1smp-g7gks.js → 2hs7ycvsav7v2.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0k5050ezjvv49.js → 2i2yt3hzjredm.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3c808qrfcq0im.js → 35xg6retpe6rp.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2zpp1we2jv6tb.js → 3y54nw0i7dsjl.js} +1 -1
- package/.next/standalone/SECURITY.md +5 -4
- package/.next/standalone/app/actions/get-hooks-config.ts +108 -17
- package/.next/standalone/app/actions/pack-actions.ts +153 -0
- package/.next/standalone/app/audit/_components/run-progress.tsx +1 -1
- package/.next/standalone/app/audit/_components/share-templates.ts +13 -13
- package/.next/standalone/app/policies/hooks-client.tsx +320 -14
- package/.next/standalone/components/navbar.tsx +2 -28
- package/.next/standalone/components/reach-developers.tsx +8 -1
- package/.next/standalone/docs-old/agenteye/python-sdk-skill.mdx +1 -1
- package/.next/standalone/docs-old/agenteye/python-sdk.mdx +59 -35
- package/.next/standalone/docs-old/i18n/README.ar.md +1 -0
- package/.next/standalone/docs-old/i18n/README.de.md +1 -0
- package/.next/standalone/docs-old/i18n/README.es.md +1 -0
- package/.next/standalone/docs-old/i18n/README.fr.md +1 -0
- package/.next/standalone/docs-old/i18n/README.he.md +1 -0
- package/.next/standalone/docs-old/i18n/README.hi.md +1 -0
- package/.next/standalone/docs-old/i18n/README.it.md +1 -0
- package/.next/standalone/docs-old/i18n/README.ja.md +1 -0
- package/.next/standalone/docs-old/i18n/README.ko.md +1 -0
- package/.next/standalone/docs-old/i18n/README.pt-br.md +1 -0
- package/.next/standalone/docs-old/i18n/README.ru.md +1 -0
- package/.next/standalone/docs-old/i18n/README.tr.md +1 -0
- package/.next/standalone/docs-old/i18n/README.vi.md +1 -0
- package/.next/standalone/docs-old/i18n/README.zh.md +1 -0
- package/.next/standalone/fp-cloud-cli/CHANGELOG.md +431 -0
- package/.next/standalone/fp-cloud-cli/LICENSE +42 -0
- package/.next/standalone/fp-cloud-cli/MANIFEST.in +10 -0
- package/.next/standalone/fp-cloud-cli/README.md +259 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/__init__.py +10 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/__main__.py +4 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/_click_compat.py +64 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/_context.py +332 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/_version.py +1 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/analytics.py +432 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/analytics_config.py +77 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/analytics_registry.py +83 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/app.py +492 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/auth.py +160 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/client.py +1694 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/__init__.py +0 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/_write.py +214 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/agent_cmds.py +407 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/alerts_cmds.py +445 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/audits_cmds.py +1054 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/auth_cmds.py +512 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/errors_cmds.py +190 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/evals_cmds.py +161 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/events_cmds.py +159 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/fleet_cmds.py +416 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/guardrails_cmds.py +148 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/incidents_cmds.py +472 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/keys_cmds.py +407 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/list_cmds.py +63 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/orgs_cmds.py +319 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/policies_cmds.py +499 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/queries_cmds.py +378 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/sessions_cmds.py +151 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/settings_cmds.py +150 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/usage_cmds.py +35 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/users_cmds.py +404 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/config.py +330 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/dates.py +78 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/enforcement.py +345 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/errors.py +98 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/models.py +891 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/orgs.py +30 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/output.py +6593 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/permissions.py +208 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/policy_check.py +290 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/py.typed +0 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/select.py +322 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/theme.py +53 -0
- package/.next/standalone/fp-cloud-cli/pyproject.toml +106 -0
- package/.next/standalone/fp-cloud-cli/skill/SKILL.md +302 -0
- package/.next/standalone/fp-cloud-cli/skill/agents/openai.yaml +8 -0
- package/.next/standalone/fp-cloud-cli/skill/references/commands.md +254 -0
- package/.next/standalone/fp-cloud-cli/tests/__init__.py +0 -0
- package/.next/standalone/fp-cloud-cli/tests/conftest.py +92 -0
- package/.next/standalone/fp-cloud-cli/tests/test_alerting.py +644 -0
- package/.next/standalone/fp-cloud-cli/tests/test_analytics.py +385 -0
- package/.next/standalone/fp-cloud-cli/tests/test_audits.py +950 -0
- package/.next/standalone/fp-cloud-cli/tests/test_auth.py +98 -0
- package/.next/standalone/fp-cloud-cli/tests/test_auth_mode.py +272 -0
- package/.next/standalone/fp-cloud-cli/tests/test_click_compat.py +118 -0
- package/.next/standalone/fp-cloud-cli/tests/test_client.py +299 -0
- package/.next/standalone/fp-cloud-cli/tests/test_commands.py +814 -0
- package/.next/standalone/fp-cloud-cli/tests/test_config.py +99 -0
- package/.next/standalone/fp-cloud-cli/tests/test_dashboards_agent.py +340 -0
- package/.next/standalone/fp-cloud-cli/tests/test_dates.py +76 -0
- package/.next/standalone/fp-cloud-cli/tests/test_enforcement_logic.py +481 -0
- package/.next/standalone/fp-cloud-cli/tests/test_facets.py +170 -0
- package/.next/standalone/fp-cloud-cli/tests/test_failproofai_home.py +722 -0
- package/.next/standalone/fp-cloud-cli/tests/test_fp_home_contract.py +198 -0
- package/.next/standalone/fp-cloud-cli/tests/test_hardening.py +186 -0
- package/.next/standalone/fp-cloud-cli/tests/test_help_table_coverage.py +127 -0
- package/.next/standalone/fp-cloud-cli/tests/test_keys_queries.py +496 -0
- package/.next/standalone/fp-cloud-cli/tests/test_list.py +59 -0
- package/.next/standalone/fp-cloud-cli/tests/test_multivalue.py +183 -0
- package/.next/standalone/fp-cloud-cli/tests/test_no_customer_identifiers.py +168 -0
- package/.next/standalone/fp-cloud-cli/tests/test_operator.py +492 -0
- package/.next/standalone/fp-cloud-cli/tests/test_orgs.py +692 -0
- package/.next/standalone/fp-cloud-cli/tests/test_output.py +1732 -0
- package/.next/standalone/fp-cloud-cli/tests/test_policy_check.py +191 -0
- package/.next/standalone/fp-cloud-cli/tests/test_readme_matches_reality.py +125 -0
- package/.next/standalone/fp-cloud-cli/tests/test_review_fixes.py +161 -0
- package/.next/standalone/fp-cloud-cli/tests/test_telemetry_completeness.py +90 -0
- package/.next/standalone/fp-cloud-cli/tests/test_usage.py +115 -0
- package/.next/standalone/fp-cloud-cli/tests/test_v1_origin_diagnostic.py +70 -0
- package/.next/standalone/fp-cloud-cli/tests/test_v1_routing.py +214 -0
- package/.next/standalone/fp-cloud-cli/tests/test_whoami.py +104 -0
- package/.next/standalone/fp-cloud-cli/uv.lock +552 -0
- package/.next/standalone/package.json +29 -22
- package/.next/standalone/sdk/python/CHANGELOG.md +70 -0
- package/.next/standalone/sdk/python/LICENSE +42 -0
- package/.next/standalone/sdk/python/MANIFEST.in +21 -0
- package/.next/standalone/sdk/python/README.md +466 -0
- package/.next/standalone/sdk/python/docs/README.md +249 -0
- package/.next/standalone/sdk/python/docs/_shared/README.md +43 -0
- package/.next/standalone/sdk/python/docs/_shared/__init__.py +195 -0
- package/.next/standalone/sdk/python/docs/crewai/README.md +328 -0
- package/.next/standalone/sdk/python/docs/crewai/examples/quickstart.py +61 -0
- package/.next/standalone/sdk/python/docs/crewai/examples/research_crew.py +92 -0
- package/.next/standalone/sdk/python/docs/langgraph/README.md +481 -0
- package/.next/standalone/sdk/python/docs/langgraph/examples/quickstart.py +51 -0
- package/.next/standalone/sdk/python/docs/langgraph/examples/research_agent.py +86 -0
- package/.next/standalone/sdk/python/docs/langgraph/examples/supervisor_handoff.py +83 -0
- package/.next/standalone/sdk/python/docs/llama_index/README.md +332 -0
- package/.next/standalone/sdk/python/docs/llama_index/examples/quickstart.py +62 -0
- package/.next/standalone/sdk/python/docs/llama_index/examples/research_agent.py +93 -0
- package/.next/standalone/sdk/python/docs/manual/README.md +491 -0
- package/.next/standalone/sdk/python/docs/manual/examples/quickstart.py +57 -0
- package/.next/standalone/sdk/python/docs/manual/examples/research_agent.py +133 -0
- package/.next/standalone/sdk/python/docs/pydantic_ai/README.md +323 -0
- package/.next/standalone/sdk/python/docs/pydantic_ai/examples/quickstart.py +48 -0
- package/.next/standalone/sdk/python/docs/pydantic_ai/examples/research_agent.py +94 -0
- package/.next/standalone/sdk/python/failproofai_sdk/__init__.py +131 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_context.py +218 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_environment.py +78 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_events.py +752 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_resolver.py +118 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_runtime.py +21 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_schema.py +317 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_scopes.py +422 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_version.py +1 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_writer.py +750 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/__init__.py +274 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/_compat.py +247 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/_core.py +1076 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/crewai.py +1669 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/langchain.py +2151 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/llama_index.py +1588 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/pydantic_ai.py +977 -0
- package/.next/standalone/sdk/python/failproofai_sdk/py.typed +0 -0
- package/.next/standalone/sdk/python/pyproject.toml +92 -0
- package/.next/standalone/sdk/python/skill/SKILL.md +399 -0
- package/.next/standalone/sdk/python/skill/agents/openai.yaml +8 -0
- package/.next/standalone/sdk/python/skill/references/events.md +234 -0
- package/.next/standalone/sdk/python/skill/references/frameworks.md +314 -0
- package/.next/standalone/sdk/python/skill/references/install.md +100 -0
- package/.next/standalone/sdk/python/skill/references/integration.md +352 -0
- package/.next/standalone/sdk/python/tests/__init__.py +0 -0
- package/.next/standalone/sdk/python/tests/conftest.py +169 -0
- package/.next/standalone/sdk/python/tests/integrations/__init__.py +6 -0
- package/.next/standalone/sdk/python/tests/integrations/test_crewai.py +1299 -0
- package/.next/standalone/sdk/python/tests/integrations/test_langchain.py +2338 -0
- package/.next/standalone/sdk/python/tests/integrations/test_llama_index.py +1576 -0
- package/.next/standalone/sdk/python/tests/integrations/test_pydantic_ai.py +1102 -0
- package/.next/standalone/sdk/python/tests/test_context.py +410 -0
- package/.next/standalone/sdk/python/tests/test_docs.py +411 -0
- package/.next/standalone/sdk/python/tests/test_durability.py +1352 -0
- package/.next/standalone/sdk/python/tests/test_encoding.py +571 -0
- package/.next/standalone/sdk/python/tests/test_integrations.py +1127 -0
- package/.next/standalone/sdk/python/tests/test_no_customer_identifiers.py +214 -0
- package/.next/standalone/sdk/python/tests/test_packaging.py +185 -0
- package/.next/standalone/sdk/python/tests/test_resolver_umbrella.py +178 -0
- package/.next/standalone/sdk/python/tests/test_scopes.py +487 -0
- package/.next/standalone/sdk/python/tests/test_sdk.py +923 -0
- package/.next/standalone/sdk/python/tests/test_server_contract.py +650 -0
- package/.next/standalone/sdk/python/tests/test_site_docs.py +371 -0
- package/.next/standalone/sdk/python/tests/test_skill_snippets.py +165 -0
- package/.next/standalone/sdk/python/tests/test_spool_contract.py +287 -0
- package/.next/standalone/sdk/python/tests/test_spool_creation.py +360 -0
- package/.next/standalone/sdk/python/tests/test_wire_format.py +279 -0
- package/.next/standalone/sdk/python/tests/test_zero_dependencies.py +378 -0
- package/.next/standalone/sdk/python/uv.lock +6005 -0
- package/.next/standalone/server.js +1 -1
- package/README.md +77 -21
- package/bin/failproofai.mjs +145 -11
- package/dist/cli.mjs +4482 -2205
- package/dist/index.js +5 -5
- package/dist/worker.mjs +2399 -1978
- package/package.json +29 -22
- package/policy-pack/SHA256SUMS +2 -0
- package/policy-pack/failproofai-pack.json +732 -0
- package/policy-pack/failproofai-pack.mjs +2017 -0
- package/scripts/build-policy-pack.mjs +121 -0
- package/scripts/changelog-section.py +77 -0
- package/scripts/prune-standalone.mjs +3 -0
- package/scripts/python-version.py +232 -0
- package/scripts/translate-docs/cli.ts +21 -0
- package/scripts/translate-docs/mintlify-nav.ts +85 -13
- package/scripts/translate-docs/translator.ts +66 -11
- package/src/audit/cache.ts +40 -3
- package/src/audit/cli.ts +1 -1
- package/src/audit/index.ts +1 -1
- package/src/audit/replay.ts +89 -3
- package/src/audit/report.ts +5 -5
- package/src/audit/schedule-cli.ts +49 -53
- package/src/hooks/builtin-policies.ts +217 -579
- package/src/hooks/cloud-enrollment-cli.ts +66 -27
- package/src/hooks/cloud-managed-policies.ts +6 -1
- package/src/hooks/configure-wizard.ts +1 -1
- package/src/hooks/custom-hooks-loader.ts +89 -6
- package/src/hooks/fp-home.ts +92 -4
- package/src/hooks/fp-reset.ts +26 -2
- package/src/hooks/handler.ts +181 -11
- package/src/hooks/harness-cli.ts +79 -25
- package/src/hooks/hook-activity-store.ts +13 -3
- package/src/hooks/install-prompt.ts +25 -3
- package/src/hooks/manager.ts +418 -172
- package/src/hooks/migrations.ts +2 -2
- package/src/hooks/pack-cli.ts +505 -0
- package/src/hooks/pack-failclosed.ts +189 -0
- package/src/hooks/pack-manifest.ts +344 -0
- package/src/hooks/pack-store.ts +930 -0
- package/src/hooks/policy-catalog.ts +535 -0
- package/src/hooks/policy-evaluator.ts +24 -16
- package/src/hooks/policy-presets.ts +4 -2
- package/src/hooks/policy-registry.ts +8 -2
- package/src/hooks/policy-types.ts +26 -0
- package/src/hooks/session-pause-cli.ts +33 -9
- package/src/hooks/tui.ts +570 -0
- package/src/hooks/types.ts +15 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_usx0j._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_zk340._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0agdw2a._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d1u_na._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0oy4kxb._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zf034w._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +0 -3
- package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +0 -3
- package/.next/standalone/.next/server/chunks/_1ger0a2._.js +0 -3
- package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +0 -3
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_1iiwm5e._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kc8_v5._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kgy9da._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0mhqk5a._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pltpn8._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qkcyf-._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w0x_ok._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1_dfocn._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1bfex62._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1kzapg5._.js +0 -6
- package/.next/standalone/.next/static/chunks/36fw6yk1s6x_f.js +0 -1
- package/.next/standalone/.next/static/chunks/3tzq0260n17ae.js +0 -6
- package/.next/standalone/.next/static/chunks/3wq9x0c-7ld5d.css +0 -2
- /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → nwbyWT06Ud0XdXOrVJyvS}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → nwbyWT06Ud0XdXOrVJyvS}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → nwbyWT06Ud0XdXOrVJyvS}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"""Framework adapters: the registry behind `failproofai_sdk.instrument()`.
|
|
2
|
+
|
|
3
|
+
import failproofai_sdk
|
|
4
|
+
failproofai_sdk.instrument() # every framework already imported
|
|
5
|
+
failproofai_sdk.instrument("crewai") # exactly one
|
|
6
|
+
failproofai_sdk.uninstrument() # put everything back
|
|
7
|
+
|
|
8
|
+
**This module is stdlib only, and imports no adapter until asked.** The registry
|
|
9
|
+
maps a name to a dotted module path *as a string*; `importlib.import_module`
|
|
10
|
+
runs on demand. That is what keeps `import failproofai_sdk` free of LangChain.
|
|
11
|
+
|
|
12
|
+
Auto-detection reads `sys.modules`, deliberately **not**
|
|
13
|
+
`importlib.util.find_spec`. `find_spec` would report "installed", and acting on
|
|
14
|
+
that means importing a framework the user is not using — 200ms and a pile of
|
|
15
|
+
transitive imports charged to a library that was supposed to be invisible. If
|
|
16
|
+
the framework is not imported, there is nothing to instrument.
|
|
17
|
+
|
|
18
|
+
Writing an adapter
|
|
19
|
+
------------------
|
|
20
|
+
A module registered here must expose a module-level object named `adapter`
|
|
21
|
+
implementing `failproofai_sdk.integrations._core.Adapter`:
|
|
22
|
+
|
|
23
|
+
name: str # the registry name
|
|
24
|
+
module: str # the framework module auto-detect looks for in sys.modules
|
|
25
|
+
def install(**options) -> None
|
|
26
|
+
def uninstall() -> None
|
|
27
|
+
|
|
28
|
+
`install()` must save the **original attribute object** it replaces — use
|
|
29
|
+
`_core.Patcher`, which also does the "somebody patched on top of us" check — and
|
|
30
|
+
`uninstall()` must restore that saved object. Never re-import to restore: that
|
|
31
|
+
hands back whatever the current value happens to be, which is how two
|
|
32
|
+
instrumentation libraries silently un-patch each other.
|
|
33
|
+
|
|
34
|
+
Every callback the adapter hands to the framework goes through `_core.safe`, and
|
|
35
|
+
every event goes through a `_core.RunTracker`. Adapters own the translation
|
|
36
|
+
table and nothing else.
|
|
37
|
+
|
|
38
|
+
The four names are registered here **now**, before their modules exist, so that
|
|
39
|
+
adding an adapter is one new file rather than an edit to this one.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
import importlib
|
|
43
|
+
import logging
|
|
44
|
+
import sys
|
|
45
|
+
import threading
|
|
46
|
+
from typing import Any
|
|
47
|
+
|
|
48
|
+
from failproofai_sdk.integrations import _compat, _core
|
|
49
|
+
from failproofai_sdk.integrations._core import Adapter
|
|
50
|
+
|
|
51
|
+
logger = logging.getLogger("failproofai_sdk.integrations")
|
|
52
|
+
|
|
53
|
+
__all__ = ["instrument", "uninstrument", "active", "available"]
|
|
54
|
+
|
|
55
|
+
# name -> dotted module path. A string, imported on demand.
|
|
56
|
+
_REGISTRY: dict[str, str] = {
|
|
57
|
+
"langchain": "failproofai_sdk.integrations.langchain",
|
|
58
|
+
"crewai": "failproofai_sdk.integrations.crewai",
|
|
59
|
+
"llama_index": "failproofai_sdk.integrations.llama_index",
|
|
60
|
+
"pydantic_ai": "failproofai_sdk.integrations.pydantic_ai",
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# Spellings people actually type. LangGraph is served by the LangChain adapter
|
|
64
|
+
# because LangGraph runs on langchain-core's callback manager.
|
|
65
|
+
_ALIASES: dict[str, str] = {
|
|
66
|
+
"langgraph": "langchain",
|
|
67
|
+
"langchain_core": "langchain",
|
|
68
|
+
"llamaindex": "llama_index",
|
|
69
|
+
"llama-index": "llama_index",
|
|
70
|
+
"pydantic-ai": "pydantic_ai",
|
|
71
|
+
"pydanticai": "pydantic_ai",
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# name -> the framework modules whose presence in sys.modules means "this
|
|
75
|
+
# framework is in use". Kept here rather than read off `adapter.module` so that
|
|
76
|
+
# detection imports nothing at all, not even our own adapter module.
|
|
77
|
+
_DETECT: dict[str, tuple[str, ...]] = {
|
|
78
|
+
"langchain": ("langchain_core", "langchain", "langgraph"),
|
|
79
|
+
"crewai": ("crewai",),
|
|
80
|
+
"llama_index": ("llama_index", "llama_index.core"),
|
|
81
|
+
"pydantic_ai": ("pydantic_ai",),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# Guards _ACTIVE and every install/uninstall. `instrument()` is called from
|
|
85
|
+
# application startup, which in a web server can be several threads at once.
|
|
86
|
+
_LOCK = threading.Lock()
|
|
87
|
+
_ACTIVE: dict[str, Adapter] = {}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def available() -> tuple[str, ...]:
|
|
91
|
+
"""Every registry name that can be instrumented, aliases excluded."""
|
|
92
|
+
return tuple(sorted(_REGISTRY))
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def active() -> tuple[str, ...]:
|
|
96
|
+
"""Names currently instrumented."""
|
|
97
|
+
with _LOCK:
|
|
98
|
+
return tuple(sorted(_ACTIVE))
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _canonical(name: str) -> str:
|
|
102
|
+
key = str(name).strip().lower().replace(" ", "")
|
|
103
|
+
key = _ALIASES.get(key, key)
|
|
104
|
+
if key not in _REGISTRY:
|
|
105
|
+
valid = ", ".join(sorted(set(_REGISTRY) | set(_ALIASES)))
|
|
106
|
+
raise ValueError(
|
|
107
|
+
f"failproofai_sdk: unknown framework {name!r}. Valid names are: {valid}. "
|
|
108
|
+
f"(Call failproofai_sdk.instrument() with no argument to auto-detect.)"
|
|
109
|
+
)
|
|
110
|
+
return key
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _detected() -> list[str]:
|
|
114
|
+
"""Registry names whose framework is already imported in this process."""
|
|
115
|
+
found = []
|
|
116
|
+
for name in _REGISTRY:
|
|
117
|
+
modules = _DETECT.get(name, (name,))
|
|
118
|
+
if any(module in sys.modules for module in modules):
|
|
119
|
+
found.append(name)
|
|
120
|
+
return found
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _load(name: str) -> Adapter:
|
|
124
|
+
module = importlib.import_module(_REGISTRY[name])
|
|
125
|
+
adapter = getattr(module, "adapter", None)
|
|
126
|
+
if adapter is None:
|
|
127
|
+
# A module that is itself the adapter is fine too; the attribute is the
|
|
128
|
+
# convention, not the contract.
|
|
129
|
+
adapter = module
|
|
130
|
+
for attribute in ("install", "uninstall"):
|
|
131
|
+
if not callable(getattr(adapter, attribute, None)):
|
|
132
|
+
raise TypeError(
|
|
133
|
+
f"failproofai_sdk: adapter {_REGISTRY[name]!r} does not implement "
|
|
134
|
+
f"{attribute}() — see failproofai_sdk.integrations._core.Adapter."
|
|
135
|
+
)
|
|
136
|
+
return adapter # type: ignore[return-value]
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def instrument(framework: str | None = None, **options: Any) -> tuple[str, ...]:
|
|
140
|
+
"""Install the adapters. Returns the names newly instrumented.
|
|
141
|
+
|
|
142
|
+
With no argument, instruments every framework already imported in this
|
|
143
|
+
process. Instrumenting something already active is a no-op that returns
|
|
144
|
+
`()`, so calling this from two code paths (or from a reloading dev server)
|
|
145
|
+
cannot double-record.
|
|
146
|
+
|
|
147
|
+
An unknown name raises `ValueError` listing the valid ones — a typo that
|
|
148
|
+
silently records nothing is the worst outcome available. An adapter whose
|
|
149
|
+
`install()` raises is logged and skipped; the others still install, because
|
|
150
|
+
a broken LlamaIndex should not cost you LangGraph. `FAILPROOFAI_SDK_STRICT=1` turns
|
|
151
|
+
that skip back into a raise.
|
|
152
|
+
"""
|
|
153
|
+
if framework is None:
|
|
154
|
+
names = _detected()
|
|
155
|
+
if not names:
|
|
156
|
+
# WARNING, not debug. This fires only when somebody explicitly asked
|
|
157
|
+
# for instrumentation and got none — the import-order mistake of
|
|
158
|
+
# calling instrument() above the `import langchain` line — and the
|
|
159
|
+
# result is a process that records nothing at all, with the adapter
|
|
160
|
+
# installed and the docs followed. At debug level the message that
|
|
161
|
+
# names the exact fix was invisible under every default logging
|
|
162
|
+
# config, which made the one mistake that costs you all your
|
|
163
|
+
# telemetry the one mistake we said nothing about.
|
|
164
|
+
logger.warning(
|
|
165
|
+
"failproofai_sdk: instrument() found no supported framework in sys.modules, "
|
|
166
|
+
"so NOTHING was instrumented. Import your framework before calling "
|
|
167
|
+
"instrument(), or name one explicitly: %s.",
|
|
168
|
+
# The names this call would have accepted, rather than one
|
|
169
|
+
# hardcoded example. A reader who is not using CrewAI has to
|
|
170
|
+
# work out for themselves whether the message is a suggestion
|
|
171
|
+
# or a diagnosis, which is a poor use of the one line they get.
|
|
172
|
+
", ".join(f"instrument({name!r})" for name in sorted(_REGISTRY)),
|
|
173
|
+
)
|
|
174
|
+
else:
|
|
175
|
+
names = [_canonical(framework)]
|
|
176
|
+
|
|
177
|
+
installed: list[str] = []
|
|
178
|
+
with _LOCK:
|
|
179
|
+
for name in names:
|
|
180
|
+
if name in _ACTIVE:
|
|
181
|
+
continue
|
|
182
|
+
adapter = None
|
|
183
|
+
try:
|
|
184
|
+
adapter = _load(name)
|
|
185
|
+
adapter.install(**options)
|
|
186
|
+
except Exception as exc:
|
|
187
|
+
# An install is NOT atomic, so a failure part-way through leaves
|
|
188
|
+
# global state behind. `langchain.install()` sets `_STATE.enabled`,
|
|
189
|
+
# registers an append-only configure hook whose own uninstall
|
|
190
|
+
# comment says "There is no deregister API", builds the tracer and
|
|
191
|
+
# exports an env var — and only THEN probes a capability that can
|
|
192
|
+
# raise (a `_compat.probe` under FAILPROOFAI_SDK_STRICT_INTEGRATIONS=1
|
|
193
|
+
# always does). Catching here left the adapter fully patched and
|
|
194
|
+
# never recorded in `_ACTIVE`, so `active()` denied it existed and
|
|
195
|
+
# `uninstrument()` — which iterates `_ACTIVE` — could never undo it.
|
|
196
|
+
# It recorded for the life of the process and could not be removed.
|
|
197
|
+
if adapter is not None:
|
|
198
|
+
try:
|
|
199
|
+
adapter.uninstall()
|
|
200
|
+
except Exception:
|
|
201
|
+
logger.debug(
|
|
202
|
+
"failproofai_sdk: rollback of a failed %r install did not "
|
|
203
|
+
"complete cleanly",
|
|
204
|
+
name,
|
|
205
|
+
exc_info=True,
|
|
206
|
+
)
|
|
207
|
+
if _core.strict():
|
|
208
|
+
raise
|
|
209
|
+
# `FAILPROOFAI_SDK_STRICT_INTEGRATIONS=1` is documented as the
|
|
210
|
+
# supported way to make a compat problem loud — `_compat.py`'s
|
|
211
|
+
# own docstring calls warn-by-default "only defensible because
|
|
212
|
+
# there is a supported way to make it fail loudly". It was not
|
|
213
|
+
# one: the flag makes `_compat.warn` RAISE, this handler caught
|
|
214
|
+
# it, and the operator got neither behaviour — not the raise the
|
|
215
|
+
# flag promises, and not the best-effort instrumentation the
|
|
216
|
+
# warning text promises ("Instrumenting anyway"). An adapter
|
|
217
|
+
# silently recorded nothing while the log said it was fine.
|
|
218
|
+
if isinstance(exc, _compat.FailproofAICompatWarning) and (
|
|
219
|
+
_compat.strict_integrations()
|
|
220
|
+
):
|
|
221
|
+
raise
|
|
222
|
+
logger.warning(
|
|
223
|
+
"failproofai_sdk: could not instrument %r; the rest of your process is "
|
|
224
|
+
"unaffected and other adapters still installed. Set "
|
|
225
|
+
"FAILPROOFAI_SDK_STRICT=1 to raise instead.",
|
|
226
|
+
name,
|
|
227
|
+
exc_info=True,
|
|
228
|
+
)
|
|
229
|
+
continue
|
|
230
|
+
_ACTIVE[name] = adapter
|
|
231
|
+
installed.append(name)
|
|
232
|
+
return tuple(installed)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def uninstrument(framework: str | None = None) -> tuple[str, ...]:
|
|
236
|
+
"""Reverse `instrument()`. Returns the names removed. Never raises.
|
|
237
|
+
|
|
238
|
+
With no argument, removes everything. An unknown name, or a name that was
|
|
239
|
+
never instrumented, is a no-op — teardown that can fail is teardown people
|
|
240
|
+
stop calling.
|
|
241
|
+
"""
|
|
242
|
+
if framework is None:
|
|
243
|
+
with _LOCK:
|
|
244
|
+
names = list(_ACTIVE)
|
|
245
|
+
else:
|
|
246
|
+
try:
|
|
247
|
+
names = [_canonical(framework)]
|
|
248
|
+
except ValueError as exc:
|
|
249
|
+
logger.warning("%s", exc)
|
|
250
|
+
return ()
|
|
251
|
+
|
|
252
|
+
removed: list[str] = []
|
|
253
|
+
with _LOCK:
|
|
254
|
+
for name in names:
|
|
255
|
+
adapter = _ACTIVE.pop(name, None)
|
|
256
|
+
if adapter is None:
|
|
257
|
+
continue
|
|
258
|
+
try:
|
|
259
|
+
adapter.uninstall()
|
|
260
|
+
except Exception:
|
|
261
|
+
logger.warning(
|
|
262
|
+
"failproofai_sdk: %r did not uninstall cleanly; it is no longer "
|
|
263
|
+
"registered, but some patches may remain.",
|
|
264
|
+
name,
|
|
265
|
+
exc_info=True,
|
|
266
|
+
)
|
|
267
|
+
removed.append(name)
|
|
268
|
+
if not _ACTIVE:
|
|
269
|
+
# Nothing is instrumented any more, so a later instrument() starts
|
|
270
|
+
# from a clean slate rather than inheriting a degraded call site or
|
|
271
|
+
# a warning that has "already been shown".
|
|
272
|
+
_core.reset_failures()
|
|
273
|
+
_compat.reset_warnings()
|
|
274
|
+
return tuple(removed)
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""Version and capability probes for the framework adapters.
|
|
2
|
+
|
|
3
|
+
Extras (`failproofai_sdk[langchain]`) express a *floor*, not enforcement: most users
|
|
4
|
+
already have the framework and will never install an extra. So the real check
|
|
5
|
+
happens here, at `instrument()` time, in three tiers:
|
|
6
|
+
|
|
7
|
+
1. **framework not importable** -> a hard `ImportError` whose message contains
|
|
8
|
+
the literal install command. Instrumenting is an explicit user action, so
|
|
9
|
+
silently doing nothing is never the right answer.
|
|
10
|
+
2. **importable but outside the declared range** -> `FailproofAICompatWarning`,
|
|
11
|
+
once, then best-effort. A ceiling exists because without one a clean build a
|
|
12
|
+
year from now pulls the next major, the callback API shifts, and the adapter
|
|
13
|
+
stops receiving events *while raising nothing*.
|
|
14
|
+
3. **a capability probe fails** -> warn and no-op **that hook only**, never the
|
|
15
|
+
whole adapter.
|
|
16
|
+
|
|
17
|
+
`FAILPROOFAI_SDK_STRICT_INTEGRATIONS=1` promotes every warning here to an exception.
|
|
18
|
+
Warn-by-default is only defensible because there is a supported way to make it
|
|
19
|
+
fail loudly.
|
|
20
|
+
|
|
21
|
+
Why the version comparison is naive
|
|
22
|
+
-----------------------------------
|
|
23
|
+
`import failproofai_sdk` is contractually zero-dependency, so this cannot use
|
|
24
|
+
`packaging`. `parse_version` therefore reads the **leading numeric components
|
|
25
|
+
only** and stops at the first component that is not purely numeric:
|
|
26
|
+
|
|
27
|
+
"1.5.2" -> (1, 5, 2)
|
|
28
|
+
"2.0.0b1" -> (2, 0, 0) # pre-release suffix ignored
|
|
29
|
+
"0.14.23.post1" -> (0, 14, 23) # local/post segments ignored
|
|
30
|
+
"1.2.dev0" -> (1, 2)
|
|
31
|
+
|
|
32
|
+
That means `2.0.0b1` compares **equal** to `2.0.0`, so a pre-release of a major
|
|
33
|
+
we have declared a ceiling against will not be flagged. That is deliberate:
|
|
34
|
+
the alternative is shipping a PEP 440 parser, and being wrong about a release
|
|
35
|
+
candidate is much cheaper than a runtime dependency.
|
|
36
|
+
|
|
37
|
+
Versions are always read with `importlib.metadata.version(dist)` and **never**
|
|
38
|
+
`module.__version__` — that attribute is not guaranteed to exist and several of
|
|
39
|
+
the frameworks we target do not define it.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
import importlib
|
|
43
|
+
import importlib.metadata
|
|
44
|
+
import logging
|
|
45
|
+
import os
|
|
46
|
+
import threading
|
|
47
|
+
import warnings
|
|
48
|
+
from types import ModuleType
|
|
49
|
+
from typing import Any, Callable
|
|
50
|
+
|
|
51
|
+
logger = logging.getLogger("failproofai_sdk.integrations")
|
|
52
|
+
|
|
53
|
+
__all__ = [
|
|
54
|
+
"FailproofAICompatWarning",
|
|
55
|
+
"parse_version",
|
|
56
|
+
"version_string",
|
|
57
|
+
"version_tuple",
|
|
58
|
+
"require_module",
|
|
59
|
+
"check_version",
|
|
60
|
+
"probe",
|
|
61
|
+
"warn",
|
|
62
|
+
"strict_integrations",
|
|
63
|
+
"set_strict_integrations",
|
|
64
|
+
"reset_warnings",
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class FailproofAICompatWarning(UserWarning):
|
|
69
|
+
"""A framework is outside the range this adapter was written against."""
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
_TRUTHY = frozenset({"1", "true", "yes", "on"})
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _env_flag(name: str) -> bool:
|
|
76
|
+
"""Read a boolean env var. Shared with `_core` so both flags parse alike."""
|
|
77
|
+
return os.environ.get(name, "").strip().lower() in _TRUTHY
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# Cached, because it is read on every warning and every `safe()` failure, and
|
|
81
|
+
# resettable, because a test that cannot flip the switch cannot test the policy.
|
|
82
|
+
# Same shape as `failproofai_sdk._environment`: a module global that overrides the env
|
|
83
|
+
# var, with None meaning "not decided yet, go look".
|
|
84
|
+
_strict_integrations: bool | None = None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def strict_integrations() -> bool:
|
|
88
|
+
global _strict_integrations
|
|
89
|
+
if _strict_integrations is None:
|
|
90
|
+
_strict_integrations = _env_flag("FAILPROOFAI_SDK_STRICT_INTEGRATIONS")
|
|
91
|
+
return _strict_integrations
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def set_strict_integrations(value: bool | None) -> None:
|
|
95
|
+
"""Override the flag. `None` re-reads `FAILPROOFAI_SDK_STRICT_INTEGRATIONS`."""
|
|
96
|
+
global _strict_integrations
|
|
97
|
+
_strict_integrations = value
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
_warned: set[str] = set()
|
|
101
|
+
_warn_lock = threading.Lock()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def reset_warnings() -> None:
|
|
105
|
+
"""Forget which warnings have already fired (tests; also `uninstrument()`)."""
|
|
106
|
+
with _warn_lock:
|
|
107
|
+
_warned.clear()
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def warn(message: str, *, key: str | None = None) -> None:
|
|
111
|
+
"""Warn once per `key`, or raise if strict.
|
|
112
|
+
|
|
113
|
+
Deduplicated because these fire from `install()` *and* from hot callbacks:
|
|
114
|
+
a per-call warning on a chatty framework is its own outage.
|
|
115
|
+
"""
|
|
116
|
+
if strict_integrations():
|
|
117
|
+
raise FailproofAICompatWarning(message)
|
|
118
|
+
dedup = key or message
|
|
119
|
+
with _warn_lock:
|
|
120
|
+
if dedup in _warned:
|
|
121
|
+
return
|
|
122
|
+
_warned.add(dedup)
|
|
123
|
+
warnings.warn(message, FailproofAICompatWarning, stacklevel=3)
|
|
124
|
+
logger.warning("%s", message)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def parse_version(text: str) -> tuple[int, ...]:
|
|
128
|
+
"""Leading numeric components of a version string. See the module docstring."""
|
|
129
|
+
parts: list[int] = []
|
|
130
|
+
for chunk in str(text).split("."):
|
|
131
|
+
digits = ""
|
|
132
|
+
for ch in chunk:
|
|
133
|
+
if not ch.isdigit():
|
|
134
|
+
break
|
|
135
|
+
digits += ch
|
|
136
|
+
if not digits:
|
|
137
|
+
break
|
|
138
|
+
parts.append(int(digits))
|
|
139
|
+
if len(digits) != len(chunk):
|
|
140
|
+
# A partially numeric component ("0b1", "dev0", "post1") ends the
|
|
141
|
+
# numeric prefix — everything after it is a pre/post/local segment.
|
|
142
|
+
break
|
|
143
|
+
return tuple(parts)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def version_string(dist: str) -> str | None:
|
|
147
|
+
"""The installed version of a distribution, or None if it is not installed.
|
|
148
|
+
|
|
149
|
+
`dist` is the *distribution* name (`langchain-core`), which is frequently
|
|
150
|
+
not the module name (`langchain_core`).
|
|
151
|
+
"""
|
|
152
|
+
try:
|
|
153
|
+
return importlib.metadata.version(dist)
|
|
154
|
+
except importlib.metadata.PackageNotFoundError:
|
|
155
|
+
return None
|
|
156
|
+
except Exception: # pragma: no cover - a broken METADATA must not break us
|
|
157
|
+
logger.debug("failproofai_sdk: could not read version of %r", dist, exc_info=True)
|
|
158
|
+
return None
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def version_tuple(dist: str) -> tuple[int, ...] | None:
|
|
162
|
+
text = version_string(dist)
|
|
163
|
+
return parse_version(text) if text else None
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def require_module(module: str, *, dist: str, extra: str) -> ModuleType:
|
|
167
|
+
"""Import a framework module or raise with the literal install command.
|
|
168
|
+
|
|
169
|
+
Tier 1. `instrument("langchain")` on a machine without LangChain is a
|
|
170
|
+
mistake the user can fix in one command, so we hand them the command.
|
|
171
|
+
"""
|
|
172
|
+
try:
|
|
173
|
+
return importlib.import_module(module)
|
|
174
|
+
except ImportError as exc:
|
|
175
|
+
raise ImportError(
|
|
176
|
+
f"failproofai_sdk: cannot instrument {extra!r} because {module!r} is not importable. "
|
|
177
|
+
f"Install it with: pip install 'failproofai_sdk[{extra}]' "
|
|
178
|
+
f"(or install {dist} directly)."
|
|
179
|
+
) from exc
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def check_version(
|
|
183
|
+
framework: str,
|
|
184
|
+
dist: str,
|
|
185
|
+
*,
|
|
186
|
+
minimum: str | None = None,
|
|
187
|
+
below: str | None = None,
|
|
188
|
+
reason: str | None = None,
|
|
189
|
+
) -> bool:
|
|
190
|
+
"""Tier 2. True when `dist` is inside [minimum, below); warns once if not.
|
|
191
|
+
|
|
192
|
+
Returns True (best effort) for an unknown version too — a framework
|
|
193
|
+
installed from a git checkout has no usable metadata, and refusing to
|
|
194
|
+
instrument it would be a worse answer than trying.
|
|
195
|
+
"""
|
|
196
|
+
found = version_string(dist)
|
|
197
|
+
if found is None:
|
|
198
|
+
return True
|
|
199
|
+
got = parse_version(found)
|
|
200
|
+
if not got:
|
|
201
|
+
return True
|
|
202
|
+
|
|
203
|
+
if minimum is not None and got < parse_version(minimum):
|
|
204
|
+
warn(
|
|
205
|
+
f"failproofai_sdk: {dist} {found} is older than the {minimum} this {framework} "
|
|
206
|
+
f"adapter was written against"
|
|
207
|
+
+ (f" ({reason})" if reason else "")
|
|
208
|
+
+ ". Instrumenting anyway; some events may be missing.",
|
|
209
|
+
key=f"{framework}:{dist}:min",
|
|
210
|
+
)
|
|
211
|
+
return False
|
|
212
|
+
if below is not None and got >= parse_version(below):
|
|
213
|
+
warn(
|
|
214
|
+
f"failproofai_sdk: {dist} {found} is newer than the <{below} this {framework} "
|
|
215
|
+
f"adapter was written against. Instrumenting anyway, but a callback API "
|
|
216
|
+
f"change would make it stop recording silently — please report this.",
|
|
217
|
+
key=f"{framework}:{dist}:max",
|
|
218
|
+
)
|
|
219
|
+
return False
|
|
220
|
+
return True
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def probe(framework: str, hook: str, check: Callable[[], Any]) -> bool:
|
|
224
|
+
"""Tier 3. Run a capability probe; on failure warn and disable ONE hook.
|
|
225
|
+
|
|
226
|
+
if probe("langchain", "on_interrupt", lambda: langgraph.callbacks.GraphCallbackHandler):
|
|
227
|
+
...wire it up...
|
|
228
|
+
|
|
229
|
+
A missing capability is never a reason to abandon the whole adapter: the
|
|
230
|
+
other 90% of the events are still correct and still worth having.
|
|
231
|
+
"""
|
|
232
|
+
try:
|
|
233
|
+
ok = bool(check())
|
|
234
|
+
except Exception as exc:
|
|
235
|
+
warn(
|
|
236
|
+
f"failproofai_sdk: {framework} capability probe for {hook!r} failed ({exc!r}); "
|
|
237
|
+
f"that hook is disabled, the rest of the adapter is unaffected.",
|
|
238
|
+
key=f"{framework}:{hook}",
|
|
239
|
+
)
|
|
240
|
+
return False
|
|
241
|
+
if not ok:
|
|
242
|
+
warn(
|
|
243
|
+
f"failproofai_sdk: {framework} does not provide {hook!r} in this version; "
|
|
244
|
+
f"that hook is disabled, the rest of the adapter is unaffected.",
|
|
245
|
+
key=f"{framework}:{hook}",
|
|
246
|
+
)
|
|
247
|
+
return ok
|