agentevo-cloud 0.1.0__py3-none-any.whl
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.
- agentevo_cloud/__init__.py +452 -0
- agentevo_cloud/analytics.py +238 -0
- agentevo_cloud/app.py +2091 -0
- agentevo_cloud/cli.py +124 -0
- agentevo_cloud/config.py +108 -0
- agentevo_cloud/db.py +442 -0
- agentevo_cloud/devices.py +786 -0
- agentevo_cloud/events.py +363 -0
- agentevo_cloud/jobs.py +553 -0
- agentevo_cloud/managed.py +325 -0
- agentevo_cloud/metering.py +264 -0
- agentevo_cloud/oidc.py +165 -0
- agentevo_cloud/openapi.py +44 -0
- agentevo_cloud/promote.py +215 -0
- agentevo_cloud/recall.py +449 -0
- agentevo_cloud/records.py +209 -0
- agentevo_cloud/revoke.py +213 -0
- agentevo_cloud/screen.py +240 -0
- agentevo_cloud/sync.py +343 -0
- agentevo_cloud/tenants.py +593 -0
- agentevo_cloud/tokens.py +388 -0
- agentevo_cloud/webhooks.py +730 -0
- agentevo_cloud/workers.py +985 -0
- agentevo_cloud-0.1.0.dist-info/METADATA +127 -0
- agentevo_cloud-0.1.0.dist-info/RECORD +28 -0
- agentevo_cloud-0.1.0.dist-info/WHEEL +4 -0
- agentevo_cloud-0.1.0.dist-info/entry_points.txt +2 -0
- agentevo_cloud-0.1.0.dist-info/licenses/LICENSE +90 -0
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
"""Agentevo SaaS control plane and managed intelligence APIs."""
|
|
2
|
+
|
|
3
|
+
from agentevo_cloud.analytics import (
|
|
4
|
+
FRESHNESS_HORIZON_DAYS as FRESHNESS_HORIZON_DAYS,
|
|
5
|
+
AnalyticsSummary as AnalyticsSummary,
|
|
6
|
+
summarize as summarize,
|
|
7
|
+
)
|
|
8
|
+
from agentevo_cloud.app import create_app as create_app
|
|
9
|
+
from agentevo_cloud.config import Settings as Settings
|
|
10
|
+
from agentevo_cloud.db import SCHEMA_VERSION as SCHEMA_VERSION, Database as Database
|
|
11
|
+
from agentevo_cloud.devices import (
|
|
12
|
+
DEVICE_POLL_INTERVAL as DEVICE_POLL_INTERVAL,
|
|
13
|
+
KEY_FORMAT_ED25519 as KEY_FORMAT_ED25519,
|
|
14
|
+
KEY_FORMAT_PEM as KEY_FORMAT_PEM,
|
|
15
|
+
MAX_KEY_ID_LENGTH as MAX_KEY_ID_LENGTH,
|
|
16
|
+
Approver as Approver,
|
|
17
|
+
DeviceAuthorization as DeviceAuthorization,
|
|
18
|
+
DeviceFlow as DeviceFlow,
|
|
19
|
+
DevicePoll as DevicePoll,
|
|
20
|
+
DeviceRecord as DeviceRecord,
|
|
21
|
+
RepoLink as RepoLink,
|
|
22
|
+
approve_device_code as approve_device_code,
|
|
23
|
+
deny_device_code as deny_device_code,
|
|
24
|
+
exchange_ci_token as exchange_ci_token,
|
|
25
|
+
find_repo as find_repo,
|
|
26
|
+
get_device_key as get_device_key,
|
|
27
|
+
link_repo as link_repo,
|
|
28
|
+
list_device_keys as list_device_keys,
|
|
29
|
+
list_devices as list_devices,
|
|
30
|
+
list_repos as list_repos,
|
|
31
|
+
poll_device_flow as poll_device_flow,
|
|
32
|
+
register_device as register_device,
|
|
33
|
+
revoke_device as revoke_device,
|
|
34
|
+
start_device_flow as start_device_flow,
|
|
35
|
+
unlink_repo as unlink_repo,
|
|
36
|
+
)
|
|
37
|
+
from agentevo_cloud.events import (
|
|
38
|
+
EVENT_BATCH_LIMIT as EVENT_BATCH_LIMIT,
|
|
39
|
+
EVENT_BATCH_MAX_BYTES as EVENT_BATCH_MAX_BYTES,
|
|
40
|
+
EVENT_PAYLOAD_MAX_BYTES as EVENT_PAYLOAD_MAX_BYTES,
|
|
41
|
+
BatchReceipt as BatchReceipt,
|
|
42
|
+
ingest_batch as ingest_batch,
|
|
43
|
+
parse_event as parse_event,
|
|
44
|
+
)
|
|
45
|
+
from agentevo_cloud.jobs import (
|
|
46
|
+
DRIVE_BATCH_LIMIT as DRIVE_BATCH_LIMIT,
|
|
47
|
+
TERMINAL_STATES as TERMINAL_STATES,
|
|
48
|
+
DriveReport as DriveReport,
|
|
49
|
+
Job as Job,
|
|
50
|
+
JobState as JobState,
|
|
51
|
+
JobType as JobType,
|
|
52
|
+
Worker as Worker,
|
|
53
|
+
WorkerOutcome as WorkerOutcome,
|
|
54
|
+
cancel_job as cancel_job,
|
|
55
|
+
create_job as create_job,
|
|
56
|
+
drive_due as drive_due,
|
|
57
|
+
get_job as get_job,
|
|
58
|
+
list_jobs as list_jobs,
|
|
59
|
+
)
|
|
60
|
+
from agentevo_cloud.managed import (
|
|
61
|
+
ManagedObject as ManagedObject,
|
|
62
|
+
get_managed as get_managed,
|
|
63
|
+
index_accepted as index_accepted,
|
|
64
|
+
latest_managed as latest_managed,
|
|
65
|
+
list_managed as list_managed,
|
|
66
|
+
)
|
|
67
|
+
from agentevo_cloud.metering import (
|
|
68
|
+
QUOTA_LIMIT_HEADER as QUOTA_LIMIT_HEADER,
|
|
69
|
+
QUOTA_REMAINING_HEADER as QUOTA_REMAINING_HEADER,
|
|
70
|
+
QUOTA_RESET_HEADER as QUOTA_RESET_HEADER,
|
|
71
|
+
QUOTA_WARNING_HEADER as QUOTA_WARNING_HEADER,
|
|
72
|
+
RETRY_AFTER_HEADER as RETRY_AFTER_HEADER,
|
|
73
|
+
Usage as Usage,
|
|
74
|
+
check_quota as check_quota,
|
|
75
|
+
check_rate_limit as check_rate_limit,
|
|
76
|
+
current_cycle as current_cycle,
|
|
77
|
+
cycle_reset as cycle_reset,
|
|
78
|
+
is_billable as is_billable,
|
|
79
|
+
quota_headers as quota_headers,
|
|
80
|
+
quota_warning as quota_warning,
|
|
81
|
+
record_billable as record_billable,
|
|
82
|
+
record_checked as record_checked,
|
|
83
|
+
retry_after_for as retry_after_for,
|
|
84
|
+
seconds_until as seconds_until,
|
|
85
|
+
unrecord_billable as unrecord_billable,
|
|
86
|
+
usage_for as usage_for,
|
|
87
|
+
)
|
|
88
|
+
from agentevo_cloud.oidc import (
|
|
89
|
+
CLOCK_SKEW_SECONDS as CLOCK_SKEW_SECONDS,
|
|
90
|
+
JWKS_TIMEOUT_SECONDS as JWKS_TIMEOUT_SECONDS,
|
|
91
|
+
JwksFetcher as JwksFetcher,
|
|
92
|
+
VerifiedClaims as VerifiedClaims,
|
|
93
|
+
fetch_jwks as fetch_jwks,
|
|
94
|
+
verify_jwt as verify_jwt,
|
|
95
|
+
)
|
|
96
|
+
from agentevo_cloud.promote import (
|
|
97
|
+
VerifyTally as VerifyTally,
|
|
98
|
+
promote_managed as promote_managed,
|
|
99
|
+
)
|
|
100
|
+
from agentevo_cloud.recall import (
|
|
101
|
+
DEFAULT_MAX_OBJECTS as DEFAULT_MAX_OBJECTS,
|
|
102
|
+
DEFAULT_MAX_TOKENS as DEFAULT_MAX_TOKENS,
|
|
103
|
+
MAX_RECALL_OBJECTS as MAX_RECALL_OBJECTS,
|
|
104
|
+
MAX_RECALL_TOKENS as MAX_RECALL_TOKENS,
|
|
105
|
+
RecallHit as RecallHit,
|
|
106
|
+
RecallQuery as RecallQuery,
|
|
107
|
+
RecallResult as RecallResult,
|
|
108
|
+
parse_environment as parse_environment,
|
|
109
|
+
parse_query as parse_query,
|
|
110
|
+
recall as recall,
|
|
111
|
+
)
|
|
112
|
+
from agentevo_cloud.records import (
|
|
113
|
+
MAX_AUDIT_LIMIT as MAX_AUDIT_LIMIT,
|
|
114
|
+
AuditEntry as AuditEntry,
|
|
115
|
+
IdempotentReplay as IdempotentReplay,
|
|
116
|
+
audit as audit,
|
|
117
|
+
idempotency_lookup as idempotency_lookup,
|
|
118
|
+
idempotency_store as idempotency_store,
|
|
119
|
+
list_audit as list_audit,
|
|
120
|
+
)
|
|
121
|
+
from agentevo_cloud.revoke import (
|
|
122
|
+
RevokeResult as RevokeResult,
|
|
123
|
+
revoke_object as revoke_object,
|
|
124
|
+
)
|
|
125
|
+
from agentevo_cloud.screen import (
|
|
126
|
+
MAX_EMBEDDING_DIMS as MAX_EMBEDDING_DIMS,
|
|
127
|
+
RAW_TOP_LEVEL_KEYS as RAW_TOP_LEVEL_KEYS,
|
|
128
|
+
TOMBSTONE_REASON_MAX as TOMBSTONE_REASON_MAX,
|
|
129
|
+
assert_no_secrets as assert_no_secrets,
|
|
130
|
+
check_body_shape as check_body_shape,
|
|
131
|
+
check_declared_classification as check_declared_classification,
|
|
132
|
+
)
|
|
133
|
+
from agentevo_cloud.sync import (
|
|
134
|
+
checked_pull_args as checked_pull_args,
|
|
135
|
+
parse_envelope as parse_envelope,
|
|
136
|
+
pull_page as pull_page,
|
|
137
|
+
push_batch as push_batch,
|
|
138
|
+
store_server_delta as store_server_delta,
|
|
139
|
+
verify_envelope_signature as verify_envelope_signature,
|
|
140
|
+
)
|
|
141
|
+
from agentevo_cloud.tenants import (
|
|
142
|
+
ADMIN_ROLE as ADMIN_ROLE,
|
|
143
|
+
BACKUP_RETENTION_DAYS as BACKUP_RETENTION_DAYS,
|
|
144
|
+
ELEVATED_SCOPES as ELEVATED_SCOPES,
|
|
145
|
+
FREE_MONTHLY_QUOTA as FREE_MONTHLY_QUOTA,
|
|
146
|
+
GRANTABLE_SCOPES as GRANTABLE_SCOPES,
|
|
147
|
+
MEMBER_ROLE as MEMBER_ROLE,
|
|
148
|
+
OWNER_ROLE as OWNER_ROLE,
|
|
149
|
+
PLAN_QUOTAS as PLAN_QUOTAS,
|
|
150
|
+
PRO_MONTHLY_QUOTA as PRO_MONTHLY_QUOTA,
|
|
151
|
+
ROLES as ROLES,
|
|
152
|
+
SYNC_TERMS_VERSION as SYNC_TERMS_VERSION,
|
|
153
|
+
TEAM_MONTHLY_QUOTA as TEAM_MONTHLY_QUOTA,
|
|
154
|
+
Entitlements as Entitlements,
|
|
155
|
+
Membership as Membership,
|
|
156
|
+
SyncConsent as SyncConsent,
|
|
157
|
+
SyncMode as SyncMode,
|
|
158
|
+
Tenant as Tenant,
|
|
159
|
+
Workspace as Workspace,
|
|
160
|
+
WorkspaceDeletion as WorkspaceDeletion,
|
|
161
|
+
WorkspaceExport as WorkspaceExport,
|
|
162
|
+
add_membership as add_membership,
|
|
163
|
+
create_tenant as create_tenant,
|
|
164
|
+
create_workspace as create_workspace,
|
|
165
|
+
delete_workspace_data as delete_workspace_data,
|
|
166
|
+
entitlements_for as entitlements_for,
|
|
167
|
+
export_workspace_data as export_workspace_data,
|
|
168
|
+
get_membership as get_membership,
|
|
169
|
+
get_sync_mode as get_sync_mode,
|
|
170
|
+
get_tenant as get_tenant,
|
|
171
|
+
list_workspaces as list_workspaces,
|
|
172
|
+
require_managed as require_managed,
|
|
173
|
+
require_searchable as require_searchable,
|
|
174
|
+
searchable_workspaces as searchable_workspaces,
|
|
175
|
+
set_plan as set_plan,
|
|
176
|
+
set_sync_mode as set_sync_mode,
|
|
177
|
+
tenant_managed as tenant_managed,
|
|
178
|
+
)
|
|
179
|
+
from agentevo_cloud.tokens import (
|
|
180
|
+
ACCESS_KIND as ACCESS_KIND,
|
|
181
|
+
ACCESS_PREFIX as ACCESS_PREFIX,
|
|
182
|
+
CI_KIND as CI_KIND,
|
|
183
|
+
CI_PREFIX as CI_PREFIX,
|
|
184
|
+
PAT_KIND as PAT_KIND,
|
|
185
|
+
PAT_PREFIX as PAT_PREFIX,
|
|
186
|
+
REFRESH_KIND as REFRESH_KIND,
|
|
187
|
+
REFRESH_PREFIX as REFRESH_PREFIX,
|
|
188
|
+
IssuedToken as IssuedToken,
|
|
189
|
+
Principal as Principal,
|
|
190
|
+
TokenRecord as TokenRecord,
|
|
191
|
+
hash_token as hash_token,
|
|
192
|
+
issue_access as issue_access,
|
|
193
|
+
issue_ci as issue_ci,
|
|
194
|
+
issue_pat as issue_pat,
|
|
195
|
+
issue_refresh as issue_refresh,
|
|
196
|
+
list_tokens as list_tokens,
|
|
197
|
+
revoke_token as revoke_token,
|
|
198
|
+
rotate_refresh as rotate_refresh,
|
|
199
|
+
verify_token as verify_token,
|
|
200
|
+
)
|
|
201
|
+
from agentevo_cloud.webhooks import (
|
|
202
|
+
EVENT_ID_HEADER as EVENT_ID_HEADER,
|
|
203
|
+
MAX_DELIVERY_LIMIT as MAX_DELIVERY_LIMIT,
|
|
204
|
+
MAX_WEBHOOK_CONFIGS as MAX_WEBHOOK_CONFIGS,
|
|
205
|
+
ROTATION_GRACE_SECONDS as ROTATION_GRACE_SECONDS,
|
|
206
|
+
SIGNATURE_HEADER as SIGNATURE_HEADER,
|
|
207
|
+
WEBHOOK_EVENTS as WEBHOOK_EVENTS,
|
|
208
|
+
WILDCARD_EVENT as WILDCARD_EVENT,
|
|
209
|
+
Delivery as Delivery,
|
|
210
|
+
IssuedSecret as IssuedSecret,
|
|
211
|
+
Sender as Sender,
|
|
212
|
+
WebhookConfig as WebhookConfig,
|
|
213
|
+
build_event as build_event,
|
|
214
|
+
create_config as create_config,
|
|
215
|
+
deliver_to_config as deliver_to_config,
|
|
216
|
+
dispatch as dispatch,
|
|
217
|
+
get_config as get_config,
|
|
218
|
+
list_configs as list_configs,
|
|
219
|
+
list_deliveries as list_deliveries,
|
|
220
|
+
replay_delivery as replay_delivery,
|
|
221
|
+
revoke_config as revoke_config,
|
|
222
|
+
rotate_config as rotate_config,
|
|
223
|
+
sign as sign,
|
|
224
|
+
urllib_sender as urllib_sender,
|
|
225
|
+
verify_signature as verify_signature,
|
|
226
|
+
verify_with_rotation as verify_with_rotation,
|
|
227
|
+
)
|
|
228
|
+
from agentevo_cloud.workers import (
|
|
229
|
+
DEFAULT_COMPILE_PROJECT as DEFAULT_COMPILE_PROJECT,
|
|
230
|
+
VERIFICATION_VALIDITY_DAYS as VERIFICATION_VALIDITY_DAYS,
|
|
231
|
+
WORKERS as WORKERS,
|
|
232
|
+
compile_experiences_worker as compile_experiences_worker,
|
|
233
|
+
compile_workflow_worker as compile_workflow_worker,
|
|
234
|
+
ensure_workspace as ensure_workspace,
|
|
235
|
+
parse_bindings as parse_bindings,
|
|
236
|
+
parse_canary_steps as parse_canary_steps,
|
|
237
|
+
parse_fallbacks as parse_fallbacks,
|
|
238
|
+
parse_required_str as parse_required_str,
|
|
239
|
+
parse_rollback as parse_rollback,
|
|
240
|
+
parse_skill_refs as parse_skill_refs,
|
|
241
|
+
verify_worker as verify_worker,
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
__all__ = [
|
|
245
|
+
"ACCESS_KIND",
|
|
246
|
+
"ACCESS_PREFIX",
|
|
247
|
+
"ADMIN_ROLE",
|
|
248
|
+
"BACKUP_RETENTION_DAYS",
|
|
249
|
+
"CI_KIND",
|
|
250
|
+
"CI_PREFIX",
|
|
251
|
+
"CLOCK_SKEW_SECONDS",
|
|
252
|
+
"DEFAULT_COMPILE_PROJECT",
|
|
253
|
+
"DEFAULT_MAX_OBJECTS",
|
|
254
|
+
"DEFAULT_MAX_TOKENS",
|
|
255
|
+
"DEVICE_POLL_INTERVAL",
|
|
256
|
+
"DRIVE_BATCH_LIMIT",
|
|
257
|
+
"ELEVATED_SCOPES",
|
|
258
|
+
"EVENT_BATCH_LIMIT",
|
|
259
|
+
"EVENT_BATCH_MAX_BYTES",
|
|
260
|
+
"EVENT_ID_HEADER",
|
|
261
|
+
"EVENT_PAYLOAD_MAX_BYTES",
|
|
262
|
+
"FREE_MONTHLY_QUOTA",
|
|
263
|
+
"FRESHNESS_HORIZON_DAYS",
|
|
264
|
+
"GRANTABLE_SCOPES",
|
|
265
|
+
"JWKS_TIMEOUT_SECONDS",
|
|
266
|
+
"KEY_FORMAT_ED25519",
|
|
267
|
+
"KEY_FORMAT_PEM",
|
|
268
|
+
"MAX_AUDIT_LIMIT",
|
|
269
|
+
"MAX_DELIVERY_LIMIT",
|
|
270
|
+
"MAX_EMBEDDING_DIMS",
|
|
271
|
+
"MAX_KEY_ID_LENGTH",
|
|
272
|
+
"MAX_RECALL_OBJECTS",
|
|
273
|
+
"MAX_RECALL_TOKENS",
|
|
274
|
+
"MAX_WEBHOOK_CONFIGS",
|
|
275
|
+
"MEMBER_ROLE",
|
|
276
|
+
"OWNER_ROLE",
|
|
277
|
+
"PAT_KIND",
|
|
278
|
+
"PAT_PREFIX",
|
|
279
|
+
"PLAN_QUOTAS",
|
|
280
|
+
"PRO_MONTHLY_QUOTA",
|
|
281
|
+
"QUOTA_LIMIT_HEADER",
|
|
282
|
+
"QUOTA_REMAINING_HEADER",
|
|
283
|
+
"QUOTA_RESET_HEADER",
|
|
284
|
+
"QUOTA_WARNING_HEADER",
|
|
285
|
+
"RAW_TOP_LEVEL_KEYS",
|
|
286
|
+
"REFRESH_KIND",
|
|
287
|
+
"REFRESH_PREFIX",
|
|
288
|
+
"RETRY_AFTER_HEADER",
|
|
289
|
+
"ROLES",
|
|
290
|
+
"ROTATION_GRACE_SECONDS",
|
|
291
|
+
"SCHEMA_VERSION",
|
|
292
|
+
"SIGNATURE_HEADER",
|
|
293
|
+
"SYNC_TERMS_VERSION",
|
|
294
|
+
"TEAM_MONTHLY_QUOTA",
|
|
295
|
+
"TERMINAL_STATES",
|
|
296
|
+
"TOMBSTONE_REASON_MAX",
|
|
297
|
+
"VERIFICATION_VALIDITY_DAYS",
|
|
298
|
+
"WEBHOOK_EVENTS",
|
|
299
|
+
"WILDCARD_EVENT",
|
|
300
|
+
"WORKERS",
|
|
301
|
+
"AnalyticsSummary",
|
|
302
|
+
"Approver",
|
|
303
|
+
"AuditEntry",
|
|
304
|
+
"BatchReceipt",
|
|
305
|
+
"Database",
|
|
306
|
+
"Delivery",
|
|
307
|
+
"DeviceAuthorization",
|
|
308
|
+
"DeviceFlow",
|
|
309
|
+
"DevicePoll",
|
|
310
|
+
"DeviceRecord",
|
|
311
|
+
"DriveReport",
|
|
312
|
+
"Entitlements",
|
|
313
|
+
"IdempotentReplay",
|
|
314
|
+
"IssuedSecret",
|
|
315
|
+
"IssuedToken",
|
|
316
|
+
"Job",
|
|
317
|
+
"JobState",
|
|
318
|
+
"JobType",
|
|
319
|
+
"JwksFetcher",
|
|
320
|
+
"ManagedObject",
|
|
321
|
+
"Membership",
|
|
322
|
+
"Principal",
|
|
323
|
+
"RecallHit",
|
|
324
|
+
"RecallQuery",
|
|
325
|
+
"RecallResult",
|
|
326
|
+
"RepoLink",
|
|
327
|
+
"RevokeResult",
|
|
328
|
+
"Sender",
|
|
329
|
+
"Settings",
|
|
330
|
+
"SyncConsent",
|
|
331
|
+
"SyncMode",
|
|
332
|
+
"Tenant",
|
|
333
|
+
"TokenRecord",
|
|
334
|
+
"Usage",
|
|
335
|
+
"VerifiedClaims",
|
|
336
|
+
"VerifyTally",
|
|
337
|
+
"WebhookConfig",
|
|
338
|
+
"Worker",
|
|
339
|
+
"WorkerOutcome",
|
|
340
|
+
"Workspace",
|
|
341
|
+
"WorkspaceDeletion",
|
|
342
|
+
"WorkspaceExport",
|
|
343
|
+
"add_membership",
|
|
344
|
+
"approve_device_code",
|
|
345
|
+
"assert_no_secrets",
|
|
346
|
+
"audit",
|
|
347
|
+
"build_event",
|
|
348
|
+
"cancel_job",
|
|
349
|
+
"check_body_shape",
|
|
350
|
+
"check_declared_classification",
|
|
351
|
+
"check_quota",
|
|
352
|
+
"check_rate_limit",
|
|
353
|
+
"checked_pull_args",
|
|
354
|
+
"compile_experiences_worker",
|
|
355
|
+
"compile_workflow_worker",
|
|
356
|
+
"create_app",
|
|
357
|
+
"create_config",
|
|
358
|
+
"create_job",
|
|
359
|
+
"create_tenant",
|
|
360
|
+
"create_workspace",
|
|
361
|
+
"current_cycle",
|
|
362
|
+
"cycle_reset",
|
|
363
|
+
"delete_workspace_data",
|
|
364
|
+
"deliver_to_config",
|
|
365
|
+
"deny_device_code",
|
|
366
|
+
"dispatch",
|
|
367
|
+
"drive_due",
|
|
368
|
+
"ensure_workspace",
|
|
369
|
+
"entitlements_for",
|
|
370
|
+
"exchange_ci_token",
|
|
371
|
+
"export_workspace_data",
|
|
372
|
+
"fetch_jwks",
|
|
373
|
+
"find_repo",
|
|
374
|
+
"get_config",
|
|
375
|
+
"get_device_key",
|
|
376
|
+
"get_job",
|
|
377
|
+
"get_managed",
|
|
378
|
+
"get_membership",
|
|
379
|
+
"get_sync_mode",
|
|
380
|
+
"get_tenant",
|
|
381
|
+
"hash_token",
|
|
382
|
+
"idempotency_lookup",
|
|
383
|
+
"idempotency_store",
|
|
384
|
+
"index_accepted",
|
|
385
|
+
"ingest_batch",
|
|
386
|
+
"is_billable",
|
|
387
|
+
"issue_access",
|
|
388
|
+
"issue_ci",
|
|
389
|
+
"issue_pat",
|
|
390
|
+
"issue_refresh",
|
|
391
|
+
"latest_managed",
|
|
392
|
+
"link_repo",
|
|
393
|
+
"list_audit",
|
|
394
|
+
"list_configs",
|
|
395
|
+
"list_deliveries",
|
|
396
|
+
"list_device_keys",
|
|
397
|
+
"list_devices",
|
|
398
|
+
"list_jobs",
|
|
399
|
+
"list_managed",
|
|
400
|
+
"list_repos",
|
|
401
|
+
"list_tokens",
|
|
402
|
+
"list_workspaces",
|
|
403
|
+
"parse_bindings",
|
|
404
|
+
"parse_canary_steps",
|
|
405
|
+
"parse_envelope",
|
|
406
|
+
"parse_environment",
|
|
407
|
+
"parse_event",
|
|
408
|
+
"parse_fallbacks",
|
|
409
|
+
"parse_query",
|
|
410
|
+
"parse_required_str",
|
|
411
|
+
"parse_rollback",
|
|
412
|
+
"parse_skill_refs",
|
|
413
|
+
"poll_device_flow",
|
|
414
|
+
"promote_managed",
|
|
415
|
+
"pull_page",
|
|
416
|
+
"push_batch",
|
|
417
|
+
"quota_headers",
|
|
418
|
+
"quota_warning",
|
|
419
|
+
"recall",
|
|
420
|
+
"record_billable",
|
|
421
|
+
"record_checked",
|
|
422
|
+
"register_device",
|
|
423
|
+
"replay_delivery",
|
|
424
|
+
"require_managed",
|
|
425
|
+
"require_searchable",
|
|
426
|
+
"retry_after_for",
|
|
427
|
+
"revoke_config",
|
|
428
|
+
"revoke_device",
|
|
429
|
+
"revoke_object",
|
|
430
|
+
"revoke_token",
|
|
431
|
+
"rotate_config",
|
|
432
|
+
"rotate_refresh",
|
|
433
|
+
"searchable_workspaces",
|
|
434
|
+
"seconds_until",
|
|
435
|
+
"set_plan",
|
|
436
|
+
"set_sync_mode",
|
|
437
|
+
"sign",
|
|
438
|
+
"start_device_flow",
|
|
439
|
+
"store_server_delta",
|
|
440
|
+
"summarize",
|
|
441
|
+
"tenant_managed",
|
|
442
|
+
"unlink_repo",
|
|
443
|
+
"unrecord_billable",
|
|
444
|
+
"urllib_sender",
|
|
445
|
+
"usage_for",
|
|
446
|
+
"verify_envelope_signature",
|
|
447
|
+
"verify_jwt",
|
|
448
|
+
"verify_signature",
|
|
449
|
+
"verify_token",
|
|
450
|
+
"verify_with_rotation",
|
|
451
|
+
"verify_worker",
|
|
452
|
+
]
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"""Team analytics: tenant-scoped aggregates over the managed index.
|
|
2
|
+
|
|
3
|
+
Spec grounding: PRD §15 ("record improved execution metrics"), PRD §16
|
|
4
|
+
Measurement ("Baseline vs learned-run evaluation visible in dashboard/API"),
|
|
5
|
+
and BRD ("outcome analytics must all be real"). This endpoint reports only
|
|
6
|
+
what the cloud stores: managed-object registry state, event outcomes,
|
|
7
|
+
async-job outcomes, and verification freshness. It serves counts, never
|
|
8
|
+
bodies.
|
|
9
|
+
|
|
10
|
+
Spec-silent choices: the route is billable (the metering exception list is
|
|
11
|
+
closed and analytics is not on it); any tenant role may read it (aggregates
|
|
12
|
+
carry no object bodies); the ``workspace`` filter scopes the object and
|
|
13
|
+
verification sections while events and jobs stay tenant-wide (those rows
|
|
14
|
+
are not workspace-attributed), labeled honestly in ``tenant_wide``.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import sqlite3
|
|
20
|
+
from dataclasses import dataclass
|
|
21
|
+
from datetime import UTC, datetime, timedelta
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
from agentevo_core.errors import AgentevoError, ErrorCode
|
|
25
|
+
|
|
26
|
+
#: Freshness horizon: verified objects expiring within this window count as
|
|
27
|
+
#: "expiring" rather than "fresh".
|
|
28
|
+
FRESHNESS_HORIZON_DAYS = 7
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass(frozen=True)
|
|
32
|
+
class AnalyticsSummary:
|
|
33
|
+
"""One analytics snapshot (counts only, no object bodies)."""
|
|
34
|
+
|
|
35
|
+
tenant_id: str
|
|
36
|
+
workspace: str | None
|
|
37
|
+
generated_at: str
|
|
38
|
+
objects_total: int
|
|
39
|
+
objects_versions: int
|
|
40
|
+
objects_by_state: dict[str, int]
|
|
41
|
+
objects_by_type: dict[str, int]
|
|
42
|
+
objects_by_risk: dict[str, int]
|
|
43
|
+
objects_tombstoned: int
|
|
44
|
+
verification_fresh: int
|
|
45
|
+
verification_expiring: int
|
|
46
|
+
verification_expired: int
|
|
47
|
+
verification_never: int
|
|
48
|
+
events_total: int
|
|
49
|
+
events_by_type: dict[str, int]
|
|
50
|
+
jobs_total: int
|
|
51
|
+
jobs_by_state: dict[str, int]
|
|
52
|
+
jobs_by_type: dict[str, int]
|
|
53
|
+
tenant_wide: tuple[str, ...]
|
|
54
|
+
|
|
55
|
+
def to_dict(self) -> dict[str, Any]:
|
|
56
|
+
"""JSON shape served by GET /v1/analytics/summary."""
|
|
57
|
+
return {
|
|
58
|
+
"tenant_id": self.tenant_id,
|
|
59
|
+
"workspace": self.workspace,
|
|
60
|
+
"generated_at": self.generated_at,
|
|
61
|
+
"objects": {
|
|
62
|
+
"total": self.objects_total,
|
|
63
|
+
"versions": self.objects_versions,
|
|
64
|
+
"by_state": self.objects_by_state,
|
|
65
|
+
"by_type": self.objects_by_type,
|
|
66
|
+
"by_risk": self.objects_by_risk,
|
|
67
|
+
"tombstoned": self.objects_tombstoned,
|
|
68
|
+
},
|
|
69
|
+
"verification": {
|
|
70
|
+
"fresh": self.verification_fresh,
|
|
71
|
+
"expiring_7d": self.verification_expiring,
|
|
72
|
+
"expired": self.verification_expired,
|
|
73
|
+
"never": self.verification_never,
|
|
74
|
+
},
|
|
75
|
+
"events": {"total": self.events_total, "by_type": self.events_by_type},
|
|
76
|
+
"jobs": {
|
|
77
|
+
"total": self.jobs_total,
|
|
78
|
+
"by_state": self.jobs_by_state,
|
|
79
|
+
"by_type": self.jobs_by_type,
|
|
80
|
+
},
|
|
81
|
+
"tenant_wide": list(self.tenant_wide),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def summarize(
|
|
86
|
+
conn: sqlite3.Connection,
|
|
87
|
+
tenant_id: str,
|
|
88
|
+
*,
|
|
89
|
+
workspace: str | None = None,
|
|
90
|
+
workspaces: tuple[str, ...] = (),
|
|
91
|
+
now: datetime | None = None,
|
|
92
|
+
) -> AnalyticsSummary:
|
|
93
|
+
"""Aggregate one tenant's managed-index counts (404 on unknown workspace).
|
|
94
|
+
|
|
95
|
+
An explicit ``workspace`` scopes the object/verification sections to it;
|
|
96
|
+
otherwise token ``workspaces`` scope them when set. Events and jobs are
|
|
97
|
+
always tenant-wide (their rows carry no workspace).
|
|
98
|
+
"""
|
|
99
|
+
moment = now or datetime.now(UTC)
|
|
100
|
+
slugs: tuple[str, ...] | None
|
|
101
|
+
if workspace is not None:
|
|
102
|
+
row = conn.execute(
|
|
103
|
+
"SELECT 1 FROM workspaces WHERE tenant_id = ? AND slug = ?",
|
|
104
|
+
(tenant_id, workspace),
|
|
105
|
+
).fetchone()
|
|
106
|
+
if row is None:
|
|
107
|
+
raise AgentevoError(ErrorCode.NOT_FOUND, f"unknown workspace: {workspace}", {})
|
|
108
|
+
slugs = (workspace,)
|
|
109
|
+
elif workspaces:
|
|
110
|
+
slugs = workspaces
|
|
111
|
+
else:
|
|
112
|
+
slugs = None
|
|
113
|
+
latest = _latest_objects(conn, tenant_id, slugs)
|
|
114
|
+
by_state: dict[str, int] = {}
|
|
115
|
+
by_type: dict[str, int] = {}
|
|
116
|
+
by_risk: dict[str, int] = {}
|
|
117
|
+
fresh = expiring = expired = never = 0
|
|
118
|
+
horizon = moment + timedelta(days=FRESHNESS_HORIZON_DAYS)
|
|
119
|
+
for item in latest:
|
|
120
|
+
by_state[item["state"]] = by_state.get(item["state"], 0) + 1
|
|
121
|
+
by_type[item["object_type"]] = by_type.get(item["object_type"], 0) + 1
|
|
122
|
+
by_risk[item["risk_class"]] = by_risk.get(item["risk_class"], 0) + 1
|
|
123
|
+
bucket = _freshness_bucket(item["verified_at"], item["valid_until"], moment, horizon)
|
|
124
|
+
if bucket == "fresh":
|
|
125
|
+
fresh += 1
|
|
126
|
+
elif bucket == "expiring":
|
|
127
|
+
expiring += 1
|
|
128
|
+
elif bucket == "expired":
|
|
129
|
+
expired += 1
|
|
130
|
+
else:
|
|
131
|
+
never += 1
|
|
132
|
+
event_rows = conn.execute(
|
|
133
|
+
"SELECT event_type, COUNT(*) AS total FROM managed_events"
|
|
134
|
+
" WHERE tenant_id = ? GROUP BY event_type ORDER BY event_type ASC",
|
|
135
|
+
(tenant_id,),
|
|
136
|
+
).fetchall()
|
|
137
|
+
job_rows = conn.execute(
|
|
138
|
+
"SELECT job_type, state, COUNT(*) AS total FROM jobs"
|
|
139
|
+
" WHERE tenant_id = ? GROUP BY job_type, state",
|
|
140
|
+
(tenant_id,),
|
|
141
|
+
).fetchall()
|
|
142
|
+
jobs_by_state: dict[str, int] = {}
|
|
143
|
+
jobs_by_type: dict[str, int] = {}
|
|
144
|
+
jobs_total = 0
|
|
145
|
+
for row in job_rows:
|
|
146
|
+
jobs_by_state[row["state"]] = jobs_by_state.get(row["state"], 0) + row["total"]
|
|
147
|
+
jobs_by_type[row["job_type"]] = jobs_by_type.get(row["job_type"], 0) + row["total"]
|
|
148
|
+
jobs_total += row["total"]
|
|
149
|
+
return AnalyticsSummary(
|
|
150
|
+
tenant_id=tenant_id,
|
|
151
|
+
workspace=workspace,
|
|
152
|
+
generated_at=moment.isoformat(),
|
|
153
|
+
objects_total=len(latest),
|
|
154
|
+
objects_versions=_count_versions(conn, tenant_id, slugs),
|
|
155
|
+
objects_by_state=by_state,
|
|
156
|
+
objects_by_type=by_type,
|
|
157
|
+
objects_by_risk=by_risk,
|
|
158
|
+
objects_tombstoned=_count_tombstoned(conn, tenant_id, slugs),
|
|
159
|
+
verification_fresh=fresh,
|
|
160
|
+
verification_expiring=expiring,
|
|
161
|
+
verification_expired=expired,
|
|
162
|
+
verification_never=never,
|
|
163
|
+
events_total=sum(row["total"] for row in event_rows),
|
|
164
|
+
events_by_type={row["event_type"]: row["total"] for row in event_rows},
|
|
165
|
+
jobs_total=jobs_total,
|
|
166
|
+
jobs_by_state=jobs_by_state,
|
|
167
|
+
jobs_by_type=jobs_by_type,
|
|
168
|
+
tenant_wide=("events", "jobs") if workspace is not None else (),
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _latest_objects(
|
|
173
|
+
conn: sqlite3.Connection, tenant_id: str, slugs: tuple[str, ...] | None
|
|
174
|
+
) -> list[dict[str, Any]]:
|
|
175
|
+
"""Latest kind=object version per object_id (the live registry view)."""
|
|
176
|
+
clauses = ["tenant_id = ?", "kind = 'object'"]
|
|
177
|
+
params: list[str] = [tenant_id]
|
|
178
|
+
if slugs is not None:
|
|
179
|
+
placeholders = ", ".join("?" for _ in slugs)
|
|
180
|
+
clauses.append(f"workspace_id IN ({placeholders})")
|
|
181
|
+
params.extend(slugs)
|
|
182
|
+
where = " AND ".join(clauses)
|
|
183
|
+
query = (
|
|
184
|
+
"SELECT m.state, m.object_type, m.risk_class, m.verified_at, m.valid_until" # noqa: S608
|
|
185
|
+
" FROM managed_objects AS m JOIN (SELECT object_id, MAX(version) AS version"
|
|
186
|
+
f" FROM managed_objects WHERE {where} GROUP BY object_id) AS latest"
|
|
187
|
+
" ON m.object_id = latest.object_id AND m.version = latest.version"
|
|
188
|
+
" WHERE m.tenant_id = ? AND m.kind = 'object'"
|
|
189
|
+
)
|
|
190
|
+
rows = conn.execute(query, (*params, tenant_id)).fetchall()
|
|
191
|
+
return [dict(row) for row in rows]
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _count_versions(conn: sqlite3.Connection, tenant_id: str, slugs: tuple[str, ...] | None) -> int:
|
|
195
|
+
"""Stored kind=object versions (every version, not just latest)."""
|
|
196
|
+
clauses = ["tenant_id = ?", "kind = 'object'"]
|
|
197
|
+
params: list[str] = [tenant_id]
|
|
198
|
+
if slugs is not None:
|
|
199
|
+
placeholders = ", ".join("?" for _ in slugs)
|
|
200
|
+
clauses.append(f"workspace_id IN ({placeholders})")
|
|
201
|
+
params.extend(slugs)
|
|
202
|
+
query = "SELECT COUNT(*) AS total FROM managed_objects WHERE " + " AND ".join(clauses) # noqa: S608
|
|
203
|
+
row = conn.execute(query, params).fetchone()
|
|
204
|
+
return int(row["total"])
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _count_tombstoned(
|
|
208
|
+
conn: sqlite3.Connection, tenant_id: str, slugs: tuple[str, ...] | None
|
|
209
|
+
) -> int:
|
|
210
|
+
"""Distinct object ids carrying a tombstone row."""
|
|
211
|
+
clauses = ["tenant_id = ?", "kind = 'tombstone'"]
|
|
212
|
+
params: list[str] = [tenant_id]
|
|
213
|
+
if slugs is not None:
|
|
214
|
+
placeholders = ", ".join("?" for _ in slugs)
|
|
215
|
+
clauses.append(f"workspace_id IN ({placeholders})")
|
|
216
|
+
params.extend(slugs)
|
|
217
|
+
query = (
|
|
218
|
+
"SELECT COUNT(DISTINCT object_id) AS total FROM managed_objects WHERE " # noqa: S608
|
|
219
|
+
+ " AND ".join(clauses)
|
|
220
|
+
)
|
|
221
|
+
row = conn.execute(query, params).fetchone()
|
|
222
|
+
return int(row["total"])
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _freshness_bucket(
|
|
226
|
+
verified_at: str | None, valid_until: str | None, moment: datetime, horizon: datetime
|
|
227
|
+
) -> str:
|
|
228
|
+
"""Freshness bucket mirroring recall's validity gate (usable ⟺ end > now)."""
|
|
229
|
+
if verified_at is None:
|
|
230
|
+
return "never"
|
|
231
|
+
if valid_until is None:
|
|
232
|
+
return "fresh"
|
|
233
|
+
end = datetime.fromisoformat(valid_until)
|
|
234
|
+
if end <= moment:
|
|
235
|
+
return "expired"
|
|
236
|
+
if end <= horizon:
|
|
237
|
+
return "expiring"
|
|
238
|
+
return "fresh"
|