create-windy 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +258 -121
- package/package.json +1 -1
- package/template/.windy-template.json +2 -2
- package/template/apps/agent-server/adk_web_agents/canonical_debug/agent.py +29 -9
- package/template/apps/agent-server/adk_web_agents/services.py +25 -0
- package/template/apps/agent-server/src/southwind_agent_server/adk_adapter.py +40 -11
- package/template/apps/agent-server/src/southwind_agent_server/adk_debug_sessions.py +325 -0
- package/template/apps/agent-server/src/southwind_agent_server/adk_model.py +70 -6
- package/template/apps/agent-server/src/southwind_agent_server/adk_tools.py +1 -1
- package/template/apps/agent-server/src/southwind_agent_server/app.py +16 -4
- package/template/apps/agent-server/src/southwind_agent_server/approval_resolution.py +30 -0
- package/template/apps/agent-server/src/southwind_agent_server/contracts.py +9 -0
- package/template/apps/agent-server/src/southwind_agent_server/deterministic.py +2 -1
- package/template/apps/agent-server/src/southwind_agent_server/engine.py +3 -1
- package/template/apps/agent-server/src/southwind_agent_server/openai_engine.py +95 -130
- package/template/apps/agent-server/src/southwind_agent_server/openai_engine_validation.py +133 -0
- package/template/apps/agent-server/src/southwind_agent_server/openai_provider.py +89 -20
- package/template/apps/agent-server/src/southwind_agent_server/postgres_report_insert.py +34 -0
- package/template/apps/agent-server/src/southwind_agent_server/postgres_report_queries.py +234 -0
- package/template/apps/agent-server/src/southwind_agent_server/postgres_report_replay.py +44 -0
- package/template/apps/agent-server/src/southwind_agent_server/postgres_report_status.py +83 -0
- package/template/apps/agent-server/src/southwind_agent_server/postgres_report_store.py +331 -0
- package/template/apps/agent-server/src/southwind_agent_server/postgres_report_summary.py +304 -0
- package/template/apps/agent-server/src/southwind_agent_server/report_lifecycle.py +217 -0
- package/template/apps/agent-server/src/southwind_agent_server/report_metrics.py +149 -0
- package/template/apps/agent-server/src/southwind_agent_server/report_presenter.py +280 -0
- package/template/apps/agent-server/src/southwind_agent_server/report_routes.py +126 -0
- package/template/apps/agent-server/src/southwind_agent_server/report_store.py +344 -0
- package/template/apps/agent-server/src/southwind_agent_server/report_types.py +111 -0
- package/template/apps/agent-server/src/southwind_agent_server/runtime_factory.py +31 -2
- package/template/apps/agent-server/src/southwind_agent_server/service.py +56 -51
- package/template/apps/agent-server/src/southwind_agent_server/signal_events.py +18 -1
- package/template/apps/agent-server/tests/test_adk_adapter.py +7 -9
- package/template/apps/agent-server/tests/test_adk_debug_sessions.py +215 -0
- package/template/apps/agent-server/tests/test_adk_web_canonical_debug.py +48 -1
- package/template/apps/agent-server/tests/test_openai_provider.py +58 -6
- package/template/apps/agent-server/tests/test_postgres_report_status.py +59 -0
- package/template/apps/agent-server/tests/test_postgres_report_summary.py +298 -0
- package/template/apps/agent-server/tests/test_provider_attempt_reporting.py +152 -0
- package/template/apps/agent-server/tests/test_report_attempt_lifecycle.py +126 -0
- package/template/apps/agent-server/tests/test_report_resilience.py +181 -0
- package/template/apps/agent-server/tests/test_report_store.py +286 -0
- package/template/apps/agent-server/tests/test_report_summary_parity.py +115 -0
- package/template/apps/agent-server/tests/test_report_terminal_sync.py +177 -0
- package/template/apps/server/src/agent/debug-session-facade-provider-failure.test.ts +269 -0
- package/template/apps/server/src/agent/debug-session-facade.test.ts +335 -0
- package/template/apps/server/src/agent/debug-session-facade.ts +302 -0
- package/template/apps/server/src/agent/debug-session-id.test.ts +37 -0
- package/template/apps/server/src/agent/debug-session-id.ts +37 -0
- package/template/apps/server/src/agent/debug-session-provider-reader.ts +54 -0
- package/template/apps/server/src/agent/debug-session-routes.test.ts +64 -0
- package/template/apps/server/src/agent/debug-session-routes.ts +73 -0
- package/template/apps/server/src/agent/debug-session-validation.ts +106 -0
- package/template/apps/server/src/agent/drizzle-run-binding-repository.ts +2 -0
- package/template/apps/server/src/agent/provider-input-governance.test.ts +18 -0
- package/template/apps/server/src/agent/provider-input-governance.ts +37 -1
- package/template/apps/server/src/agent/remote-runtime.test.ts +7 -0
- package/template/apps/server/src/agent/remote-runtime.ts +1 -0
- package/template/apps/server/src/agent/run-authorization.test.ts +98 -0
- package/template/apps/server/src/agent/run-authorization.ts +32 -1
- package/template/apps/server/src/agent/run-binding-repository.ts +1 -0
- package/template/apps/server/src/agent/run-contracts.ts +5 -0
- package/template/apps/server/src/agent/run-facade-adk-debug.test.ts +39 -0
- package/template/apps/server/src/agent/run-facade.test.ts +78 -0
- package/template/apps/server/src/agent/run-facade.ts +33 -3
- package/template/apps/server/src/agent/run-input-commitment.ts +50 -0
- package/template/apps/server/src/agent/run-input-verification.ts +41 -0
- package/template/apps/server/src/agent/runtime-bootstrap-report.test.ts +136 -0
- package/template/apps/server/src/agent/runtime-bootstrap.ts +66 -1
- package/template/apps/server/src/agent/tool-host.test.ts +1 -0
- package/template/apps/server/src/agent/unavailable-routes.ts +4 -0
- package/template/apps/server/src/ai-runtime-report/actor-directory.ts +94 -0
- package/template/apps/server/src/ai-runtime-report/actor-handle.test.ts +27 -0
- package/template/apps/server/src/ai-runtime-report/actor-handle.ts +81 -0
- package/template/apps/server/src/ai-runtime-report/contracts.ts +91 -0
- package/template/apps/server/src/ai-runtime-report/remote-parser.ts +212 -0
- package/template/apps/server/src/ai-runtime-report/remote-port.ts +138 -0
- package/template/apps/server/src/ai-runtime-report/routes.ts +172 -0
- package/template/apps/server/src/ai-runtime-report/scope.ts +63 -0
- package/template/apps/server/src/ai-runtime-report/service.test.ts +271 -0
- package/template/apps/server/src/ai-runtime-report/service.ts +206 -0
- package/template/apps/server/src/license/runtime-audit-reliability.test.ts +2 -0
- package/template/apps/server/src/license/runtime-service.test.ts +6 -0
- package/template/apps/server/src/module-host/ai-operation-port.test.ts +63 -0
- package/template/apps/server/src/module-host/ai-operation-port.ts +30 -5
- package/template/apps/server/src/module-host/debug-session-provider-registration.test.ts +162 -0
- package/template/apps/server/src/module-host/debug-session-provider-registration.ts +84 -0
- package/template/apps/server/src/module-host/initialize-contracts.ts +7 -0
- package/template/apps/server/src/module-host/initialize.ts +6 -197
- package/template/apps/server/src/module-host/request-context.ts +1 -0
- package/template/apps/server/src/module-host/scoped-host.ts +214 -0
- package/template/apps/server/src/module-host.ts +1 -0
- package/template/apps/server/src/runtime-adk-debug.test.ts +25 -1
- package/template/apps/server/src/runtime-adk-debug.ts +8 -2
- package/template/apps/server/src/system/operations.test.ts +54 -0
- package/template/apps/server/src/system/operations.ts +23 -1
- package/template/apps/server/src/system/seed.test.ts +4 -0
- package/template/apps/server/src/system/system-resource-monitoring.test.ts +82 -0
- package/template/apps/server/src/system/system-resource-monitoring.ts +300 -0
- package/template/apps/web/src/layout/AppSidebar.vue +6 -1
- package/template/apps/web/src/layout/AppSidebar.webtest.ts +2 -0
- package/template/apps/web/src/pages/auth/LoginPage.vue +6 -1
- package/template/apps/web/src/pages/settings/BrandingSettingsSection.vue +20 -3
- package/template/apps/web/src/pages/settings/BrandingSettingsSection.webtest.ts +80 -0
- package/template/apps/web/src/pages/system/AiRuntimePage.vue +129 -0
- package/template/apps/web/src/pages/system/SystemOperationsPage.vue +2 -1
- package/template/apps/web/src/pages/system/ai-runtime-presenter.ts +60 -0
- package/template/apps/web/src/pages/system/ai-runtime-presenter.webtest.ts +48 -0
- package/template/apps/web/src/pages/system/audit-time-range.ts +34 -2
- package/template/apps/web/src/pages/system/components/AuditLogFilters.vue +4 -3
- package/template/apps/web/src/pages/system/components/AuditLogFilters.webtest.ts +26 -1
- package/template/apps/web/src/pages/system/components/OperationGpuMonitor.vue +98 -0
- package/template/apps/web/src/pages/system/components/OperationGpuMonitor.webtest.ts +61 -0
- package/template/apps/web/src/pages/system/components/OperationServiceMonitor.vue +53 -0
- package/template/apps/web/src/pages/system/components/OperationServiceMonitor.webtest.ts +20 -1
- package/template/apps/web/src/pages/system/components/SystemResourcePage.webtest.ts +14 -1
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeActivityChart.vue +85 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeCharts.webtest.ts +82 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeFilters.vue +98 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeFilters.webtest.ts +27 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeRunDetailDialog.vue +168 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeRunTable.vue +130 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeSummaryCards.vue +82 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeSummaryCards.webtest.ts +102 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeTokenByModelChart.vue +101 -0
- package/template/apps/web/src/pages/system/composables/useAiRuntimeReport.ts +185 -0
- package/template/apps/web/src/pages/system/composables/useAiRuntimeReport.webtest.ts +158 -0
- package/template/apps/web/src/pages/system/composables/useSystemResource.ts +21 -7
- package/template/apps/web/src/router/error-navigation.ts +22 -0
- package/template/apps/web/src/router/index.ts +12 -23
- package/template/apps/web/src/router/platform-admin-route-bindings.ts +4 -0
- package/template/apps/web/src/services/ai-runtime-api.ts +164 -0
- package/template/apps/web/src/services/ai-runtime-api.webtest.ts +111 -0
- package/template/apps/web/src/services/operations-api.ts +28 -0
- package/template/docker-compose.agent-debug.yml +1 -1
- package/template/docs/architecture/ai-runtime.md +2 -1
- package/template/docs/architecture/module-host.md +3 -0
- package/template/docs/platform/agent-run-attestation.md +65 -0
- package/template/docs/platform/agent-runtime-observability.md +135 -0
- package/template/docs/platform/agent-runtime.md +47 -3
- package/template/docs/platform/operations-overview.md +13 -0
- package/template/docs/platform/platform-authentication.md +4 -3
- package/template/packages/config/package.json +1 -1
- package/template/packages/database/drizzle/0045_tiny_cassandra_nova.sql +65 -0
- package/template/packages/database/drizzle/0046_tranquil_living_mummy.sql +6 -0
- package/template/packages/database/drizzle/0047_oval_luke_cage.sql +18 -0
- package/template/packages/database/drizzle/meta/0045_snapshot.json +8814 -0
- package/template/packages/database/drizzle/meta/0046_snapshot.json +8820 -0
- package/template/packages/database/drizzle/meta/0047_snapshot.json +8835 -0
- package/template/packages/database/drizzle/meta/_journal.json +21 -0
- package/template/packages/database/package.json +1 -1
- package/template/packages/database/src/agent-runtime-schema.test.ts +54 -0
- package/template/packages/database/src/schema/agent-runtime.ts +114 -0
- package/template/packages/jobs/package.json +1 -1
- package/template/packages/modules/package.json +1 -1
- package/template/packages/modules/src/system-admin-routes.ts +10 -0
- package/template/packages/modules/src/system-admin-ui-catalog.ts +1 -1
- package/template/packages/modules/src/system-api-control-plane.ts +17 -1
- package/template/packages/modules/src/system-audit-actions.ts +10 -0
- package/template/packages/modules/src/system-features.ts +8 -0
- package/template/packages/modules/src/system-module-catalog.test.ts +35 -0
- package/template/packages/modules/src/system-module-catalog.ts +15 -4
- package/template/packages/modules/src/system-modules-access.test.ts +69 -0
- package/template/packages/modules/src/system-modules.ts +13 -0
- package/template/packages/modules/src/system-permissions.ts +2 -0
- package/template/packages/server-sdk/index.ts +11 -0
- package/template/packages/server-sdk/package.json +2 -1
- package/template/packages/server-sdk/src/ai-operation-port.ts +5 -0
- package/template/packages/server-sdk/src/debug-session-provider.ts +63 -0
- package/template/packages/server-sdk/src/module-host.ts +5 -0
- package/template/packages/shared/index.ts +3 -0
- package/template/packages/shared/package.json +1 -1
- package/template/packages/shared/src/ai-runtime-report.ts +134 -0
- package/template/scripts/lib/postgres-regression.ts +201 -0
package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeTokenByModelChart.vue
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { Chart } from "@antv/g2";
|
|
3
|
+
import {
|
|
4
|
+
computed,
|
|
5
|
+
onBeforeUnmount,
|
|
6
|
+
onMounted,
|
|
7
|
+
useTemplateRef,
|
|
8
|
+
watch,
|
|
9
|
+
} from "vue";
|
|
10
|
+
import type { AiRuntimeModelUsage } from "@/services/ai-runtime-api";
|
|
11
|
+
import { formatTokenCount } from "../../ai-runtime-presenter";
|
|
12
|
+
|
|
13
|
+
const props = defineProps<{ models: readonly AiRuntimeModelUsage[] }>();
|
|
14
|
+
const container = useTemplateRef<HTMLDivElement>("container");
|
|
15
|
+
const data = computed(() =>
|
|
16
|
+
props.models.map((model) => ({
|
|
17
|
+
model: model.model,
|
|
18
|
+
totalTokens: model.totalTokens,
|
|
19
|
+
inputTokens: model.inputTokens,
|
|
20
|
+
outputTokens: model.outputTokens,
|
|
21
|
+
cachedInputTokens: model.cachedInputTokens,
|
|
22
|
+
reasoningTokens: model.reasoningTokens,
|
|
23
|
+
})),
|
|
24
|
+
);
|
|
25
|
+
const hasData = computed(() =>
|
|
26
|
+
data.value.some(({ totalTokens }) => (totalTokens ?? 0) > 0),
|
|
27
|
+
);
|
|
28
|
+
let chart: Chart | undefined;
|
|
29
|
+
|
|
30
|
+
onMounted(renderChart);
|
|
31
|
+
watch(data, renderChart);
|
|
32
|
+
onBeforeUnmount(destroyChart);
|
|
33
|
+
|
|
34
|
+
async function renderChart() {
|
|
35
|
+
destroyChart();
|
|
36
|
+
if (!container.value || !hasData.value) return;
|
|
37
|
+
chart = new Chart({ container: container.value, autoFit: true, height: 300 });
|
|
38
|
+
chart.options({
|
|
39
|
+
type: "interval",
|
|
40
|
+
data: data.value,
|
|
41
|
+
encode: { x: "model", y: "totalTokens" },
|
|
42
|
+
style: { fill: "#0f766e" },
|
|
43
|
+
axis: {
|
|
44
|
+
x: { title: false, labelAutoRotate: true },
|
|
45
|
+
y: { title: "Token", labelFormatter: formatTokenCount },
|
|
46
|
+
},
|
|
47
|
+
tooltip: {
|
|
48
|
+
items: [
|
|
49
|
+
{
|
|
50
|
+
field: "totalTokens",
|
|
51
|
+
name: "总 Token",
|
|
52
|
+
valueFormatter: formatTokenCount,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
field: "inputTokens",
|
|
56
|
+
name: "输入",
|
|
57
|
+
valueFormatter: formatTokenCount,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
field: "outputTokens",
|
|
61
|
+
name: "输出",
|
|
62
|
+
valueFormatter: formatTokenCount,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
field: "cachedInputTokens",
|
|
66
|
+
name: "缓存输入(输入子集)",
|
|
67
|
+
valueFormatter: formatTokenCount,
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
field: "reasoningTokens",
|
|
71
|
+
name: "推理(输出子集)",
|
|
72
|
+
valueFormatter: formatTokenCount,
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
animate: false,
|
|
77
|
+
});
|
|
78
|
+
await chart.render();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function destroyChart() {
|
|
82
|
+
chart?.destroy();
|
|
83
|
+
chart = undefined;
|
|
84
|
+
container.value?.replaceChildren();
|
|
85
|
+
}
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
<template>
|
|
89
|
+
<div
|
|
90
|
+
v-if="hasData"
|
|
91
|
+
ref="container"
|
|
92
|
+
class="h-[300px] w-full"
|
|
93
|
+
aria-label="按模型统计的总 Token 柱状图"
|
|
94
|
+
/>
|
|
95
|
+
<p
|
|
96
|
+
v-else
|
|
97
|
+
class="grid h-[300px] place-items-center text-sm text-muted-foreground"
|
|
98
|
+
>
|
|
99
|
+
当前范围内没有已上报的模型 Token 数据。
|
|
100
|
+
</p>
|
|
101
|
+
</template>
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { onBeforeUnmount, onMounted, shallowRef, watch } from "vue";
|
|
2
|
+
import {
|
|
3
|
+
getAiRuntimeRun,
|
|
4
|
+
getAiRuntimeSummary,
|
|
5
|
+
listAiRuntimeActors,
|
|
6
|
+
listAiRuntimeRuns,
|
|
7
|
+
type AiRuntimeActor,
|
|
8
|
+
type AiRuntimePage,
|
|
9
|
+
type AiRuntimeRange,
|
|
10
|
+
type AiRuntimeRunDetail,
|
|
11
|
+
type AiRuntimeRunItem,
|
|
12
|
+
type AiRuntimeSummary,
|
|
13
|
+
} from "@/services/ai-runtime-api";
|
|
14
|
+
|
|
15
|
+
const REFRESH_INTERVAL_MS = 60_000;
|
|
16
|
+
|
|
17
|
+
export function useAiRuntimeReport() {
|
|
18
|
+
const range = shallowRef<AiRuntimeRange>("day");
|
|
19
|
+
const actorHandle = shallowRef<string>();
|
|
20
|
+
const page = shallowRef(1);
|
|
21
|
+
const pageSize = shallowRef(20);
|
|
22
|
+
const summary = shallowRef<AiRuntimeSummary>();
|
|
23
|
+
const runs = shallowRef<AiRuntimePage<AiRuntimeRunItem>>();
|
|
24
|
+
const actors = shallowRef<readonly AiRuntimeActor[]>([]);
|
|
25
|
+
const actorQuery = shallowRef("");
|
|
26
|
+
const detail = shallowRef<AiRuntimeRunDetail>();
|
|
27
|
+
const detailOpen = shallowRef(false);
|
|
28
|
+
const loading = shallowRef(false);
|
|
29
|
+
const detailLoading = shallowRef(false);
|
|
30
|
+
const error = shallowRef("");
|
|
31
|
+
const detailError = shallowRef("");
|
|
32
|
+
let requestRevision = 0;
|
|
33
|
+
let detailRequestRevision = 0;
|
|
34
|
+
let actorRequestRevision = 0;
|
|
35
|
+
let detailRequest: AbortController | undefined;
|
|
36
|
+
let timer: ReturnType<typeof setInterval> | undefined;
|
|
37
|
+
|
|
38
|
+
onMounted(async () => {
|
|
39
|
+
await Promise.all([loadActors(), reload()]);
|
|
40
|
+
timer = setInterval(() => void reload(), REFRESH_INTERVAL_MS);
|
|
41
|
+
});
|
|
42
|
+
onBeforeUnmount(() => {
|
|
43
|
+
if (timer) clearInterval(timer);
|
|
44
|
+
actorRequestRevision += 1;
|
|
45
|
+
cancelDetailRequest();
|
|
46
|
+
});
|
|
47
|
+
watch(detailOpen, (open) => {
|
|
48
|
+
if (!open) cancelDetailRequest();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
async function reload() {
|
|
52
|
+
const revision = ++requestRevision;
|
|
53
|
+
loading.value = true;
|
|
54
|
+
error.value = "";
|
|
55
|
+
const query = { range: range.value, actorHandle: actorHandle.value };
|
|
56
|
+
try {
|
|
57
|
+
const [nextSummary, nextRuns] = await Promise.all([
|
|
58
|
+
getAiRuntimeSummary(query),
|
|
59
|
+
listAiRuntimeRuns({
|
|
60
|
+
...query,
|
|
61
|
+
page: page.value,
|
|
62
|
+
pageSize: pageSize.value,
|
|
63
|
+
}),
|
|
64
|
+
]);
|
|
65
|
+
if (revision !== requestRevision) return;
|
|
66
|
+
summary.value = nextSummary;
|
|
67
|
+
runs.value = nextRuns;
|
|
68
|
+
} catch (currentError) {
|
|
69
|
+
if (revision === requestRevision) error.value = message(currentError);
|
|
70
|
+
} finally {
|
|
71
|
+
if (revision === requestRevision) loading.value = false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function loadActors(query = actorQuery.value) {
|
|
76
|
+
const revision = ++actorRequestRevision;
|
|
77
|
+
try {
|
|
78
|
+
const result = await listAiRuntimeActors(query, 1, 100);
|
|
79
|
+
if (revision === actorRequestRevision) actors.value = result.items;
|
|
80
|
+
} catch {
|
|
81
|
+
if (revision === actorRequestRevision) actors.value = [];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function searchActors(query: string) {
|
|
86
|
+
actorQuery.value = query;
|
|
87
|
+
await loadActors(query);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function setRange(value: AiRuntimeRange) {
|
|
91
|
+
if (range.value === value) return;
|
|
92
|
+
range.value = value;
|
|
93
|
+
page.value = 1;
|
|
94
|
+
void reload();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function setActor(value: string | undefined) {
|
|
98
|
+
if (actorHandle.value === value) return;
|
|
99
|
+
actorHandle.value = value;
|
|
100
|
+
page.value = 1;
|
|
101
|
+
void reload();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function setPage(value: number) {
|
|
105
|
+
if (page.value === value) return;
|
|
106
|
+
page.value = value;
|
|
107
|
+
void reload();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function setPageSize(value: number) {
|
|
111
|
+
if (pageSize.value === value) return;
|
|
112
|
+
pageSize.value = value;
|
|
113
|
+
page.value = 1;
|
|
114
|
+
void reload();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function openRun(runId: string) {
|
|
118
|
+
const revision = ++detailRequestRevision;
|
|
119
|
+
detailRequest?.abort();
|
|
120
|
+
const controller = new AbortController();
|
|
121
|
+
detailRequest = controller;
|
|
122
|
+
detail.value = undefined;
|
|
123
|
+
detailError.value = "";
|
|
124
|
+
detailOpen.value = true;
|
|
125
|
+
detailLoading.value = true;
|
|
126
|
+
try {
|
|
127
|
+
const result = await getAiRuntimeRun(runId, controller.signal);
|
|
128
|
+
if (revision === detailRequestRevision && !controller.signal.aborted) {
|
|
129
|
+
detail.value = result;
|
|
130
|
+
}
|
|
131
|
+
} catch (currentError) {
|
|
132
|
+
if (
|
|
133
|
+
revision === detailRequestRevision &&
|
|
134
|
+
!controller.signal.aborted &&
|
|
135
|
+
!isAbortError(currentError)
|
|
136
|
+
) {
|
|
137
|
+
detailError.value = message(currentError);
|
|
138
|
+
}
|
|
139
|
+
} finally {
|
|
140
|
+
if (revision === detailRequestRevision) {
|
|
141
|
+
detailLoading.value = false;
|
|
142
|
+
detailRequest = undefined;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function cancelDetailRequest() {
|
|
148
|
+
detailRequestRevision += 1;
|
|
149
|
+
detailRequest?.abort();
|
|
150
|
+
detailRequest = undefined;
|
|
151
|
+
detailLoading.value = false;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
range,
|
|
156
|
+
actorHandle,
|
|
157
|
+
page,
|
|
158
|
+
pageSize,
|
|
159
|
+
summary,
|
|
160
|
+
runs,
|
|
161
|
+
actors,
|
|
162
|
+
actorQuery,
|
|
163
|
+
detail,
|
|
164
|
+
detailOpen,
|
|
165
|
+
loading,
|
|
166
|
+
detailLoading,
|
|
167
|
+
error,
|
|
168
|
+
detailError,
|
|
169
|
+
reload,
|
|
170
|
+
searchActors,
|
|
171
|
+
setRange,
|
|
172
|
+
setActor,
|
|
173
|
+
setPage,
|
|
174
|
+
setPageSize,
|
|
175
|
+
openRun,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function message(error: unknown) {
|
|
180
|
+
return error instanceof Error ? error.message : String(error);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function isAbortError(error: unknown) {
|
|
184
|
+
return error instanceof Error && error.name === "AbortError";
|
|
185
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { flushPromises, mount } from "@vue/test-utils";
|
|
2
|
+
import { defineComponent, h } from "vue";
|
|
3
|
+
import { beforeEach, describe, expect, test, vi } from "vitest";
|
|
4
|
+
|
|
5
|
+
const mocks = vi.hoisted(() => ({
|
|
6
|
+
getAiRuntimeRun: vi.fn(),
|
|
7
|
+
getAiRuntimeSummary: vi.fn(),
|
|
8
|
+
listAiRuntimeActors: vi.fn(),
|
|
9
|
+
listAiRuntimeRuns: vi.fn(),
|
|
10
|
+
}));
|
|
11
|
+
vi.mock("@/services/ai-runtime-api", () => mocks);
|
|
12
|
+
|
|
13
|
+
import { useAiRuntimeReport } from "./useAiRuntimeReport";
|
|
14
|
+
|
|
15
|
+
describe("AI Runtime 报表请求协调", () => {
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
vi.clearAllMocks();
|
|
18
|
+
mocks.getAiRuntimeSummary.mockResolvedValue({});
|
|
19
|
+
mocks.listAiRuntimeActors.mockResolvedValue({
|
|
20
|
+
items: [],
|
|
21
|
+
total: 0,
|
|
22
|
+
page: 1,
|
|
23
|
+
pageSize: 100,
|
|
24
|
+
});
|
|
25
|
+
mocks.listAiRuntimeRuns.mockResolvedValue({
|
|
26
|
+
items: [],
|
|
27
|
+
total: 0,
|
|
28
|
+
page: 1,
|
|
29
|
+
pageSize: 20,
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("连续打开详情时取消旧请求且旧响应不能覆盖新 Run", async () => {
|
|
34
|
+
const first = deferred<{ runId: string }>();
|
|
35
|
+
const second = deferred<{ runId: string }>();
|
|
36
|
+
const signals: AbortSignal[] = [];
|
|
37
|
+
mocks.getAiRuntimeRun.mockImplementation(
|
|
38
|
+
(runId: string, signal: AbortSignal) => {
|
|
39
|
+
signals.push(signal);
|
|
40
|
+
return runId === "run_first" ? first.promise : second.promise;
|
|
41
|
+
},
|
|
42
|
+
);
|
|
43
|
+
let report!: ReturnType<typeof useAiRuntimeReport>;
|
|
44
|
+
const wrapper = mount(
|
|
45
|
+
defineComponent({
|
|
46
|
+
setup() {
|
|
47
|
+
report = useAiRuntimeReport();
|
|
48
|
+
return () => h("div");
|
|
49
|
+
},
|
|
50
|
+
}),
|
|
51
|
+
);
|
|
52
|
+
await flushPromises();
|
|
53
|
+
|
|
54
|
+
const firstRequest = report.openRun("run_first");
|
|
55
|
+
const secondRequest = report.openRun("run_second");
|
|
56
|
+
second.resolve({ runId: "run_second" });
|
|
57
|
+
await secondRequest;
|
|
58
|
+
|
|
59
|
+
expect(signals[0]?.aborted).toBe(true);
|
|
60
|
+
expect(signals[1]?.aborted).toBe(false);
|
|
61
|
+
expect(report.detail.value?.runId).toBe("run_second");
|
|
62
|
+
|
|
63
|
+
first.resolve({ runId: "run_first" });
|
|
64
|
+
await firstRequest;
|
|
65
|
+
expect(report.detail.value?.runId).toBe("run_second");
|
|
66
|
+
wrapper.unmount();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("连续搜索操作者时旧响应不能覆盖新查询", async () => {
|
|
70
|
+
const first = deferred<{
|
|
71
|
+
items: Array<{ actorHandle: string; label: string }>;
|
|
72
|
+
}>();
|
|
73
|
+
const second = deferred<{
|
|
74
|
+
items: Array<{ actorHandle: string; label: string }>;
|
|
75
|
+
}>();
|
|
76
|
+
mocks.listAiRuntimeActors.mockImplementation((query: string) => {
|
|
77
|
+
if (query === "first") return first.promise;
|
|
78
|
+
if (query === "second") return second.promise;
|
|
79
|
+
return Promise.resolve({ items: [], total: 0, page: 1, pageSize: 100 });
|
|
80
|
+
});
|
|
81
|
+
let report!: ReturnType<typeof useAiRuntimeReport>;
|
|
82
|
+
const wrapper = mount(
|
|
83
|
+
defineComponent({
|
|
84
|
+
setup() {
|
|
85
|
+
report = useAiRuntimeReport();
|
|
86
|
+
return () => h("div");
|
|
87
|
+
},
|
|
88
|
+
}),
|
|
89
|
+
);
|
|
90
|
+
await flushPromises();
|
|
91
|
+
|
|
92
|
+
const firstRequest = report.searchActors("first");
|
|
93
|
+
const secondRequest = report.searchActors("second");
|
|
94
|
+
second.resolve({
|
|
95
|
+
items: [{ actorHandle: "actor-second", label: "第二位操作者" }],
|
|
96
|
+
});
|
|
97
|
+
await secondRequest;
|
|
98
|
+
first.resolve({
|
|
99
|
+
items: [{ actorHandle: "actor-first", label: "第一位操作者" }],
|
|
100
|
+
});
|
|
101
|
+
await firstRequest;
|
|
102
|
+
|
|
103
|
+
expect(report.actorQuery.value).toBe("second");
|
|
104
|
+
expect(report.actors.value).toEqual([
|
|
105
|
+
{ actorHandle: "actor-second", label: "第二位操作者" },
|
|
106
|
+
]);
|
|
107
|
+
wrapper.unmount();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("旧操作者搜索晚失败时不能清空新查询结果", async () => {
|
|
111
|
+
const first = deferred<{
|
|
112
|
+
items: Array<{ actorHandle: string; label: string }>;
|
|
113
|
+
}>();
|
|
114
|
+
const second = deferred<{
|
|
115
|
+
items: Array<{ actorHandle: string; label: string }>;
|
|
116
|
+
}>();
|
|
117
|
+
mocks.listAiRuntimeActors.mockImplementation((query: string) => {
|
|
118
|
+
if (query === "first") return first.promise;
|
|
119
|
+
if (query === "second") return second.promise;
|
|
120
|
+
return Promise.resolve({ items: [], total: 0, page: 1, pageSize: 100 });
|
|
121
|
+
});
|
|
122
|
+
let report!: ReturnType<typeof useAiRuntimeReport>;
|
|
123
|
+
const wrapper = mount(
|
|
124
|
+
defineComponent({
|
|
125
|
+
setup() {
|
|
126
|
+
report = useAiRuntimeReport();
|
|
127
|
+
return () => h("div");
|
|
128
|
+
},
|
|
129
|
+
}),
|
|
130
|
+
);
|
|
131
|
+
await flushPromises();
|
|
132
|
+
|
|
133
|
+
const firstRequest = report.searchActors("first");
|
|
134
|
+
const secondRequest = report.searchActors("second");
|
|
135
|
+
second.resolve({
|
|
136
|
+
items: [{ actorHandle: "actor-second", label: "第二位操作者" }],
|
|
137
|
+
});
|
|
138
|
+
await secondRequest;
|
|
139
|
+
first.reject(new Error("旧查询失败"));
|
|
140
|
+
await firstRequest;
|
|
141
|
+
|
|
142
|
+
expect(report.actorQuery.value).toBe("second");
|
|
143
|
+
expect(report.actors.value).toEqual([
|
|
144
|
+
{ actorHandle: "actor-second", label: "第二位操作者" },
|
|
145
|
+
]);
|
|
146
|
+
wrapper.unmount();
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
function deferred<T>() {
|
|
151
|
+
let resolve!: (value: T) => void;
|
|
152
|
+
let reject!: (reason?: unknown) => void;
|
|
153
|
+
const promise = new Promise<T>((complete, fail) => {
|
|
154
|
+
resolve = complete;
|
|
155
|
+
reject = fail;
|
|
156
|
+
});
|
|
157
|
+
return { promise, reject, resolve };
|
|
158
|
+
}
|
|
@@ -15,6 +15,17 @@ import {
|
|
|
15
15
|
type SystemRecord,
|
|
16
16
|
updateSystemResource,
|
|
17
17
|
} from "@/services/system-api";
|
|
18
|
+
// @windy-module system.audit begin
|
|
19
|
+
import { defaultAuditFilters } from "../audit-time-range";
|
|
20
|
+
// @windy-module system.audit end
|
|
21
|
+
|
|
22
|
+
interface SystemResourceFilters {
|
|
23
|
+
q?: string;
|
|
24
|
+
actorId?: string;
|
|
25
|
+
action?: string;
|
|
26
|
+
occurredAfter?: string;
|
|
27
|
+
occurredBefore?: string;
|
|
28
|
+
}
|
|
18
29
|
|
|
19
30
|
export interface UseSystemResourceOptions {
|
|
20
31
|
apiResource: string;
|
|
@@ -32,13 +43,9 @@ export function useSystemResource(options: UseSystemResourceOptions) {
|
|
|
32
43
|
const pageSize = shallowRef(DEFAULT_PAGE_SIZE);
|
|
33
44
|
const q = shallowRef("");
|
|
34
45
|
const status = shallowRef("");
|
|
35
|
-
const auditFilters = shallowRef<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
action?: string;
|
|
39
|
-
occurredAfter?: string;
|
|
40
|
-
occurredBefore?: string;
|
|
41
|
-
}>({});
|
|
46
|
+
const auditFilters = shallowRef<SystemResourceFilters>(
|
|
47
|
+
initialResourceFilters(options.apiResource),
|
|
48
|
+
);
|
|
42
49
|
const exportOptions = shallowRef<{ includeScheduler?: boolean }>({});
|
|
43
50
|
const loading = shallowRef(false);
|
|
44
51
|
const saving = shallowRef(false);
|
|
@@ -258,6 +265,13 @@ export function useSystemResource(options: UseSystemResourceOptions) {
|
|
|
258
265
|
};
|
|
259
266
|
}
|
|
260
267
|
|
|
268
|
+
function initialResourceFilters(_apiResource: string): SystemResourceFilters {
|
|
269
|
+
// @windy-module system.audit begin
|
|
270
|
+
if (_apiResource === "audit-logs") return defaultAuditFilters();
|
|
271
|
+
// @windy-module system.audit end
|
|
272
|
+
return {};
|
|
273
|
+
}
|
|
274
|
+
|
|
261
275
|
function downloadJson(filename: string, payload: unknown) {
|
|
262
276
|
const blob = new Blob([JSON.stringify(payload, null, 2)], {
|
|
263
277
|
type: "application/json",
|
|
@@ -39,6 +39,28 @@ export function safeErrorReturnTo(value: string): string {
|
|
|
39
39
|
return target.startsWith("/error/") ? "/" : target;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
export function deniedRoute(from: string, reason: string) {
|
|
43
|
+
return {
|
|
44
|
+
name: "access-denied",
|
|
45
|
+
query: { from, reason },
|
|
46
|
+
replace: true,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function serviceUnavailableRoute(from: string, reason: string) {
|
|
51
|
+
return {
|
|
52
|
+
name: "service-unavailable",
|
|
53
|
+
query: { from, reason },
|
|
54
|
+
replace: true,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function requestIdFrom(value: unknown): string | undefined {
|
|
59
|
+
if (typeof value !== "string") return undefined;
|
|
60
|
+
const requestId = value.trim();
|
|
61
|
+
return requestId && requestId.length <= 128 ? requestId : undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
42
64
|
function scheduleErrorNavigation(
|
|
43
65
|
router: Router,
|
|
44
66
|
target: Parameters<Router["replace"]>[0],
|
|
@@ -18,6 +18,9 @@ import LicensePage from "@/pages/license/LicensePage.vue";
|
|
|
18
18
|
// @windy-module system.operations begin
|
|
19
19
|
import SystemOperationsPage from "@/pages/system/SystemOperationsPage.vue";
|
|
20
20
|
// @windy-module system.operations end
|
|
21
|
+
// @windy-module system.agent begin
|
|
22
|
+
import AiRuntimePage from "@/pages/system/AiRuntimePage.vue";
|
|
23
|
+
// @windy-module system.agent end
|
|
21
24
|
// @windy-module system.notification begin
|
|
22
25
|
import SystemNotificationsPage from "@/pages/system/SystemNotificationsPage.vue";
|
|
23
26
|
// @windy-module system.notification end
|
|
@@ -40,7 +43,12 @@ import { useLicenseRuntime } from "@/composables/useLicenseRuntime";
|
|
|
40
43
|
import { evaluateWebAccess, type WebAccessSnapshot } from "@/layout/navigation";
|
|
41
44
|
import { isApiErrorStatus } from "@/services/http";
|
|
42
45
|
import { loginRoute, safeReturnTo } from "./auth-navigation";
|
|
43
|
-
import {
|
|
46
|
+
import {
|
|
47
|
+
deniedRoute,
|
|
48
|
+
requestIdFrom,
|
|
49
|
+
safeErrorReturnTo,
|
|
50
|
+
serviceUnavailableRoute,
|
|
51
|
+
} from "./error-navigation";
|
|
44
52
|
import {
|
|
45
53
|
assertNoRouteCollisions,
|
|
46
54
|
materializeBusinessAdminRoutes,
|
|
@@ -88,6 +96,9 @@ const platformAdminPageComponents: Partial<
|
|
|
88
96
|
// @windy-module system.operations begin
|
|
89
97
|
operations: SystemOperationsPage,
|
|
90
98
|
// @windy-module system.operations end
|
|
99
|
+
// @windy-module system.agent begin
|
|
100
|
+
"ai-runtime": AiRuntimePage,
|
|
101
|
+
// @windy-module system.agent end
|
|
91
102
|
// @windy-module system.configuration begin
|
|
92
103
|
configuration: ModuleConfigurationPage,
|
|
93
104
|
// @windy-module system.configuration end
|
|
@@ -323,26 +334,4 @@ export function createAppRouter(
|
|
|
323
334
|
return appRouter;
|
|
324
335
|
}
|
|
325
336
|
|
|
326
|
-
function deniedRoute(from: string, reason: string) {
|
|
327
|
-
return {
|
|
328
|
-
name: "access-denied",
|
|
329
|
-
query: { from, reason },
|
|
330
|
-
replace: true,
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
function serviceUnavailableRoute(from: string, reason: string) {
|
|
335
|
-
return {
|
|
336
|
-
name: "service-unavailable",
|
|
337
|
-
query: { from, reason },
|
|
338
|
-
replace: true,
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
function requestIdFrom(value: unknown): string | undefined {
|
|
343
|
-
if (typeof value !== "string") return undefined;
|
|
344
|
-
const requestId = value.trim();
|
|
345
|
-
return requestId && requestId.length <= 128 ? requestId : undefined;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
337
|
export const router = createAppRouter();
|
|
@@ -7,6 +7,7 @@ export type PlatformAdminPageKey =
|
|
|
7
7
|
| "license"
|
|
8
8
|
| "notification"
|
|
9
9
|
| "operations"
|
|
10
|
+
| "ai-runtime"
|
|
10
11
|
| "configuration";
|
|
11
12
|
|
|
12
13
|
export interface PlatformAdminRouteBinding {
|
|
@@ -52,6 +53,9 @@ export const platformAdminRouteBindings: readonly PlatformAdminRouteBinding[] =
|
|
|
52
53
|
// @windy-module system.operations begin
|
|
53
54
|
{ routeKey: "system.ops", page: "operations" },
|
|
54
55
|
// @windy-module system.operations end
|
|
56
|
+
// @windy-module system.agent begin
|
|
57
|
+
{ routeKey: "system.ai-runtime", page: "ai-runtime" },
|
|
58
|
+
// @windy-module system.agent end
|
|
55
59
|
// @windy-module system.configuration begin
|
|
56
60
|
{ routeKey: "system.config", page: "configuration" },
|
|
57
61
|
// @windy-module system.configuration end
|