failproofai 1.0.3 → 1.0.4-beta.2
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 +5 -5
- package/.next/standalone/.next/prerender-manifest.json +5 -5
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +2 -2
- 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.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/build-manifest.json +2 -2
- 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.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.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/invite/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -4
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route_client-reference-manifest.js +1 -1
- 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-request/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/audit/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +37 -4
- 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/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- 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/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
- 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/build-manifest.json +2 -2
- 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.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/build-manifest.json +2 -2
- 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.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/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- 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/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
- 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]__0-9ti7z._.js → [externals]__1lh7m5d._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__0u3wrvq._.js → [externals]__1rqkg_y._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1adacul._.js +1 -1
- package/.next/standalone/.next/server/chunks/{_1-akg6w._.js → _08w6xzm._.js} +2 -2
- package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1ek68ln._.js +4 -4
- package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +2 -2
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +2 -2
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- 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 +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +2 -2
- 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 +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0rgu2r3._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_0-oij9d._.js +23 -0
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0l2hi_d._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0oq1dh6._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1es2j7i._.js +47 -13
- package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -69
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_rerun-button_tsx_0blihgw._.js +3 -0
- 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 +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_13n822a._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +2 -2
- 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-config_ts_04t589g._.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 +5 -5
- 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 +54 -21
- package/.next/standalone/.next/static/chunks/010bv1w6j171t.js +1 -0
- package/.next/standalone/.next/static/chunks/04r6ch8uf_n8m.js +1 -0
- package/.next/standalone/.next/static/chunks/{29-iqnp9v_axt.css → 0o-hh5_turzlz.css} +1 -1
- package/.next/standalone/.next/static/chunks/{40nr6ehj4p3cw.css → 0qmsunv04x4na.css} +1 -1
- package/.next/standalone/.next/static/chunks/{0ollg-nnu3t3e.js → 0wz8yftk18ts2.js} +1 -1
- package/.next/standalone/.next/static/chunks/0zebh1n9jkfbt.js +1 -0
- package/.next/standalone/.next/static/chunks/1eyl7fzkmnkf_.js +1 -0
- package/.next/standalone/.next/static/chunks/1pb1oztsbwcss.js +1 -0
- package/.next/standalone/.next/static/chunks/{1-_zkmnm-jwei.js → 2aquitk72k2op.js} +1 -1
- package/.next/standalone/.next/static/chunks/2bi_1y0a_smt7.js +6 -0
- package/.next/standalone/.next/static/chunks/{42yfkhekx440s.js → 2ej3b8gk5ittu.js} +1 -1
- package/.next/standalone/.next/static/chunks/{354-n7p7labpn.js → 2zafgs90r_leq.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2xm5mj1d21b4n.js → 32spub4wqjem-.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1a6u7l3giejch.js → 3m4upvybtrexd.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2k9f4tyv04809.css → 3uyhmm01g64k4.css} +1 -0
- package/.next/standalone/.next/static/chunks/{turbopack-2x-o099q64i8h.js → turbopack-3s3c-u_u_l0u9.js} +1 -1
- package/.next/standalone/.opencode/plugins/failproofai.mjs +51 -27
- package/.next/standalone/app/actions/get-leaks.ts +100 -0
- package/.next/standalone/app/audit/_components/audit-dashboard.tsx +112 -33
- package/.next/standalone/app/audit/_components/audit-poster.tsx +29 -10
- package/.next/standalone/app/audit/_components/come-back-better-section.tsx +6 -1
- package/.next/standalone/app/audit/_components/empty-state.tsx +4 -1
- package/.next/standalone/app/audit/_components/how-to-improve-section.tsx +19 -5
- package/.next/standalone/app/audit/_components/leak-section.tsx +179 -0
- package/.next/standalone/app/audit/_components/share-templates.ts +110 -44
- package/.next/standalone/app/audit/audit-styles.css +114 -1
- package/.next/standalone/fp-cloud-cli/CHANGELOG.md +26 -3
- package/.next/standalone/fp-cloud-cli/fp_cli/_click_compat.py +31 -8
- package/.next/standalone/fp-cloud-cli/fp_cli/permissions.py +1 -0
- package/.next/standalone/fp-cloud-cli/pyproject.toml +9 -2
- package/.next/standalone/fp-cloud-cli/tests/test_click_compat.py +54 -0
- package/.next/standalone/fp-cloud-cli/uv.lock +9 -12
- package/.next/standalone/lib/auth/api-server-client.ts +28 -0
- package/.next/standalone/lib/claude-sessions.ts +101 -27
- package/.next/standalone/node_modules/@next/env/package.json +1 -1
- package/.next/standalone/node_modules/next/dist/build/swc/index.js +1 -1
- package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo-experimental.runtime.prod.js +1 -1
- package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo.runtime.prod.js +1 -1
- package/.next/standalone/node_modules/next/dist/compiled/next-server/app-route-turbo.runtime.prod.js +1 -1
- package/.next/standalone/node_modules/next/dist/compiled/next-server/pages-turbo.runtime.prod.js +5 -5
- package/.next/standalone/node_modules/next/dist/experimental/testmode/fetch.js +10 -2
- package/.next/standalone/node_modules/next/dist/lib/patch-incorrect-lockfile.js +3 -3
- package/.next/standalone/node_modules/next/dist/lib/typescript/runTypeScriptCli.js +2 -2
- package/.next/standalone/node_modules/next/dist/lib/verify-typescript-setup.js +3 -1
- package/.next/standalone/node_modules/next/dist/server/config.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-turbopack.js +2 -2
- package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-webpack.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/lib/app-info-log.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/lib/incremental-cache/file-system-cache.js +17 -4
- package/.next/standalone/node_modules/next/dist/server/lib/start-server.js +1 -1
- package/.next/standalone/node_modules/next/dist/shared/lib/router/utils/escape-path-delimiters.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/anonymous-meta.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/events/swc-load-failure.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/events/version.js +2 -2
- package/.next/standalone/node_modules/next/package.json +11 -11
- package/.next/standalone/package.json +14 -13
- package/.next/standalone/sdk/python/CHANGELOG.md +155 -0
- package/.next/standalone/sdk/python/README.md +8 -0
- package/.next/standalone/sdk/python/examples/evaluator_worker.py +121 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_version.py +1 -1
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/__init__.py +101 -0
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/__main__.py +49 -0
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/_sandbox_runner.py +63 -0
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/authoring.py +404 -0
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/client.py +299 -0
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/protocol.py +754 -0
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/runtime.py +936 -0
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/source.py +684 -0
- package/.next/standalone/sdk/python/tests/fixtures/evaluator_v2/README.md +28 -0
- package/.next/standalone/sdk/python/tests/fixtures/evaluator_v2/contract.json +252 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_authoring.py +124 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_client.py +253 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_example.py +35 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_http_e2e.py +636 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_main.py +47 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_protocol.py +246 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_review_fixes.py +164 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_runtime.py +1103 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_source.py +430 -0
- package/.next/standalone/sdk/python/tests/test_zero_dependencies.py +17 -0
- package/.next/standalone/sdk/python/uv.lock +29 -29
- package/.next/standalone/server.js +1 -1
- package/README.md +13 -58
- package/dist/cli.mjs +5368 -8756
- package/dist/worker.mjs +539 -312
- package/lib/auth/api-server-client.ts +28 -0
- package/lib/claude-sessions.ts +101 -27
- package/package.json +14 -13
- package/pi-extension/index.ts +42 -4
- package/scripts/changelog-open.py +115 -0
- package/src/audit/cli.ts +134 -0
- package/src/audit/desktop-notify.ts +420 -0
- package/src/audit/harm-report.ts +101 -0
- package/src/audit/index.ts +141 -13
- package/src/audit/leak-fingerprint.ts +200 -0
- package/src/audit/leak-notice.ts +161 -0
- package/src/audit/leak-record.ts +232 -0
- package/src/audit/leak-scan.ts +292 -0
- package/src/audit/leak-store.ts +217 -0
- package/src/audit/macos-notifier.ts +310 -0
- package/src/audit/redact-example.ts +266 -14
- package/src/audit/report-harm.ts +12 -1
- package/src/audit/report.ts +6 -3
- package/src/audit/schedule-cli.ts +37 -0
- package/src/audit/scoring.ts +49 -0
- package/src/audit/types.ts +41 -0
- package/src/hooks/builtin-policies.ts +67 -2
- package/src/hooks/configure-wizard.ts +20 -0
- package/src/hooks/fp-config.ts +71 -11
- package/src/hooks/fp-home.ts +27 -0
- package/src/hooks/handler.ts +59 -1
- package/src/hooks/integrations.ts +47 -13
- package/src/hooks/notice.ts +155 -0
- package/src/hooks/uninstall-cli.ts +15 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1b5jzxr._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0j6gd_k._.js +0 -23
- package/.next/standalone/.next/server/chunks/ssr/_0v8eebd._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_1ylae7o._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_1ao30b1.js +0 -3
- package/.next/standalone/.next/static/chunks/03slu0c8_71y7.js +0 -1
- package/.next/standalone/.next/static/chunks/0nm11ysn0153h.js +0 -1
- package/.next/standalone/.next/static/chunks/0u64yey446k4f.css +0 -1
- package/.next/standalone/.next/static/chunks/1r9676mkvihto.js +0 -1
- package/.next/standalone/.next/static/chunks/30pct5s734jua.js +0 -6
- package/.next/standalone/.next/static/chunks/3pibxnbhjza7n.js +0 -69
- package/.next/standalone/.next/static/chunks/3qfoeqhy5l10x.js +0 -1
- package/.next/standalone/.next/static/chunks/3zkg2s2vzxc3d.js +0 -1
- package/.next/standalone/.next/static/chunks/41b5ptaocvi_l.js +0 -1
- /package/.next/standalone/.next/static/{JBYgkXkA1PS48m9P_hKNw → aKNv4Kmv98Xpwns0Xa-C-}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{JBYgkXkA1PS48m9P_hKNw → aKNv4Kmv98Xpwns0Xa-C-}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{JBYgkXkA1PS48m9P_hKNw → aKNv4Kmv98Xpwns0Xa-C-}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import copy
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from failproofai_sdk.evaluator import (
|
|
10
|
+
ClaimRequest,
|
|
11
|
+
ClaimResponse,
|
|
12
|
+
DefinitionsResponse,
|
|
13
|
+
ErrorResponse,
|
|
14
|
+
HeartbeatRequest,
|
|
15
|
+
HeartbeatResponse,
|
|
16
|
+
PlanRequest,
|
|
17
|
+
PlanResponse,
|
|
18
|
+
ProtocolError,
|
|
19
|
+
RegisterRequest,
|
|
20
|
+
RegisterResponse,
|
|
21
|
+
ResultRequest,
|
|
22
|
+
ResultResponse,
|
|
23
|
+
SessionTranscript,
|
|
24
|
+
UnsupportedProtocolVersion,
|
|
25
|
+
protocol,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
FIXTURE = Path(__file__).parent / "fixtures" / "evaluator_v2" / "contract.json"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _contract():
|
|
32
|
+
return json.loads(FIXTURE.read_text(encoding="utf-8"))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@pytest.mark.parametrize(
|
|
36
|
+
("sample", "model"),
|
|
37
|
+
[
|
|
38
|
+
("register_request", RegisterRequest),
|
|
39
|
+
("register_response", RegisterResponse),
|
|
40
|
+
("claim_request", ClaimRequest),
|
|
41
|
+
("claim_response", ClaimResponse),
|
|
42
|
+
("definitions_response", DefinitionsResponse),
|
|
43
|
+
("transcript_response", SessionTranscript),
|
|
44
|
+
("plan_request", PlanRequest),
|
|
45
|
+
("plan_response", PlanResponse),
|
|
46
|
+
("heartbeat_request", HeartbeatRequest),
|
|
47
|
+
("heartbeat_response", HeartbeatResponse),
|
|
48
|
+
("result_request", ResultRequest),
|
|
49
|
+
("result_response", ResultResponse),
|
|
50
|
+
("error_response", ErrorResponse),
|
|
51
|
+
],
|
|
52
|
+
)
|
|
53
|
+
def test_golden_messages_round_trip(sample, model):
|
|
54
|
+
wire = _contract()["samples"][sample]
|
|
55
|
+
assert model.from_wire(wire).to_wire() == wire
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_unknown_additive_fields_are_tolerated():
|
|
59
|
+
wire = dict(_contract()["samples"]["claim_request"])
|
|
60
|
+
wire["future_optional_field"] = True
|
|
61
|
+
assert ClaimRequest.from_wire(wire).capacity == 2
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_unsupported_major_version_fails_loudly():
|
|
65
|
+
wire = dict(_contract()["samples"]["claim_request"])
|
|
66
|
+
wire["protocol_version"] = "3"
|
|
67
|
+
with pytest.raises(
|
|
68
|
+
UnsupportedProtocolVersion, match="supported major version is 2"
|
|
69
|
+
):
|
|
70
|
+
ClaimRequest.from_wire(wire)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_transcript_event_count_is_an_integrity_check():
|
|
74
|
+
wire = dict(_contract()["samples"]["transcript_response"])
|
|
75
|
+
wire["event_count"] = 99
|
|
76
|
+
with pytest.raises(ProtocolError, match="transcript contains 2 events"):
|
|
77
|
+
SessionTranscript.from_wire(wire)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@pytest.mark.parametrize(
|
|
81
|
+
("sample", "model", "path", "value", "message"),
|
|
82
|
+
[
|
|
83
|
+
(
|
|
84
|
+
"claim_response",
|
|
85
|
+
ClaimResponse,
|
|
86
|
+
("assignments", 0),
|
|
87
|
+
42,
|
|
88
|
+
r"assignments\[0\] must be an object",
|
|
89
|
+
),
|
|
90
|
+
(
|
|
91
|
+
"register_response",
|
|
92
|
+
RegisterResponse,
|
|
93
|
+
("disabled_definitions",),
|
|
94
|
+
[42],
|
|
95
|
+
r"disabled_definitions\[0\] must be a string",
|
|
96
|
+
),
|
|
97
|
+
(
|
|
98
|
+
"heartbeat_response",
|
|
99
|
+
HeartbeatResponse,
|
|
100
|
+
("accepted_run_ids", 0),
|
|
101
|
+
None,
|
|
102
|
+
r"accepted_run_ids\[0\] must be a string",
|
|
103
|
+
),
|
|
104
|
+
(
|
|
105
|
+
"plan_response",
|
|
106
|
+
PlanResponse,
|
|
107
|
+
("runs", 0),
|
|
108
|
+
"not-an-object",
|
|
109
|
+
r"runs\[0\] must be an object",
|
|
110
|
+
),
|
|
111
|
+
(
|
|
112
|
+
"result_request",
|
|
113
|
+
ResultRequest,
|
|
114
|
+
("results", 0, "labels", 0),
|
|
115
|
+
7,
|
|
116
|
+
r"labels\[0\] must be a string",
|
|
117
|
+
),
|
|
118
|
+
(
|
|
119
|
+
"register_request",
|
|
120
|
+
RegisterRequest,
|
|
121
|
+
("definitions", 0, "result_kind"),
|
|
122
|
+
"unknown",
|
|
123
|
+
"result_kind must be one of",
|
|
124
|
+
),
|
|
125
|
+
],
|
|
126
|
+
)
|
|
127
|
+
def test_nested_wire_values_fail_with_protocol_errors(
|
|
128
|
+
sample, model, path, value, message
|
|
129
|
+
):
|
|
130
|
+
wire = copy.deepcopy(_contract()["samples"][sample])
|
|
131
|
+
target = wire
|
|
132
|
+
for part in path[:-1]:
|
|
133
|
+
target = target[part]
|
|
134
|
+
target[path[-1]] = value
|
|
135
|
+
with pytest.raises(ProtocolError, match=message):
|
|
136
|
+
model.from_wire(wire)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@pytest.mark.parametrize(
|
|
140
|
+
("sample", "model", "field", "value", "message"),
|
|
141
|
+
[
|
|
142
|
+
(
|
|
143
|
+
"claim_response",
|
|
144
|
+
ClaimResponse,
|
|
145
|
+
"lease_generation",
|
|
146
|
+
0,
|
|
147
|
+
"lease_generation must be greater than zero",
|
|
148
|
+
),
|
|
149
|
+
(
|
|
150
|
+
"claim_response",
|
|
151
|
+
ClaimResponse,
|
|
152
|
+
"event_count",
|
|
153
|
+
-1,
|
|
154
|
+
"event_count must not be negative",
|
|
155
|
+
),
|
|
156
|
+
(
|
|
157
|
+
"result_response",
|
|
158
|
+
ResultResponse,
|
|
159
|
+
"result_count",
|
|
160
|
+
-1,
|
|
161
|
+
"result_count must not be negative",
|
|
162
|
+
),
|
|
163
|
+
],
|
|
164
|
+
)
|
|
165
|
+
def test_server_response_counters_and_generations_are_bounded(
|
|
166
|
+
sample, model, field, value, message
|
|
167
|
+
):
|
|
168
|
+
wire = copy.deepcopy(_contract()["samples"][sample])
|
|
169
|
+
if sample == "claim_response":
|
|
170
|
+
wire["assignments"][0][field] = value
|
|
171
|
+
else:
|
|
172
|
+
wire[field] = value
|
|
173
|
+
with pytest.raises(ProtocolError, match=message):
|
|
174
|
+
model.from_wire(wire)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def test_fixture_constants_match_the_sdk_contract():
|
|
178
|
+
contract = _contract()
|
|
179
|
+
assert contract["protocol"] == {
|
|
180
|
+
"supported_major_versions": [protocol.PROTOCOL_VERSION],
|
|
181
|
+
"transcript_schema_version": protocol.TRANSCRIPT_SCHEMA_VERSION,
|
|
182
|
+
"result_schema_version": protocol.RESULT_SCHEMA_VERSION,
|
|
183
|
+
}
|
|
184
|
+
assert contract["http"] == {
|
|
185
|
+
"register": protocol.REGISTER_PATH,
|
|
186
|
+
"claim": protocol.CLAIM_PATH,
|
|
187
|
+
"transcript": protocol.TRANSCRIPT_PATH,
|
|
188
|
+
"definitions": protocol.DEFINITIONS_PATH,
|
|
189
|
+
"plan": protocol.PLAN_PATH,
|
|
190
|
+
"heartbeat": protocol.HEARTBEAT_PATH,
|
|
191
|
+
"result": protocol.RESULT_PATH,
|
|
192
|
+
"worker_id_header": protocol.WORKER_ID_HEADER,
|
|
193
|
+
"lease_generation_header": protocol.LEASE_GENERATION_HEADER,
|
|
194
|
+
}
|
|
195
|
+
assert contract["timing"] == {
|
|
196
|
+
"heartbeat_interval_seconds": protocol.HEARTBEAT_INTERVAL_SECONDS,
|
|
197
|
+
"lease_duration_seconds": protocol.LEASE_DURATION_SECONDS,
|
|
198
|
+
"poll_interval_seconds": protocol.DEFAULT_POLL_INTERVAL_SECONDS,
|
|
199
|
+
"max_attempts": protocol.MAX_ATTEMPTS,
|
|
200
|
+
}
|
|
201
|
+
assert contract["limits"] == {
|
|
202
|
+
"max_catalog_definitions": protocol.MAX_CATALOG_DEFINITIONS,
|
|
203
|
+
"max_claim_capacity": protocol.MAX_CLAIM_CAPACITY,
|
|
204
|
+
"max_transcript_bytes": protocol.MAX_TRANSCRIPT_BYTES,
|
|
205
|
+
"max_results_per_run": protocol.MAX_RESULTS_PER_RUN,
|
|
206
|
+
"max_eval_key_bytes": protocol.MAX_EVAL_KEY_BYTES,
|
|
207
|
+
"max_display_name_bytes": protocol.MAX_DISPLAY_NAME_BYTES,
|
|
208
|
+
"max_version_bytes": protocol.MAX_VERSION_BYTES,
|
|
209
|
+
"max_worker_id_bytes": protocol.MAX_WORKER_ID_BYTES,
|
|
210
|
+
"max_label_bytes": protocol.MAX_LABEL_BYTES,
|
|
211
|
+
"max_labels_per_result": protocol.MAX_LABELS_PER_RESULT,
|
|
212
|
+
"max_summary_bytes": protocol.MAX_SUMMARY_BYTES,
|
|
213
|
+
"max_reasoning_bytes": protocol.MAX_REASONING_BYTES,
|
|
214
|
+
"max_unit_bytes": protocol.MAX_UNIT_BYTES,
|
|
215
|
+
"max_display_value_bytes": protocol.MAX_DISPLAY_VALUE_BYTES,
|
|
216
|
+
"max_description_bytes": protocol.MAX_DESCRIPTION_BYTES,
|
|
217
|
+
"max_error_code_bytes": protocol.MAX_ERROR_CODE_BYTES,
|
|
218
|
+
"max_error_message_bytes": protocol.MAX_ERROR_MESSAGE_BYTES,
|
|
219
|
+
}
|
|
220
|
+
assert contract["errors"] == protocol.ERROR_SPECS
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def test_session_helpers_use_the_protocol_event_vocabulary():
|
|
224
|
+
session = SessionTranscript.from_wire(_contract()["samples"]["transcript_response"])
|
|
225
|
+
assert session.count("tool_use") == 1
|
|
226
|
+
assert session.events_of_type("agent_end")[0].payload["summary"] == "Done"
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def test_falsy_or_missing_execution_mode_is_rejected_not_defaulted():
|
|
230
|
+
# F2: a falsy/absent execution_mode was silently coerced to 'local', which
|
|
231
|
+
# could run a server-authored ('python') definition down the customer path.
|
|
232
|
+
# It must now be a hard protocol error, not a default.
|
|
233
|
+
samples = _contract()["samples"]
|
|
234
|
+
for bad in ("", None):
|
|
235
|
+
defs = json.loads(json.dumps(samples["definitions_response"]))
|
|
236
|
+
defs["definitions"][0]["execution_mode"] = bad
|
|
237
|
+
with pytest.raises(ProtocolError, match="execution_mode"):
|
|
238
|
+
DefinitionsResponse.from_wire(defs)
|
|
239
|
+
plan = json.loads(json.dumps(samples["plan_response"]))
|
|
240
|
+
plan["runs"][0]["execution_mode"] = bad
|
|
241
|
+
with pytest.raises(ProtocolError, match="execution_mode"):
|
|
242
|
+
PlanResponse.from_wire(plan)
|
|
243
|
+
absent = json.loads(json.dumps(samples["definitions_response"]))
|
|
244
|
+
del absent["definitions"][0]["execution_mode"]
|
|
245
|
+
with pytest.raises(ProtocolError, match="execution_mode"):
|
|
246
|
+
DefinitionsResponse.from_wire(absent)
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""Regression tests for Evaluator v2 PR-review fixes.
|
|
2
|
+
|
|
3
|
+
Covers: control-character rejection in bounded result text, reasoning carried on
|
|
4
|
+
non-score primary results, and the object-repr guard no longer false-rejecting
|
|
5
|
+
ordinary hex literals.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
from failproofai_sdk.evaluator.authoring import EvalResult, Metric, Score
|
|
12
|
+
from failproofai_sdk.evaluator.source import _forbid_object_reprs, UnsafeEvaluatorSource
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_bounded_rejects_c0_control_characters():
|
|
16
|
+
# NUL / ESC in reasoning would be accepted by the SDK but rejected by the
|
|
17
|
+
# server with a non-retryable 422, silently losing a successful eval.
|
|
18
|
+
with pytest.raises(ValueError):
|
|
19
|
+
EvalResult(score=Score(1.0, passed=True), reasoning="bad\x00value")
|
|
20
|
+
with pytest.raises(ValueError):
|
|
21
|
+
EvalResult(score=Score(1.0, passed=True), summary="esc\x1b[31m")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_bounded_keeps_tab_newline_cr():
|
|
25
|
+
r = EvalResult(score=Score(1.0, passed=True), reasoning="line one\nline\ttwo\r")
|
|
26
|
+
items = r.result_items("q")
|
|
27
|
+
assert items[0].reasoning == "line one\nline\ttwo\r"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_reasoning_carried_on_metric_primary():
|
|
31
|
+
# A metric-kind eval's primary result is the metric whose key == eval_key.
|
|
32
|
+
r = EvalResult(metrics={"latency": Metric(12.0)}, reasoning="slow tail")
|
|
33
|
+
items = {i.result_key: i for i in r.result_items("latency")}
|
|
34
|
+
assert items["latency"].reasoning == "slow tail"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_reasoning_not_smeared_onto_secondary_metrics():
|
|
38
|
+
r = EvalResult(
|
|
39
|
+
score=Score(1.0, passed=True),
|
|
40
|
+
metrics={"aux": Metric(3.0)},
|
|
41
|
+
reasoning="about the score",
|
|
42
|
+
)
|
|
43
|
+
items = {i.result_key: i for i in r.result_items("q")}
|
|
44
|
+
assert items["q"].reasoning == "about the score"
|
|
45
|
+
assert items["aux"].reasoning is None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_object_repr_guard_allows_plain_hex_literals():
|
|
49
|
+
# A hex colour / digest in result text must not be mistaken for a pointer repr.
|
|
50
|
+
assert _forbid_object_reprs("summary", "background 0xFFFFFF, sha 0xdeadbeef1234") == (
|
|
51
|
+
"background 0xFFFFFF, sha 0xdeadbeef1234"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_object_repr_guard_still_rejects_pointer_reprs():
|
|
56
|
+
with pytest.raises(UnsafeEvaluatorSource):
|
|
57
|
+
_forbid_object_reprs("summary", "<foo.Bar object at 0x7f9c1a2b3c4d>")
|
|
58
|
+
# ...even with the leading '<' stripped (the reshape bypass).
|
|
59
|
+
with pytest.raises(UnsafeEvaluatorSource):
|
|
60
|
+
_forbid_object_reprs("summary", "foo.Bar object at 0x7f9c1a2b3c4d>")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# ---- runtime fixes (Section 1/2 of the follow-up review) --------------------
|
|
64
|
+
import asyncio
|
|
65
|
+
from dataclasses import replace
|
|
66
|
+
|
|
67
|
+
from failproofai_sdk.evaluator import (
|
|
68
|
+
ClaimResponse, EvaluatorAPIError, PlannedRun, PlanResponse, SessionTranscript,
|
|
69
|
+
WorkerConfig, WorkerRuntime,
|
|
70
|
+
)
|
|
71
|
+
from failproofai_sdk.evaluator import RegisterResponse, HeartbeatResponse
|
|
72
|
+
import json as _json
|
|
73
|
+
from pathlib import Path as _Path
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _samples_fx():
|
|
77
|
+
return _json.loads(
|
|
78
|
+
(_Path(__file__).parent / "fixtures" / "evaluator_v2" / "contract.json").read_text()
|
|
79
|
+
)["samples"]
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class _FakeClient:
|
|
83
|
+
def __init__(self):
|
|
84
|
+
s = _samples_fx()
|
|
85
|
+
self.assignment = replace(
|
|
86
|
+
ClaimResponse.from_wire(s["claim_response"]).assignments[0],
|
|
87
|
+
definitions_url="",
|
|
88
|
+
)
|
|
89
|
+
self.session = SessionTranscript.from_wire(s["transcript_response"])
|
|
90
|
+
self.plans, self.submissions, self.heartbeats = [], [], []
|
|
91
|
+
|
|
92
|
+
def transcript(self, assignment, *, worker_id):
|
|
93
|
+
return self.session
|
|
94
|
+
|
|
95
|
+
def plan(self, assignment_id, request):
|
|
96
|
+
self.plans.append(request)
|
|
97
|
+
return PlanResponse(
|
|
98
|
+
assignment_id=assignment_id,
|
|
99
|
+
assignment_status="planned" if request.selected else "skipped",
|
|
100
|
+
runs=tuple(
|
|
101
|
+
PlannedRun(f"run-{i.eval_key}", i.eval_key, i.eval_version)
|
|
102
|
+
for i in request.selected
|
|
103
|
+
),
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
def submit_result(self, run_id, request):
|
|
107
|
+
self.submissions.append((run_id, request))
|
|
108
|
+
|
|
109
|
+
def heartbeat(self, request):
|
|
110
|
+
self.heartbeats.append(request)
|
|
111
|
+
return HeartbeatResponse(
|
|
112
|
+
lease_expires_at="2026-08-28T12:02:30.000000Z",
|
|
113
|
+
accepted_run_ids=tuple(r.evaluation_run_id for r in request.runs),
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _rt(evaluator, client):
|
|
118
|
+
return WorkerRuntime(
|
|
119
|
+
evaluator,
|
|
120
|
+
WorkerConfig(server_url="https://cloud.example", credential="x",
|
|
121
|
+
worker_id="worker-test", max_concurrency=2),
|
|
122
|
+
client=client,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def test_transcript_too_large_skips_assignment_without_crashing():
|
|
127
|
+
from failproofai_sdk.evaluator import Evaluator, EvalResult, Score
|
|
128
|
+
ev = Evaluator(name="t", version="1")
|
|
129
|
+
ev.eval("quality", version="1")(lambda s: EvalResult(score=Score(1.0, passed=True)))
|
|
130
|
+
|
|
131
|
+
class TooLarge(_FakeClient):
|
|
132
|
+
def transcript(self, assignment, *, worker_id):
|
|
133
|
+
raise EvaluatorAPIError(
|
|
134
|
+
status=413, code="transcript_too_large",
|
|
135
|
+
message="transcript exceeds 26214400 bytes", retryable=False,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
c = TooLarge()
|
|
139
|
+
# returns cleanly — no plan, no submission, no exception
|
|
140
|
+
asyncio.run(_rt(ev, c).process_assignment(c.assignment))
|
|
141
|
+
assert c.plans == [] and c.submissions == []
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def test_idempotent_replay_runs_a_definition_this_attempt_skipped():
|
|
145
|
+
from failproofai_sdk.evaluator import Evaluator, EvalResult, Score, ConditionResult
|
|
146
|
+
ev = Evaluator(name="t", version="1")
|
|
147
|
+
|
|
148
|
+
@ev.eval("quality", version="1", when=lambda s: ConditionResult(False, "nope"))
|
|
149
|
+
def quality(session):
|
|
150
|
+
return EvalResult(score=Score(1.0, passed=True))
|
|
151
|
+
|
|
152
|
+
class ReplaySkipped(_FakeClient):
|
|
153
|
+
def plan(self, assignment_id, request):
|
|
154
|
+
# This attempt selected nothing, but the server replays the first
|
|
155
|
+
# attempt's run for the now-skipped eval.
|
|
156
|
+
return PlanResponse(
|
|
157
|
+
assignment_id=assignment_id, assignment_status="planned",
|
|
158
|
+
runs=(PlannedRun("run-quality", "quality", "1"),),
|
|
159
|
+
idempotent_replay=True,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
c = ReplaySkipped()
|
|
163
|
+
asyncio.run(_rt(ev, c).process_assignment(c.assignment))
|
|
164
|
+
assert [rid for rid, _ in c.submissions] == ["run-quality"]
|