agentengine-sdk-python 0.8.0__py3-none-any.whl → 0.8.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/METADATA +43 -7
- agentengine_sdk_python-0.8.2.dist-info/RECORD +778 -0
- {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/WHEEL +1 -1
- ksadk/a2a/__init__.py +2 -0
- ksadk/a2a/_space_client_events.py +381 -0
- ksadk/a2a/bootstrap.py +0 -2
- ksadk/a2a/card.py +6 -111
- ksadk/a2a/control_plane.py +239 -1
- ksadk/a2a/event_adapter.py +156 -64
- ksadk/a2a/executor.py +195 -172
- ksadk/a2a/routes.py +12 -5
- ksadk/a2a/server.py +1 -8
- ksadk/a2a/service_env.py +33 -0
- ksadk/a2a/space_client.py +43 -349
- ksadk/a2a_card.py +101 -0
- ksadk/a2ui/core.py +222 -53
- ksadk/agui/_agent_helpers.py +243 -0
- ksadk/agui/a2ui_projection.py +12 -5
- ksadk/agui/agent.py +474 -275
- ksadk/agui/config.py +11 -5
- ksadk/agui/routes.py +7 -15
- ksadk/api/client.py +530 -40
- ksadk/api/environment.py +27 -0
- ksadk/builders/code_builder.py +352 -53
- ksadk/builders/container_builder.py +71 -44
- ksadk/builders/framework_requirements.py +2 -1
- ksadk/builders/ks3_uploader.py +2 -2
- ksadk/builders/managed_runtime_builder.py +60 -28
- ksadk/cli/__init__.py +25 -0
- ksadk/cli/cmd_a2a.py +18 -31
- ksadk/cli/cmd_create.py +13 -26
- ksadk/cli/cmd_dashboard.py +15 -1
- ksadk/cli/cmd_deploy.py +28 -3
- ksadk/cli/cmd_eval.py +443 -0
- ksadk/cli/cmd_evalset.py +284 -0
- ksadk/cli/cmd_files.py +5 -1
- ksadk/cli/cmd_hermes.py +137 -163
- ksadk/cli/cmd_invoke.py +28 -38
- ksadk/cli/cmd_launch.py +12 -0
- ksadk/cli/cmd_managed_runtime.py +95 -0
- ksadk/cli/cmd_observe.py +85 -0
- ksadk/cli/cmd_openclaw.py +85 -479
- ksadk/cli/cmd_replay.py +28 -4
- ksadk/cli/cmd_run.py +29 -67
- ksadk/cli/cmd_studio.py +201 -0
- ksadk/cli/cmd_web.py +15 -6
- ksadk/cli/dry_run.py +2 -1
- ksadk/cli/env_options.py +83 -0
- ksadk/cli/global_options.py +3 -1
- ksadk/cli/hermes_env.py +151 -0
- ksadk/cli/invoke_payload.py +49 -0
- ksadk/cli/network_options.py +7 -5
- ksadk/cli/openclaw_env.py +458 -0
- ksadk/cli/runtime_bootstrap.py +51 -0
- ksadk/cli/ui.py +6 -0
- ksadk/cli/workflow_common.py +5 -5
- ksadk/codex/__init__.py +3 -3
- ksadk/codex/client.py +833 -49
- ksadk/codex/phase.py +1 -1
- ksadk/codex/runtime.py +720 -127
- ksadk/common/kop_client.py +232 -0
- ksadk/configs/__init__.py +1 -3
- ksadk/configs/env_registry.py +244 -45
- ksadk/configs/env_registry_pcm.py +181 -0
- ksadk/configs/env_var_spec.py +18 -0
- ksadk/configs/global_config.py +6 -1
- ksadk/configs/settings.py +5 -108
- ksadk/context_engine/__init__.py +98 -0
- ksadk/context_engine/assembler.py +176 -0
- ksadk/context_engine/baseline.py +419 -0
- ksadk/context_engine/cache_observability.py +228 -0
- ksadk/context_engine/capabilities.py +443 -0
- ksadk/context_engine/contributors.py +344 -0
- ksadk/context_engine/hosted_pipeline.py +417 -0
- ksadk/context_engine/models.py +117 -0
- ksadk/context_engine/planner.py +698 -0
- ksadk/context_engine/policies.py +319 -0
- ksadk/context_engine/projection.py +31 -0
- ksadk/context_engine/shadow_plan.py +266 -0
- ksadk/context_engine/tokenizer.py +164 -0
- ksadk/conversations/__init__.py +85 -94
- ksadk/conversations/context.py +126 -6
- ksadk/conversations/message_projection.py +292 -28
- ksadk/conversations/model_context.py +49 -3
- ksadk/conversations/runtime_compaction.py +207 -5
- ksadk/conversations/runtime_input.py +193 -3
- ksadk/conversations/runtime_invocation.py +81 -3
- ksadk/conversations/runtime_observability.py +157 -4
- ksadk/conversations/runtime_payloads.py +42 -0
- ksadk/conversations/runtime_preparation.py +280 -2
- ksadk/conversations/runtime_resume.py +71 -32
- ksadk/conversations/runtime_stream_events.py +94 -9
- ksadk/conversations/runtime_streaming.py +175 -22
- ksadk/conversations/semantic_summary.py +311 -1
- ksadk/conversations/session_lock.py +51 -0
- ksadk/deployment/env_forward.py +72 -0
- ksadk/deployment/managed_runtime.py +38 -0
- ksadk/deployment/manager.py +24 -8
- ksadk/deployment/providers/serverless.py +228 -92
- ksadk/evaluation/__init__.py +116 -0
- ksadk/evaluation/a2a_adapter.py +452 -0
- ksadk/evaluation/adapters.py +67 -0
- ksadk/evaluation/agent_eval_client.py +395 -0
- ksadk/evaluation/cloud_binding.py +80 -0
- ksadk/evaluation/cloud_converter.py +194 -0
- ksadk/evaluation/cloud_service.py +199 -0
- ksadk/evaluation/contracts.py +478 -0
- ksadk/evaluation/evalset.py +351 -0
- ksadk/evaluation/evaluators.py +627 -0
- ksadk/evaluation/evidence.py +258 -0
- ksadk/evaluation/executor.py +143 -0
- ksadk/evaluation/local_adapter.py +550 -0
- ksadk/evaluation/service_env.py +27 -0
- ksadk/evaluation/storage.py +68 -0
- ksadk/evaluation/studio_build_adapter.py +253 -0
- ksadk/evaluation/target.py +82 -0
- ksadk/events/__init__.py +13 -13
- ksadk/events/_v1_compat/__init__.py +1 -0
- ksadk/events/_v1_compat/models.py +299 -0
- ksadk/events/_v1_compat/parser.py +247 -0
- ksadk/events/_v1_compat/projection.py +753 -0
- ksadk/events/adapters/__init__.py +34 -0
- ksadk/events/adapters/_a2a_snapshot.py +622 -0
- ksadk/events/adapters/_a2a_support.py +275 -0
- ksadk/events/adapters/_codex_interactions.py +375 -0
- ksadk/events/adapters/_codex_items.py +801 -0
- ksadk/events/adapters/_codex_validators.py +311 -0
- ksadk/events/adapters/_langgraph_support.py +785 -0
- ksadk/events/adapters/a2a.py +841 -0
- ksadk/events/adapters/adk.py +503 -0
- ksadk/events/adapters/codex.py +717 -0
- ksadk/events/adapters/langgraph.py +745 -0
- ksadk/events/canonical.py +440 -0
- ksadk/events/canonical_replay.py +497 -0
- ksadk/events/canonical_store.py +468 -0
- ksadk/events/cold_recovery.py +257 -0
- ksadk/events/content.py +89 -0
- ksadk/events/identity.py +86 -0
- ksadk/events/pipeline.py +455 -0
- ksadk/events/projections.py +66 -0
- ksadk/events/reducer.py +661 -0
- ksadk/events/replay.py +3 -36
- ksadk/events/runtime_event.py +20 -256
- ksadk/events/session_event.py +324 -0
- ksadk/events/store.py +3 -316
- ksadk/events/v1_compat.py +41 -0
- ksadk/harness/__init__.py +3 -2
- ksadk/harness/app.py +114 -123
- ksadk/harness/config.py +1 -1
- ksadk/harness/runtime.py +467 -0
- ksadk/hermes_terminal.py +5 -0
- ksadk/identity/resolver.py +4 -3
- ksadk/interaction/__init__.py +28 -0
- ksadk/interaction/contracts.py +136 -0
- ksadk/interaction/ledger.py +189 -0
- ksadk/interaction/provider.py +122 -0
- ksadk/interaction/providers/__init__.py +52 -0
- ksadk/interaction/providers/adk.py +40 -0
- ksadk/interaction/providers/codex.py +94 -0
- ksadk/interaction/providers/langgraph.py +58 -0
- ksadk/kernel/__init__.py +117 -0
- ksadk/kernel/authorization.py +258 -0
- ksadk/kernel/bootstrap.py +1074 -0
- ksadk/kernel/contract_fingerprints.py +61 -0
- ksadk/kernel/contracts.py +362 -0
- ksadk/kernel/control.py +219 -0
- ksadk/kernel/errors.py +79 -0
- ksadk/kernel/ingress.py +1102 -0
- ksadk/kernel/mapping.py +111 -0
- ksadk/kernel/memory_store.py +1133 -0
- ksadk/kernel/postgres_store.py +1756 -0
- ksadk/kernel/recovery.py +452 -0
- ksadk/kernel/runtime_identity.py +103 -0
- ksadk/kernel/sql/001_agent_kernel.sql +120 -0
- ksadk/kernel/sqlite_store.py +1410 -0
- ksadk/kernel/state.py +145 -0
- ksadk/kernel/store.py +232 -0
- ksadk/kernel/worker.py +953 -0
- ksadk/knowledge_base/client.py +22 -4
- ksadk/knowledge_base/service.py +32 -5
- ksadk/managed_a2a_card.py +160 -0
- ksadk/managed_runtime.py +16 -9
- ksadk/memory/__init__.py +61 -0
- ksadk/memory/adk/backends/base_ltm_backend.py +96 -1
- ksadk/memory/adk/backends/http_ltm_backend.py +14 -5
- ksadk/memory/adk/backends/inmemory_ltm_backend.py +136 -3
- ksadk/memory/adk/backends/sdk_ltm_backend.py +394 -5
- ksadk/memory/adk/backends/sqlite_ltm_backend.py +95 -0
- ksadk/memory/adk/long_term_memory.py +3 -2
- ksadk/memory/coordinator.py +461 -0
- ksadk/memory/events.py +202 -0
- ksadk/memory/extraction.py +228 -0
- ksadk/memory/ltm_backend_factory.py +16 -3
- ksadk/memory/models.py +316 -0
- ksadk/memory/policy.py +215 -0
- ksadk/memory/provider.py +47 -0
- ksadk/memory/provider_adapter.py +159 -0
- ksadk/memory/provider_resolver.py +71 -0
- ksadk/memory/providers/__init__.py +5 -0
- ksadk/memory/providers/local_sqlite.py +490 -0
- ksadk/memory/resolved_policy.py +145 -0
- ksadk/memory/service.py +198 -8
- ksadk/memory/tool.py +4 -0
- ksadk/model_proxy/bootstrap.py +16 -9
- ksadk/model_proxy/config.py +22 -1
- ksadk/model_proxy/detect.py +202 -9
- ksadk/model_proxy/namespace.py +67 -15
- ksadk/model_proxy/server.py +168 -6
- ksadk/model_proxy/transform.py +105 -3
- ksadk/observability/__init__.py +25 -0
- ksadk/observability/session_log.py +420 -0
- ksadk/observability/trajectory.py +105 -0
- ksadk/prompts/__init__.py +71 -0
- ksadk/prompts/compiler.py +215 -0
- ksadk/prompts/models.py +82 -0
- ksadk/prompts/projection.py +89 -0
- ksadk/prompts/resolved.py +137 -0
- ksadk/prompts/sources.py +233 -0
- ksadk/runners/_langgraph_runner_streams.py +822 -0
- ksadk/runners/adk_runner.py +14 -31
- ksadk/runners/base_runner.py +44 -3
- ksadk/runners/factory.py +3 -3
- ksadk/runners/langgraph_runner.py +252 -433
- ksadk/runners/utils/__init__.py +0 -10
- ksadk/runners/utils/loader.py +16 -0
- ksadk/runtime/__init__.py +67 -41
- ksadk/runtime/_runner_adapter/__init__.py +1 -0
- ksadk/runtime/_runner_adapter/stream_mapping.py +788 -0
- ksadk/runtime/adapter.py +199 -16
- ksadk/runtime/conversation_execution.py +687 -0
- ksadk/runtime/executor.py +392 -0
- ksadk/runtime/factory.py +342 -0
- ksadk/runtime/framework_adapters.py +6 -12
- ksadk/runtime/hosted_finalizer.py +268 -0
- ksadk/runtime/launch.py +61 -0
- ksadk/runtime/preprocessing.py +49 -14
- ksadk/runtime/runner_adapter.py +330 -391
- ksadk/runtime/runner_loading.py +36 -0
- ksadk/runtime/usage.py +33 -0
- ksadk/sandbox/backends/e2b.py +36 -3
- ksadk/server/__init__.py +39 -4
- ksadk/server/app.py +18 -492
- ksadk/server/composition.py +36 -2
- ksadk/server/factory.py +210 -149
- ksadk/server/routes/common.py +15 -46
- ksadk/server/routes/control.py +13 -9
- ksadk/server/routes/kernel_ingress.py +258 -0
- ksadk/server/routes/misc.py +10 -33
- ksadk/server/routes/models.py +14 -6
- ksadk/server/routes/openai_compat.py +129 -27
- ksadk/server/routes/projection.py +84 -16
- ksadk/server/routes/run.py +347 -460
- ksadk/server/routes/sessions.py +37 -40
- ksadk/server/routes/streaming.py +52 -7
- ksadk/server/routes/workspace.py +54 -21
- ksadk/server/static/assets/{ArtifactsPanel-Cjnqt-8W.js → ArtifactsPanel-DSU1sWD_.js} +1 -1
- ksadk/server/static/assets/{CodeBlock-BqyqZfuB.js → CodeBlock-DOH6MVcn.js} +4 -4
- ksadk/server/static/assets/{MathMessageMarkdown-BpH7jiFM.js → MathMessageMarkdown-BL8my1R2.js} +4 -4
- ksadk/server/static/assets/MathMessageMarkdown-BorAY7qD.css +1 -0
- ksadk/server/static/assets/MermaidBlock-Dz4IP-Tx.js +312 -0
- ksadk/server/static/assets/NativeTerminalPanel-DUrK0JpZ.js +1 -0
- ksadk/server/static/assets/abnfDiagram-VCTEODGH-g20pFzNV.js +1 -0
- ksadk/server/static/assets/arc-C4FzinUA.js +1 -0
- ksadk/server/static/assets/architecture-7GRP2DOG-DjKratdf.js +1 -0
- ksadk/server/static/assets/architectureDiagram-5GKGNRK7-DpIqL5h4.js +36 -0
- ksadk/server/static/assets/blockDiagram-NRAW4CY4-BjkljTNz.js +129 -0
- ksadk/server/static/assets/c4Diagram-UCG6FXSJ-Dk_ieq2X.js +38 -0
- ksadk/server/static/assets/channel-4cQHKtx1.js +1 -0
- ksadk/server/static/assets/chunk-2Q5K7J3B-DmgWkESh.js +1 -0
- ksadk/server/static/assets/{chunk-FMBD7UC4-BGhvzDYt.js → chunk-5VM5RSS4-BVHAchFb.js} +1 -1
- ksadk/server/static/assets/{chunk-ND2GUHAM-D98vPd-E.js → chunk-F27PBJKO-C-ipQzuS.js} +1 -1
- ksadk/server/static/assets/chunk-G27WJ6UU-C2EyJbIK.js +231 -0
- ksadk/server/static/assets/{chunk-4BX2VUAB-CsWIQRhc.js → chunk-JWPE2WC7-vYvVJb_M.js} +1 -1
- ksadk/server/static/assets/chunk-LCL6LL3I-BcI6ysPT.js +206 -0
- ksadk/server/static/assets/chunk-POPQ4Y6H-C030x_Z1.js +1 -0
- ksadk/server/static/assets/chunk-RHFEMEQ7-yyGpsz4n.js +168 -0
- ksadk/server/static/assets/chunk-SVP7TREG-BLTlmMU7.js +88 -0
- ksadk/server/static/assets/chunk-XXDRQBXY-C_32ArgP.js +1 -0
- ksadk/server/static/assets/classDiagram-DTDB5LWJ-DLFd9Xi0.js +1 -0
- ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-DLFd9Xi0.js +1 -0
- ksadk/server/static/assets/{cose-bilkent-S5V4N54A-CX3yotHq.js → cose-bilkent-JH36ORCC-9YQYwdl0.js} +1 -1
- ksadk/server/static/assets/cynefin-OW5HDTMX-DjEM48Qp.js +1 -0
- ksadk/server/static/assets/cynefinDiagram-5FMLGOSQ-ErLF5N13.js +62 -0
- ksadk/server/static/assets/cytoscape.esm-CyCl8rPi.js +321 -0
- ksadk/server/static/assets/dagre-3AP2YEHR-DFiQF-6f.js +4 -0
- ksadk/server/static/assets/dagre-BuhGnRI1.js +1 -0
- ksadk/server/static/assets/diagram-S7CK7UJ4-Bt1v8-GC.js +30 -0
- ksadk/server/static/assets/diagram-UQ7AKVKN-DSCxJdBK.js +41 -0
- ksadk/server/static/assets/diagram-VSXAHHWV-DHfYwp_9.js +3 -0
- ksadk/server/static/assets/diagram-VX7I27RA-DdzD-4Le.js +24 -0
- ksadk/server/static/assets/diagram-Z3DM3KII-Du-nAJ9F.js +24 -0
- ksadk/server/static/assets/{dist-D82mtD2s.js → dist-8hqcx0sU.js} +1 -1
- ksadk/server/static/assets/dist-B7seoj3d.js +1 -0
- ksadk/server/static/assets/{dist-DHn6nBzr.js → dist-BE7bu-DL.js} +1 -1
- ksadk/server/static/assets/{dist-eoW6tZXA.js → dist-BVDffZ0U.js} +1 -1
- ksadk/server/static/assets/{dist-C2xjE7cO.js → dist-BX8z72IC.js} +1 -1
- ksadk/server/static/assets/{dist-CgF3fj15.js → dist-Bf2M8m3N.js} +1 -1
- ksadk/server/static/assets/{dist-Bs-jQcRX.js → dist-BjU6y-A2.js} +1 -1
- ksadk/server/static/assets/{dist-Djgl2wVZ.js → dist-BytlxIDT.js} +1 -1
- ksadk/server/static/assets/{dist-CVQRZjPu.js → dist-CSh_DE14.js} +1 -1
- ksadk/server/static/assets/{dist-6CVKuTAe.js → dist-CXYYBw3_.js} +1 -1
- ksadk/server/static/assets/{dist-DlzCRy0S.js → dist-C_wsv-Qd.js} +1 -1
- ksadk/server/static/assets/{dist-DF06ORQo.js → dist-ClxGviKw.js} +1 -1
- ksadk/server/static/assets/{dist-Cf23NY4L.js → dist-CttYUqzS.js} +1 -1
- ksadk/server/static/assets/{dist-ivOya1LD.js → dist-DNp6rLRA.js} +1 -1
- ksadk/server/static/assets/{dist-BibcG1V9.js → dist-DUX-id_F.js} +1 -1
- ksadk/server/static/assets/{dist-BUHRxvDG.js → dist-DmldrHd_.js} +1 -1
- ksadk/server/static/assets/{dist-BZ9n9AjO.js → dist-DnfXs8Vn.js} +2 -2
- ksadk/server/static/assets/{dist-DqNnNFrw.js → dist-Dq9gLD7L.js} +1 -1
- ksadk/server/static/assets/{dist-CGcp2ex8.js → dist-W-TIVntx.js} +1 -1
- ksadk/server/static/assets/{dist-Cwt7koer.js → dist-dDdADKFA.js} +1 -1
- ksadk/server/static/assets/{dist-C7-3jInC.js → dist-dydu68Fl.js} +1 -1
- ksadk/server/static/assets/{dist-C99THtIA.js → dist-ngtN0DJB.js} +1 -1
- ksadk/server/static/assets/ebnfDiagram-PWID7BFC-Da9C9NO1.js +1 -0
- ksadk/server/static/assets/erDiagram-SSCWMZ5O-EAGqqR79.js +99 -0
- ksadk/server/static/assets/eventmodeling-NTZA5JFV-CMgMaJrC.js +1 -0
- ksadk/server/static/assets/flowDiagram-A5DVABFB-BBKL0x3P.js +1 -0
- ksadk/server/static/assets/ganttDiagram-EL5Y4UJY-DqJsKb59.js +292 -0
- ksadk/server/static/assets/gitGraph-4MIJSDKK-Dw63mrhw.js +1 -0
- ksadk/server/static/assets/gitGraphDiagram-WWUBYQGX-B6g4dtDi.js +106 -0
- ksadk/server/static/assets/index-8ipRcQ-M.js +240 -0
- ksadk/server/static/assets/index-ByFCqjlh.css +2 -0
- ksadk/server/static/assets/info-A6RAGUB7-B6iAblII.js +1 -0
- ksadk/server/static/assets/infoDiagram-RXCK75RN-Djm_nbd5.js +2 -0
- ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-DLOHGvWw.js → ishikawaDiagram-5VMMS53U-DWya9SG2.js} +6 -6
- ksadk/server/static/assets/{journeyDiagram-JHISSGLW-BIzKeejT.js → journeyDiagram-EYS64GPL-DutxhSNI.js} +6 -6
- ksadk/server/static/assets/{kanban-definition-UN3LZRKU-BWHoFSw2.js → kanban-definition-3QL26DDD-DJdOF8Fm.js} +10 -10
- ksadk/server/static/assets/katex-vFWytM5c.js +257 -0
- ksadk/server/static/assets/{linear-CvMBw9cA.js → linear-BHjG8b-J.js} +1 -1
- ksadk/server/static/assets/mermaid-parser.core-KGSy4jWT.js +166 -0
- ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-CSAQxEAe.js → mindmap-definition-FBJOCRG2-9-HC88D4.js} +29 -29
- ksadk/server/static/assets/packet-AYTQ26CC-BOYAipuq.js +1 -0
- ksadk/server/static/assets/pegDiagram-XKGWAZYB-DrEZd4fD.js +1 -0
- ksadk/server/static/assets/pie-WAS4IAKB-Ctq0Kbku.js +1 -0
- ksadk/server/static/assets/pieDiagram-E7YTZNPT-DpjqOFFp.js +39 -0
- ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-CxIR4JXI.js → quadrantDiagram-AXDQQJYC-BiNhgOXX.js} +6 -6
- ksadk/server/static/assets/radar-RG4KPBEZ-DN0-ul_0.js +1 -0
- ksadk/server/static/assets/railroad-74A4TZTK-C6cC_to0.js +1 -0
- ksadk/server/static/assets/railroad-abnf-HS5TGJTU-BimjIuRc.js +1 -0
- ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-CvY65ABq.js +1 -0
- ksadk/server/static/assets/railroad-peg-WCYAUIDC-D72Iq_Tv.js +1 -0
- ksadk/server/static/assets/railroadDiagram-O6MQD6OU-_Y3Ojg6G.js +1 -0
- ksadk/server/static/assets/requirementDiagram-EFPCY7ZU-DeCFdg9K.js +84 -0
- ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-DpGa6EAV.js → sankeyDiagram-P5KCCOFB-CPH75rhw.js} +7 -7
- ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-NTKNGGe5.js → sequenceDiagram-WJ2MYXX4-OEpQQdr4.js} +10 -10
- ksadk/server/static/assets/sizeCapture-X5ZJPWSS-heUErzhI.js +1 -0
- ksadk/server/static/assets/stateDiagram-HBIQ2CUA-DvA3jSMB.js +1 -0
- ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BgQY03nz.js +1 -0
- ksadk/server/static/assets/swimlanes-XN3QIQJK-CR-dfN_t.js +1 -0
- ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-DLVw3q5Q.js +8 -0
- ksadk/server/static/assets/{timeline-definition-PNZ67QCA-jqoYs3jQ.js → timeline-definition-24CTP7MA-DAVJf1TX.js} +7 -7
- ksadk/server/static/assets/treeView-Q6P3EWNA-BMpO0wsB.js +1 -0
- ksadk/server/static/assets/treemap-WGGIJYW6-Et0aqzU3.js +1 -0
- ksadk/server/static/assets/vennDiagram-4TSXK5OY-DwM8eDkh.js +34 -0
- ksadk/server/static/assets/wardley-WFR3VGLG-CbY0mhpl.js +1 -0
- ksadk/server/static/assets/wardleyDiagram-VM6X3IG4-DZL-Zz2t.js +78 -0
- ksadk/server/static/assets/xychartDiagram-S5SC5T6Z-Dy6Yh_hu.js +7 -0
- ksadk/server/static/index.html +2 -2
- ksadk/sessions/__init__.py +26 -9
- ksadk/sessions/_local_service_sync.py +688 -0
- ksadk/sessions/_local_tables.py +21 -0
- ksadk/sessions/_postgres_schema.py +305 -0
- ksadk/sessions/_postgres_tables.py +15 -0
- ksadk/sessions/base.py +80 -1
- ksadk/sessions/in_memory.py +53 -2
- ksadk/sessions/local_service.py +63 -604
- ksadk/sessions/postgres_service.py +58 -148
- ksadk/sessions/resilient.py +35 -1
- ksadk/skills/runtime/backends/e2b.py +1 -1
- ksadk/skills/service_client.py +13 -3
- ksadk/studio/__init__.py +5 -0
- ksadk/studio/agent_avatar_assets.py +120 -0
- ksadk/studio/agent_lifecycle.py +51 -0
- ksadk/studio/api.py +1598 -0
- ksadk/studio/api_catalog_routes.py +324 -0
- ksadk/studio/api_contracts.py +286 -0
- ksadk/studio/api_helpers.py +132 -0
- ksadk/studio/api_memory_routes.py +82 -0
- ksadk/studio/authoring.py +749 -0
- ksadk/studio/authoring_coordinator.py +1017 -0
- ksadk/studio/builder.py +397 -0
- ksadk/studio/capabilities.py +368 -0
- ksadk/studio/cloud.py +2099 -0
- ksadk/studio/codex_agent_service.py +694 -0
- ksadk/studio/codex_authoring.py +469 -0
- ksadk/studio/codex_builder.py +437 -0
- ksadk/studio/codex_manifest.py +307 -0
- ksadk/studio/codex_run.py +395 -0
- ksadk/studio/compiler.py +190 -0
- ksadk/studio/contracts.py +755 -0
- ksadk/studio/errors.py +47 -0
- ksadk/studio/event_store.py +214 -0
- ksadk/studio/framework_run.py +342 -0
- ksadk/studio/hosted_kernel.py +317 -0
- ksadk/studio/identifiers.py +33 -0
- ksadk/studio/manifest_resolver.py +131 -0
- ksadk/studio/mcp_runtime.py +229 -0
- ksadk/studio/model_client.py +629 -0
- ksadk/studio/model_profile_service.py +214 -0
- ksadk/studio/operations.py +275 -0
- ksadk/studio/otel_trace.py +1060 -0
- ksadk/studio/pagination.py +182 -0
- ksadk/studio/pcm_memory.py +85 -0
- ksadk/studio/python_tool_inspection.py +183 -0
- ksadk/studio/repository.py +327 -0
- ksadk/studio/resource_catalog.py +1260 -0
- ksadk/studio/run_service.py +1133 -0
- ksadk/studio/runtime_catalog.py +74 -0
- ksadk/studio/runtime_source.py +365 -0
- ksadk/studio/service.py +2249 -0
- ksadk/studio/shared_web.py +1004 -0
- ksadk/studio/skill_discovery.py +628 -0
- ksadk/studio/static/assets/PrismRenderer-IYN-ffww.js +3 -0
- ksadk/studio/static/assets/index-BLpcdrgW.css +1 -0
- ksadk/studio/static/assets/index-CYekR2Xv.js +260 -0
- ksadk/studio/static/assets/react-vendor-BNmTiJ-I.js +9 -0
- ksadk/studio/static/assets/rolldown-runtime-hePW80VL.js +1 -0
- ksadk/studio/static/index.html +15 -0
- ksadk/studio/templates.py +722 -0
- ksadk/studio/validator.py +265 -0
- ksadk/studio/workspace.py +135 -0
- ksadk/tools/gateway.py +9 -0
- ksadk/toolsets/__init__.py +27 -1
- ksadk/toolsets/a2a.py +426 -0
- ksadk/tracing/__init__.py +19 -21
- ksadk/tracing/setup.py +317 -403
- ksadk/tracing/span_utils.py +0 -44
- ksadk/version.py +1 -1
- ksadk_runtime_common/memory_backend/registry.py +56 -56
- ksadk_runtime_common/schemas/runtime_event_v1.json +37 -0
- ksadk_runtime_common/schemas/runtime_event_v2.json +271 -0
- ksadk_runtime_common/workspace_files/router.py +8 -2
- agentengine_sdk_python-0.8.0.dist-info/RECORD +0 -578
- ksadk/a2a/card_builder.py +0 -64
- ksadk/a2a/client.py +0 -309
- ksadk/events/parser.py +0 -191
- ksadk/harness/runner.py +0 -211
- ksadk/runners/codex_runner.py +0 -132
- ksadk/runners/utils/langfuse.py +0 -230
- ksadk/server/static/assets/MathMessageMarkdown-DeYh8QPr.css +0 -1
- ksadk/server/static/assets/MermaidBlock-DWuxETaJ.js +0 -303
- ksadk/server/static/assets/NativeTerminalPanel-MDTopWMO.js +0 -1
- ksadk/server/static/assets/arc-CFKwSk_B.js +0 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-CynqOWjZ.js +0 -1
- ksadk/server/static/assets/architectureDiagram-3BPJPVTR-Dej8jX8c.js +0 -36
- ksadk/server/static/assets/blockDiagram-GPEHLZMM-1F64jqvy.js +0 -132
- ksadk/server/static/assets/c4Diagram-AAUBKEIU-9q7AUAKb.js +0 -10
- ksadk/server/static/assets/channel-iUGv2N24.js +0 -1
- ksadk/server/static/assets/chunk-2J33WTMH-6qgwJm7Q.js +0 -1
- ksadk/server/static/assets/chunk-55IACEB6-BK8paA20.js +0 -1
- ksadk/server/static/assets/chunk-727SXJPM-B-JEhy7h.js +0 -206
- ksadk/server/static/assets/chunk-AQP2D5EJ-DYBvMHAa.js +0 -231
- ksadk/server/static/assets/chunk-QZHKN3VN-YQIEXeF6.js +0 -1
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-CDAUuoXK.js +0 -1
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-CDAUuoXK.js +0 -1
- ksadk/server/static/assets/cytoscape.esm-DelgaX4f.js +0 -321
- ksadk/server/static/assets/dagre-BM42HDAG-C7TguAWo.js +0 -4
- ksadk/server/static/assets/dagre-Bx709z4p.js +0 -1
- ksadk/server/static/assets/diagram-2AECGRRQ-Dd17_kws.js +0 -43
- ksadk/server/static/assets/diagram-5GNKFQAL-DVIClIil.js +0 -10
- ksadk/server/static/assets/diagram-KO2AKTUF-Df_3gYQa.js +0 -3
- ksadk/server/static/assets/diagram-LMA3HP47-BhLk6ZtA.js +0 -24
- ksadk/server/static/assets/diagram-OG6HWLK6-D4GNGvuu.js +0 -24
- ksadk/server/static/assets/dist-DyvYLMW9.js +0 -1
- ksadk/server/static/assets/erDiagram-TEJ5UH35-CnfRcY2M.js +0 -85
- ksadk/server/static/assets/eventmodeling-FCH6USID-ChYO-ijM.js +0 -1
- ksadk/server/static/assets/flowDiagram-I6XJVG4X-DGaJjGNJ.js +0 -162
- ksadk/server/static/assets/ganttDiagram-6RSMTGT7-DpCoB2g9.js +0 -292
- ksadk/server/static/assets/gitGraph-WXDBUCRP-BN_0Fk-j.js +0 -1
- ksadk/server/static/assets/gitGraphDiagram-PVQCEYII-9IKVFBVT.js +0 -106
- ksadk/server/static/assets/graphlib-B8gBHxth.js +0 -1
- ksadk/server/static/assets/index-QSwCHuB8.js +0 -177
- ksadk/server/static/assets/index-jOPmTOZH.css +0 -2
- ksadk/server/static/assets/info-J43DQDTF-Cp6o3vId.js +0 -1
- ksadk/server/static/assets/infoDiagram-5YYISTIA-CYIIsb8y.js +0 -2
- ksadk/server/static/assets/katex-DATssjp1.js +0 -265
- ksadk/server/static/assets/mermaid-parser.core-mGAcEwcq.js +0 -161
- ksadk/server/static/assets/packet-YPE3B663-wQzct3cT.js +0 -1
- ksadk/server/static/assets/pie-LRSECV5Y-BgTQiv-9.js +0 -1
- ksadk/server/static/assets/pieDiagram-4H26LBE5-By-ra07M.js +0 -30
- ksadk/server/static/assets/radar-GUYGQ44K-BIq5fXsn.js +0 -1
- ksadk/server/static/assets/requirementDiagram-4Y6WPE33-DpW90iu3.js +0 -84
- ksadk/server/static/assets/stateDiagram-AJRCARHV-DGpd9yFG.js +0 -1
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-B3o579IQ.js +0 -1
- ksadk/server/static/assets/treeView-BLDUP644-Dqi-E0EM.js +0 -1
- ksadk/server/static/assets/treemap-LRROVOQU-ClQP9CJ9.js +0 -1
- ksadk/server/static/assets/vennDiagram-CIIHVFJN-l3Z7-wbP.js +0 -34
- ksadk/server/static/assets/wardley-L42UT6IY-8XLvnNIs.js +0 -1
- ksadk/server/static/assets/wardleyDiagram-YWT4CUSO-BR54GPmr.js +0 -78
- ksadk/server/static/assets/xychartDiagram-2RQKCTM6-SkiH8GsK.js +0 -7
- ksadk/tracing/exporters/langfuse_exporter.py +0 -460
- {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/entry_points.txt +0 -0
- {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/licenses/LICENSE +0 -0
- {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,1017 @@
|
|
|
1
|
+
"""Application-level commits for Agent authoring inspections."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import copy
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
import shutil
|
|
10
|
+
import threading
|
|
11
|
+
import time
|
|
12
|
+
from collections import OrderedDict
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any, cast
|
|
15
|
+
|
|
16
|
+
from ksadk.studio.authoring import AgentAuthoringService, ConversationProposal
|
|
17
|
+
from ksadk.studio.codex_manifest import CodexAgentManifest
|
|
18
|
+
from ksadk.studio.contracts import (
|
|
19
|
+
AgentBindings,
|
|
20
|
+
AgentDraft,
|
|
21
|
+
AgentSpec,
|
|
22
|
+
CapabilitiesSpec,
|
|
23
|
+
CapabilityBinding,
|
|
24
|
+
ModelSpec,
|
|
25
|
+
RuntimeRef,
|
|
26
|
+
Usage,
|
|
27
|
+
)
|
|
28
|
+
from ksadk.studio.errors import StudioError
|
|
29
|
+
from ksadk.studio.identifiers import generate_agent_slug, is_generated_agent_slug
|
|
30
|
+
from ksadk.studio.templates import default_agent_spec
|
|
31
|
+
|
|
32
|
+
LOGGER = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
# 对话创建阶段推进序列。前端只消费阶段名展示两段式文案,不解析内容。
|
|
35
|
+
CONVERSATION_STAGES = (
|
|
36
|
+
"resolving_model",
|
|
37
|
+
"generating",
|
|
38
|
+
"codex_writing",
|
|
39
|
+
"validating",
|
|
40
|
+
"correcting",
|
|
41
|
+
"done",
|
|
42
|
+
"failed",
|
|
43
|
+
)
|
|
44
|
+
# 进度记录只保留最近的少量请求,避免长驻进程无限增长。
|
|
45
|
+
_CONVERSATION_STATUS_LIMIT = 64
|
|
46
|
+
_LOCAL_FALLBACK_MODEL_ERRORS = frozenset(
|
|
47
|
+
{
|
|
48
|
+
"AUTHORING_MODEL_OUTPUT_INVALID",
|
|
49
|
+
"MODEL_EMPTY_RESPONSE",
|
|
50
|
+
"MODEL_RATE_LIMITED",
|
|
51
|
+
"MODEL_REQUEST_FAILED",
|
|
52
|
+
"MODEL_RESPONSE_INVALID",
|
|
53
|
+
"MODEL_RESPONSE_TOO_LARGE",
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
_LOCAL_FALLBACK_REASON = {
|
|
57
|
+
"AUTHORING_MODEL_OUTPUT_INVALID": "invalid-model-output",
|
|
58
|
+
"MODEL_EMPTY_RESPONSE": "empty-model-output",
|
|
59
|
+
"MODEL_RATE_LIMITED": "model-rate-limited",
|
|
60
|
+
"MODEL_REQUEST_FAILED": "model-request-failed",
|
|
61
|
+
"MODEL_RESPONSE_INVALID": "invalid-model-response",
|
|
62
|
+
"MODEL_RESPONSE_TOO_LARGE": "model-response-too-large",
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _deduplicated_bindings(resource_ids: list[str]) -> list[CapabilityBinding]:
|
|
67
|
+
"""Build a deterministic binding list from Studio-selected resource ids."""
|
|
68
|
+
|
|
69
|
+
seen: set[str] = set()
|
|
70
|
+
result: list[CapabilityBinding] = []
|
|
71
|
+
for resource_id in resource_ids:
|
|
72
|
+
normalized = str(resource_id or "").strip()
|
|
73
|
+
if not normalized or normalized in seen:
|
|
74
|
+
continue
|
|
75
|
+
seen.add(normalized)
|
|
76
|
+
result.append(CapabilityBinding(resource_id=normalized))
|
|
77
|
+
return result
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _inject_managed_bindings(
|
|
81
|
+
proposal: Any,
|
|
82
|
+
*,
|
|
83
|
+
model_spec: ModelSpec,
|
|
84
|
+
bindings: AgentBindings,
|
|
85
|
+
) -> Any:
|
|
86
|
+
"""Replace all model/resource fields invented by the authoring model.
|
|
87
|
+
|
|
88
|
+
The LLM may propose product semantics (name, runtime, instructions and
|
|
89
|
+
execution strategy), but it is never a source of connection information or
|
|
90
|
+
capability identities. Those are selected in Studio and validated before
|
|
91
|
+
this point. The Profile id remains the sole persisted model reference;
|
|
92
|
+
compiler/run materialisation resolves the current endpoint, credential and
|
|
93
|
+
parameters from the catalog. This also prevents provider discovery data,
|
|
94
|
+
limits and pricing from leaking into every Agent Revision. Resetting
|
|
95
|
+
capabilities is intentional: a model-generated inline Tool/MCP/Skill
|
|
96
|
+
contract must not become a deployable capability.
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
spec = proposal.spec
|
|
100
|
+
managed_bindings = bindings
|
|
101
|
+
# Codex Runtime owns its native tool surface. Studio-selected KsADK
|
|
102
|
+
# Tool contracts are meaningful for generic ADK/LangGraph Agents, but
|
|
103
|
+
# serialising them into a Codex Draft Patch advertises a binding the
|
|
104
|
+
# runtime cannot execute. Enforce the same boundary as the UI here so a
|
|
105
|
+
# direct API caller cannot bypass it.
|
|
106
|
+
if proposal.runtimeType == "codex" and bindings.tools:
|
|
107
|
+
managed_bindings = bindings.model_copy(update={"tools": []})
|
|
108
|
+
# Resolution above is still intentional: it validates the selected Profile
|
|
109
|
+
# before a proposal is returned. Do not serialize its catalog contract.
|
|
110
|
+
_ = model_spec
|
|
111
|
+
return proposal.model_copy(
|
|
112
|
+
update={
|
|
113
|
+
"spec": spec.model_copy(
|
|
114
|
+
update={
|
|
115
|
+
"model": None,
|
|
116
|
+
"bindings": managed_bindings.model_copy(deep=True),
|
|
117
|
+
"capabilities": CapabilitiesSpec(),
|
|
118
|
+
}
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class StudioAuthoringCoordinator:
|
|
125
|
+
"""Coordinates repositories without expanding the StudioService façade."""
|
|
126
|
+
|
|
127
|
+
def __init__(self, studio: Any) -> None:
|
|
128
|
+
self.studio = studio
|
|
129
|
+
self.backend = AgentAuthoringService(studio.workspace)
|
|
130
|
+
# Codex authoring 执行器可注入替换(测试);默认惰性探测可用性。
|
|
131
|
+
self.codex_authoring: Any = getattr(studio, "codex_authoring_executor", None)
|
|
132
|
+
self._id_lock = threading.Lock()
|
|
133
|
+
self._status_lock = threading.Lock()
|
|
134
|
+
self._conversation_status: OrderedDict[str, dict[str, Any]] = OrderedDict()
|
|
135
|
+
|
|
136
|
+
def _codex_authoring_executor(self) -> Any:
|
|
137
|
+
if self.codex_authoring is not None:
|
|
138
|
+
return self.codex_authoring
|
|
139
|
+
from ksadk.studio.codex_authoring import CodexAuthoringExecutor
|
|
140
|
+
|
|
141
|
+
self.codex_authoring = CodexAuthoringExecutor(
|
|
142
|
+
self.studio.workspace,
|
|
143
|
+
credential_resolver=self.studio.credentials,
|
|
144
|
+
)
|
|
145
|
+
return self.codex_authoring
|
|
146
|
+
|
|
147
|
+
def _use_codex_authoring(self) -> bool:
|
|
148
|
+
"""Use heavy Codex filesystem authoring only by explicit opt-in.
|
|
149
|
+
|
|
150
|
+
Conversational Draft Patch authoring is a bounded structured-chat
|
|
151
|
+
request. Probing a local Codex installation and silently switching to
|
|
152
|
+
its full filesystem/tool harness made a small form action huge and
|
|
153
|
+
unreliable (and changed the selected model's request shape). Keep the
|
|
154
|
+
optional expert authoring path, but never choose it implicitly.
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
mode = str(os.environ.get("KSADK_STUDIO_AUTHORIZER") or "").strip().lower()
|
|
158
|
+
if mode in {"codex", "codex-writing", "1", "true"}:
|
|
159
|
+
return True
|
|
160
|
+
if mode in {"chat", "off", "0", "false", "none"}:
|
|
161
|
+
return False
|
|
162
|
+
return False
|
|
163
|
+
|
|
164
|
+
# ------------------------------------------------------------------
|
|
165
|
+
# Conversation authoring stage tracking
|
|
166
|
+
# ------------------------------------------------------------------
|
|
167
|
+
|
|
168
|
+
def _record_conversation_stage(
|
|
169
|
+
self,
|
|
170
|
+
request_id: str | None,
|
|
171
|
+
stage: str,
|
|
172
|
+
*,
|
|
173
|
+
detail: str | None = None,
|
|
174
|
+
) -> None:
|
|
175
|
+
if not request_id:
|
|
176
|
+
return
|
|
177
|
+
entry = {
|
|
178
|
+
"requestId": request_id,
|
|
179
|
+
"stage": stage,
|
|
180
|
+
"updatedAt": time.time(),
|
|
181
|
+
**({"detail": detail} if detail else {}),
|
|
182
|
+
}
|
|
183
|
+
with self._status_lock:
|
|
184
|
+
self._conversation_status[request_id] = entry
|
|
185
|
+
self._conversation_status.move_to_end(request_id)
|
|
186
|
+
while len(self._conversation_status) > _CONVERSATION_STATUS_LIMIT:
|
|
187
|
+
self._conversation_status.popitem(last=False)
|
|
188
|
+
|
|
189
|
+
def conversation_status(self, request_id: str) -> dict[str, Any] | None:
|
|
190
|
+
with self._status_lock:
|
|
191
|
+
entry = self._conversation_status.get(request_id)
|
|
192
|
+
return dict(entry) if entry else None
|
|
193
|
+
|
|
194
|
+
def _local_conversation_fallback(
|
|
195
|
+
self,
|
|
196
|
+
*,
|
|
197
|
+
messages: list[dict[str, str]],
|
|
198
|
+
previous_proposal: ConversationProposal | None,
|
|
199
|
+
runtime_type: str,
|
|
200
|
+
model_spec: ModelSpec,
|
|
201
|
+
bindings: AgentBindings,
|
|
202
|
+
reason_code: str,
|
|
203
|
+
) -> dict[str, Any]:
|
|
204
|
+
"""Return a reviewable draft when the selected authoring model is unavailable.
|
|
205
|
+
|
|
206
|
+
This is intentionally not a hidden second model or an auto-deploy
|
|
207
|
+
mechanism. Studio keeps the user's selected runtime and resource
|
|
208
|
+
bindings, produces only deterministic editable semantic fields, and
|
|
209
|
+
tells the caller that a manual review is required.
|
|
210
|
+
"""
|
|
211
|
+
|
|
212
|
+
latest_user_message = next(
|
|
213
|
+
(
|
|
214
|
+
str(item.get("content") or "").strip()
|
|
215
|
+
for item in reversed(messages)
|
|
216
|
+
if str(item.get("role") or "").strip() == "user"
|
|
217
|
+
and str(item.get("content") or "").strip()
|
|
218
|
+
),
|
|
219
|
+
"请根据已确认的需求完成 Agent,并在部署前检查配置和权限。",
|
|
220
|
+
)
|
|
221
|
+
previous_instructions = (
|
|
222
|
+
previous_proposal.spec.instructions if previous_proposal else None
|
|
223
|
+
)
|
|
224
|
+
fallback_payload = {
|
|
225
|
+
"name": previous_proposal.name if previous_proposal else "待确认 Agent",
|
|
226
|
+
"slug": previous_proposal.slug if previous_proposal else "conversation-agent",
|
|
227
|
+
"description": (
|
|
228
|
+
previous_proposal.description
|
|
229
|
+
if previous_proposal and previous_proposal.description
|
|
230
|
+
else latest_user_message[:1024]
|
|
231
|
+
),
|
|
232
|
+
"spec": {
|
|
233
|
+
"instructions": {
|
|
234
|
+
"system": (
|
|
235
|
+
previous_instructions.system
|
|
236
|
+
if previous_instructions and previous_instructions.system
|
|
237
|
+
else "根据用户需求完成任务;不确定时先澄清,并遵守已绑定能力的权限边界。"
|
|
238
|
+
),
|
|
239
|
+
"task": latest_user_message,
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
}
|
|
243
|
+
proposal = self.backend.parse_conversation_proposal(
|
|
244
|
+
json.dumps(fallback_payload, ensure_ascii=False),
|
|
245
|
+
base=previous_proposal,
|
|
246
|
+
runtime_type=runtime_type,
|
|
247
|
+
)
|
|
248
|
+
return {
|
|
249
|
+
"proposal": _inject_managed_bindings(
|
|
250
|
+
proposal,
|
|
251
|
+
model_spec=model_spec,
|
|
252
|
+
bindings=bindings,
|
|
253
|
+
).model_dump(by_alias=True, mode="json"),
|
|
254
|
+
"requiresConfirmation": True,
|
|
255
|
+
"authoringMode": "local-fallback",
|
|
256
|
+
"fallback": {
|
|
257
|
+
"active": True,
|
|
258
|
+
"reason": _LOCAL_FALLBACK_REASON.get(reason_code, "model-unavailable"),
|
|
259
|
+
},
|
|
260
|
+
"usage": Usage(source="local-fallback").model_dump(
|
|
261
|
+
by_alias=True, mode="json"
|
|
262
|
+
),
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
def create(
|
|
266
|
+
self,
|
|
267
|
+
*,
|
|
268
|
+
name: str,
|
|
269
|
+
slug: str | None = None,
|
|
270
|
+
runtime_type: str,
|
|
271
|
+
template: str = "blank",
|
|
272
|
+
description: str = "",
|
|
273
|
+
spec: AgentSpec | None = None,
|
|
274
|
+
) -> AgentDraft:
|
|
275
|
+
with self._id_lock:
|
|
276
|
+
agent_id = self._allocate_agent_id(slug)
|
|
277
|
+
resolved_slug = slug or agent_id
|
|
278
|
+
resolved = (spec or default_agent_spec(template, description=description)).model_copy(
|
|
279
|
+
deep=True
|
|
280
|
+
)
|
|
281
|
+
canonical_runtime = self.backend.runtime_ref(agent_id, runtime_type)
|
|
282
|
+
proposed_runtime = resolved.runtime
|
|
283
|
+
if proposed_runtime is not None and proposed_runtime.type != runtime_type:
|
|
284
|
+
raise StudioError(
|
|
285
|
+
"AGENT_RUNTIME_MISMATCH",
|
|
286
|
+
"AgentSpec Runtime 与创建方式不一致",
|
|
287
|
+
status_code=422,
|
|
288
|
+
field="runtimeType",
|
|
289
|
+
details={
|
|
290
|
+
"runtimeType": runtime_type,
|
|
291
|
+
"specRuntimeType": proposed_runtime.type,
|
|
292
|
+
},
|
|
293
|
+
)
|
|
294
|
+
if proposed_runtime is not None:
|
|
295
|
+
if runtime_type == "codex" and proposed_runtime.version:
|
|
296
|
+
canonical_runtime.version = proposed_runtime.version
|
|
297
|
+
elif runtime_type in {"adk", "langgraph"}:
|
|
298
|
+
canonical_runtime.entry_point = (
|
|
299
|
+
proposed_runtime.entry_point or canonical_runtime.entry_point
|
|
300
|
+
)
|
|
301
|
+
canonical_runtime.agent_variable = proposed_runtime.agent_variable
|
|
302
|
+
canonical_runtime.version = proposed_runtime.version
|
|
303
|
+
canonical_runtime.detection = proposed_runtime.detection
|
|
304
|
+
resolved.runtime = canonical_runtime
|
|
305
|
+
if description:
|
|
306
|
+
resolved.description = description
|
|
307
|
+
draft = self.studio.create_studio_agent(
|
|
308
|
+
agent_id=agent_id,
|
|
309
|
+
name=name,
|
|
310
|
+
description=description,
|
|
311
|
+
template=template,
|
|
312
|
+
spec=resolved,
|
|
313
|
+
labels={"agentkit.ksyun.com/slug": self.backend.normalize_slug(resolved_slug)},
|
|
314
|
+
)
|
|
315
|
+
return cast(AgentDraft, draft)
|
|
316
|
+
|
|
317
|
+
def inspect_import(self, content: bytes, *, filename: str) -> dict:
|
|
318
|
+
return self.backend.inspect_import(content, filename=filename)
|
|
319
|
+
|
|
320
|
+
def commit_import(
|
|
321
|
+
self,
|
|
322
|
+
inspection_token: str,
|
|
323
|
+
*,
|
|
324
|
+
name: str | None = None,
|
|
325
|
+
slug: str | None = None,
|
|
326
|
+
) -> AgentDraft:
|
|
327
|
+
inspection = self.backend.load_import(inspection_token)
|
|
328
|
+
display_name = str(name or inspection.display_name).strip()
|
|
329
|
+
agent_id = self._allocate_agent_id(slug)
|
|
330
|
+
resolved_slug = slug or agent_id
|
|
331
|
+
if inspection.kind == "codex-manifest":
|
|
332
|
+
created = self._commit_codex_import(
|
|
333
|
+
agent_id,
|
|
334
|
+
display_name,
|
|
335
|
+
CodexAgentManifest.model_validate(inspection.payload),
|
|
336
|
+
resolved_slug=resolved_slug,
|
|
337
|
+
)
|
|
338
|
+
self.backend.consume_import(inspection_token)
|
|
339
|
+
return created
|
|
340
|
+
|
|
341
|
+
imported = AgentDraft.model_validate(inspection.payload)
|
|
342
|
+
spec = imported.spec.model_copy(deep=True)
|
|
343
|
+
source = self.backend.source_directory(inspection)
|
|
344
|
+
if spec.runtime is None:
|
|
345
|
+
raise StudioError(
|
|
346
|
+
"AGENT_RUNTIME_REQUIRED",
|
|
347
|
+
"导入 Agent 必须声明 RuntimeRef",
|
|
348
|
+
status_code=422,
|
|
349
|
+
)
|
|
350
|
+
if spec.runtime.type in {"adk", "langgraph"}:
|
|
351
|
+
imported_runtime = spec.runtime
|
|
352
|
+
canonical_runtime = self.backend.runtime_ref(agent_id, imported_runtime.type)
|
|
353
|
+
canonical_runtime.entry_point = (
|
|
354
|
+
imported_runtime.entry_point or canonical_runtime.entry_point
|
|
355
|
+
)
|
|
356
|
+
canonical_runtime.agent_variable = imported_runtime.agent_variable
|
|
357
|
+
canonical_runtime.version = imported_runtime.version
|
|
358
|
+
canonical_runtime.detection = imported_runtime.detection
|
|
359
|
+
spec.runtime = canonical_runtime
|
|
360
|
+
created = self.studio.create_agent(
|
|
361
|
+
agent_id=agent_id,
|
|
362
|
+
name=display_name,
|
|
363
|
+
description=spec.description,
|
|
364
|
+
template=imported.metadata.labels.get(
|
|
365
|
+
"agentkit.ksyun.com/template",
|
|
366
|
+
"blank",
|
|
367
|
+
),
|
|
368
|
+
spec=spec,
|
|
369
|
+
labels={
|
|
370
|
+
"agentkit.ksyun.com/slug": self.backend.normalize_slug(resolved_slug),
|
|
371
|
+
"agentkit.ksyun.com/source": "import",
|
|
372
|
+
"agentkit.ksyun.com/source-digest": inspection.source_digest,
|
|
373
|
+
},
|
|
374
|
+
)
|
|
375
|
+
if source is not None and created.spec.runtime and created.spec.runtime.project_path:
|
|
376
|
+
target = self.studio.workspace.resolve(created.spec.runtime.project_path)
|
|
377
|
+
shutil.rmtree(target, ignore_errors=True)
|
|
378
|
+
shutil.copytree(
|
|
379
|
+
source,
|
|
380
|
+
target,
|
|
381
|
+
symlinks=False,
|
|
382
|
+
ignore=shutil.ignore_patterns("__pycache__", "*.pyc", ".DS_Store"),
|
|
383
|
+
)
|
|
384
|
+
self.backend.consume_import(inspection_token)
|
|
385
|
+
return cast(AgentDraft, self.studio.drafts.get(agent_id))
|
|
386
|
+
|
|
387
|
+
def inspect_project(self, project_path: str) -> dict:
|
|
388
|
+
inspection = self.backend.inspect_project(project_path)
|
|
389
|
+
spec, unresolved = self._project_agent_spec(
|
|
390
|
+
inspection,
|
|
391
|
+
agent_id="agentkit-preview",
|
|
392
|
+
model_profile_id=None,
|
|
393
|
+
)
|
|
394
|
+
return {
|
|
395
|
+
**inspection,
|
|
396
|
+
"agentSpec": spec.model_dump(by_alias=True, exclude_none=True, mode="json"),
|
|
397
|
+
"bindingProjection": {
|
|
398
|
+
"preserved": spec.bindings.model_dump(
|
|
399
|
+
by_alias=True,
|
|
400
|
+
exclude_none=True,
|
|
401
|
+
mode="json",
|
|
402
|
+
),
|
|
403
|
+
"unresolved": unresolved,
|
|
404
|
+
},
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
def commit_project(
|
|
408
|
+
self,
|
|
409
|
+
inspection_token: str,
|
|
410
|
+
*,
|
|
411
|
+
name: str | None = None,
|
|
412
|
+
slug: str | None = None,
|
|
413
|
+
model_profile_id: str | None = None,
|
|
414
|
+
) -> AgentDraft:
|
|
415
|
+
inspection = self.backend.load_project(inspection_token)
|
|
416
|
+
runtime_type = str(inspection["runtimeType"])
|
|
417
|
+
display_name = str(name or inspection.get("name") or "Imported Agent").strip()
|
|
418
|
+
resolved_slug = slug
|
|
419
|
+
if runtime_type == "codex":
|
|
420
|
+
manifest_path = self.studio.workspace.resolve(
|
|
421
|
+
Path(str(inspection["projectPath"])) / "agentengine.yaml",
|
|
422
|
+
must_exist=True,
|
|
423
|
+
)
|
|
424
|
+
created = self.commit_import(
|
|
425
|
+
self.inspect_import(
|
|
426
|
+
manifest_path.read_bytes(),
|
|
427
|
+
filename="agentengine.yaml",
|
|
428
|
+
)["inspectionToken"],
|
|
429
|
+
name=display_name,
|
|
430
|
+
slug=resolved_slug,
|
|
431
|
+
)
|
|
432
|
+
self.backend.consume_project(inspection_token)
|
|
433
|
+
return created
|
|
434
|
+
|
|
435
|
+
agent_id = self._allocate_agent_id(resolved_slug)
|
|
436
|
+
resolved_slug = resolved_slug or agent_id
|
|
437
|
+
spec, unresolved = self._project_agent_spec(
|
|
438
|
+
inspection,
|
|
439
|
+
agent_id=agent_id,
|
|
440
|
+
model_profile_id=model_profile_id,
|
|
441
|
+
)
|
|
442
|
+
if unresolved:
|
|
443
|
+
raise StudioError(
|
|
444
|
+
"PROJECT_BINDINGS_UNRESOLVED",
|
|
445
|
+
"项目中的 Tool、MCP 或 Skill 绑定无法无损映射,请先安装或修正对应资源",
|
|
446
|
+
status_code=422,
|
|
447
|
+
details={"unresolved": unresolved},
|
|
448
|
+
)
|
|
449
|
+
created = self.studio.create_agent(
|
|
450
|
+
agent_id=agent_id,
|
|
451
|
+
name=display_name,
|
|
452
|
+
spec=spec,
|
|
453
|
+
labels={
|
|
454
|
+
"agentkit.ksyun.com/slug": self.backend.normalize_slug(resolved_slug),
|
|
455
|
+
"agentkit.ksyun.com/source": "project-detection",
|
|
456
|
+
"agentkit.ksyun.com/source-digest": str(inspection["sourceDigest"]),
|
|
457
|
+
},
|
|
458
|
+
)
|
|
459
|
+
self.backend.consume_project(inspection_token)
|
|
460
|
+
return cast(AgentDraft, created)
|
|
461
|
+
|
|
462
|
+
async def compose_conversation(
|
|
463
|
+
self,
|
|
464
|
+
*,
|
|
465
|
+
messages: list[dict[str, str]],
|
|
466
|
+
model_profile_id: str,
|
|
467
|
+
runtime_type: str = "codex",
|
|
468
|
+
agent_model_profile_ids: list[str] | None = None,
|
|
469
|
+
agent_default_model_profile_id: str | None = None,
|
|
470
|
+
tool_resource_ids: list[str] | None = None,
|
|
471
|
+
mcp_resource_ids: list[str] | None = None,
|
|
472
|
+
skill_resource_ids: list[str] | None = None,
|
|
473
|
+
request_id: str | None = None,
|
|
474
|
+
) -> dict:
|
|
475
|
+
started = time.monotonic()
|
|
476
|
+
self._record_conversation_stage(request_id, "resolving_model")
|
|
477
|
+
LOGGER.info(
|
|
478
|
+
"conversation authoring started: modelProfileId=%s messages=%d requestId=%s",
|
|
479
|
+
model_profile_id,
|
|
480
|
+
len(messages),
|
|
481
|
+
request_id or "-",
|
|
482
|
+
)
|
|
483
|
+
try:
|
|
484
|
+
result = await self._compose_conversation_inner(
|
|
485
|
+
messages=messages,
|
|
486
|
+
model_profile_id=model_profile_id,
|
|
487
|
+
runtime_type=runtime_type,
|
|
488
|
+
agent_model_profile_ids=agent_model_profile_ids or [],
|
|
489
|
+
agent_default_model_profile_id=agent_default_model_profile_id,
|
|
490
|
+
tool_resource_ids=tool_resource_ids or [],
|
|
491
|
+
mcp_resource_ids=mcp_resource_ids or [],
|
|
492
|
+
skill_resource_ids=skill_resource_ids or [],
|
|
493
|
+
request_id=request_id,
|
|
494
|
+
started=started,
|
|
495
|
+
)
|
|
496
|
+
except Exception as exc:
|
|
497
|
+
self._record_conversation_stage(request_id, "failed", detail=str(exc))
|
|
498
|
+
LOGGER.warning(
|
|
499
|
+
"conversation authoring failed after %.2fs: modelProfileId=%s reason=%s",
|
|
500
|
+
time.monotonic() - started,
|
|
501
|
+
model_profile_id,
|
|
502
|
+
exc,
|
|
503
|
+
)
|
|
504
|
+
raise
|
|
505
|
+
return result
|
|
506
|
+
|
|
507
|
+
async def _compose_conversation_inner(
|
|
508
|
+
self,
|
|
509
|
+
*,
|
|
510
|
+
messages: list[dict[str, str]],
|
|
511
|
+
model_profile_id: str,
|
|
512
|
+
runtime_type: str,
|
|
513
|
+
agent_model_profile_ids: list[str],
|
|
514
|
+
agent_default_model_profile_id: str | None,
|
|
515
|
+
tool_resource_ids: list[str],
|
|
516
|
+
mcp_resource_ids: list[str],
|
|
517
|
+
skill_resource_ids: list[str],
|
|
518
|
+
request_id: str | None,
|
|
519
|
+
started: float,
|
|
520
|
+
) -> dict:
|
|
521
|
+
# The authoring model is a one-off control-plane choice. The Agent's
|
|
522
|
+
# model allow-list is a separate deploy-time contract and can contain
|
|
523
|
+
# multiple profiles. Keep backwards compatibility for API callers
|
|
524
|
+
# that only send ``modelProfileId`` by using it as the one Agent model.
|
|
525
|
+
selected_agent_model_ids = [
|
|
526
|
+
binding.resource_id
|
|
527
|
+
for binding in _deduplicated_bindings(agent_model_profile_ids or [model_profile_id])
|
|
528
|
+
]
|
|
529
|
+
if not selected_agent_model_ids:
|
|
530
|
+
raise StudioError(
|
|
531
|
+
"AGENT_MODEL_REQUIRED",
|
|
532
|
+
"Agent 至少需要选择一个可用 Model Profile",
|
|
533
|
+
status_code=422,
|
|
534
|
+
)
|
|
535
|
+
default_agent_model_id = str(
|
|
536
|
+
agent_default_model_profile_id or selected_agent_model_ids[0]
|
|
537
|
+
).strip()
|
|
538
|
+
if default_agent_model_id not in selected_agent_model_ids:
|
|
539
|
+
raise StudioError(
|
|
540
|
+
"AGENT_DEFAULT_MODEL_NOT_SELECTED",
|
|
541
|
+
"Agent 默认模型必须包含在可用模型列表中",
|
|
542
|
+
status_code=422,
|
|
543
|
+
details={
|
|
544
|
+
"defaultModelProfileId": default_agent_model_id,
|
|
545
|
+
"modelProfileIds": selected_agent_model_ids,
|
|
546
|
+
},
|
|
547
|
+
)
|
|
548
|
+
authoring_bindings = AgentBindings(
|
|
549
|
+
model_profile_id=model_profile_id,
|
|
550
|
+
model_profile_ids=[model_profile_id],
|
|
551
|
+
)
|
|
552
|
+
bindings = AgentBindings(
|
|
553
|
+
model_profile_id=default_agent_model_id,
|
|
554
|
+
model_profile_ids=selected_agent_model_ids,
|
|
555
|
+
tools=_deduplicated_bindings(tool_resource_ids),
|
|
556
|
+
mcp_servers=_deduplicated_bindings(mcp_resource_ids),
|
|
557
|
+
skills=_deduplicated_bindings(skill_resource_ids),
|
|
558
|
+
)
|
|
559
|
+
# Resolve every selected resource now. This validates kind, readiness
|
|
560
|
+
# and capability contracts before an LLM response can be displayed as a
|
|
561
|
+
# deployable Draft Patch.
|
|
562
|
+
authoring_model_spec = self.studio.catalog.resolve_model(authoring_bindings)
|
|
563
|
+
if authoring_model_spec is None:
|
|
564
|
+
raise StudioError(
|
|
565
|
+
"AGENT_MODEL_REQUIRED",
|
|
566
|
+
"对话构建需要选择一个 Model Profile",
|
|
567
|
+
status_code=422,
|
|
568
|
+
)
|
|
569
|
+
# Resolve every Agent model before returning a Draft Patch. This
|
|
570
|
+
# catches a deleted, wrong-kind or malformed secondary profile rather
|
|
571
|
+
# than accepting a non-deployable multi-model Agent in the browser.
|
|
572
|
+
self.studio.catalog.resolve_models(bindings)
|
|
573
|
+
model_spec = self.studio.catalog.resolve_model(bindings)
|
|
574
|
+
if model_spec is None: # defensive: bindings above requires a default
|
|
575
|
+
raise StudioError(
|
|
576
|
+
"AGENT_MODEL_REQUIRED",
|
|
577
|
+
"Agent 需要选择一个默认 Model Profile",
|
|
578
|
+
status_code=422,
|
|
579
|
+
)
|
|
580
|
+
self.studio.catalog.policy_preview(bindings)
|
|
581
|
+
self.studio.catalog.resolve_mcp_servers(bindings)
|
|
582
|
+
self.studio.catalog.resolve_skills(bindings)
|
|
583
|
+
model = self.studio.catalog.resolver.resolve_model(authoring_model_spec)
|
|
584
|
+
# authoring 自身的 max_tokens 跟随 profile:未配置则 payload 不带该字段
|
|
585
|
+
# (服务端默认);finishReason=length 截断由 model_client 一次性扩容重试兜底。
|
|
586
|
+
LOGGER.info(
|
|
587
|
+
"conversation authoring model resolved: model=%s endpoint=%s",
|
|
588
|
+
getattr(model, "model", "-"),
|
|
589
|
+
getattr(model, "endpoint_url", "-"),
|
|
590
|
+
)
|
|
591
|
+
normalized_runtime_type = str(runtime_type or "").strip().lower()
|
|
592
|
+
if normalized_runtime_type not in {"codex", "adk", "langgraph"}:
|
|
593
|
+
raise StudioError(
|
|
594
|
+
"AGENT_RUNTIME_INVALID",
|
|
595
|
+
"对话构建 Runtime 仅支持 Codex、ADK 或 LangGraph",
|
|
596
|
+
status_code=422,
|
|
597
|
+
field="runtimeType",
|
|
598
|
+
)
|
|
599
|
+
normalized_messages = self.backend.conversation_messages(
|
|
600
|
+
messages,
|
|
601
|
+
runtime_type=normalized_runtime_type,
|
|
602
|
+
)
|
|
603
|
+
previous_proposal = None
|
|
604
|
+
for item in reversed(messages):
|
|
605
|
+
if str(item.get("role") or "").strip() != "assistant":
|
|
606
|
+
continue
|
|
607
|
+
try:
|
|
608
|
+
previous_proposal = self.backend.parse_conversation_proposal(
|
|
609
|
+
str(item.get("content") or ""),
|
|
610
|
+
runtime_type=normalized_runtime_type,
|
|
611
|
+
)
|
|
612
|
+
except StudioError:
|
|
613
|
+
continue
|
|
614
|
+
break
|
|
615
|
+
|
|
616
|
+
if self._use_codex_authoring():
|
|
617
|
+
codex_result = await self._compose_conversation_codex(
|
|
618
|
+
messages=messages,
|
|
619
|
+
model=model,
|
|
620
|
+
previous_proposal=previous_proposal,
|
|
621
|
+
request_id=request_id,
|
|
622
|
+
started=started,
|
|
623
|
+
model_profile_id=model_profile_id,
|
|
624
|
+
model_spec=model_spec,
|
|
625
|
+
bindings=bindings,
|
|
626
|
+
)
|
|
627
|
+
if codex_result is not None:
|
|
628
|
+
return codex_result
|
|
629
|
+
|
|
630
|
+
request_options = {
|
|
631
|
+
"network_policy": self.backend.authoring_network_policy(model.endpoint_url),
|
|
632
|
+
# The authoring output is deliberately small. Keep a bounded
|
|
633
|
+
# request budget so an unavailable upstream cannot make a simple
|
|
634
|
+
# create flow appear to hang for minutes.
|
|
635
|
+
"timeout_seconds": 20,
|
|
636
|
+
"backoff_seconds": 1,
|
|
637
|
+
"response_format": {"type": "json_object"},
|
|
638
|
+
# finishReason=length 截断时在 model_client 内自动扩容 max_tokens
|
|
639
|
+
# 重发一次(一次性),避免大 JSON 被截断成空响应。
|
|
640
|
+
"retry_on_length": True,
|
|
641
|
+
}
|
|
642
|
+
self._record_conversation_stage(request_id, "generating")
|
|
643
|
+
try:
|
|
644
|
+
response = await self.studio.model_client.complete(
|
|
645
|
+
model,
|
|
646
|
+
messages=normalized_messages,
|
|
647
|
+
max_attempts=2,
|
|
648
|
+
**request_options,
|
|
649
|
+
)
|
|
650
|
+
except StudioError as exc:
|
|
651
|
+
if exc.code not in _LOCAL_FALLBACK_MODEL_ERRORS:
|
|
652
|
+
raise
|
|
653
|
+
LOGGER.warning(
|
|
654
|
+
"conversation authoring model unavailable; returning local fallback: "
|
|
655
|
+
"modelProfileId=%s reason=%s",
|
|
656
|
+
model_profile_id,
|
|
657
|
+
exc.code,
|
|
658
|
+
)
|
|
659
|
+
self._record_conversation_stage(request_id, "done")
|
|
660
|
+
return self._local_conversation_fallback(
|
|
661
|
+
messages=messages,
|
|
662
|
+
previous_proposal=previous_proposal,
|
|
663
|
+
runtime_type=normalized_runtime_type,
|
|
664
|
+
model_spec=model_spec,
|
|
665
|
+
bindings=bindings,
|
|
666
|
+
reason_code=exc.code,
|
|
667
|
+
)
|
|
668
|
+
self._record_conversation_stage(request_id, "validating")
|
|
669
|
+
try:
|
|
670
|
+
proposal = self.backend.parse_conversation_proposal(
|
|
671
|
+
response.content,
|
|
672
|
+
base=previous_proposal,
|
|
673
|
+
runtime_type=normalized_runtime_type,
|
|
674
|
+
)
|
|
675
|
+
except StudioError as exc:
|
|
676
|
+
if exc.code != "AUTHORING_MODEL_OUTPUT_INVALID":
|
|
677
|
+
raise
|
|
678
|
+
LOGGER.warning(
|
|
679
|
+
"conversation authoring patch invalid; returning local fallback: "
|
|
680
|
+
"modelProfileId=%s",
|
|
681
|
+
model_profile_id,
|
|
682
|
+
)
|
|
683
|
+
self._record_conversation_stage(request_id, "done")
|
|
684
|
+
return self._local_conversation_fallback(
|
|
685
|
+
messages=messages,
|
|
686
|
+
previous_proposal=previous_proposal,
|
|
687
|
+
runtime_type=normalized_runtime_type,
|
|
688
|
+
model_spec=model_spec,
|
|
689
|
+
bindings=bindings,
|
|
690
|
+
reason_code=exc.code,
|
|
691
|
+
)
|
|
692
|
+
self._record_conversation_stage(request_id, "done")
|
|
693
|
+
LOGGER.info(
|
|
694
|
+
"conversation authoring finished in %.2fs: modelProfileId=%s slug=%s",
|
|
695
|
+
time.monotonic() - started,
|
|
696
|
+
model_profile_id,
|
|
697
|
+
getattr(proposal, "slug", "-"),
|
|
698
|
+
)
|
|
699
|
+
return {
|
|
700
|
+
"proposal": _inject_managed_bindings(
|
|
701
|
+
proposal,
|
|
702
|
+
model_spec=model_spec,
|
|
703
|
+
bindings=bindings,
|
|
704
|
+
).model_dump(
|
|
705
|
+
by_alias=True, mode="json"
|
|
706
|
+
),
|
|
707
|
+
"requiresConfirmation": True,
|
|
708
|
+
"authoringMode": "chat",
|
|
709
|
+
"usage": response.usage.model_dump(by_alias=True, mode="json"),
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
async def _compose_conversation_codex(
|
|
713
|
+
self,
|
|
714
|
+
*,
|
|
715
|
+
messages: list[dict[str, str]],
|
|
716
|
+
model: Any,
|
|
717
|
+
previous_proposal: Any,
|
|
718
|
+
request_id: str | None,
|
|
719
|
+
started: float,
|
|
720
|
+
model_profile_id: str,
|
|
721
|
+
model_spec: ModelSpec,
|
|
722
|
+
bindings: AgentBindings,
|
|
723
|
+
) -> dict | None:
|
|
724
|
+
"""让真实 Codex 会话在工作区写 agentkit.yaml;任何失败降级 chat 链。
|
|
725
|
+
|
|
726
|
+
返回 ``None`` 表示应降级(探测失败/超时/重试后仍不合法),调用方继续走
|
|
727
|
+
既有 chat 链路,保证零回归。
|
|
728
|
+
"""
|
|
729
|
+
|
|
730
|
+
self._record_conversation_stage(request_id, "codex_writing")
|
|
731
|
+
executor = self._codex_authoring_executor()
|
|
732
|
+
try:
|
|
733
|
+
result = await executor.compose(
|
|
734
|
+
messages=messages,
|
|
735
|
+
model=model,
|
|
736
|
+
base=previous_proposal,
|
|
737
|
+
request_id=request_id,
|
|
738
|
+
)
|
|
739
|
+
except Exception as exc:
|
|
740
|
+
LOGGER.warning(
|
|
741
|
+
"codex authoring failed after %.2fs, falling back to chat chain: "
|
|
742
|
+
"modelProfileId=%s requestId=%s reason=%s",
|
|
743
|
+
time.monotonic() - started,
|
|
744
|
+
model_profile_id,
|
|
745
|
+
request_id or "-",
|
|
746
|
+
exc,
|
|
747
|
+
)
|
|
748
|
+
self._record_conversation_stage(
|
|
749
|
+
request_id, "generating", detail=f"codex authoring 降级: {exc}"
|
|
750
|
+
)
|
|
751
|
+
return None
|
|
752
|
+
self._record_conversation_stage(request_id, "done")
|
|
753
|
+
LOGGER.info(
|
|
754
|
+
"conversation authoring finished in %.2fs via codex: modelProfileId=%s "
|
|
755
|
+
"slug=%s attempts=%d",
|
|
756
|
+
time.monotonic() - started,
|
|
757
|
+
model_profile_id,
|
|
758
|
+
getattr(result.proposal, "slug", "-"),
|
|
759
|
+
result.attempts,
|
|
760
|
+
)
|
|
761
|
+
return {
|
|
762
|
+
"proposal": _inject_managed_bindings(
|
|
763
|
+
result.proposal,
|
|
764
|
+
model_spec=model_spec,
|
|
765
|
+
bindings=bindings,
|
|
766
|
+
).model_dump(
|
|
767
|
+
by_alias=True, mode="json"
|
|
768
|
+
),
|
|
769
|
+
"requiresConfirmation": True,
|
|
770
|
+
"authoringMode": "codex",
|
|
771
|
+
"usage": result.usage.model_dump(by_alias=True, mode="json"),
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
def _project_agent_spec(
|
|
775
|
+
self,
|
|
776
|
+
inspection: dict[str, Any],
|
|
777
|
+
*,
|
|
778
|
+
agent_id: str,
|
|
779
|
+
model_profile_id: str | None,
|
|
780
|
+
) -> tuple[AgentSpec, list[dict[str, Any]]]:
|
|
781
|
+
"""Project detected project config without silently dropping authoring fields."""
|
|
782
|
+
|
|
783
|
+
config = inspection.get("evidence", {}).get("config", {})
|
|
784
|
+
if not isinstance(config, dict):
|
|
785
|
+
config = {}
|
|
786
|
+
embedded = config.get("spec")
|
|
787
|
+
payload: dict[str, Any] = copy.deepcopy(embedded) if isinstance(embedded, dict) else {}
|
|
788
|
+
|
|
789
|
+
for field in (
|
|
790
|
+
"description",
|
|
791
|
+
"model",
|
|
792
|
+
"capabilities",
|
|
793
|
+
"bindings",
|
|
794
|
+
"execution",
|
|
795
|
+
"context",
|
|
796
|
+
"memory",
|
|
797
|
+
"security",
|
|
798
|
+
"evaluation",
|
|
799
|
+
):
|
|
800
|
+
if field not in payload and field in config:
|
|
801
|
+
payload[field] = copy.deepcopy(config[field])
|
|
802
|
+
|
|
803
|
+
instructions = payload.get("instructions")
|
|
804
|
+
if not isinstance(instructions, dict):
|
|
805
|
+
instructions = {}
|
|
806
|
+
instructions.setdefault(
|
|
807
|
+
"system",
|
|
808
|
+
str(
|
|
809
|
+
config.get("prompt")
|
|
810
|
+
or config.get("instruction")
|
|
811
|
+
or "You are a reliable assistant imported from an existing project."
|
|
812
|
+
),
|
|
813
|
+
)
|
|
814
|
+
instructions.setdefault("task", str(config.get("task_prompt") or config.get("task") or ""))
|
|
815
|
+
payload["instructions"] = instructions
|
|
816
|
+
|
|
817
|
+
model_payload = payload.get("model")
|
|
818
|
+
if isinstance(model_payload, str) and model_payload.strip():
|
|
819
|
+
upstream = (
|
|
820
|
+
(os.environ.get("OPENAI_BASE_URL") or os.environ.get("OPENAI_API_BASE") or "")
|
|
821
|
+
.strip()
|
|
822
|
+
.rstrip("/")
|
|
823
|
+
)
|
|
824
|
+
endpoint = (
|
|
825
|
+
{"endpointUrl": upstream}
|
|
826
|
+
if upstream.endswith("/chat/completions")
|
|
827
|
+
else {"baseUrl": upstream or "https://api.openai.com/v1"}
|
|
828
|
+
)
|
|
829
|
+
payload["model"] = {
|
|
830
|
+
"model": model_payload.strip(),
|
|
831
|
+
"credentialRef": "env://AGENTKIT_MODEL_API_KEY",
|
|
832
|
+
**endpoint,
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
bindings = payload.get("bindings")
|
|
836
|
+
if not isinstance(bindings, dict):
|
|
837
|
+
bindings = {}
|
|
838
|
+
else:
|
|
839
|
+
bindings = copy.deepcopy(bindings)
|
|
840
|
+
capabilities = payload.get("capabilities")
|
|
841
|
+
if not isinstance(capabilities, dict):
|
|
842
|
+
capabilities = {}
|
|
843
|
+
else:
|
|
844
|
+
capabilities = copy.deepcopy(capabilities)
|
|
845
|
+
unresolved: list[dict[str, Any]] = []
|
|
846
|
+
legacy_fields = (
|
|
847
|
+
("tools", "tools"),
|
|
848
|
+
("mcpServers", "mcp_servers"),
|
|
849
|
+
("skills", "skills"),
|
|
850
|
+
)
|
|
851
|
+
for canonical, legacy in legacy_fields:
|
|
852
|
+
if canonical in bindings or canonical in capabilities:
|
|
853
|
+
continue
|
|
854
|
+
raw = config.get(canonical, config.get(legacy))
|
|
855
|
+
if raw is None:
|
|
856
|
+
continue
|
|
857
|
+
if not isinstance(raw, list):
|
|
858
|
+
unresolved.append(
|
|
859
|
+
{"kind": canonical, "value": copy.deepcopy(raw), "reason": "not-a-list"}
|
|
860
|
+
)
|
|
861
|
+
continue
|
|
862
|
+
projected_bindings: list[dict[str, Any]] = []
|
|
863
|
+
projected_capabilities: list[dict[str, Any]] = []
|
|
864
|
+
for item in raw:
|
|
865
|
+
if isinstance(item, str) and item.strip():
|
|
866
|
+
projected_bindings.append({"resourceId": item.strip()})
|
|
867
|
+
elif isinstance(item, dict) and (item.get("resourceId") or item.get("resource_id")):
|
|
868
|
+
projected_bindings.append(copy.deepcopy(item))
|
|
869
|
+
elif isinstance(item, dict) and item.get("name") and item.get("version"):
|
|
870
|
+
projected_capabilities.append(copy.deepcopy(item))
|
|
871
|
+
else:
|
|
872
|
+
unresolved.append(
|
|
873
|
+
{
|
|
874
|
+
"kind": canonical,
|
|
875
|
+
"value": copy.deepcopy(item),
|
|
876
|
+
"reason": "unsupported-binding-shape",
|
|
877
|
+
}
|
|
878
|
+
)
|
|
879
|
+
if projected_bindings:
|
|
880
|
+
bindings[canonical] = projected_bindings
|
|
881
|
+
if projected_capabilities:
|
|
882
|
+
capabilities[canonical] = projected_capabilities
|
|
883
|
+
|
|
884
|
+
if model_profile_id:
|
|
885
|
+
existing_profiles = list(
|
|
886
|
+
bindings.get("modelProfileIds") or bindings.get("model_profile_ids") or []
|
|
887
|
+
)
|
|
888
|
+
bindings["modelProfileId"] = model_profile_id
|
|
889
|
+
bindings["modelProfileIds"] = list(
|
|
890
|
+
dict.fromkeys([model_profile_id, *existing_profiles])
|
|
891
|
+
)
|
|
892
|
+
if "modelParameters" not in bindings and "model_parameters" in config:
|
|
893
|
+
bindings["modelParameters"] = copy.deepcopy(config["model_parameters"])
|
|
894
|
+
if "policyTemplate" not in bindings:
|
|
895
|
+
policy = config.get("policy_template", config.get("policy"))
|
|
896
|
+
if isinstance(policy, str) and policy:
|
|
897
|
+
bindings["policyTemplate"] = policy
|
|
898
|
+
payload["bindings"] = bindings
|
|
899
|
+
payload["capabilities"] = capabilities
|
|
900
|
+
|
|
901
|
+
try:
|
|
902
|
+
spec = AgentSpec.model_validate(payload)
|
|
903
|
+
except ValueError as exc:
|
|
904
|
+
raise StudioError(
|
|
905
|
+
"PROJECT_AGENT_SPEC_INVALID",
|
|
906
|
+
"项目配置无法无损转换为 AgentSpec",
|
|
907
|
+
status_code=422,
|
|
908
|
+
details={"reason": str(exc)},
|
|
909
|
+
) from exc
|
|
910
|
+
|
|
911
|
+
runtime_type = str(inspection["runtimeType"])
|
|
912
|
+
detected_runtime = self.backend.runtime_ref(agent_id, runtime_type)
|
|
913
|
+
supplied_runtime = spec.runtime
|
|
914
|
+
if supplied_runtime is not None and supplied_runtime.type != runtime_type:
|
|
915
|
+
raise StudioError(
|
|
916
|
+
"PROJECT_RUNTIME_MISMATCH",
|
|
917
|
+
"项目配置中的 Runtime 与检测结果不一致",
|
|
918
|
+
status_code=422,
|
|
919
|
+
details={
|
|
920
|
+
"detected": runtime_type,
|
|
921
|
+
"configured": supplied_runtime.type,
|
|
922
|
+
},
|
|
923
|
+
)
|
|
924
|
+
if runtime_type in {"adk", "langgraph"}:
|
|
925
|
+
detected_runtime = RuntimeRef(
|
|
926
|
+
type=cast(Any, runtime_type),
|
|
927
|
+
project_path=str(inspection["projectPath"]),
|
|
928
|
+
entry_point=(
|
|
929
|
+
supplied_runtime.entry_point
|
|
930
|
+
if supplied_runtime and supplied_runtime.entry_point
|
|
931
|
+
else str(inspection.get("entryPoint") or "agent.py")
|
|
932
|
+
),
|
|
933
|
+
agent_variable=(
|
|
934
|
+
supplied_runtime.agent_variable
|
|
935
|
+
if supplied_runtime
|
|
936
|
+
else str(
|
|
937
|
+
inspection.get("agentVariable")
|
|
938
|
+
or ("graph" if runtime_type == "langgraph" else "root_agent")
|
|
939
|
+
)
|
|
940
|
+
),
|
|
941
|
+
version=supplied_runtime.version if supplied_runtime else None,
|
|
942
|
+
detection="auto",
|
|
943
|
+
)
|
|
944
|
+
elif supplied_runtime is not None and supplied_runtime.version:
|
|
945
|
+
detected_runtime.version = supplied_runtime.version
|
|
946
|
+
spec.runtime = detected_runtime
|
|
947
|
+
|
|
948
|
+
catalog_ids = {
|
|
949
|
+
kind: {item.resource_id for item in self.studio.catalog.list(kind=kind, limit=500)}
|
|
950
|
+
for kind in ("tool", "mcp", "skill")
|
|
951
|
+
}
|
|
952
|
+
for kind, values in (
|
|
953
|
+
("tool", spec.bindings.tools),
|
|
954
|
+
("mcp", spec.bindings.mcp_servers),
|
|
955
|
+
("skill", spec.bindings.skills),
|
|
956
|
+
):
|
|
957
|
+
for binding in values:
|
|
958
|
+
if binding.resource_id not in catalog_ids[kind]:
|
|
959
|
+
unresolved.append(
|
|
960
|
+
{
|
|
961
|
+
"kind": kind,
|
|
962
|
+
"value": binding.model_dump(
|
|
963
|
+
by_alias=True,
|
|
964
|
+
exclude_none=True,
|
|
965
|
+
mode="json",
|
|
966
|
+
),
|
|
967
|
+
"reason": "not-in-resource-catalog",
|
|
968
|
+
}
|
|
969
|
+
)
|
|
970
|
+
return spec, unresolved
|
|
971
|
+
|
|
972
|
+
def _agent_exists(self, agent_id: str) -> bool:
|
|
973
|
+
return bool(
|
|
974
|
+
self.studio.codex_manifests.exists(agent_id)
|
|
975
|
+
or self.studio._draft_exists(agent_id)
|
|
976
|
+
or (self.studio.workspace.resolve("agents") / agent_id).exists()
|
|
977
|
+
)
|
|
978
|
+
|
|
979
|
+
def _allocate_agent_id(self, slug: str | None) -> str:
|
|
980
|
+
if slug and slug.strip():
|
|
981
|
+
if is_generated_agent_slug(slug):
|
|
982
|
+
candidate = slug.strip()
|
|
983
|
+
if self._agent_exists(candidate):
|
|
984
|
+
raise StudioError(
|
|
985
|
+
"AGENT_ALREADY_EXISTS",
|
|
986
|
+
"本地标识已存在,请重新生成",
|
|
987
|
+
status_code=409,
|
|
988
|
+
field="slug",
|
|
989
|
+
details={"id": candidate},
|
|
990
|
+
)
|
|
991
|
+
return candidate
|
|
992
|
+
return self.backend.allocate_agent_id(slug)
|
|
993
|
+
return generate_agent_slug(self._agent_exists)
|
|
994
|
+
|
|
995
|
+
def _commit_codex_import(
|
|
996
|
+
self,
|
|
997
|
+
agent_id: str,
|
|
998
|
+
display_name: str,
|
|
999
|
+
manifest: CodexAgentManifest,
|
|
1000
|
+
*,
|
|
1001
|
+
resolved_slug: str,
|
|
1002
|
+
) -> AgentDraft:
|
|
1003
|
+
imported = manifest.model_copy(update={"name": agent_id}, deep=True)
|
|
1004
|
+
snapshot = self.studio.codex_manifests.save(imported)
|
|
1005
|
+
draft = self.studio.codex_agents._project(snapshot)
|
|
1006
|
+
draft.metadata.name = display_name
|
|
1007
|
+
draft.metadata.labels.update(
|
|
1008
|
+
{
|
|
1009
|
+
"agentkit.ksyun.com/slug": self.backend.normalize_slug(resolved_slug),
|
|
1010
|
+
"agentkit.ksyun.com/source": "import",
|
|
1011
|
+
}
|
|
1012
|
+
)
|
|
1013
|
+
self.studio.codex_drafts.save(draft)
|
|
1014
|
+
return cast(AgentDraft, self.studio.codex_agents._project(snapshot, current=draft))
|
|
1015
|
+
|
|
1016
|
+
|
|
1017
|
+
__all__ = ["StudioAuthoringCoordinator"]
|