create-windy 0.4.1 → 0.5.1
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 +292 -125
- 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
|
@@ -14,9 +14,22 @@ from .adk_failure import AdkFailureState
|
|
|
14
14
|
from .contracts import JsonValue
|
|
15
15
|
from .engine import EngineRunRequest, SafeEngineError
|
|
16
16
|
from .multimodal import attach_artifacts
|
|
17
|
-
from .
|
|
17
|
+
from .openai_engine_validation import (
|
|
18
|
+
attempt_outcome,
|
|
19
|
+
cancelled,
|
|
20
|
+
parse_arguments,
|
|
21
|
+
)
|
|
18
22
|
from .openai_provider import OpenAiCompatibleProvider, ProviderTurn
|
|
19
23
|
from .provider_governance import ProviderRequestGovernance
|
|
24
|
+
from .report_lifecycle import (
|
|
25
|
+
finish_attempt_report_safely,
|
|
26
|
+
start_attempt_report_safely,
|
|
27
|
+
)
|
|
28
|
+
from .report_store import (
|
|
29
|
+
AgentRuntimeReportStore,
|
|
30
|
+
InMemoryAgentRuntimeReportStore,
|
|
31
|
+
)
|
|
32
|
+
from .report_types import UsageFacts
|
|
20
33
|
|
|
21
34
|
|
|
22
35
|
class GovernedOpenAiLlm(BaseLlm):
|
|
@@ -27,6 +40,7 @@ class GovernedOpenAiLlm(BaseLlm):
|
|
|
27
40
|
_governance: ProviderRequestGovernance = PrivateAttr()
|
|
28
41
|
_cancellation: asyncio.Event = PrivateAttr()
|
|
29
42
|
_failures: AdkFailureState = PrivateAttr()
|
|
43
|
+
_report_store: AgentRuntimeReportStore = PrivateAttr()
|
|
30
44
|
_attempt: int = PrivateAttr(default=0)
|
|
31
45
|
|
|
32
46
|
def __init__(
|
|
@@ -36,6 +50,7 @@ class GovernedOpenAiLlm(BaseLlm):
|
|
|
36
50
|
governance: ProviderRequestGovernance,
|
|
37
51
|
cancellation: asyncio.Event,
|
|
38
52
|
failures: AdkFailureState,
|
|
53
|
+
report_store: AgentRuntimeReportStore | None = None,
|
|
39
54
|
) -> None:
|
|
40
55
|
super().__init__(model=provider.config.model)
|
|
41
56
|
self._request = request
|
|
@@ -43,6 +58,7 @@ class GovernedOpenAiLlm(BaseLlm):
|
|
|
43
58
|
self._governance = governance
|
|
44
59
|
self._cancellation = cancellation
|
|
45
60
|
self._failures = failures
|
|
61
|
+
self._report_store = report_store or InMemoryAgentRuntimeReportStore()
|
|
46
62
|
|
|
47
63
|
async def generate_content_async(
|
|
48
64
|
self,
|
|
@@ -50,6 +66,9 @@ class GovernedOpenAiLlm(BaseLlm):
|
|
|
50
66
|
stream: bool = False,
|
|
51
67
|
) -> AsyncGenerator[LlmResponse, None]:
|
|
52
68
|
del stream
|
|
69
|
+
report_attempt = None
|
|
70
|
+
finalized = False
|
|
71
|
+
turn: ProviderTurn | None = None
|
|
53
72
|
try:
|
|
54
73
|
cancelled(self._cancellation)
|
|
55
74
|
self._attempt += 1
|
|
@@ -59,6 +78,16 @@ class GovernedOpenAiLlm(BaseLlm):
|
|
|
59
78
|
"Agent Tool 调用轮次超过上限",
|
|
60
79
|
retryable=False,
|
|
61
80
|
)
|
|
81
|
+
report_attempt = await start_attempt_report_safely(
|
|
82
|
+
self._report_store,
|
|
83
|
+
self._request.run_id,
|
|
84
|
+
self._attempt,
|
|
85
|
+
self._provider.config.deployment_key,
|
|
86
|
+
self._provider.config.model,
|
|
87
|
+
)
|
|
88
|
+
attempt_sequence = (
|
|
89
|
+
report_attempt.sequence if report_attempt else self._attempt
|
|
90
|
+
)
|
|
62
91
|
messages = attach_artifacts(
|
|
63
92
|
adk_messages(llm_request, self._request.instructions),
|
|
64
93
|
self._request.artifacts,
|
|
@@ -67,7 +96,7 @@ class GovernedOpenAiLlm(BaseLlm):
|
|
|
67
96
|
deployment_key=self._provider.config.deployment_key,
|
|
68
97
|
operation_key=self._request.operation_key,
|
|
69
98
|
run_id=self._request.run_id,
|
|
70
|
-
attempt=
|
|
99
|
+
attempt=attempt_sequence,
|
|
71
100
|
messages=messages,
|
|
72
101
|
tools=self._request.allowed_tools,
|
|
73
102
|
policy=self._request.provider_data_policy,
|
|
@@ -96,11 +125,42 @@ class GovernedOpenAiLlm(BaseLlm):
|
|
|
96
125
|
turn,
|
|
97
126
|
{tool.key for tool in self._request.allowed_tools},
|
|
98
127
|
)
|
|
99
|
-
|
|
128
|
+
await finish_attempt_report_safely(
|
|
129
|
+
self._report_store,
|
|
130
|
+
report_attempt,
|
|
131
|
+
"completed",
|
|
132
|
+
turn.usage,
|
|
133
|
+
reported_model=turn.reported_model,
|
|
134
|
+
)
|
|
135
|
+
finalized = True
|
|
136
|
+
async for response in responses_for_turn(
|
|
137
|
+
turn,
|
|
138
|
+
attempt_sequence,
|
|
139
|
+
):
|
|
100
140
|
yield response
|
|
101
141
|
except SafeEngineError as error:
|
|
142
|
+
if not finalized:
|
|
143
|
+
await finish_attempt_report_safely(
|
|
144
|
+
self._report_store,
|
|
145
|
+
report_attempt,
|
|
146
|
+
attempt_outcome(error),
|
|
147
|
+
turn.usage if turn else UsageFacts(),
|
|
148
|
+
reported_model=turn.reported_model if turn else None,
|
|
149
|
+
safe_error_code=error.code,
|
|
150
|
+
)
|
|
102
151
|
self._failures.capture(error)
|
|
103
152
|
raise
|
|
153
|
+
except Exception:
|
|
154
|
+
if not finalized:
|
|
155
|
+
await finish_attempt_report_safely(
|
|
156
|
+
self._report_store,
|
|
157
|
+
report_attempt,
|
|
158
|
+
"failed",
|
|
159
|
+
turn.usage if turn else UsageFacts(),
|
|
160
|
+
reported_model=turn.reported_model if turn else None,
|
|
161
|
+
safe_error_code="RUNTIME_UNAVAILABLE",
|
|
162
|
+
)
|
|
163
|
+
raise
|
|
104
164
|
|
|
105
165
|
|
|
106
166
|
def adk_messages(
|
|
@@ -179,9 +239,13 @@ def validate_tool_calls(turn: ProviderTurn, allowed: set[str]) -> None:
|
|
|
179
239
|
|
|
180
240
|
async def responses_for_turn(
|
|
181
241
|
turn: ProviderTurn,
|
|
242
|
+
attempt_sequence: int,
|
|
182
243
|
) -> AsyncGenerator[LlmResponse, None]:
|
|
183
|
-
usage = turn.usage
|
|
184
|
-
metadata = {
|
|
244
|
+
usage = turn.usage.event_value()
|
|
245
|
+
metadata = {
|
|
246
|
+
"southwindUsage": usage,
|
|
247
|
+
"southwindAttemptSequence": attempt_sequence,
|
|
248
|
+
}
|
|
185
249
|
if turn.tool_calls:
|
|
186
250
|
yield LlmResponse(
|
|
187
251
|
content=types.Content(
|
|
@@ -222,7 +286,7 @@ async def responses_for_turn(
|
|
|
222
286
|
|
|
223
287
|
|
|
224
288
|
def adk_usage(
|
|
225
|
-
usage: dict[str,
|
|
289
|
+
usage: dict[str, str | int],
|
|
226
290
|
) -> types.GenerateContentResponseUsageMetadata | None:
|
|
227
291
|
input_tokens = usage.get("inputTokens")
|
|
228
292
|
output_tokens = usage.get("outputTokens")
|
|
@@ -29,16 +29,21 @@ from .http_helpers import (
|
|
|
29
29
|
provider_governance_snapshot,
|
|
30
30
|
safe_error,
|
|
31
31
|
)
|
|
32
|
-
from .
|
|
32
|
+
from .report_routes import create_report_router
|
|
33
|
+
from .report_store import AgentRuntimeReportStore
|
|
34
|
+
from .runtime_factory import (
|
|
35
|
+
default_engine,
|
|
36
|
+
default_report_store,
|
|
37
|
+
default_store,
|
|
38
|
+
)
|
|
33
39
|
from .service import (
|
|
34
40
|
AgentRunService,
|
|
35
|
-
IdempotencyConflictError,
|
|
36
41
|
OperationNotFoundError,
|
|
37
42
|
RunNotFoundError,
|
|
38
43
|
)
|
|
39
44
|
from .sqlite_store import RunStoreCapacityError
|
|
40
45
|
from .sse import event_stream
|
|
41
|
-
from .store import ReleaseNotFoundError, RunStore
|
|
46
|
+
from .store import IdempotencyConflictError, ReleaseNotFoundError, RunStore
|
|
42
47
|
from .telemetry import configure_telemetry
|
|
43
48
|
|
|
44
49
|
|
|
@@ -48,12 +53,15 @@ def create_app(
|
|
|
48
53
|
heartbeat_seconds: float = 15.0,
|
|
49
54
|
internal_token: str | None = None,
|
|
50
55
|
store: RunStore | None = None,
|
|
56
|
+
report_store: AgentRuntimeReportStore | None = None,
|
|
51
57
|
) -> FastAPI:
|
|
52
58
|
configure_telemetry()
|
|
53
|
-
|
|
59
|
+
runtime_report_store = report_store or default_report_store()
|
|
60
|
+
runtime_engine = engine or default_engine(runtime_report_store)
|
|
54
61
|
runtime = AgentRunService(
|
|
55
62
|
runtime_engine,
|
|
56
63
|
store or default_store(),
|
|
64
|
+
runtime_report_store,
|
|
57
65
|
(
|
|
58
66
|
ArtifactLoader(
|
|
59
67
|
os.environ.get(
|
|
@@ -80,6 +88,9 @@ def create_app(
|
|
|
80
88
|
version="0.1.0",
|
|
81
89
|
lifespan=lifespan,
|
|
82
90
|
)
|
|
91
|
+
application.include_router(
|
|
92
|
+
create_report_router(runtime_report_store, internal_token)
|
|
93
|
+
)
|
|
83
94
|
|
|
84
95
|
@application.get("/health/live")
|
|
85
96
|
async def live() -> dict[str, str]:
|
|
@@ -322,6 +333,7 @@ def create_app(
|
|
|
322
333
|
)
|
|
323
334
|
|
|
324
335
|
application.state.run_service = runtime
|
|
336
|
+
application.state.report_store = runtime_report_store
|
|
325
337
|
return application
|
|
326
338
|
|
|
327
339
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .store import RunRecord, RunStore
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async def resolve_pending_approval(
|
|
7
|
+
store: RunStore,
|
|
8
|
+
record: RunRecord,
|
|
9
|
+
outcome: str,
|
|
10
|
+
) -> None:
|
|
11
|
+
pending: dict[str, object] | None = None
|
|
12
|
+
for event in record.events:
|
|
13
|
+
if event.get("type") == "approval.required":
|
|
14
|
+
pending = event
|
|
15
|
+
elif event.get("type") == "approval.resolved":
|
|
16
|
+
pending = None
|
|
17
|
+
if not pending:
|
|
18
|
+
return
|
|
19
|
+
work_item_id = pending.get("workItemId")
|
|
20
|
+
tool_call_id = pending.get("toolCallId")
|
|
21
|
+
if isinstance(work_item_id, str) and isinstance(tool_call_id, str):
|
|
22
|
+
await store.append(
|
|
23
|
+
record.run_id,
|
|
24
|
+
"approval.resolved",
|
|
25
|
+
{
|
|
26
|
+
"workItemId": work_item_id,
|
|
27
|
+
"toolCallId": tool_call_id,
|
|
28
|
+
"outcome": outcome,
|
|
29
|
+
},
|
|
30
|
+
)
|
|
@@ -139,6 +139,14 @@ class RuntimeRelease(CanonicalModel):
|
|
|
139
139
|
manifest_digest: str = Field(pattern=r"^[a-f0-9]{64}$")
|
|
140
140
|
|
|
141
141
|
|
|
142
|
+
class RunReportingContext(CanonicalModel):
|
|
143
|
+
"""Trusted platform metadata for reporting, never an authorization fact."""
|
|
144
|
+
|
|
145
|
+
actor_id: str = Field(min_length=1, max_length=64)
|
|
146
|
+
actor_type: str = Field(min_length=1, max_length=32)
|
|
147
|
+
department_id: str | None = Field(default=None, max_length=64)
|
|
148
|
+
|
|
149
|
+
|
|
142
150
|
def legacy_release() -> RuntimeRelease:
|
|
143
151
|
return RuntimeRelease(
|
|
144
152
|
release_id="legacy",
|
|
@@ -174,6 +182,7 @@ class StartRunRequest(CanonicalModel):
|
|
|
174
182
|
tool_host_url: str = ""
|
|
175
183
|
release: RuntimeRelease = Field(default_factory=legacy_release)
|
|
176
184
|
trace_id: str = Field(default="", max_length=160)
|
|
185
|
+
reporting: RunReportingContext | None = None
|
|
177
186
|
|
|
178
187
|
|
|
179
188
|
class StartRunResponse(CanonicalModel):
|
|
@@ -10,6 +10,7 @@ from .engine import (
|
|
|
10
10
|
OutputDelta,
|
|
11
11
|
UsageReported,
|
|
12
12
|
)
|
|
13
|
+
from .report_types import UsageFacts
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class DeterministicEngineAdapter:
|
|
@@ -49,7 +50,7 @@ class DeterministicEngineAdapter:
|
|
|
49
50
|
if isinstance(input_text, str):
|
|
50
51
|
text = input_text
|
|
51
52
|
yield OutputDelta(text)
|
|
52
|
-
yield UsageReported(
|
|
53
|
+
yield UsageReported(1, UsageFacts())
|
|
53
54
|
yield EngineCompleted(request.input)
|
|
54
55
|
finally:
|
|
55
56
|
self._cancellations.pop(request.run_id, None)
|
|
@@ -13,6 +13,7 @@ from .contracts import (
|
|
|
13
13
|
RuntimeToolDefinition,
|
|
14
14
|
ToolNetworkPolicy,
|
|
15
15
|
)
|
|
16
|
+
from .report_types import UsageFacts
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
@dataclass(frozen=True)
|
|
@@ -75,7 +76,8 @@ class ToolCompleted:
|
|
|
75
76
|
|
|
76
77
|
@dataclass(frozen=True)
|
|
77
78
|
class UsageReported:
|
|
78
|
-
|
|
79
|
+
attempt_sequence: int
|
|
80
|
+
usage: UsageFacts
|
|
79
81
|
|
|
80
82
|
|
|
81
83
|
@dataclass(frozen=True)
|
|
@@ -19,8 +19,29 @@ from .engine import (
|
|
|
19
19
|
UsageReported,
|
|
20
20
|
)
|
|
21
21
|
from .multimodal import attach_artifacts, final_output
|
|
22
|
-
from .
|
|
22
|
+
from .openai_engine_validation import (
|
|
23
|
+
assistant_tool_message,
|
|
24
|
+
attempt_outcome,
|
|
25
|
+
cancelled,
|
|
26
|
+
parse_arguments,
|
|
27
|
+
validate_provider_tool_calls,
|
|
28
|
+
validate_runtime_request,
|
|
29
|
+
)
|
|
30
|
+
from .openai_provider import (
|
|
31
|
+
OpenAiCompatibleProvider,
|
|
32
|
+
ProviderToolCall,
|
|
33
|
+
ProviderTurn,
|
|
34
|
+
)
|
|
23
35
|
from .provider_governance import ProviderRequestGovernance
|
|
36
|
+
from .report_lifecycle import (
|
|
37
|
+
finish_attempt_report_safely,
|
|
38
|
+
start_attempt_report_safely,
|
|
39
|
+
)
|
|
40
|
+
from .report_store import (
|
|
41
|
+
AgentRuntimeReportStore,
|
|
42
|
+
InMemoryAgentRuntimeReportStore,
|
|
43
|
+
)
|
|
44
|
+
from .report_types import UsageFacts
|
|
24
45
|
from .tool_host import (
|
|
25
46
|
ToolApprovalPending,
|
|
26
47
|
ToolApprovalResolved,
|
|
@@ -36,10 +57,12 @@ class OpenAiCompatibleEngineAdapter(AgentEngineAdapter):
|
|
|
36
57
|
provider: OpenAiCompatibleProvider,
|
|
37
58
|
tool_host: ToolHostClient,
|
|
38
59
|
governance: ProviderRequestGovernance,
|
|
60
|
+
report_store: AgentRuntimeReportStore | None = None,
|
|
39
61
|
) -> None:
|
|
40
62
|
self._provider = provider
|
|
41
63
|
self._tool_host = tool_host
|
|
42
64
|
self._governance = governance
|
|
65
|
+
self._report_store = report_store or InMemoryAgentRuntimeReportStore()
|
|
43
66
|
self._cancellations: dict[str, asyncio.Event] = {}
|
|
44
67
|
|
|
45
68
|
@property
|
|
@@ -79,34 +102,79 @@ class OpenAiCompatibleEngineAdapter(AgentEngineAdapter):
|
|
|
79
102
|
try:
|
|
80
103
|
for attempt in range(1, self._provider.config.max_tool_rounds + 1):
|
|
81
104
|
cancelled(cancellation)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
tools=request.allowed_tools,
|
|
89
|
-
policy=request.provider_data_policy,
|
|
90
|
-
input_value=request.input,
|
|
91
|
-
artifact_bytes=sum(
|
|
92
|
-
artifact.byte_size for artifact in request.artifacts
|
|
93
|
-
),
|
|
94
|
-
output_schema=request.output_schema,
|
|
95
|
-
measure=lambda messages, tools, schema: len(
|
|
96
|
-
self._provider.request_body(
|
|
97
|
-
messages,
|
|
98
|
-
tools,
|
|
99
|
-
request.output_schema_key,
|
|
100
|
-
schema,
|
|
101
|
-
)
|
|
102
|
-
),
|
|
105
|
+
report_attempt = await start_attempt_report_safely(
|
|
106
|
+
self._report_store,
|
|
107
|
+
request.run_id,
|
|
108
|
+
attempt,
|
|
109
|
+
self._provider.config.deployment_key,
|
|
110
|
+
self._provider.config.model,
|
|
103
111
|
)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
governed.tools,
|
|
107
|
-
request.output_schema_key,
|
|
108
|
-
governed.output_schema,
|
|
112
|
+
attempt_sequence = (
|
|
113
|
+
report_attempt.sequence if report_attempt else attempt
|
|
109
114
|
)
|
|
115
|
+
finalized = False
|
|
116
|
+
turn: ProviderTurn | None = None
|
|
117
|
+
try:
|
|
118
|
+
governed = self._governance.authorize(
|
|
119
|
+
deployment_key=self._provider.config.deployment_key,
|
|
120
|
+
operation_key=request.operation_key,
|
|
121
|
+
run_id=request.run_id,
|
|
122
|
+
attempt=attempt_sequence,
|
|
123
|
+
messages=messages,
|
|
124
|
+
tools=request.allowed_tools,
|
|
125
|
+
policy=request.provider_data_policy,
|
|
126
|
+
input_value=request.input,
|
|
127
|
+
artifact_bytes=sum(
|
|
128
|
+
artifact.byte_size for artifact in request.artifacts
|
|
129
|
+
),
|
|
130
|
+
output_schema=request.output_schema,
|
|
131
|
+
measure=lambda messages, tools, schema: len(
|
|
132
|
+
self._provider.request_body(
|
|
133
|
+
messages,
|
|
134
|
+
tools,
|
|
135
|
+
request.output_schema_key,
|
|
136
|
+
schema,
|
|
137
|
+
)
|
|
138
|
+
),
|
|
139
|
+
)
|
|
140
|
+
turn = await self._provider.complete_turn(
|
|
141
|
+
governed.messages,
|
|
142
|
+
governed.tools,
|
|
143
|
+
request.output_schema_key,
|
|
144
|
+
governed.output_schema,
|
|
145
|
+
)
|
|
146
|
+
validate_provider_tool_calls(turn.tool_calls, allowed)
|
|
147
|
+
await finish_attempt_report_safely(
|
|
148
|
+
self._report_store,
|
|
149
|
+
report_attempt,
|
|
150
|
+
"completed",
|
|
151
|
+
turn.usage,
|
|
152
|
+
reported_model=turn.reported_model,
|
|
153
|
+
)
|
|
154
|
+
finalized = True
|
|
155
|
+
except SafeEngineError as error:
|
|
156
|
+
if not finalized:
|
|
157
|
+
await finish_attempt_report_safely(
|
|
158
|
+
self._report_store,
|
|
159
|
+
report_attempt,
|
|
160
|
+
attempt_outcome(error),
|
|
161
|
+
turn.usage if turn else UsageFacts(),
|
|
162
|
+
reported_model=(turn.reported_model if turn else None),
|
|
163
|
+
safe_error_code=error.code,
|
|
164
|
+
)
|
|
165
|
+
raise
|
|
166
|
+
except Exception:
|
|
167
|
+
if not finalized:
|
|
168
|
+
await finish_attempt_report_safely(
|
|
169
|
+
self._report_store,
|
|
170
|
+
report_attempt,
|
|
171
|
+
"failed",
|
|
172
|
+
turn.usage if turn else UsageFacts(),
|
|
173
|
+
reported_model=(turn.reported_model if turn else None),
|
|
174
|
+
safe_error_code="RUNTIME_UNAVAILABLE",
|
|
175
|
+
)
|
|
176
|
+
raise
|
|
177
|
+
yield UsageReported(attempt_sequence, turn.usage)
|
|
110
178
|
if turn.tool_calls:
|
|
111
179
|
messages.append(assistant_tool_message(turn.tool_calls))
|
|
112
180
|
async for signal in self._execute_tools(
|
|
@@ -127,7 +195,6 @@ class OpenAiCompatibleEngineAdapter(AgentEngineAdapter):
|
|
|
127
195
|
retryable=False,
|
|
128
196
|
)
|
|
129
197
|
yield OutputDelta(delta)
|
|
130
|
-
yield UsageReported(turn.usage or {"source": "unknown"})
|
|
131
198
|
yield EngineCompleted(final_output(turn.text, governed.output_schema))
|
|
132
199
|
return
|
|
133
200
|
raise SafeEngineError(
|
|
@@ -231,105 +298,3 @@ class OpenAiCompatibleEngineAdapter(AgentEngineAdapter):
|
|
|
231
298
|
|
|
232
299
|
def governance_health(self) -> dict[str, int | float | str | None]:
|
|
233
300
|
return self._governance.health()
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
def validate_runtime_request(
|
|
237
|
-
request: EngineRunRequest,
|
|
238
|
-
provider_deployment_key: str,
|
|
239
|
-
provider_capabilities: tuple[str, ...],
|
|
240
|
-
provider_input_modalities: tuple[str, ...],
|
|
241
|
-
) -> None:
|
|
242
|
-
if (
|
|
243
|
-
not request.instructions.strip()
|
|
244
|
-
or not request.user_prompt.strip()
|
|
245
|
-
or not request.execution_grant.strip()
|
|
246
|
-
or not request.tool_host_url.strip()
|
|
247
|
-
):
|
|
248
|
-
raise SafeEngineError(
|
|
249
|
-
"INVALID_INPUT",
|
|
250
|
-
"Agent Operation 运行计划不完整",
|
|
251
|
-
retryable=False,
|
|
252
|
-
)
|
|
253
|
-
if set(request.required_capabilities) - set(provider_capabilities):
|
|
254
|
-
raise SafeEngineError(
|
|
255
|
-
"MODEL_ROUTE_UNAVAILABLE",
|
|
256
|
-
"模型 Deployment 缺少 Operation 所需能力",
|
|
257
|
-
retryable=True,
|
|
258
|
-
)
|
|
259
|
-
if set(request.input_modalities) - set(provider_input_modalities):
|
|
260
|
-
raise SafeEngineError(
|
|
261
|
-
"MODEL_ROUTE_UNAVAILABLE",
|
|
262
|
-
"模型 Deployment 不支持 Operation 输入模态",
|
|
263
|
-
retryable=True,
|
|
264
|
-
)
|
|
265
|
-
if (
|
|
266
|
-
request.provider_egress.mode != "configured-endpoint"
|
|
267
|
-
or request.provider_egress.deployment_key != provider_deployment_key
|
|
268
|
-
):
|
|
269
|
-
raise SafeEngineError(
|
|
270
|
-
"FORBIDDEN",
|
|
271
|
-
"Agent Operation 未授权当前模型部署出网",
|
|
272
|
-
retryable=False,
|
|
273
|
-
)
|
|
274
|
-
if request.tool_network_policy.mode != "forbidden":
|
|
275
|
-
raise SafeEngineError(
|
|
276
|
-
"TOOL_DENIED",
|
|
277
|
-
"Agent Tool 网络策略不受支持",
|
|
278
|
-
retryable=False,
|
|
279
|
-
)
|
|
280
|
-
if (
|
|
281
|
-
not request.provider_data_policy.input_fields
|
|
282
|
-
or not request.provider_data_policy.envelope_fields
|
|
283
|
-
):
|
|
284
|
-
raise SafeEngineError(
|
|
285
|
-
"FORBIDDEN",
|
|
286
|
-
"Agent Operation 缺少 Provider 字段治理策略",
|
|
287
|
-
retryable=False,
|
|
288
|
-
)
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
def cancelled(event: asyncio.Event) -> None:
|
|
292
|
-
if event.is_set():
|
|
293
|
-
raise SafeEngineError(
|
|
294
|
-
"RUN_CANCELLED",
|
|
295
|
-
"Agent Run 已取消",
|
|
296
|
-
retryable=False,
|
|
297
|
-
)
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
def parse_arguments(raw: str) -> dict[str, JsonValue]:
|
|
301
|
-
try:
|
|
302
|
-
value = json.loads(raw or "{}")
|
|
303
|
-
except json.JSONDecodeError:
|
|
304
|
-
raise SafeEngineError(
|
|
305
|
-
"TOOL_DENIED",
|
|
306
|
-
"Agent Tool 参数无效",
|
|
307
|
-
retryable=False,
|
|
308
|
-
) from None
|
|
309
|
-
if not isinstance(value, dict):
|
|
310
|
-
raise SafeEngineError(
|
|
311
|
-
"TOOL_DENIED",
|
|
312
|
-
"Agent Tool 参数必须是对象",
|
|
313
|
-
retryable=False,
|
|
314
|
-
)
|
|
315
|
-
return value
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
def assistant_tool_message(
|
|
319
|
-
calls: list[ProviderToolCall],
|
|
320
|
-
) -> dict[str, JsonValue]:
|
|
321
|
-
return {
|
|
322
|
-
"role": "assistant",
|
|
323
|
-
"content": None,
|
|
324
|
-
"tool_calls": [
|
|
325
|
-
{
|
|
326
|
-
"id": call.call_id,
|
|
327
|
-
"type": "function",
|
|
328
|
-
"function": {
|
|
329
|
-
"name": call.name,
|
|
330
|
-
"arguments": call.arguments,
|
|
331
|
-
},
|
|
332
|
-
}
|
|
333
|
-
for call in calls
|
|
334
|
-
],
|
|
335
|
-
}
|