create-windy 0.3.2 → 0.3.5
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/README.md +27 -9
- package/dist/cli.js +825 -117
- package/package.json +1 -1
- package/template/.agents/skills/windy-business-module/SKILL.md +16 -3
- package/template/.windy-template.json +2 -2
- package/template/AGENTS.md +4 -0
- package/template/README.md +18 -11
- package/template/_gitignore +1 -0
- package/template/apps/agent-server/README.md +2 -0
- package/template/apps/agent-server/pyproject.toml +8 -0
- package/template/apps/agent-server/src/southwind_agent_server/adk_adapter.py +87 -10
- package/template/apps/agent-server/src/southwind_agent_server/adk_model.py +18 -3
- package/template/apps/agent-server/src/southwind_agent_server/adk_tools.py +78 -11
- package/template/apps/agent-server/src/southwind_agent_server/app.py +120 -115
- package/template/apps/agent-server/src/southwind_agent_server/artifact_input.py +153 -0
- package/template/apps/agent-server/src/southwind_agent_server/config.py +29 -0
- package/template/apps/agent-server/src/southwind_agent_server/contracts.py +81 -1
- package/template/apps/agent-server/src/southwind_agent_server/deterministic.py +8 -0
- package/template/apps/agent-server/src/southwind_agent_server/engine.py +28 -0
- package/template/apps/agent-server/src/southwind_agent_server/fingerprint.py +41 -0
- package/template/apps/agent-server/src/southwind_agent_server/http_helpers.py +62 -0
- package/template/apps/agent-server/src/southwind_agent_server/lease.py +15 -0
- package/template/apps/agent-server/src/southwind_agent_server/multimodal.py +76 -0
- package/template/apps/agent-server/src/southwind_agent_server/openai_engine.py +112 -20
- package/template/apps/agent-server/src/southwind_agent_server/openai_provider.py +14 -1
- package/template/apps/agent-server/src/southwind_agent_server/postgres_store.py +329 -0
- package/template/apps/agent-server/src/southwind_agent_server/postgres_store_backend.py +332 -0
- package/template/apps/agent-server/src/southwind_agent_server/provider_governance.py +64 -3
- package/template/apps/agent-server/src/southwind_agent_server/runtime_factory.py +68 -0
- package/template/apps/agent-server/src/southwind_agent_server/service.py +218 -56
- package/template/apps/agent-server/src/southwind_agent_server/signal_events.py +36 -0
- package/template/apps/agent-server/src/southwind_agent_server/sqlite_catalog.py +107 -0
- package/template/apps/agent-server/src/southwind_agent_server/sqlite_schema.py +65 -0
- package/template/apps/agent-server/src/southwind_agent_server/sqlite_serialization.py +16 -0
- package/template/apps/agent-server/src/southwind_agent_server/sqlite_store.py +89 -57
- package/template/apps/agent-server/src/southwind_agent_server/store.py +105 -2
- package/template/apps/agent-server/src/southwind_agent_server/store_cipher.py +40 -0
- package/template/apps/agent-server/src/southwind_agent_server/telemetry.py +89 -0
- package/template/apps/agent-server/src/southwind_agent_server/tool_host.py +72 -0
- package/template/apps/agent-server/tests/test_adk_adapter.py +6 -2
- package/template/apps/agent-server/tests/test_api.py +24 -1
- package/template/apps/agent-server/tests/test_artifact_input.py +110 -0
- package/template/apps/agent-server/tests/test_config.py +4 -0
- package/template/apps/agent-server/tests/test_fingerprint.py +10 -0
- package/template/apps/agent-server/tests/test_multimodal.py +51 -0
- package/template/apps/agent-server/tests/test_openai_provider.py +6 -2
- package/template/apps/agent-server/tests/test_postgres_store.py +131 -0
- package/template/apps/agent-server/tests/test_provider_governance.py +61 -0
- package/template/apps/agent-server/tests/test_sqlite_store.py +95 -10
- package/template/apps/agent-server/tests/test_telemetry.py +25 -0
- package/template/apps/agent-server/tests/test_tool_approval.py +28 -0
- package/template/apps/agent-server/uv.lock +165 -0
- package/template/apps/document-server/Dockerfile +19 -0
- package/template/apps/document-server/pyproject.toml +43 -0
- package/template/apps/document-server/src/southwind_document_server/__init__.py +1 -0
- package/template/apps/document-server/src/southwind_document_server/adapter.py +337 -0
- package/template/apps/document-server/src/southwind_document_server/app.py +151 -0
- package/template/apps/document-server/src/southwind_document_server/config.py +94 -0
- package/template/apps/document-server/src/southwind_document_server/contracts.py +103 -0
- package/template/apps/document-server/src/southwind_document_server/service.py +149 -0
- package/template/apps/document-server/src/southwind_document_server/store.py +185 -0
- package/template/apps/document-server/tests/__init__.py +1 -0
- package/template/apps/document-server/tests/test_config.py +42 -0
- package/template/apps/document-server/tests/test_service.py +140 -0
- package/template/apps/document-server/uv.lock +430 -0
- package/template/apps/embedding-server/Dockerfile +17 -0
- package/template/apps/embedding-server/pyproject.toml +44 -0
- package/template/apps/embedding-server/src/southwind_embedding_server/__init__.py +1 -0
- package/template/apps/embedding-server/src/southwind_embedding_server/adapter.py +254 -0
- package/template/apps/embedding-server/src/southwind_embedding_server/app.py +119 -0
- package/template/apps/embedding-server/src/southwind_embedding_server/config.py +120 -0
- package/template/apps/embedding-server/src/southwind_embedding_server/contracts.py +106 -0
- package/template/apps/embedding-server/src/southwind_embedding_server/error.py +6 -0
- package/template/apps/embedding-server/src/southwind_embedding_server/image_input.py +87 -0
- package/template/apps/embedding-server/tests/__init__.py +1 -0
- package/template/apps/embedding-server/tests/test_adapter.py +92 -0
- package/template/apps/embedding-server/tests/test_config.py +38 -0
- package/template/apps/embedding-server/uv.lock +449 -0
- package/template/apps/server/src/agent/background-operation-port.ts +37 -0
- package/template/apps/server/src/agent/background-operation.test.ts +231 -0
- package/template/apps/server/src/agent/background-operation.ts +252 -0
- package/template/apps/server/src/agent/drizzle-run-binding-repository.ts +114 -0
- package/template/apps/server/src/agent/execution-grants.ts +7 -1
- package/template/apps/server/src/agent/fake-runtime.test-support.ts +43 -0
- package/template/apps/server/src/agent/idempotency-key.test.ts +28 -0
- package/template/apps/server/src/agent/idempotency-key.ts +11 -0
- package/template/apps/server/src/agent/remote-runtime.test.ts +115 -1
- package/template/apps/server/src/agent/remote-runtime.ts +173 -5
- package/template/apps/server/src/agent/run-authorization.test-support.ts +9 -0
- package/template/apps/server/src/agent/run-authorization.test.ts +126 -0
- package/template/apps/server/src/agent/run-authorization.ts +180 -0
- package/template/apps/server/src/agent/run-binding-repository.ts +93 -0
- package/template/apps/server/src/agent/run-contracts.ts +88 -1
- package/template/apps/server/src/agent/run-definition-support.ts +58 -0
- package/template/apps/server/src/agent/run-errors.ts +25 -0
- package/template/apps/server/src/agent/run-facade.test.ts +57 -46
- package/template/apps/server/src/agent/run-facade.ts +185 -65
- package/template/apps/server/src/agent/run-release.ts +78 -0
- package/template/apps/server/src/agent/run-route-input.ts +169 -0
- package/template/apps/server/src/agent/run-routes.ts +63 -76
- package/template/apps/server/src/agent/runtime-bootstrap.ts +169 -13
- package/template/apps/server/src/agent/sandbox-client.ts +200 -0
- package/template/apps/server/src/agent/sandbox-protocol.ts +76 -0
- package/template/apps/server/src/agent/sandbox-registry.ts +205 -0
- package/template/apps/server/src/agent/sandbox-runtime.test.ts +137 -0
- package/template/apps/server/src/agent/sandbox-worker-server.ts +195 -0
- package/template/apps/server/src/agent/sandbox-worker.ts +12 -0
- package/template/apps/server/src/agent/tool-approval.test.ts +75 -0
- package/template/apps/server/src/agent/tool-approval.ts +115 -0
- package/template/apps/server/src/agent/tool-audit.ts +62 -0
- package/template/apps/server/src/agent/tool-host.test.ts +39 -11
- package/template/apps/server/src/agent/tool-host.ts +184 -17
- package/template/apps/server/src/agent/tool-registry.ts +52 -47
- package/template/apps/server/src/agent/vision-facade.test.ts +256 -0
- package/template/apps/server/src/agent/vision-input.ts +120 -0
- package/template/apps/server/src/ai-artifact/codec.test.ts +44 -0
- package/template/apps/server/src/ai-artifact/codec.ts +137 -0
- package/template/apps/server/src/ai-artifact/routes.ts +37 -0
- package/template/apps/server/src/ai-artifact/runtime.ts +205 -0
- package/template/apps/server/src/application-services.ts +62 -2
- package/template/apps/server/src/auth/reauthentication.test.ts +40 -0
- package/template/apps/server/src/auth/reauthentication.ts +19 -0
- package/template/apps/server/src/auth/runtime.ts +9 -1
- package/template/apps/server/src/data-access/context.ts +6 -4
- package/template/apps/server/src/data-access/scope-snapshot.ts +27 -0
- package/template/apps/server/src/data-governance/export/approval-port.ts +6 -3
- package/template/apps/server/src/data-governance/export/authorization-port.test.ts +23 -4
- package/template/apps/server/src/data-governance/export/authorization-port.ts +89 -25
- package/template/apps/server/src/data-governance/export/drizzle-repository.integration.test.ts +2 -0
- package/template/apps/server/src/data-governance/export/drizzle-repository.ts +1 -0
- package/template/apps/server/src/data-governance/export/job-runtime.test.ts +15 -4
- package/template/apps/server/src/data-governance/export/job-runtime.ts +68 -15
- package/template/apps/server/src/data-governance/export/ndjson-stream.ts +25 -6
- package/template/apps/server/src/data-governance/export/postgres-recovery.integration.test.ts +4 -2
- package/template/apps/server/src/data-governance/export/renderer-job-runtime.test.ts +235 -0
- package/template/apps/server/src/data-governance/export/renderer-runtime.ts +293 -0
- package/template/apps/server/src/data-governance/export/renderer-selection.test.ts +67 -0
- package/template/apps/server/src/data-governance/export/repository.test.ts +2 -0
- package/template/apps/server/src/data-governance/export/request-fingerprint.test.ts +13 -0
- package/template/apps/server/src/data-governance/export/request-fingerprint.ts +2 -0
- package/template/apps/server/src/data-governance/export/routes.ts +11 -1
- package/template/apps/server/src/data-governance/export/scope-snapshot.ts +6 -18
- package/template/apps/server/src/data-governance/export/service-support.ts +188 -0
- package/template/apps/server/src/data-governance/export/service.ts +90 -113
- package/template/apps/server/src/data-governance/export/source.ts +101 -0
- package/template/apps/server/src/data-governance/export/types.ts +27 -0
- package/template/apps/server/src/document/contracts.ts +76 -0
- package/template/apps/server/src/document/facade.ts +339 -0
- package/template/apps/server/src/document/remote-runtime.test.ts +96 -0
- package/template/apps/server/src/document/remote-runtime.ts +223 -0
- package/template/apps/server/src/document/routes.ts +193 -0
- package/template/apps/server/src/embedding/contracts.ts +83 -0
- package/template/apps/server/src/embedding/error.ts +10 -0
- package/template/apps/server/src/embedding/facade.ts +217 -0
- package/template/apps/server/src/embedding/input.ts +172 -0
- package/template/apps/server/src/embedding/remote-runtime.test.ts +66 -0
- package/template/apps/server/src/embedding/remote-runtime.ts +218 -0
- package/template/apps/server/src/embedding/routes.ts +152 -0
- package/template/apps/server/src/index.ts +40 -43
- package/template/apps/server/src/jobs/artifact-service.test.ts +14 -2
- package/template/apps/server/src/jobs/artifact-service.ts +10 -0
- package/template/apps/server/src/jobs/drizzle-execution-repository.ts +17 -18
- package/template/apps/server/src/jobs/drizzle-execution.integration.test.ts +19 -1
- package/template/apps/server/src/jobs/drizzle-mappers.ts +4 -1
- package/template/apps/server/src/jobs/drizzle-recovery-repository.ts +49 -2
- package/template/apps/server/src/jobs/drizzle-repository.ts +1 -0
- package/template/apps/server/src/jobs/execution-repository.ts +17 -0
- package/template/apps/server/src/jobs/execution.test.ts +34 -0
- package/template/apps/server/src/jobs/in-memory-control.ts +135 -0
- package/template/apps/server/src/jobs/in-memory-execution.ts +34 -65
- package/template/apps/server/src/jobs/legacy-service-runtime.ts +5 -2
- package/template/apps/server/src/jobs/repository.ts +3 -0
- package/template/apps/server/src/jobs/worker.ts +80 -41
- package/template/apps/server/src/module-host/ai-operation-port.ts +170 -0
- package/template/apps/server/src/module-host/durable-context.test.ts +312 -0
- package/template/apps/server/src/module-host/durable-context.ts +124 -0
- package/template/apps/server/src/module-host/durable-handler-registration.ts +72 -0
- package/template/apps/server/src/module-host/governed-export-route-support.ts +171 -0
- package/template/apps/server/src/module-host/governed-export-routes.test.ts +327 -0
- package/template/apps/server/src/module-host/governed-export-routes.ts +238 -0
- package/template/apps/server/src/module-host/governed-renderer-registration.test.ts +247 -0
- package/template/apps/server/src/module-host/governed-resource-routes.ts +331 -0
- package/template/apps/server/src/module-host/governed-resources.test.ts +344 -0
- package/template/apps/server/src/module-host/governed-resources.ts +203 -0
- package/template/apps/server/src/module-host/http-runtime.ts +95 -0
- package/template/apps/server/src/module-host/initialize-contracts.ts +14 -0
- package/template/apps/server/src/module-host/initialize.test.ts +49 -1
- package/template/apps/server/src/module-host/initialize.ts +39 -16
- package/template/apps/server/src/module-host/job-port.test.ts +262 -0
- package/template/apps/server/src/module-host/job-port.ts +334 -0
- package/template/apps/server/src/module-host/request-context.test.ts +88 -2
- package/template/apps/server/src/module-host/request-context.ts +56 -22
- package/template/apps/server/src/module-host/route-installer.test.ts +10 -0
- package/template/apps/server/src/module-host/route-installer.ts +36 -1
- package/template/apps/server/src/module-host/sandbox-tool-registration.ts +42 -0
- package/template/apps/server/src/module-host/tool-handlers.ts +67 -31
- package/template/apps/server/src/module-host.ts +1 -0
- package/template/apps/server/src/module-runtime-validation.ts +7 -3
- package/template/apps/server/src/speech/contracts.ts +76 -0
- package/template/apps/server/src/speech/facade.ts +316 -0
- package/template/apps/server/src/speech/remote-runtime.test.ts +88 -0
- package/template/apps/server/src/speech/remote-runtime.ts +227 -0
- package/template/apps/server/src/speech/routes.ts +180 -0
- package/template/apps/server/src/system/actor.ts +39 -0
- package/template/apps/server/src/system/route-helpers.ts +1 -0
- package/template/apps/server/src/workflow/service.ts +2 -1
- package/template/apps/speech-server/Dockerfile +22 -0
- package/template/apps/speech-server/pyproject.toml +43 -0
- package/template/apps/speech-server/src/southwind_speech_server/__init__.py +1 -0
- package/template/apps/speech-server/src/southwind_speech_server/adapter.py +249 -0
- package/template/apps/speech-server/src/southwind_speech_server/app.py +183 -0
- package/template/apps/speech-server/src/southwind_speech_server/config.py +100 -0
- package/template/apps/speech-server/src/southwind_speech_server/contracts.py +103 -0
- package/template/apps/speech-server/src/southwind_speech_server/errors.py +6 -0
- package/template/apps/speech-server/src/southwind_speech_server/normalizer.py +108 -0
- package/template/apps/speech-server/src/southwind_speech_server/output_validation.py +128 -0
- package/template/apps/speech-server/src/southwind_speech_server/service.py +162 -0
- package/template/apps/speech-server/src/southwind_speech_server/store.py +209 -0
- package/template/apps/speech-server/tests/__init__.py +1 -0
- package/template/apps/speech-server/tests/test_config.py +33 -0
- package/template/apps/speech-server/tests/test_service.py +154 -0
- package/template/apps/speech-server/uv.lock +430 -0
- package/template/apps/web/src/app/business-routes.ts +22 -0
- package/template/apps/web/src/components/common/AccessSnapshotBoundary.vue +112 -0
- package/template/apps/web/src/components/common/AccessSnapshotBoundary.webtest.ts +130 -0
- package/template/apps/web/src/components/common/AccessSnapshotNotice.vue +117 -0
- package/template/apps/web/src/components/common/FormDialog.vue +6 -0
- package/template/apps/web/src/components/common/ModalLayout.webtest.ts +13 -0
- package/template/apps/web/src/composables/access-snapshot-change.ts +131 -0
- package/template/apps/web/src/composables/access-snapshot-change.webtest.ts +103 -0
- package/template/apps/web/src/composables/useAccessSnapshot.ts +48 -2
- package/template/apps/web/src/composables/useAccessSnapshot.webtest.ts +54 -0
- package/template/apps/web/src/composables/useAccessSnapshotMonitor.ts +42 -0
- package/template/apps/web/src/layout/AppShell.vue +28 -25
- package/template/apps/web/src/layout/AppShell.webtest.ts +1 -0
- package/template/apps/web/src/router/business-routes.ts +68 -0
- package/template/apps/web/src/router/business-routes.webtest.ts +85 -0
- package/template/apps/web/src/router/index.ts +7 -0
- package/template/apps/web/src/router/shared-scaffold-neutrality.webtest.ts +12 -0
- package/template/apps/web/src/services/access-api.ts +5 -1
- package/template/apps/web/src/services/access-api.webtest.ts +35 -0
- package/template/docker-compose.yml +127 -0
- package/template/docs/architecture/ai-egress.md +12 -9
- package/template/docs/architecture/ai-runtime.md +23 -11
- package/template/docs/architecture/durable-jobs.md +25 -3
- package/template/docs/architecture/foundation-infrastructure.md +163 -0
- package/template/docs/architecture/module-host.md +115 -0
- package/template/docs/architecture/server-module-interface.md +39 -0
- package/template/docs/architecture/web-access-snapshot-resilience.md +60 -0
- package/template/docs/development/business-web-routes.md +66 -0
- package/template/docs/development/custom-login-page.md +3 -3
- package/template/docs/development/module-lifecycle.md +71 -0
- package/template/docs/operations/observability.md +5 -1
- package/template/docs/platform/agent-runtime.md +21 -11
- package/template/docs/platform/agent-tool-sandbox.md +58 -0
- package/template/docs/platform/agent-tools.md +5 -0
- package/template/docs/platform/document-ai-runtime.md +204 -0
- package/template/docs/platform/governed-export-renderers.md +132 -0
- package/template/docs/platform/navigation-registry.md +5 -0
- package/template/docs/platform/organization-membership.md +2 -1
- package/template/docs/platform/speech-ai-runtime.md +156 -0
- package/template/docs/platform/system-crud-api.md +7 -1
- package/template/docs/platform/vision-ai-runtime.md +97 -0
- package/template/docs/platform/web-system-crud.md +6 -0
- package/template/package.json +16 -2
- package/template/packages/ai-client/index.ts +4 -0
- package/template/packages/ai-client/package.json +5 -2
- package/template/packages/ai-client/src/agent/client/artifact.ts +26 -0
- package/template/packages/ai-client/src/agent/client/attach.test.ts +124 -0
- package/template/packages/ai-client/src/agent/client/client.ts +119 -7
- package/template/packages/ai-client/src/agent/client/run.ts +14 -0
- package/template/packages/ai-client/src/agent/client/types.ts +18 -0
- package/template/packages/ai-client/src/agent/client/vision.test.ts +53 -0
- package/template/packages/ai-client/src/agent/contracts/contracts.test.ts +1 -0
- package/template/packages/ai-client/src/agent/contracts/events.ts +33 -0
- package/template/packages/ai-client/src/agent/contracts/runs.ts +71 -0
- package/template/packages/ai-client/src/agent/index.ts +1 -0
- package/template/packages/ai-client/src/artifact-ref.ts +8 -0
- package/template/packages/ai-client/src/document/client.test.ts +114 -0
- package/template/packages/ai-client/src/document/client.ts +134 -0
- package/template/packages/ai-client/src/document/contracts.ts +152 -0
- package/template/packages/ai-client/src/document/error.ts +43 -0
- package/template/packages/ai-client/src/document/index.ts +3 -0
- package/template/packages/ai-client/src/document/run.ts +104 -0
- package/template/packages/ai-client/src/document/validation.ts +115 -0
- package/template/packages/ai-client/src/embedding/client.test.ts +77 -0
- package/template/packages/ai-client/src/embedding/client.ts +96 -0
- package/template/packages/ai-client/src/embedding/contracts.ts +46 -0
- package/template/packages/ai-client/src/embedding/error.ts +61 -0
- package/template/packages/ai-client/src/embedding/index.ts +3 -0
- package/template/packages/ai-client/src/embedding/validation.ts +117 -0
- package/template/packages/ai-client/src/speech/client.test.ts +135 -0
- package/template/packages/ai-client/src/speech/client.ts +104 -0
- package/template/packages/ai-client/src/speech/contracts.ts +129 -0
- package/template/packages/ai-client/src/speech/error.ts +43 -0
- package/template/packages/ai-client/src/speech/index.ts +3 -0
- package/template/packages/ai-client/src/speech/run.ts +133 -0
- package/template/packages/ai-client/src/speech/validation.ts +132 -0
- package/template/packages/config/package.json +2 -2
- package/template/packages/database/drizzle/0039_early_ezekiel_stane.sql +1 -0
- package/template/packages/database/drizzle/0040_colossal_baron_strucker.sql +63 -0
- package/template/packages/database/drizzle/0041_yummy_prowler.sql +21 -0
- package/template/packages/database/drizzle/0042_puzzling_rumiko_fujikawa.sql +27 -0
- package/template/packages/database/drizzle/meta/0039_snapshot.json +7755 -0
- package/template/packages/database/drizzle/meta/0040_snapshot.json +8150 -0
- package/template/packages/database/drizzle/meta/0041_snapshot.json +8323 -0
- package/template/packages/database/drizzle/meta/0042_snapshot.json +8541 -0
- package/template/packages/database/drizzle/meta/_journal.json +28 -0
- package/template/packages/database/package.json +2 -2
- package/template/packages/database/src/agent-runtime-schema.test.ts +54 -0
- package/template/packages/database/src/schema/agent-runtime.ts +149 -0
- package/template/packages/database/src/schema/common.ts +2 -14
- package/template/packages/database/src/schema/durable-jobs.ts +5 -0
- package/template/packages/database/src/schema/governed-exports.ts +17 -5
- package/template/packages/database/src/schema/index.ts +3 -0
- package/template/packages/database/src/schema-durable-recovery.test.ts +31 -0
- package/template/packages/database/src/schema-governed-exports.test.ts +21 -1
- package/template/packages/database/src/schema.test.ts +6 -23
- package/template/packages/jobs/package.json +3 -2
- package/template/packages/jobs/src/handlers.test.ts +13 -0
- package/template/packages/jobs/src/handlers.ts +7 -1
- package/template/packages/jobs/src/types.ts +4 -0
- package/template/packages/modules/package.json +2 -2
- package/template/packages/modules/src/ai-operation-composition.test-support.ts +165 -0
- package/template/packages/modules/src/ai-operation-composition.test.ts +91 -127
- package/template/packages/modules/src/ai-operation-composition.ts +59 -1
- package/template/packages/modules/src/manifest.ts +25 -1
- package/template/packages/modules/src/tool-composition.ts +18 -0
- package/template/packages/server-sdk/index.ts +50 -0
- package/template/packages/server-sdk/package.json +10 -4
- package/template/packages/server-sdk/src/ai-artifact-port.ts +34 -0
- package/template/packages/server-sdk/src/ai-operation-port.ts +65 -0
- package/template/packages/server-sdk/src/ai-operation-registration.ts +42 -1
- package/template/packages/server-sdk/src/background-ai-port.ts +43 -0
- package/template/packages/server-sdk/src/data-governance-registration.ts +116 -0
- package/template/packages/server-sdk/src/durable-handler.ts +29 -0
- package/template/packages/server-sdk/src/jobs-port.ts +69 -0
- package/template/packages/server-sdk/src/module-host.ts +16 -2
- package/template/packages/server-sdk/src/request-context.ts +6 -2
- package/template/packages/server-sdk/src/sandbox-tool.ts +21 -0
- package/template/packages/server-sdk/src/tool-registration.ts +2 -0
- package/template/packages/shared/package.json +1 -1
- package/template/packages/shared/src/rbac.ts +30 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import hmac
|
|
4
3
|
import os
|
|
5
|
-
import
|
|
4
|
+
from collections.abc import AsyncIterator
|
|
5
|
+
from contextlib import asynccontextmanager
|
|
6
6
|
from typing import Annotated, cast
|
|
7
7
|
|
|
8
8
|
from fastapi import FastAPI, Header, Query, Request
|
|
@@ -10,30 +10,36 @@ from fastapi.exceptions import RequestValidationError
|
|
|
10
10
|
from fastapi.responses import JSONResponse, StreamingResponse
|
|
11
11
|
from pydantic import ValidationError
|
|
12
12
|
|
|
13
|
-
from .
|
|
14
|
-
from .config import EnvironmentSecretResolver, OpenAiCompatibleConfig
|
|
13
|
+
from .artifact_input import ArtifactLoader
|
|
15
14
|
from .contracts import (
|
|
16
|
-
AgentError,
|
|
17
15
|
CancelRunRequest,
|
|
18
16
|
CancelRunResponse,
|
|
17
|
+
EvaluationResultRequest,
|
|
18
|
+
EvaluationResultResponse,
|
|
19
|
+
JsonValue,
|
|
19
20
|
RunResultResponse,
|
|
21
|
+
RunStatusResponse,
|
|
20
22
|
StartRunRequest,
|
|
21
23
|
StartRunResponse,
|
|
22
24
|
)
|
|
23
|
-
from .engine import AgentEngineAdapter
|
|
24
|
-
from .
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
from .engine import AgentEngineAdapter, SafeEngineError
|
|
26
|
+
from .http_helpers import (
|
|
27
|
+
authorized,
|
|
28
|
+
parse_cursor,
|
|
29
|
+
provider_governance_snapshot,
|
|
30
|
+
safe_error,
|
|
31
|
+
)
|
|
32
|
+
from .runtime_factory import default_engine, default_store
|
|
27
33
|
from .service import (
|
|
28
34
|
AgentRunService,
|
|
29
35
|
IdempotencyConflictError,
|
|
30
36
|
OperationNotFoundError,
|
|
31
37
|
RunNotFoundError,
|
|
32
38
|
)
|
|
33
|
-
from .sqlite_store import RunStoreCapacityError
|
|
39
|
+
from .sqlite_store import RunStoreCapacityError
|
|
34
40
|
from .sse import event_stream
|
|
35
|
-
from .store import
|
|
36
|
-
from .
|
|
41
|
+
from .store import ReleaseNotFoundError, RunStore
|
|
42
|
+
from .telemetry import configure_telemetry
|
|
37
43
|
|
|
38
44
|
|
|
39
45
|
def create_app(
|
|
@@ -43,16 +49,44 @@ def create_app(
|
|
|
43
49
|
internal_token: str | None = None,
|
|
44
50
|
store: RunStore | None = None,
|
|
45
51
|
) -> FastAPI:
|
|
52
|
+
configure_telemetry()
|
|
46
53
|
runtime_engine = engine or default_engine()
|
|
47
|
-
runtime = AgentRunService(
|
|
48
|
-
|
|
54
|
+
runtime = AgentRunService(
|
|
55
|
+
runtime_engine,
|
|
56
|
+
store or default_store(),
|
|
57
|
+
(
|
|
58
|
+
ArtifactLoader(
|
|
59
|
+
os.environ.get(
|
|
60
|
+
"AI_ARTIFACT_URL",
|
|
61
|
+
"http://server:9747/api/internal/ai/artifacts",
|
|
62
|
+
),
|
|
63
|
+
internal_token,
|
|
64
|
+
)
|
|
65
|
+
if internal_token
|
|
66
|
+
else None
|
|
67
|
+
),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
@asynccontextmanager
|
|
71
|
+
async def lifespan(_application: FastAPI) -> AsyncIterator[None]:
|
|
72
|
+
await runtime.recover()
|
|
73
|
+
try:
|
|
74
|
+
yield
|
|
75
|
+
finally:
|
|
76
|
+
await runtime.close()
|
|
77
|
+
|
|
78
|
+
application = FastAPI(
|
|
79
|
+
title="Southwind AI Agent Server",
|
|
80
|
+
version="0.1.0",
|
|
81
|
+
lifespan=lifespan,
|
|
82
|
+
)
|
|
49
83
|
|
|
50
84
|
@application.get("/health/live")
|
|
51
85
|
async def live() -> dict[str, str]:
|
|
52
86
|
return {"status": "live"}
|
|
53
87
|
|
|
54
88
|
@application.get("/health/ready", response_model=None)
|
|
55
|
-
async def ready() -> dict[str,
|
|
89
|
+
async def ready() -> dict[str, object] | JSONResponse:
|
|
56
90
|
health = getattr(runtime_engine, "health", None)
|
|
57
91
|
if not health:
|
|
58
92
|
return safe_error(
|
|
@@ -62,8 +96,13 @@ def create_app(
|
|
|
62
96
|
True,
|
|
63
97
|
)
|
|
64
98
|
try:
|
|
99
|
+
await runtime.probe()
|
|
65
100
|
await health()
|
|
66
|
-
return {
|
|
101
|
+
return {
|
|
102
|
+
"status": "ready",
|
|
103
|
+
"capabilities": list(runtime_engine.capabilities),
|
|
104
|
+
"inputModalities": list(runtime_engine.input_modalities),
|
|
105
|
+
}
|
|
67
106
|
except Exception:
|
|
68
107
|
return safe_error(
|
|
69
108
|
503,
|
|
@@ -111,6 +150,13 @@ def create_app(
|
|
|
111
150
|
) -> JSONResponse:
|
|
112
151
|
return safe_error(409, "RUN_CONFLICT", "幂等键已用于其他请求", False)
|
|
113
152
|
|
|
153
|
+
@application.exception_handler(ReleaseNotFoundError)
|
|
154
|
+
async def release_not_found(
|
|
155
|
+
_request: Request,
|
|
156
|
+
_error: ReleaseNotFoundError,
|
|
157
|
+
) -> JSONResponse:
|
|
158
|
+
return safe_error(404, "RUN_CONFLICT", "Agent Release 不存在", False)
|
|
159
|
+
|
|
114
160
|
@application.exception_handler(RunStoreCapacityError)
|
|
115
161
|
async def run_store_capacity(
|
|
116
162
|
_request: Request,
|
|
@@ -123,6 +169,21 @@ def create_app(
|
|
|
123
169
|
True,
|
|
124
170
|
)
|
|
125
171
|
|
|
172
|
+
@application.exception_handler(SafeEngineError)
|
|
173
|
+
async def safe_engine_error(
|
|
174
|
+
_request: Request,
|
|
175
|
+
error: SafeEngineError,
|
|
176
|
+
) -> JSONResponse:
|
|
177
|
+
status = 403 if error.code == "FORBIDDEN" else 503
|
|
178
|
+
if error.code in {"INVALID_INPUT", "BUDGET_EXCEEDED"}:
|
|
179
|
+
status = 400
|
|
180
|
+
return safe_error(
|
|
181
|
+
status,
|
|
182
|
+
error.code,
|
|
183
|
+
error.safe_message,
|
|
184
|
+
error.retryable,
|
|
185
|
+
)
|
|
186
|
+
|
|
126
187
|
@application.exception_handler(ValidationError)
|
|
127
188
|
async def validation_error(
|
|
128
189
|
_request: Request,
|
|
@@ -157,6 +218,28 @@ def create_app(
|
|
|
157
218
|
events_url=f"/api/agent/runs/{record.run_id}/events",
|
|
158
219
|
)
|
|
159
220
|
|
|
221
|
+
@application.get(
|
|
222
|
+
"/api/agent/runs/{run_id}",
|
|
223
|
+
response_model=RunStatusResponse,
|
|
224
|
+
)
|
|
225
|
+
async def get_run_status(
|
|
226
|
+
run_id: str,
|
|
227
|
+
authorization: Annotated[str | None, Header()] = None,
|
|
228
|
+
) -> RunStatusResponse | JSONResponse:
|
|
229
|
+
if not authorized(authorization, internal_token):
|
|
230
|
+
return safe_error(403, "FORBIDDEN", "内部调用认证失败", False)
|
|
231
|
+
record = await runtime.get(run_id)
|
|
232
|
+
terminal_event = cast(
|
|
233
|
+
dict[str, JsonValue] | None,
|
|
234
|
+
record.events[-1] if record.terminal and record.events else None,
|
|
235
|
+
)
|
|
236
|
+
return RunStatusResponse(
|
|
237
|
+
run_id=record.run_id,
|
|
238
|
+
status=record.status,
|
|
239
|
+
last_sequence=len(record.events),
|
|
240
|
+
terminal_event=terminal_event,
|
|
241
|
+
)
|
|
242
|
+
|
|
160
243
|
@application.get("/api/agent/runs/{run_id}/events", response_model=None)
|
|
161
244
|
async def get_events(
|
|
162
245
|
run_id: str,
|
|
@@ -217,107 +300,29 @@ def create_app(
|
|
|
217
300
|
record = await runtime.cancel(run_id)
|
|
218
301
|
return CancelRunResponse(run_id=record.run_id, status=record.status)
|
|
219
302
|
|
|
220
|
-
application.
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
def default_engine() -> AgentEngineAdapter:
|
|
225
|
-
config = OpenAiCompatibleConfig.from_environment()
|
|
226
|
-
if config is None:
|
|
227
|
-
return AdkEngineAdapter()
|
|
228
|
-
internal_token = os.environ.get("AGENT_SERVER_INTERNAL_TOKEN", "").strip()
|
|
229
|
-
if not internal_token:
|
|
230
|
-
raise ValueError("Agent Server Provider 已配置但内部 Token 缺失")
|
|
231
|
-
return AdkEngineAdapter(
|
|
232
|
-
OpenAiCompatibleProvider(
|
|
233
|
-
config,
|
|
234
|
-
EnvironmentSecretResolver(os.environ),
|
|
235
|
-
),
|
|
236
|
-
ToolHostClient(internal_token),
|
|
237
|
-
ProviderRequestGovernance(
|
|
238
|
-
ProviderAuditStore(
|
|
239
|
-
required_environment("AI_PROVIDER_AUDIT_STORE_PATH"),
|
|
240
|
-
retention_days=provider_audit_retention_days(),
|
|
241
|
-
)
|
|
242
|
-
),
|
|
243
|
-
)
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
def default_store() -> RunStore:
|
|
247
|
-
path = os.environ.get("AGENT_RUN_STORE_PATH", "").strip()
|
|
248
|
-
return SqliteRunStore(path) if path else InMemoryRunStore()
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
def required_environment(key: str) -> str:
|
|
252
|
-
value = os.environ.get(key, "").strip()
|
|
253
|
-
if not value:
|
|
254
|
-
raise ValueError(f"Provider 已配置但缺少 {key}")
|
|
255
|
-
return value
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
def provider_audit_retention_days() -> int:
|
|
259
|
-
raw = os.environ.get("AI_PROVIDER_AUDIT_RETENTION_DAYS", "90")
|
|
260
|
-
try:
|
|
261
|
-
value = int(raw)
|
|
262
|
-
except ValueError as error:
|
|
263
|
-
raise ValueError("AI_PROVIDER_AUDIT_RETENTION_DAYS 必须是整数") from error
|
|
264
|
-
if not 1 <= value <= 3650:
|
|
265
|
-
raise ValueError("AI_PROVIDER_AUDIT_RETENTION_DAYS 超出允许范围")
|
|
266
|
-
return value
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
def provider_governance_snapshot(
|
|
270
|
-
value: dict[str, int | float | str | None],
|
|
271
|
-
) -> dict[str, int | float | str | None]:
|
|
272
|
-
allowed = (
|
|
273
|
-
"status",
|
|
274
|
-
"retentionDays",
|
|
275
|
-
"retainedRecords",
|
|
276
|
-
"rejectedAttempts",
|
|
277
|
-
"redactedAttempts",
|
|
278
|
-
"lastAttemptAt",
|
|
279
|
-
"lastPurgeAt",
|
|
280
|
-
"lastDeletedRecords",
|
|
281
|
-
"totalDeletedRecords",
|
|
303
|
+
@application.post(
|
|
304
|
+
"/api/agent/evaluations",
|
|
305
|
+
response_model=EvaluationResultResponse,
|
|
282
306
|
)
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
header_cursor = int(last_event_id)
|
|
300
|
-
return max(after or 0, header_cursor)
|
|
301
|
-
|
|
307
|
+
async def record_evaluation(
|
|
308
|
+
request: EvaluationResultRequest,
|
|
309
|
+
authorization: Annotated[str | None, Header()] = None,
|
|
310
|
+
) -> EvaluationResultResponse | JSONResponse:
|
|
311
|
+
if not authorized(authorization, internal_token):
|
|
312
|
+
return safe_error(403, "FORBIDDEN", "内部调用认证失败", False)
|
|
313
|
+
evaluation_id = await runtime.record_evaluation(
|
|
314
|
+
request.release_id,
|
|
315
|
+
request.evaluation_set_key,
|
|
316
|
+
request.evaluation_set_version,
|
|
317
|
+
request.metrics,
|
|
318
|
+
)
|
|
319
|
+
return EvaluationResultResponse(
|
|
320
|
+
evaluation_id=evaluation_id,
|
|
321
|
+
release_id=request.release_id,
|
|
322
|
+
)
|
|
302
323
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
code: str,
|
|
306
|
-
message: str,
|
|
307
|
-
retryable: bool,
|
|
308
|
-
) -> JSONResponse:
|
|
309
|
-
error = AgentError.model_validate(
|
|
310
|
-
{
|
|
311
|
-
"code": code,
|
|
312
|
-
"message": message,
|
|
313
|
-
"requestId": f"request_{uuid.uuid4().hex}",
|
|
314
|
-
"retryable": retryable,
|
|
315
|
-
}
|
|
316
|
-
)
|
|
317
|
-
return JSONResponse(
|
|
318
|
-
status_code=status_code,
|
|
319
|
-
content=error.model_dump(by_alias=True),
|
|
320
|
-
)
|
|
324
|
+
application.state.run_service = runtime
|
|
325
|
+
return application
|
|
321
326
|
|
|
322
327
|
|
|
323
328
|
app = create_app(internal_token=os.environ.get("AGENT_SERVER_INTERNAL_TOKEN"))
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import base64
|
|
5
|
+
import hashlib
|
|
6
|
+
from collections.abc import Sequence
|
|
7
|
+
from io import BytesIO
|
|
8
|
+
from urllib.parse import urlsplit
|
|
9
|
+
|
|
10
|
+
import httpx
|
|
11
|
+
from PIL import Image, UnidentifiedImageError
|
|
12
|
+
|
|
13
|
+
from .config import private_hostname
|
|
14
|
+
from .contracts import RuntimeArtifact, RuntimeLimits
|
|
15
|
+
from .engine import PreparedArtifact, SafeEngineError
|
|
16
|
+
|
|
17
|
+
_IMAGE_TYPES = frozenset({"image/jpeg", "image/png", "image/webp"})
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ArtifactLoader:
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
artifact_url: str,
|
|
24
|
+
internal_token: str,
|
|
25
|
+
*,
|
|
26
|
+
transport: httpx.AsyncBaseTransport | None = None,
|
|
27
|
+
) -> None:
|
|
28
|
+
self.artifact_url = validate_artifact_url(artifact_url)
|
|
29
|
+
self.internal_token = internal_token
|
|
30
|
+
self.transport = transport
|
|
31
|
+
|
|
32
|
+
async def prepare(
|
|
33
|
+
self,
|
|
34
|
+
artifacts: list[RuntimeArtifact],
|
|
35
|
+
modalities: Sequence[str],
|
|
36
|
+
limits: RuntimeLimits,
|
|
37
|
+
) -> tuple[PreparedArtifact, ...]:
|
|
38
|
+
if not artifacts:
|
|
39
|
+
return ()
|
|
40
|
+
if (
|
|
41
|
+
"image" not in modalities
|
|
42
|
+
or limits.max_artifacts is None
|
|
43
|
+
or limits.max_pixels_per_artifact is None
|
|
44
|
+
):
|
|
45
|
+
raise invalid("Agent Operation 未授权图片输入")
|
|
46
|
+
if len(artifacts) > limits.max_artifacts:
|
|
47
|
+
raise budget("图片数量超过 Operation 上限")
|
|
48
|
+
declared = sum(item.byte_size for item in artifacts)
|
|
49
|
+
if declared > limits.max_input_bytes:
|
|
50
|
+
raise budget("图片总大小超过 Operation 上限")
|
|
51
|
+
prepared: list[PreparedArtifact] = []
|
|
52
|
+
total = 0
|
|
53
|
+
for artifact in artifacts:
|
|
54
|
+
if artifact.media_type.lower() not in _IMAGE_TYPES:
|
|
55
|
+
raise invalid("Agent Vision 仅接受 JPEG、PNG 或 WebP")
|
|
56
|
+
raw = await self._download(artifact, limits.max_input_bytes - total)
|
|
57
|
+
item = await asyncio.to_thread(
|
|
58
|
+
sanitize_image,
|
|
59
|
+
raw,
|
|
60
|
+
limits.max_pixels_per_artifact,
|
|
61
|
+
)
|
|
62
|
+
total += item.byte_size
|
|
63
|
+
if total > limits.max_input_bytes:
|
|
64
|
+
raise budget("归一化图片总大小超过 Operation 上限")
|
|
65
|
+
prepared.append(item)
|
|
66
|
+
return tuple(prepared)
|
|
67
|
+
|
|
68
|
+
async def _download(self, artifact: RuntimeArtifact, remaining: int) -> bytes:
|
|
69
|
+
body = bytearray()
|
|
70
|
+
digest = hashlib.sha256()
|
|
71
|
+
try:
|
|
72
|
+
async with httpx.AsyncClient(
|
|
73
|
+
timeout=httpx.Timeout(60, connect=5),
|
|
74
|
+
transport=self.transport,
|
|
75
|
+
) as client:
|
|
76
|
+
async with client.stream(
|
|
77
|
+
"GET",
|
|
78
|
+
self.artifact_url,
|
|
79
|
+
headers={
|
|
80
|
+
"authorization": f"Bearer {self.internal_token}",
|
|
81
|
+
"x-ai-artifact-ref": artifact.value,
|
|
82
|
+
},
|
|
83
|
+
) as response:
|
|
84
|
+
response.raise_for_status()
|
|
85
|
+
if response.headers.get("content-type", "").split(";")[0] != (
|
|
86
|
+
artifact.media_type
|
|
87
|
+
):
|
|
88
|
+
raise invalid("Artifact MIME 不一致")
|
|
89
|
+
async for chunk in response.aiter_bytes():
|
|
90
|
+
body.extend(chunk)
|
|
91
|
+
digest.update(chunk)
|
|
92
|
+
if len(body) > min(artifact.byte_size, remaining):
|
|
93
|
+
raise budget("Artifact 内容超过声明上限")
|
|
94
|
+
except SafeEngineError:
|
|
95
|
+
raise
|
|
96
|
+
except Exception as error:
|
|
97
|
+
raise SafeEngineError(
|
|
98
|
+
"RUNTIME_UNAVAILABLE",
|
|
99
|
+
"Agent Runtime 无法读取图片 Artifact",
|
|
100
|
+
retryable=True,
|
|
101
|
+
) from error
|
|
102
|
+
if len(body) != artifact.byte_size or digest.hexdigest() != artifact.sha256:
|
|
103
|
+
raise invalid("Artifact 完整性校验失败")
|
|
104
|
+
return bytes(body)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def sanitize_image(raw: bytes, max_pixels: int) -> PreparedArtifact:
|
|
108
|
+
try:
|
|
109
|
+
with Image.open(BytesIO(raw)) as source:
|
|
110
|
+
width, height = source.size
|
|
111
|
+
if width <= 0 or height <= 0 or width * height > max_pixels:
|
|
112
|
+
raise budget("图片像素超过 Operation 上限")
|
|
113
|
+
source.load()
|
|
114
|
+
normalized = source.convert(
|
|
115
|
+
"RGBA" if source.has_transparency_data else "RGB"
|
|
116
|
+
)
|
|
117
|
+
target = BytesIO()
|
|
118
|
+
normalized.save(target, format="PNG", optimize=False)
|
|
119
|
+
except SafeEngineError:
|
|
120
|
+
raise
|
|
121
|
+
except (Image.DecompressionBombError, UnidentifiedImageError, OSError):
|
|
122
|
+
raise invalid("图片内容损坏或格式不受支持") from None
|
|
123
|
+
body = target.getvalue()
|
|
124
|
+
digest = hashlib.sha256(body).hexdigest()
|
|
125
|
+
return PreparedArtifact(
|
|
126
|
+
media_type="image/png",
|
|
127
|
+
data_url=f"data:image/png;base64,{base64.b64encode(body).decode()}",
|
|
128
|
+
byte_size=len(body),
|
|
129
|
+
sha256=digest,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def validate_artifact_url(raw: str) -> str:
|
|
134
|
+
parsed = urlsplit(raw.strip())
|
|
135
|
+
if (
|
|
136
|
+
parsed.scheme not in {"http", "https"}
|
|
137
|
+
or not parsed.hostname
|
|
138
|
+
or parsed.username
|
|
139
|
+
or parsed.password
|
|
140
|
+
or parsed.query
|
|
141
|
+
or parsed.fragment
|
|
142
|
+
or not private_hostname(parsed.hostname.lower())
|
|
143
|
+
):
|
|
144
|
+
raise ValueError("Agent Artifact URL 必须是固定本地或内网地址")
|
|
145
|
+
return raw.rstrip("/")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def invalid(message: str) -> SafeEngineError:
|
|
149
|
+
return SafeEngineError("INVALID_INPUT", message, retryable=False)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def budget(message: str) -> SafeEngineError:
|
|
153
|
+
return SafeEngineError("BUDGET_EXCEEDED", message, retryable=False)
|
|
@@ -38,6 +38,8 @@ class OpenAiCompatibleConfig:
|
|
|
38
38
|
max_tool_rounds: int = 8
|
|
39
39
|
https_host_allowlist: tuple[str, ...] = ()
|
|
40
40
|
deployment_key: str = "ai.openai-compatible"
|
|
41
|
+
capabilities: tuple[str, ...] = ("text-generation",)
|
|
42
|
+
input_modalities: tuple[str, ...] = ("text",)
|
|
41
43
|
|
|
42
44
|
def __post_init__(self) -> None:
|
|
43
45
|
normalized = normalize_base_url(self.base_url)
|
|
@@ -89,6 +91,16 @@ class OpenAiCompatibleConfig:
|
|
|
89
91
|
1_024,
|
|
90
92
|
16 * 1024 * 1024,
|
|
91
93
|
),
|
|
94
|
+
capabilities=read_csv(
|
|
95
|
+
environment,
|
|
96
|
+
"AI_PROVIDER_CAPABILITIES",
|
|
97
|
+
("text-generation",),
|
|
98
|
+
),
|
|
99
|
+
input_modalities=read_csv(
|
|
100
|
+
environment,
|
|
101
|
+
"AI_PROVIDER_INPUT_MODALITIES",
|
|
102
|
+
("text",),
|
|
103
|
+
),
|
|
92
104
|
)
|
|
93
105
|
|
|
94
106
|
|
|
@@ -187,3 +199,20 @@ def read_integer(
|
|
|
187
199
|
if not minimum <= value <= maximum:
|
|
188
200
|
raise ValueError(f"{key} 超出允许范围")
|
|
189
201
|
return value
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def read_csv(
|
|
205
|
+
environment: Mapping[str, str],
|
|
206
|
+
key: str,
|
|
207
|
+
default: tuple[str, ...],
|
|
208
|
+
) -> tuple[str, ...]:
|
|
209
|
+
values = tuple(
|
|
210
|
+
dict.fromkeys(
|
|
211
|
+
item.strip()
|
|
212
|
+
for item in environment.get(key, ",".join(default)).split(",")
|
|
213
|
+
if item.strip()
|
|
214
|
+
)
|
|
215
|
+
)
|
|
216
|
+
if not values or len(values) > 32:
|
|
217
|
+
raise ValueError(f"{key} 无效")
|
|
218
|
+
return values
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from math import isfinite
|
|
3
4
|
from typing import Literal, Self
|
|
4
5
|
|
|
5
|
-
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
6
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
|
|
6
7
|
|
|
7
8
|
type JsonValue = (
|
|
8
9
|
str | int | float | bool | None | list[JsonValue] | dict[str, JsonValue]
|
|
@@ -15,6 +16,7 @@ type RunStatus = Literal[
|
|
|
15
16
|
"failed",
|
|
16
17
|
"cancelled",
|
|
17
18
|
]
|
|
19
|
+
type InputModality = Literal["text", "image", "audio", "video", "document"]
|
|
18
20
|
type AgentErrorCode = Literal[
|
|
19
21
|
"OPERATION_NOT_FOUND",
|
|
20
22
|
"OPERATION_DISABLED",
|
|
@@ -53,11 +55,17 @@ class RuntimeToolDefinition(CanonicalModel):
|
|
|
53
55
|
key: str = Field(min_length=1)
|
|
54
56
|
label: str = Field(min_length=1)
|
|
55
57
|
description: str = Field(min_length=1)
|
|
58
|
+
approval: Literal["none", "required"] = "none"
|
|
59
|
+
idempotency: Literal["none", "supported", "required"] = "supported"
|
|
60
|
+
approval_timeout_seconds: int | None = Field(default=None, ge=1, le=604_800)
|
|
56
61
|
|
|
57
62
|
|
|
58
63
|
class RuntimeLimits(CanonicalModel):
|
|
59
64
|
max_duration_seconds: int = Field(default=60, ge=1, le=300)
|
|
65
|
+
max_input_bytes: int = Field(default=1_048_576, ge=1_024, le=67_108_864)
|
|
60
66
|
max_output_bytes: int = Field(default=1_048_576, ge=1_024, le=16_777_216)
|
|
67
|
+
max_artifacts: int | None = Field(default=None, ge=1, le=16)
|
|
68
|
+
max_pixels_per_artifact: int | None = Field(default=None, ge=1, le=100_000_000)
|
|
61
69
|
|
|
62
70
|
|
|
63
71
|
class ProviderEgress(CanonicalModel):
|
|
@@ -78,6 +86,8 @@ type ProviderDataField = Literal[
|
|
|
78
86
|
"instructions",
|
|
79
87
|
"user-prompt",
|
|
80
88
|
"tool-definitions",
|
|
89
|
+
"artifact-content",
|
|
90
|
+
"output-schema",
|
|
81
91
|
"provider-output",
|
|
82
92
|
"tool-results",
|
|
83
93
|
]
|
|
@@ -112,11 +122,49 @@ class ToolNetworkPolicy(CanonicalModel):
|
|
|
112
122
|
mode: Literal["forbidden"] = "forbidden"
|
|
113
123
|
|
|
114
124
|
|
|
125
|
+
class RuntimeArtifact(CanonicalModel):
|
|
126
|
+
value: str = Field(min_length=1)
|
|
127
|
+
media_type: str = Field(min_length=1)
|
|
128
|
+
byte_size: int = Field(ge=1)
|
|
129
|
+
sha256: str = Field(pattern=r"^[a-f0-9]{64}$")
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class RuntimeRelease(CanonicalModel):
|
|
133
|
+
release_id: str = Field(min_length=1, max_length=80)
|
|
134
|
+
operation_version: str = Field(min_length=1, max_length=80)
|
|
135
|
+
workflow_version: str = Field(min_length=1, max_length=80)
|
|
136
|
+
prompt_version: str = Field(min_length=1, max_length=80)
|
|
137
|
+
route_version: str = Field(min_length=1, max_length=80)
|
|
138
|
+
tool_schema_version: str = Field(min_length=1, max_length=80)
|
|
139
|
+
manifest_digest: str = Field(pattern=r"^[a-f0-9]{64}$")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def legacy_release() -> RuntimeRelease:
|
|
143
|
+
return RuntimeRelease(
|
|
144
|
+
release_id="legacy",
|
|
145
|
+
operation_version="legacy",
|
|
146
|
+
workflow_version="legacy",
|
|
147
|
+
prompt_version="legacy",
|
|
148
|
+
route_version="legacy",
|
|
149
|
+
tool_schema_version="legacy",
|
|
150
|
+
manifest_digest="0" * 64,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
|
|
115
154
|
class StartRunRequest(CanonicalModel):
|
|
155
|
+
kind: Literal["agent"] = "agent"
|
|
116
156
|
operation_key: str = Field(min_length=1)
|
|
117
157
|
input: JsonValue
|
|
158
|
+
input_modalities: list[InputModality] = Field(
|
|
159
|
+
default_factory=lambda: default_modalities(),
|
|
160
|
+
min_length=1,
|
|
161
|
+
)
|
|
162
|
+
required_capabilities: list[str] = Field(default_factory=list, max_length=32)
|
|
163
|
+
artifacts: list[RuntimeArtifact] = Field(default_factory=list, max_length=16)
|
|
118
164
|
instructions: str = ""
|
|
119
165
|
user_prompt: str = ""
|
|
166
|
+
output_schema_key: str = ""
|
|
167
|
+
output_schema: dict[str, JsonValue] | None = None
|
|
120
168
|
allowed_tools: list[RuntimeToolDefinition] = Field(default_factory=list)
|
|
121
169
|
limits: RuntimeLimits = Field(default_factory=RuntimeLimits)
|
|
122
170
|
provider_egress: ProviderEgress = Field(default_factory=ProviderEgress)
|
|
@@ -124,6 +172,8 @@ class StartRunRequest(CanonicalModel):
|
|
|
124
172
|
tool_network_policy: ToolNetworkPolicy = Field(default_factory=ToolNetworkPolicy)
|
|
125
173
|
execution_grant: str = ""
|
|
126
174
|
tool_host_url: str = ""
|
|
175
|
+
release: RuntimeRelease = Field(default_factory=legacy_release)
|
|
176
|
+
trace_id: str = Field(default="", max_length=160)
|
|
127
177
|
|
|
128
178
|
|
|
129
179
|
class StartRunResponse(CanonicalModel):
|
|
@@ -132,6 +182,13 @@ class StartRunResponse(CanonicalModel):
|
|
|
132
182
|
events_url: str
|
|
133
183
|
|
|
134
184
|
|
|
185
|
+
class RunStatusResponse(CanonicalModel):
|
|
186
|
+
run_id: str
|
|
187
|
+
status: RunStatus
|
|
188
|
+
last_sequence: int = Field(ge=0)
|
|
189
|
+
terminal_event: dict[str, JsonValue] | None = None
|
|
190
|
+
|
|
191
|
+
|
|
135
192
|
class RunResultResponse(CanonicalModel):
|
|
136
193
|
run_id: str
|
|
137
194
|
status: Literal["completed"]
|
|
@@ -152,3 +209,26 @@ class AgentError(CanonicalModel):
|
|
|
152
209
|
message: str
|
|
153
210
|
request_id: str
|
|
154
211
|
retryable: bool
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class EvaluationResultRequest(CanonicalModel):
|
|
215
|
+
release_id: str = Field(min_length=1, max_length=80)
|
|
216
|
+
evaluation_set_key: str = Field(min_length=1, max_length=160)
|
|
217
|
+
evaluation_set_version: str = Field(min_length=1, max_length=80)
|
|
218
|
+
metrics: dict[str, float] = Field(min_length=1, max_length=100)
|
|
219
|
+
|
|
220
|
+
@field_validator("metrics")
|
|
221
|
+
@classmethod
|
|
222
|
+
def validate_metrics(cls, metrics: dict[str, float]) -> dict[str, float]:
|
|
223
|
+
if not all(isfinite(value) for value in metrics.values()):
|
|
224
|
+
raise ValueError("评估指标必须是有限数值")
|
|
225
|
+
return metrics
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class EvaluationResultResponse(CanonicalModel):
|
|
229
|
+
evaluation_id: str
|
|
230
|
+
release_id: str
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def default_modalities() -> list[InputModality]:
|
|
234
|
+
return ["text"]
|
|
@@ -20,6 +20,14 @@ class DeterministicEngineAdapter:
|
|
|
20
20
|
def __init__(self) -> None:
|
|
21
21
|
self._cancellations: dict[str, asyncio.Event] = {}
|
|
22
22
|
|
|
23
|
+
@property
|
|
24
|
+
def capabilities(self) -> tuple[str, ...]:
|
|
25
|
+
return ("text-generation",)
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def input_modalities(self) -> tuple[str, ...]:
|
|
29
|
+
return ("text",)
|
|
30
|
+
|
|
23
31
|
def supports(self, operation_key: str) -> bool:
|
|
24
32
|
return operation_key in self.operations
|
|
25
33
|
|