failproofai 1.0.12-beta.1 → 1.0.12
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 +4 -4
- 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.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 +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
- 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.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 +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- 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.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.nft.json +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/logout/route.js.nft.json +1 -1
- 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.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 +3 -3
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- 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/server-reference-manifest.json +14 -14
- package/.next/standalone/.next/server/app/policies/page.js +2 -2
- 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.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.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.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 +8 -8
- 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/_0tovk6q0miud6._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0trp3ycqv17cl._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1ek68lnh2daur._.js +2 -2
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4vkpxxu7._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_0_em82s16zfym._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__08ch3staq3ejw._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0bigvy9xmv2ka._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h86bl2qf0k_o._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pia5h3p8re4c._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1-ey9fgesfbc0._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__15usvs-ms-0hf._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__167mipqmjprq5._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__16gb3_92zvk5x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1avi2kypusyb_._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1r7drlqli-ado._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0b78b0j8iyv6l._.js → [root-of-the-server]__1vofkb2oq7p7e._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0eoura3_9gjb-._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1a3tubds7s7ow._.js → _0ouu886szh6yj._.js} +4 -4
- package/.next/standalone/.next/server/chunks/ssr/_0pedwblqdoh58._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1d98fyfrcaqhy._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1djqs9i27e4kg._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo5_vm5f.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_actions_get-scheduled-audit_ts_0ei9snixb5fd-._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47q0xq5w._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x33jqb6._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpcz0uxur._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_settings_02tf1h47jmhhd._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules_html-to-image_es_index_1i8oid1gc7s1f.js → node_modules_html-to-image_es_index_0tqc05n832v7a.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/package_json_[json]_cjs_013tntikdft59._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_0agdmptetetdt._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_0mdwgjisv4fub._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589gp4p60j._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_pack-cli_ts_0t7me658zqmh_._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- 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/preview-props.json +3 -3
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +23 -23
- package/.next/standalone/.next/static/chunks/{1b32-afqacaen.js → 0el9n3h9_zp5m.js} +1 -1
- package/.next/standalone/.next/static/chunks/0k7g_8x3o6gd-.js +1 -0
- package/.next/standalone/.next/static/chunks/{42e8zab314654.js → 0umzct31duk5g.js} +1 -1
- package/.next/standalone/.next/static/chunks/{24w3x2da9sk49.js → 0wobqgosirs8p.js} +1 -1
- package/.next/standalone/.next/static/chunks/12rug3jilwqzs.js +1 -0
- package/.next/standalone/.next/static/chunks/{2-tf6_7u623kd.js → 1kduj74-715jh.js} +1 -1
- package/.next/standalone/.next/static/chunks/2701amhrbsbot.js +1 -0
- package/.next/standalone/.next/static/chunks/{18433qz-8njv3.js → 27cavoc8paelg.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0_zfad4co-t2z.js → 2ve23a7keupjw.js} +1 -1
- package/.next/standalone/.next/static/chunks/3bv6xaoitjpnh.js +6 -0
- package/.next/standalone/app/actions/update-jev-config.ts +5 -3
- package/.next/standalone/app/settings/jev-panel.tsx +1 -1
- package/.next/standalone/fp-cloud-cli/CHANGELOG.md +28 -18
- package/.next/standalone/fp-cloud-cli/README.md +127 -9
- package/.next/standalone/fp-cloud-cli/fp_cli/app.py +15 -1
- package/.next/standalone/fp-cloud-cli/fp_cli/client.py +199 -34
- package/.next/standalone/fp-cloud-cli/fp_cli/cloud_output.py +523 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/_cloud.py +45 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/_write.py +3 -1
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/account_cmds.py +171 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/agent_cmds.py +80 -7
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/alerts_cmds.py +23 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/audits_cmds.py +2 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/contracts_cmds.py +67 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/dashboards_cmds.py +159 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/evaluators_cmds.py +380 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/events_cmds.py +14 -1
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/fleet_cmds.py +12 -216
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/incidents_cmds.py +82 -6
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/list_cmds.py +4 -1
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/orgs_cmds.py +17 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/policies_cmds.py +143 -1
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/queries_cmds.py +70 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/sentiment_cmds.py +49 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/sessions_cmds.py +95 -1
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/settings_cmds.py +2 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/usage_cmds.py +21 -1
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/users_cmds.py +14 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/enforcement.py +7 -53
- package/.next/standalone/fp-cloud-cli/fp_cli/errors.py +3 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/evaluator_spec.py +266 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/output.py +189 -152
- package/.next/standalone/fp-cloud-cli/fp_cli/theme.py +21 -24
- package/.next/standalone/fp-cloud-cli/skill/SKILL.md +124 -23
- package/.next/standalone/fp-cloud-cli/skill/agents/openai.yaml +2 -1
- package/.next/standalone/fp-cloud-cli/skill/references/commands.md +173 -22
- package/.next/standalone/fp-cloud-cli/tests/fake_cloud_smoke.py +312 -0
- package/.next/standalone/fp-cloud-cli/tests/test_cloud_account.py +125 -0
- package/.next/standalone/fp-cloud-cli/tests/test_cloud_agent.py +253 -0
- package/.next/standalone/fp-cloud-cli/tests/test_cloud_human_views.py +287 -0
- package/.next/standalone/fp-cloud-cli/tests/test_cloud_jev.py +32 -197
- package/.next/standalone/fp-cloud-cli/tests/test_cloud_observability.py +116 -0
- package/.next/standalone/fp-cloud-cli/tests/test_cloud_transport.py +141 -0
- package/.next/standalone/fp-cloud-cli/tests/test_commands.py +3 -3
- package/.next/standalone/fp-cloud-cli/tests/test_dashboards_agent.py +3 -3
- package/.next/standalone/fp-cloud-cli/tests/test_evaluator_authoring.py +449 -0
- package/.next/standalone/fp-cloud-cli/tests/test_facets.py +1 -1
- package/.next/standalone/fp-cloud-cli/tests/test_help_table_coverage.py +15 -0
- package/.next/standalone/fp-cloud-cli/tests/test_operator.py +1 -1
- package/.next/standalone/fp-cloud-cli/tests/test_orgs.py +1 -1
- package/.next/standalone/fp-cloud-cli/tests/test_output.py +37 -37
- package/.next/standalone/fp-cloud-cli/tests/test_round4_behaviour.py +144 -0
- package/.next/standalone/fp-cloud-cli/tests/test_screen_language.py +80 -0
- package/.next/standalone/fp-cloud-cli/tests/test_session_reevaluate.py +33 -0
- package/.next/standalone/fp-cloud-cli/tests/test_staging_parity.py +198 -0
- package/.next/standalone/fp-cloud-cli/tests/test_whoami.py +1 -1
- package/.next/standalone/hermes-plugin/__init__.py +11 -0
- package/.next/standalone/hermes-plugin/plugin.yaml +1 -1
- package/.next/standalone/package.json +11 -10
- package/.next/standalone/sdk/python/uv.lock +31 -8
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-4/package-lock.json +68 -176
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-4/package.json +2 -2
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-5/package-lock.json +4 -4
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-5/package.json +1 -1
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-6/package-lock.json +7 -7
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-6/package.json +1 -1
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-7/package-lock.json +7 -7
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-7/package.json +1 -1
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-0.3/package-lock.json +98 -115
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-0.3/package.json +3 -3
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-1/package-lock.json +4 -4
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-1/package.json +1 -1
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-dup-core/package-lock.json +4 -4
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-dup-core/package.json +1 -1
- package/.next/standalone/sdk/typescript/integration/fixtures/llamaindex-0.11/package-lock.json +149 -171
- package/.next/standalone/sdk/typescript/integration/fixtures/llamaindex-0.11/package.json +2 -2
- package/.next/standalone/sdk/typescript/integration/fixtures/llamaindex-0.12/package-lock.json +4 -4
- package/.next/standalone/sdk/typescript/integration/fixtures/llamaindex-0.12/package.json +1 -1
- package/.next/standalone/sdk/typescript/integration/fixtures/mastra-0/package-lock.json +25 -25
- package/.next/standalone/sdk/typescript/integration/fixtures/mastra-0/package.json +1 -1
- package/.next/standalone/sdk/typescript/integration/fixtures/mastra-1/package-lock.json +7 -7
- package/.next/standalone/sdk/typescript/integration/fixtures/mastra-1/package.json +1 -1
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/package-lock.json +10 -10
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/package.json +1 -1
- package/.next/standalone/sdk/typescript/integration/fixtures/runtimes/package-lock.json +7 -7
- package/.next/standalone/sdk/typescript/integration/fixtures/runtimes/package.json +1 -1
- package/.next/standalone/server.js +1 -1
- package/dist/cli.mjs +1760 -638
- package/dist/worker.mjs +426 -76
- package/hermes-plugin/__init__.py +11 -0
- package/hermes-plugin/plugin.yaml +1 -1
- package/package.json +11 -10
- package/src/audit/open-browser.ts +24 -7
- package/src/hooks/agent-roster.ts +9 -1
- package/src/hooks/agent-skill.ts +206 -0
- package/src/hooks/cloud-connection.ts +27 -51
- package/src/hooks/cloud-introspect.ts +5 -2
- package/src/hooks/cloud-login.ts +248 -0
- package/src/hooks/cloud-managed-policies.ts +54 -4
- package/src/hooks/codex-system-hooks.ts +10 -4
- package/src/hooks/config-status.ts +9 -3
- package/src/hooks/configure-wizard.ts +356 -32
- package/src/hooks/delivery-health.ts +47 -2
- package/src/hooks/effective-reviewers.ts +14 -0
- package/src/hooks/fp-config.ts +19 -0
- package/src/hooks/fp-home.ts +1 -1
- package/src/hooks/handler.ts +43 -5
- package/src/hooks/hook-activity-store.ts +7 -1
- package/src/hooks/integrations.ts +18 -3
- package/src/hooks/jev-activity.ts +117 -1
- package/src/hooks/jev-cli.ts +7 -9
- package/src/hooks/manager.ts +80 -46
- package/src/hooks/policy-evaluator.ts +17 -0
- package/src/hooks/policy-reviewability.ts +4 -2
- package/src/hooks/semantic/cloud-jev.ts +219 -28
- package/src/hooks/semantic/combine.ts +26 -1
- package/src/hooks/semantic/intent.ts +127 -24
- package/src/hooks/semantic/jev-client.ts +1 -1
- package/src/hooks/semantic/jev-config.ts +6 -27
- package/src/hooks/semantic/jev-review.ts +111 -4
- package/src/hooks/tui.ts +280 -0
- package/src/hooks/uninstall-cli.ts +12 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0588st-tpiuia._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_128ebxr83nk6g._.js +0 -3
- package/.next/standalone/.next/static/chunks/0jutamwqz8pg5.js +0 -1
- package/.next/standalone/.next/static/chunks/114hbolvrv94e.js +0 -1
- package/.next/standalone/.next/static/chunks/1p-hsi58i-6h0.js +0 -6
- package/.next/standalone/.next/static/chunks/3-pku9qwv1yfu.js +0 -1
- /package/.next/standalone/.next/static/{xq8UgF5yFxty1VWxY2hyn → D7LY1YSrw96ZxKSpxxrFw}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{xq8UgF5yFxty1VWxY2hyn → D7LY1YSrw96ZxKSpxxrFw}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{xq8UgF5yFxty1VWxY2hyn → D7LY1YSrw96ZxKSpxxrFw}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
"""Author, test and deploy managed evaluators, independently of result readers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
from datetime import datetime, timedelta, timezone
|
|
8
|
+
from typing import List, Optional
|
|
9
|
+
|
|
10
|
+
import typer
|
|
11
|
+
|
|
12
|
+
from .. import client as api, cloud_output, dates, evaluator_spec as spec, output
|
|
13
|
+
from .._context import GLOBALS_EPILOG, require_auth
|
|
14
|
+
from ..errors import ApiError, NotFoundError
|
|
15
|
+
from . import _cloud, _write
|
|
16
|
+
|
|
17
|
+
PATH = "/api/evaluator-definitions"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def definitions(cctx) -> list:
|
|
21
|
+
return _cloud.rows(api.cloud_request(cctx, "GET", PATH), "definitions")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def select(items: list, handle: str) -> dict:
|
|
25
|
+
key, _, version = handle.partition("@")
|
|
26
|
+
matches = [r for r in items if r.get("eval_key") == key and (not version or str(r.get("eval_version")) == version)]
|
|
27
|
+
if not matches:
|
|
28
|
+
raise NotFoundError(f"no evaluator {handle}; run `fp evaluators list --all`")
|
|
29
|
+
if not version:
|
|
30
|
+
enabled = [r for r in matches if r.get("enabled", True)]
|
|
31
|
+
matches = enabled or matches
|
|
32
|
+
if len(matches) != 1:
|
|
33
|
+
spec.fail(f"{handle} matches multiple versions; use KEY@VERSION")
|
|
34
|
+
return matches[0]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def list_defs(ctx: typer.Context, key: Optional[str] = typer.Option(None, "--key"),
|
|
38
|
+
all_versions: bool = typer.Option(False, "--all")) -> None:
|
|
39
|
+
"""List definitions (enabled only unless --all). Needs evaluations:read."""
|
|
40
|
+
items = definitions(require_auth(ctx.obj))
|
|
41
|
+
items = [r for r in items if (not key or r.get("eval_key") == key) and (all_versions or r.get("enabled", True))]
|
|
42
|
+
_cloud.emit(ctx.obj, items, "Evaluators")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def show(ctx: typer.Context, handle: str = typer.Argument(..., metavar="KEY[@VERSION]")) -> None:
|
|
46
|
+
"""Show one definition, including its full source."""
|
|
47
|
+
_cloud.emit(ctx.obj, select(definitions(require_auth(ctx.obj)), handle), "Evaluator")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _create(state, cctx, body, sessions, disabled=False, force=False) -> dict:
|
|
51
|
+
if sessions:
|
|
52
|
+
if spec.source_kind(body) == "jev":
|
|
53
|
+
output.warn("Jev dry-runs spend model budget.")
|
|
54
|
+
preview = api.cloud_request(cctx, "POST", f"{PATH}/dry-run", body=spec.dry_run_body(body, sessions))
|
|
55
|
+
if not state.json:
|
|
56
|
+
cloud_output.dry_run(preview)
|
|
57
|
+
if dry_run_failed(preview) and force:
|
|
58
|
+
output.warn("Dry-run failed; --force authorizes creating the evaluator anyway.")
|
|
59
|
+
else:
|
|
60
|
+
check_dry_run(preview)
|
|
61
|
+
_write.confirm(state, f"Create evaluator {body['eval_key']}@{body['eval_version']}", assume_yes=False)
|
|
62
|
+
row = api.cloud_request(cctx, "POST", PATH, body=body)
|
|
63
|
+
if not isinstance(row, dict) or not row.get("id"):
|
|
64
|
+
raise ApiError("Cloud created no identifiable evaluator; check evaluators list --all before retrying")
|
|
65
|
+
if disabled:
|
|
66
|
+
api.cloud_request(cctx, "PATCH", f"{PATH}/{row['id']}", body={"enabled": False})
|
|
67
|
+
row = {**row, "enabled": False}
|
|
68
|
+
_write.record_action("evaluator_created", resource="evaluator", mode=spec.source_kind(body), success=True)
|
|
69
|
+
return row
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def create(
|
|
73
|
+
ctx: typer.Context,
|
|
74
|
+
key: Optional[str] = typer.Option(None, "--key"),
|
|
75
|
+
name: Optional[str] = typer.Option(None, "--name"),
|
|
76
|
+
version: Optional[str] = typer.Option(None, "--version"),
|
|
77
|
+
label: Optional[List[str]] = typer.Option(None, "--label"),
|
|
78
|
+
condition: Optional[str] = typer.Option(None, "--condition", help="Python expression or @file."),
|
|
79
|
+
code: Optional[str] = typer.Option(None, "--code", help="Restricted Python expression file, or - for stdin."),
|
|
80
|
+
result_kind: Optional[str] = typer.Option(None, "--result-kind"),
|
|
81
|
+
timeout: Optional[float] = typer.Option(None, "--timeout"),
|
|
82
|
+
judge: bool = typer.Option(False, "--judge"),
|
|
83
|
+
criteria: Optional[str] = typer.Option(None, "--criteria"),
|
|
84
|
+
step: Optional[List[str]] = typer.Option(None, "--step"),
|
|
85
|
+
threshold: Optional[float] = typer.Option(None, "--threshold"),
|
|
86
|
+
jev: Optional[str] = typer.Option(None, "--jev", help="noul or score."),
|
|
87
|
+
instructions: Optional[str] = typer.Option(None, "--instructions"),
|
|
88
|
+
true: Optional[str] = typer.Option(None, "--true"),
|
|
89
|
+
false: Optional[str] = typer.Option(None, "--false"),
|
|
90
|
+
level: Optional[List[str]] = typer.Option(None, "--level", help="3–5 distinct levels, worst to best."),
|
|
91
|
+
file: Optional[str] = typer.Option(None, "--file"),
|
|
92
|
+
dry_run_on: Optional[List[str]] = typer.Option(None, "--dry-run-on"),
|
|
93
|
+
disabled: bool = typer.Option(False, "--disabled"),
|
|
94
|
+
force: bool = typer.Option(False, "--force", help="Create despite failed --dry-run-on results (review the failures first)."),
|
|
95
|
+
) -> None:
|
|
96
|
+
"""Create a code, LLM judge or Jev evaluator (enabled by default).
|
|
97
|
+
|
|
98
|
+
Needs evaluations:trigger; session dry-run also needs events:read.
|
|
99
|
+
|
|
100
|
+
Code: `fp --json evaluators create --key concise --name Concise --code concise.py --result-kind score --dry-run-on run-001`
|
|
101
|
+
|
|
102
|
+
Judge: `fp --json evaluators create --key relevance --name Relevance --judge --criteria "Grade relevance to the question" --step "Assess answer specificity" --threshold 0.7`
|
|
103
|
+
|
|
104
|
+
Jev: `fp --json evaluators create --key resolved --name Resolved --jev noul --instructions "Assess whether the request was resolved" --true "Fully resolved" --false "Not resolved" --threshold 0.8`
|
|
105
|
+
|
|
106
|
+
Spec: `fp --json evaluators create --file evaluator.json`
|
|
107
|
+
"""
|
|
108
|
+
body = spec.build(
|
|
109
|
+
key=key, name=name, version=version, label=label, condition=condition,
|
|
110
|
+
code=code, result_kind=result_kind, timeout=timeout, judge=judge, criteria=criteria,
|
|
111
|
+
step=step, threshold_value=threshold, jev=jev, instructions=instructions,
|
|
112
|
+
true=true, false=false, level=level, file=file,
|
|
113
|
+
)
|
|
114
|
+
# Validate before any request, including the judge refusal / session bound.
|
|
115
|
+
if dry_run_on:
|
|
116
|
+
spec.dry_run_body(body, dry_run_on)
|
|
117
|
+
row = _create(ctx.obj, require_auth(ctx.obj), body, dry_run_on, disabled, force)
|
|
118
|
+
if ctx.obj.json:
|
|
119
|
+
output.emit_json(row)
|
|
120
|
+
else:
|
|
121
|
+
cloud_output.evaluator({**body, **row}, "Evaluator created")
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def deploy(
|
|
125
|
+
ctx: typer.Context,
|
|
126
|
+
key: str = typer.Argument(..., metavar="EVAL_KEY"),
|
|
127
|
+
name: Optional[str] = typer.Option(None, "--name"),
|
|
128
|
+
version: Optional[str] = typer.Option(None, "--version"),
|
|
129
|
+
label: Optional[List[str]] = typer.Option(None, "--label"),
|
|
130
|
+
condition: Optional[str] = typer.Option(None, "--condition"),
|
|
131
|
+
code: Optional[str] = typer.Option(None, "--code"),
|
|
132
|
+
result_kind: Optional[str] = typer.Option(None, "--result-kind"),
|
|
133
|
+
timeout: Optional[float] = typer.Option(None, "--timeout"),
|
|
134
|
+
judge: bool = typer.Option(False, "--judge"),
|
|
135
|
+
criteria: Optional[str] = typer.Option(None, "--criteria"),
|
|
136
|
+
step: Optional[List[str]] = typer.Option(None, "--step"),
|
|
137
|
+
threshold: Optional[float] = typer.Option(None, "--threshold"),
|
|
138
|
+
jev: Optional[str] = typer.Option(None, "--jev"),
|
|
139
|
+
instructions: Optional[str] = typer.Option(None, "--instructions"),
|
|
140
|
+
true: Optional[str] = typer.Option(None, "--true"),
|
|
141
|
+
false: Optional[str] = typer.Option(None, "--false"),
|
|
142
|
+
level: Optional[List[str]] = typer.Option(None, "--level"),
|
|
143
|
+
file: Optional[str] = typer.Option(None, "--file"),
|
|
144
|
+
dry_run_on: Optional[List[str]] = typer.Option(None, "--dry-run-on"),
|
|
145
|
+
backfill: Optional[str] = typer.Option(None, "--backfill", help="Score stored sessions too, e.g. 7d (up to 90d). Judge/Jev backfills spend model budget."),
|
|
146
|
+
force: bool = typer.Option(False, "--force", help="Deploy despite failed --dry-run-on results (review the failures first)."),
|
|
147
|
+
) -> None:
|
|
148
|
+
"""Publish a NEW immutable version, then disable enabled predecessors.
|
|
149
|
+
|
|
150
|
+
Numeric versions auto-increment; for other version names pass --version.
|
|
151
|
+
A failed predecessor PATCH leaves the new version enabled and reports its id.
|
|
152
|
+
"""
|
|
153
|
+
body = spec.build(
|
|
154
|
+
key=None if file else key, name=name, version=None if file else version, label=label,
|
|
155
|
+
condition=condition, code=code, result_kind=result_kind, timeout=timeout,
|
|
156
|
+
judge=judge, criteria=criteria, step=step, threshold_value=threshold, jev=jev,
|
|
157
|
+
instructions=instructions, true=true, false=false, level=level, file=file, complete=False,
|
|
158
|
+
)
|
|
159
|
+
if file and body.get("eval_key") is not None and body["eval_key"] != key:
|
|
160
|
+
spec.fail("The spec eval_key must match the deploy argument")
|
|
161
|
+
body["eval_key"] = key
|
|
162
|
+
if version is not None:
|
|
163
|
+
body["eval_version"] = version
|
|
164
|
+
if dry_run_on:
|
|
165
|
+
spec.dry_run_body(body, dry_run_on)
|
|
166
|
+
backfill_body = None
|
|
167
|
+
if backfill:
|
|
168
|
+
if not re.fullmatch(r"[1-9][0-9]*d", backfill) or int(backfill[:-1]) > 90:
|
|
169
|
+
spec.fail("--backfill must be a duration in days from 1d to 90d")
|
|
170
|
+
now = datetime.now(timezone.utc)
|
|
171
|
+
backfill_body = {
|
|
172
|
+
"from": dates._iso(now - timedelta(days=int(backfill[:-1]))),
|
|
173
|
+
"to": dates._iso(now),
|
|
174
|
+
"eval_key": key,
|
|
175
|
+
"dry_run": False,
|
|
176
|
+
}
|
|
177
|
+
if spec.source_kind(body) != "code":
|
|
178
|
+
output.warn("Backfilling a judge/Jev evaluator spends the org's model budget.")
|
|
179
|
+
cctx = require_auth(ctx.obj)
|
|
180
|
+
existing = [r for r in definitions(cctx) if r.get("eval_key") == key]
|
|
181
|
+
if "eval_version" not in body:
|
|
182
|
+
versions = [str(r.get("eval_version", "")) for r in existing]
|
|
183
|
+
if any(not v.isdigit() for v in versions):
|
|
184
|
+
spec.fail("Non-numeric versions exist; specify a new --version")
|
|
185
|
+
body["eval_version"] = str(max([int(v) for v in versions] or [0]) + 1)
|
|
186
|
+
if any(str(r.get("eval_version")) == body["eval_version"] for r in existing):
|
|
187
|
+
spec.fail(f"Version {key}@{body['eval_version']} already exists; deploy needs a NEW version")
|
|
188
|
+
body.setdefault("display_name", existing[-1].get("display_name", key) if existing else key)
|
|
189
|
+
if existing and not file:
|
|
190
|
+
# A new version keeps the previous version's scope and labels unless a flag
|
|
191
|
+
# changes them; silently dropping the condition would widen the evaluator to
|
|
192
|
+
# every session. A --file spec states every field itself.
|
|
193
|
+
latest = max(existing, key=lambda r: str(r.get("created_at") or ""))
|
|
194
|
+
if condition is None:
|
|
195
|
+
body["condition_source"] = latest.get("condition_source")
|
|
196
|
+
if not label:
|
|
197
|
+
body["labels"] = list(latest.get("labels") or [])
|
|
198
|
+
spec.validate(body)
|
|
199
|
+
created = _create(ctx.obj, cctx, body, dry_run_on, force=force)
|
|
200
|
+
disabled_ids = []
|
|
201
|
+
try:
|
|
202
|
+
for old in existing:
|
|
203
|
+
if old.get("enabled", True):
|
|
204
|
+
api.cloud_request(cctx, "PATCH", f"{PATH}/{old['id']}", body={"enabled": False})
|
|
205
|
+
disabled_ids.append(old["id"])
|
|
206
|
+
if backfill_body:
|
|
207
|
+
api.cloud_request(cctx, "POST", "/api/evaluations/backfill", body=backfill_body)
|
|
208
|
+
except Exception as exc:
|
|
209
|
+
output.warn(f"Published {key}@{body['eval_version']} (id {created['id']}); disabled {len(disabled_ids)} predecessors. Follow-up failed; inspect evaluators list --all before retrying.")
|
|
210
|
+
raise exc
|
|
211
|
+
_write.record_action("evaluator_deployed", resource="evaluator", success=True)
|
|
212
|
+
if ctx.obj.json:
|
|
213
|
+
output.emit_json({"created": created, "disabled": disabled_ids})
|
|
214
|
+
else:
|
|
215
|
+
cloud_output.deployed({**body, **created}, [r for r in existing if r["id"] in disabled_ids])
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def draft(
|
|
219
|
+
ctx: typer.Context,
|
|
220
|
+
intent: str = typer.Argument(...),
|
|
221
|
+
kind: str = typer.Option("code", "--kind"),
|
|
222
|
+
out: str = typer.Option("evaluator.json", "--out"),
|
|
223
|
+
) -> None:
|
|
224
|
+
"""AI-compose an API spec file. Session only; review it before deployment."""
|
|
225
|
+
_cloud.session_only(ctx.obj, "evaluators draft")
|
|
226
|
+
if kind not in ("code", "judge", "jev"):
|
|
227
|
+
spec.fail("--kind must be code, judge or jev")
|
|
228
|
+
data = api.cloud_request(require_auth(ctx.obj), "POST", "/api/agent/compose-eval",
|
|
229
|
+
body={"intent": f"Author a {kind} evaluator. {intent}", "resultKind": "score"})
|
|
230
|
+
if not isinstance(data, dict):
|
|
231
|
+
spec.fail("Cloud draft was not an evaluator JSON spec")
|
|
232
|
+
body = spec.from_draft(data, kind)
|
|
233
|
+
_cloud.write_file(out, json.dumps(body, indent=2, ensure_ascii=False) + "\n")
|
|
234
|
+
_cloud.emit(ctx.obj, {"out": out, "spec": body}, "Evaluator draft")
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def dry_run(
|
|
238
|
+
ctx: typer.Context,
|
|
239
|
+
code: Optional[str] = typer.Option(None, "--code"),
|
|
240
|
+
result_kind: Optional[str] = typer.Option(None, "--result-kind"),
|
|
241
|
+
timeout: Optional[float] = typer.Option(None, "--timeout"),
|
|
242
|
+
condition: Optional[str] = typer.Option(None, "--condition"),
|
|
243
|
+
judge: bool = typer.Option(False, "--judge"),
|
|
244
|
+
criteria: Optional[str] = typer.Option(None, "--criteria"),
|
|
245
|
+
step: Optional[List[str]] = typer.Option(None, "--step"),
|
|
246
|
+
threshold: Optional[float] = typer.Option(None, "--threshold"),
|
|
247
|
+
jev: Optional[str] = typer.Option(None, "--jev"),
|
|
248
|
+
instructions: Optional[str] = typer.Option(None, "--instructions"),
|
|
249
|
+
true: Optional[str] = typer.Option(None, "--true"),
|
|
250
|
+
false: Optional[str] = typer.Option(None, "--false"),
|
|
251
|
+
level: Optional[List[str]] = typer.Option(None, "--level"),
|
|
252
|
+
file: Optional[str] = typer.Option(None, "--file"),
|
|
253
|
+
session: Optional[List[str]] = typer.Option(None, "--session"),
|
|
254
|
+
) -> None:
|
|
255
|
+
"""Compile only, or run on ≤10 sessions. Cloud refuses judge dry-runs.
|
|
256
|
+
|
|
257
|
+
Jev dry-runs execute model calls and spend budget, including before deployment.
|
|
258
|
+
"""
|
|
259
|
+
body = spec.build(
|
|
260
|
+
code=code, result_kind=result_kind, timeout=timeout, condition=condition,
|
|
261
|
+
judge=judge, criteria=criteria, step=step, threshold_value=threshold, jev=jev,
|
|
262
|
+
instructions=instructions, true=true, false=false, level=level, file=file, complete=False,
|
|
263
|
+
)
|
|
264
|
+
body = spec.dry_run_body(body, session)
|
|
265
|
+
if spec.source_kind(body) == "jev":
|
|
266
|
+
output.warn("Jev dry-runs spend model budget.")
|
|
267
|
+
result = api.cloud_request(require_auth(ctx.obj), "POST", f"{PATH}/dry-run", body=body)
|
|
268
|
+
_cloud.emit(ctx.obj, result, "Evaluator dry-run")
|
|
269
|
+
if dry_run_failed(result):
|
|
270
|
+
# Preserve the full dry-run result in --json, but do not signal success.
|
|
271
|
+
raise typer.Exit(code=1)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def dry_run_failed(result):
|
|
275
|
+
return isinstance(result, dict) and (
|
|
276
|
+
result.get("ok") is False
|
|
277
|
+
or any(r.get("ok") is False for r in result.get("runs", []) if isinstance(r, dict))
|
|
278
|
+
or result.get("summary", {}).get("failed", 0) > 0
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def check_dry_run(result):
|
|
283
|
+
if dry_run_failed(result):
|
|
284
|
+
failures = [r for r in result.get("runs", []) if r.get("ok") is False]
|
|
285
|
+
detail = "; ".join(str(r.get("error_message") or r.get("error_kind") or "run failed") for r in failures)
|
|
286
|
+
raise ApiError(f"Dry-run failed; evaluator was not created. {detail}".strip())
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def toggle(ctx, handle, enabled):
|
|
290
|
+
cctx = require_auth(ctx.obj)
|
|
291
|
+
row = select(definitions(cctx), handle)
|
|
292
|
+
_write.confirm(ctx.obj, f"{'Enable' if enabled else 'Disable'} {handle}", assume_yes=False)
|
|
293
|
+
result = api.cloud_request(cctx, "PATCH", f"{PATH}/{row['id']}", body={"enabled": enabled})
|
|
294
|
+
_write.record_action("evaluator_toggled", resource="evaluator", success=True)
|
|
295
|
+
if ctx.obj.json:
|
|
296
|
+
output.emit_json(result or {**row, "enabled": enabled})
|
|
297
|
+
else:
|
|
298
|
+
cloud_output.evaluator({**row, **(result or {}), "enabled": enabled})
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def enable(ctx: typer.Context, handle: str = typer.Argument(...)) -> None:
|
|
302
|
+
"""Enable KEY[@VERSION]."""
|
|
303
|
+
toggle(ctx, handle, True)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def disable(ctx: typer.Context, handle: str = typer.Argument(...)) -> None:
|
|
307
|
+
"""Disable KEY[@VERSION]."""
|
|
308
|
+
toggle(ctx, handle, False)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def label_def(ctx: typer.Context, handle: str = typer.Argument(...),
|
|
312
|
+
add: Optional[List[str]] = typer.Option(None, "--add"),
|
|
313
|
+
remove: Optional[List[str]] = typer.Option(None, "--remove")) -> None:
|
|
314
|
+
"""Add/remove labels on KEY[@VERSION]."""
|
|
315
|
+
if not add and not remove:
|
|
316
|
+
spec.fail("Pass --add or --remove")
|
|
317
|
+
if set(add or []) & set(remove or []):
|
|
318
|
+
spec.fail("A label cannot be both added and removed")
|
|
319
|
+
cctx = require_auth(ctx.obj)
|
|
320
|
+
row = select(definitions(cctx), handle)
|
|
321
|
+
labels = list(dict.fromkeys([v for v in row.get("labels", []) if v not in (remove or [])] + (add or [])))
|
|
322
|
+
spec.validate_labels(labels)
|
|
323
|
+
_write.confirm(ctx.obj, f"Label {handle}", assume_yes=False)
|
|
324
|
+
data = api.cloud_request(cctx, "PATCH", f"{PATH}/{row['id']}", body={"labels": labels})
|
|
325
|
+
if ctx.obj.json:
|
|
326
|
+
output.emit_json(data or {**row, "labels": labels})
|
|
327
|
+
else:
|
|
328
|
+
cloud_output.evaluator({**row, **(data or {}), "labels": labels}, "Evaluator labels")
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def instances(ctx: typer.Context) -> None:
|
|
332
|
+
"""List managed evaluator instances."""
|
|
333
|
+
_cloud.emit(ctx.obj, api.cloud_request(require_auth(ctx.obj), "GET", "/api/evaluator-instances"), "Instances")
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def schema(ctx: typer.Context) -> None:
|
|
337
|
+
"""Schema profile. Needs evaluations:trigger and events:read."""
|
|
338
|
+
_cloud.emit(ctx.obj, api.cloud_request(require_auth(ctx.obj), "GET", "/api/evaluator-schema-profile"), "Schema profile")
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def jobs(ctx: typer.Context) -> None:
|
|
342
|
+
"""List evaluation jobs."""
|
|
343
|
+
_cloud.emit(ctx.obj, api.cloud_request(require_auth(ctx.obj), "GET", "/api/evaluation-jobs"), "Evaluation jobs")
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def backfill(ctx: typer.Context, frm: str = typer.Option(..., "--from"),
|
|
347
|
+
to: str = typer.Option(..., "--to"), key: Optional[str] = typer.Option(None, "--key"),
|
|
348
|
+
dry_run: bool = typer.Option(False, "--dry-run")) -> None:
|
|
349
|
+
"""Score stored sessions an evaluator has not scored yet (at most 90 days).
|
|
350
|
+
|
|
351
|
+
Covers code, LLM judge and Jev evaluators (every enabled one, or only --key).
|
|
352
|
+
Only missing (session, evaluator) pairs are queued; --dry-run counts them.
|
|
353
|
+
Needs evaluations:trigger.
|
|
354
|
+
"""
|
|
355
|
+
try:
|
|
356
|
+
frm = dates._validate_iso(frm, "--from")
|
|
357
|
+
to = dates._validate_iso(to, "--to")
|
|
358
|
+
start = datetime.fromisoformat(frm.replace("Z", "+00:00"))
|
|
359
|
+
end = datetime.fromisoformat(to.replace("Z", "+00:00"))
|
|
360
|
+
except ValueError as exc:
|
|
361
|
+
spec.fail(str(exc))
|
|
362
|
+
if end <= start or end - start > timedelta(days=90):
|
|
363
|
+
spec.fail("Backfill needs an increasing range of at most 90 days")
|
|
364
|
+
if not dry_run:
|
|
365
|
+
output.warn("Judge and Jev evaluators in the backfill spend the org's model budget; --dry-run counts the work first.")
|
|
366
|
+
_write.confirm(ctx.obj, "Backfill evaluations", assume_yes=dry_run)
|
|
367
|
+
_cloud.emit(ctx.obj, api.cloud_request(require_auth(ctx.obj), "POST", "/api/evaluations/backfill",
|
|
368
|
+
body={"from": dates._iso(start), "to": dates._iso(end), "eval_key": key, "dry_run": dry_run}), "Evaluation backfill")
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def register(app: typer.Typer) -> None:
|
|
372
|
+
group = typer.Typer(no_args_is_help=True, rich_markup_mode="markdown", help="Author, test and deploy code, LLM-judge and Jev evaluators.", context_settings={"help_option_names": ["-h", "--help"]})
|
|
373
|
+
for name, fn in (
|
|
374
|
+
("list", list_defs), ("show", show), ("create", create), ("deploy", deploy),
|
|
375
|
+
("draft", draft), ("dry-run", dry_run), ("enable", enable), ("disable", disable),
|
|
376
|
+
("label", label_def), ("instances", instances), ("schema", schema), ("jobs", jobs),
|
|
377
|
+
("backfill", backfill),
|
|
378
|
+
):
|
|
379
|
+
group.command(name, epilog=GLOBALS_EPILOG)(fn)
|
|
380
|
+
app.add_typer(group, name="evaluators")
|
|
@@ -70,6 +70,8 @@ def events(
|
|
|
70
70
|
* `fp --json events --session-id run-001 --all` — a run's full timeline: event types + summaries (light, fast)
|
|
71
71
|
* `fp --json events --full --session-id run-001 --all | jq '.events[].payload'` — that run's raw payloads (full feed, bounded to one run)
|
|
72
72
|
"""
|
|
73
|
+
if ctx.invoked_subcommand:
|
|
74
|
+
return
|
|
73
75
|
state: AppState = ctx.obj
|
|
74
76
|
cctx = require_auth(state)
|
|
75
77
|
frm, to = resolve_dates(since, ts_from, ts_to)
|
|
@@ -155,5 +157,16 @@ def events(
|
|
|
155
157
|
output.recheck_filters_hint(active_filters)
|
|
156
158
|
|
|
157
159
|
|
|
160
|
+
def latency(ctx: typer.Context, since: Optional[str] = typer.Option(None, "--since")) -> None:
|
|
161
|
+
"""Aggregate event latency."""
|
|
162
|
+
from . import _cloud
|
|
163
|
+
frm, to = resolve_dates(since, None, None)
|
|
164
|
+
_cloud.emit(ctx.obj, api.cloud_request(require_auth(ctx.obj), "GET", "/api/events/latency_aggregate",
|
|
165
|
+
params={"ts_from": frm, "ts_to": to}), "Event latency")
|
|
166
|
+
|
|
167
|
+
|
|
158
168
|
def register(app: typer.Typer) -> None:
|
|
159
|
-
|
|
169
|
+
group = typer.Typer(invoke_without_command=True, rich_markup_mode="markdown", context_settings={"help_option_names": ["-h", "--help"]})
|
|
170
|
+
group.callback(epilog=GLOBALS_EPILOG)(events)
|
|
171
|
+
group.command("latency", epilog=GLOBALS_EPILOG)(latency)
|
|
172
|
+
app.add_typer(group, name="events")
|