capability-runtime 0.1.0__tar.gz → 0.1.2__tar.gz
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.
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/PKG-INFO +2 -2
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/pyproject.toml +2 -2
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/__init__.py +3 -2
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/adapters/agent_adapter.py +294 -5
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/adapters/agently_backend.py +19 -3
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/protocol/__init__.py +2 -1
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/protocol/agent.py +8 -1
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/reporting/node_report.py +20 -1
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/runtime.py +16 -3
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/sdk_lifecycle.py +100 -4
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/services.py +10 -2
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/types.py +3 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/utils/usage.py +26 -6
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime.egg-info/PKG-INFO +2 -2
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime.egg-info/SOURCES.txt +1 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime.egg-info/requires.txt +1 -1
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_agently_backend.py +85 -1
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_dependency_pins.py +2 -1
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_node_report_builder.py +53 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_node_report_contract_v1.py +12 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_per_capability_llm_config_model_routing.py +131 -1
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_public_api_exports.py +1 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_release_tag_version_guardrail.py +2 -2
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_runtime_initial_history_and_meta.py +47 -0
- capability_runtime-0.1.2/tests/test_upstream_prompt_profile_contract.py +101 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/README.md +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/setup.cfg +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/adapters/__init__.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/adapters/triggerflow_workflow_engine.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/adapters/workflow_engine.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/config.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/errors.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/guards.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/host_protocol.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/host_toolkit/__init__.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/host_toolkit/approvals_profiles.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/host_toolkit/evidence_hooks.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/host_toolkit/history.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/host_toolkit/invoke_capability.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/host_toolkit/resume.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/host_toolkit/system_prompt.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/host_toolkit/turn_delta.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/logging_utils.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/manifest.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/output_validator.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/protocol/capability.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/protocol/chat_backend.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/protocol/context.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/protocol/workflow.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/registry.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/reporting/__init__.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/runtime_ui_events_mixin.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/service_facade.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/structured_output.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/structured_stream.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/ui_events/__init__.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/ui_events/projector.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/ui_events/session.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/ui_events/store.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/ui_events/transport.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/ui_events/v1.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/upstream_compat.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/utils/__init__.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/workflow_runtime.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime.egg-info/dependency_links.txt +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime.egg-info/top_level.txt +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_agently_backend_replay.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_backend_mode.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_bilingual_docs_surface.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_bridge_artifacts_passthrough.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_bridge_register_tool_public_api.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_coding_agent_examples_atomic.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_coding_agent_examples_recipes.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_config_glue.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_docs_pinned_dependency_versions.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_docs_scheme2_no_skilladapter_residue.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_error_observability.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_examples_alignment_fixes_l1.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_examples_no_agent_sdk_imports.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_examples_real_evidence_strict_integration.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_examples_real_integration.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_examples_smoke.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_examples_ui_events_showcase_offline.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_examples_ui_events_showcase_real_fallback.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_examples_ui_events_showcase_real_integration.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_examples_ui_events_showcase_ui_contract.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_examples_workflow_skills_first_smoke.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_guards.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_host_toolkit_approvals_profiles.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_host_toolkit_history_assembler.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_host_toolkit_invoke_capability.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_host_toolkit_invoke_capability_shared_runtime.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_host_toolkit_resume_helper.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_host_toolkit_system_prompt_evidence.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_host_toolkit_turn_delta.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_integration_agently_requester_smoke.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_integration_approval_event_shape.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_integration_sources_redis_pgsql_smoke.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_loop.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_node_report_engine_identity_contract.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_offline_backend_injection_evidence.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_preflight_gate.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_project_identity_naming_matrix.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_public_repo_hygiene.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_qa_agent.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_registry.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_replay_tool_calls_alignment.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_repo_hygiene_no_tracked_env_or_pyc.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_repo_no_deep_imports_in_user_facing_docs.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_review_followups_module_contracts.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_runtime_bridge_fake_backend.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_runtime_concurrency.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_runtime_engine.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_runtime_hitl_host_protocol.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_runtime_hooks_and_schema_gate.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_runtime_manifest.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_runtime_run_stream_semantics_v1.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_runtime_service_facade_rpc_mapping.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_runtime_service_session_bridge.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_runtime_status_mapping.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_runtime_structured_output_bridge.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_runtime_structured_stream.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_sandbox_permissions_passthrough_contract.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_services_call_callback.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_services_map_node_status.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_skills_conformance_smoke.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_upstream_adapter_tool_registration.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_upstream_chat_backend_protocol_contract.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_upstream_chat_sse_usage_contract.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_upstream_compat_spaces_schema.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_upstream_runtime_client_server_contract.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_upstream_sandbox_profile_precedence_contract.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_upstream_skills_bundles_contract.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_upstream_tool_descriptor_compat.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_upstream_verification.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_versioning_strategy_guard.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_wal_backend_injection_contract.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_wal_locator_resolution_contract.py +0 -0
- {capability_runtime-0.1.0 → capability_runtime-0.1.2}/tests/test_workflow_host_runtime_surface.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: capability-runtime
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Bridge/glue layer that composes Agently (LLM/TriggerFlow) with skills-runtime-sdk (skills/tools/WAL/events).
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -8,7 +8,7 @@ Description-Content-Type: text/markdown
|
|
|
8
8
|
Requires-Dist: PyYAML>=6
|
|
9
9
|
Requires-Dist: pydantic<3,>=2
|
|
10
10
|
Requires-Dist: agently==4.0.8
|
|
11
|
-
Requires-Dist: skills-runtime-sdk==0.1.
|
|
11
|
+
Requires-Dist: skills-runtime-sdk==0.1.12
|
|
12
12
|
Provides-Extra: dev
|
|
13
13
|
Requires-Dist: pytest>=7; extra == "dev"
|
|
14
14
|
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "capability-runtime"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
description = "Bridge/glue layer that composes Agently (LLM/TriggerFlow) with skills-runtime-sdk (skills/tools/WAL/events)."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -9,7 +9,7 @@ dependencies = [
|
|
|
9
9
|
"PyYAML>=6",
|
|
10
10
|
"pydantic>=2,<3",
|
|
11
11
|
"agently==4.0.8",
|
|
12
|
-
"skills-runtime-sdk==0.1.
|
|
12
|
+
"skills-runtime-sdk==0.1.12",
|
|
13
13
|
]
|
|
14
14
|
|
|
15
15
|
[project.optional-dependencies]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""capability-runtime:统一 Runtime 入口(能力协议 + 执行 + 报告)。"""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
-
__version__ = "0.1.
|
|
4
|
+
__version__ = "0.1.2"
|
|
5
5
|
|
|
6
6
|
# === 统一入口 ===
|
|
7
7
|
from .config import CustomTool, RuntimeConfig
|
|
@@ -18,7 +18,7 @@ from .host_protocol import ApprovalTicket, HostRunSnapshot, HostRunStatus, Resum
|
|
|
18
18
|
from .manifest import CapabilityDescriptor, CapabilityManifestEntry, CapabilityVisibility
|
|
19
19
|
|
|
20
20
|
# === Protocol 导出 ===
|
|
21
|
-
from .protocol.agent import AgentIOSchema, AgentSpec
|
|
21
|
+
from .protocol.agent import AgentIOSchema, AgentSpec, PromptRenderMode
|
|
22
22
|
from .protocol.capability import (
|
|
23
23
|
CapabilityKind,
|
|
24
24
|
CapabilityRef,
|
|
@@ -76,6 +76,7 @@ __all__ = [
|
|
|
76
76
|
"RuntimeServiceFacade",
|
|
77
77
|
"AgentSpec",
|
|
78
78
|
"AgentIOSchema",
|
|
79
|
+
"PromptRenderMode",
|
|
79
80
|
"WorkflowSpec",
|
|
80
81
|
"Step",
|
|
81
82
|
"LoopStep",
|
|
@@ -10,8 +10,11 @@ AgentAdapter:AgentSpec 的执行适配器(统一 mock/bridge/sdk_native)
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
import asyncio
|
|
13
|
+
import hashlib
|
|
13
14
|
import inspect
|
|
14
15
|
import json
|
|
16
|
+
import re
|
|
17
|
+
from dataclasses import dataclass
|
|
15
18
|
from typing import Any, AsyncIterator, Dict, List, Optional, Union
|
|
16
19
|
|
|
17
20
|
from skills_runtime.core.contracts import AgentEvent
|
|
@@ -30,6 +33,34 @@ _SECTION_TASK = "## 任务"
|
|
|
30
33
|
_SECTION_INPUT = "## 输入"
|
|
31
34
|
_SECTION_OUTPUT_PREFIX = "## 输出要求"
|
|
32
35
|
_SECTION_SKILLS = "## 使用以下 Skills"
|
|
36
|
+
_RUNTIME_PROMPT_KEY = "_runtime_prompt"
|
|
37
|
+
_PROMPT_HASH_RE = re.compile(r"^sha256:[0-9a-f]{64}$")
|
|
38
|
+
_ALLOWED_PROMPT_ROLES = {"system", "developer", "user", "assistant", "tool"}
|
|
39
|
+
_ALLOWED_PROMPT_PROFILES = {"default_agent", "generation_direct", "structured_transform"}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class _InvalidPromptMessages(ValueError):
|
|
43
|
+
"""prompt control envelope 非法。"""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass(frozen=True)
|
|
47
|
+
class _PromptRenderPlan:
|
|
48
|
+
"""
|
|
49
|
+
AgentAdapter 内部 prompt 渲染计划。
|
|
50
|
+
|
|
51
|
+
字段:
|
|
52
|
+
- mode:三种 prompt rendering strategy 之一;
|
|
53
|
+
- task:传给 SDK Agent.run_stream_async 的 task 文本;
|
|
54
|
+
- prompt_profile:透传给上游 SDK 的 prompt profile;
|
|
55
|
+
- precomposed_messages:最终 provider messages 覆写;
|
|
56
|
+
- evidence:写入 NodeReport.meta 的最小披露摘要。
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
mode: str
|
|
60
|
+
task: str
|
|
61
|
+
prompt_profile: Optional[str]
|
|
62
|
+
precomposed_messages: Optional[List[Dict[str, Any]]]
|
|
63
|
+
evidence: Dict[str, Any]
|
|
33
64
|
|
|
34
65
|
|
|
35
66
|
class AgentAdapter:
|
|
@@ -168,6 +199,29 @@ class AgentAdapter:
|
|
|
168
199
|
真实执行(bridge/sdk_native):驱动 SDK Agent.run_stream_async 并聚合 NodeReport。
|
|
169
200
|
"""
|
|
170
201
|
|
|
202
|
+
try:
|
|
203
|
+
prompt_plan = self._resolve_prompt_render_plan(spec=spec, input=input)
|
|
204
|
+
except _InvalidPromptMessages as exc:
|
|
205
|
+
report = self._services.build_fail_closed_report(
|
|
206
|
+
run_id=context.run_id,
|
|
207
|
+
status="failed",
|
|
208
|
+
reason="invalid_prompt_messages",
|
|
209
|
+
completion_reason="invalid_prompt_messages",
|
|
210
|
+
meta={
|
|
211
|
+
"capability_id": spec.base.id,
|
|
212
|
+
"source": "prompt_rendering",
|
|
213
|
+
"prompt_error": str(exc),
|
|
214
|
+
},
|
|
215
|
+
)
|
|
216
|
+
yield CapabilityResult(
|
|
217
|
+
status=CapabilityStatus.FAILED,
|
|
218
|
+
error=str(exc),
|
|
219
|
+
error_code="INVALID_PROMPT_MESSAGES",
|
|
220
|
+
report=report,
|
|
221
|
+
node_report=report,
|
|
222
|
+
)
|
|
223
|
+
return
|
|
224
|
+
|
|
171
225
|
# preflight gate(生产默认 fail-closed)
|
|
172
226
|
issues: List[FrameworkIssue] = []
|
|
173
227
|
if getattr(self._services.config, "preflight_mode", "error") != "off":
|
|
@@ -184,6 +238,7 @@ class AgentAdapter:
|
|
|
184
238
|
"code": "SKILL_PREFLIGHT_FAILED",
|
|
185
239
|
"details": {"issues": [self._services.redact_issue(i) for i in issues]},
|
|
186
240
|
},
|
|
241
|
+
**prompt_plan.evidence,
|
|
187
242
|
},
|
|
188
243
|
)
|
|
189
244
|
yield CapabilityResult(
|
|
@@ -196,8 +251,34 @@ class AgentAdapter:
|
|
|
196
251
|
)
|
|
197
252
|
return
|
|
198
253
|
|
|
199
|
-
task =
|
|
200
|
-
|
|
254
|
+
task = prompt_plan.task
|
|
255
|
+
create_agent_kwargs: Dict[str, Any] = {"llm_config": spec.llm_config}
|
|
256
|
+
if prompt_plan.prompt_profile is not None:
|
|
257
|
+
create_agent_kwargs["prompt_profile"] = prompt_plan.prompt_profile
|
|
258
|
+
if prompt_plan.precomposed_messages is not None:
|
|
259
|
+
create_agent_kwargs["precomposed_messages"] = prompt_plan.precomposed_messages
|
|
260
|
+
try:
|
|
261
|
+
agent = self._services.create_sdk_agent(**create_agent_kwargs)
|
|
262
|
+
except Exception as exc:
|
|
263
|
+
report = self._services.build_fail_closed_report(
|
|
264
|
+
run_id=context.run_id,
|
|
265
|
+
status="failed",
|
|
266
|
+
reason="engine_error",
|
|
267
|
+
completion_reason="engine_exception",
|
|
268
|
+
meta={
|
|
269
|
+
"source": "sdk_agent_create",
|
|
270
|
+
"engine_exception": type(exc).__name__,
|
|
271
|
+
**prompt_plan.evidence,
|
|
272
|
+
},
|
|
273
|
+
)
|
|
274
|
+
yield CapabilityResult(
|
|
275
|
+
status=CapabilityStatus.FAILED,
|
|
276
|
+
error=str(exc),
|
|
277
|
+
error_code="ENGINE_ERROR",
|
|
278
|
+
report=report,
|
|
279
|
+
node_report=report,
|
|
280
|
+
)
|
|
281
|
+
return
|
|
201
282
|
|
|
202
283
|
events: List[AgentEvent] = []
|
|
203
284
|
host_meta = self._services.get_host_meta(context=context)
|
|
@@ -241,6 +322,7 @@ class AgentAdapter:
|
|
|
241
322
|
completion_reason="run_cancelled",
|
|
242
323
|
meta={"capability_id": spec.base.id, "source": "sdk_agent"},
|
|
243
324
|
)
|
|
325
|
+
self._apply_prompt_evidence(report=report, evidence=prompt_plan.evidence)
|
|
244
326
|
yield CapabilityResult(
|
|
245
327
|
status=CapabilityStatus.CANCELLED,
|
|
246
328
|
error="execution cancelled",
|
|
@@ -255,7 +337,7 @@ class AgentAdapter:
|
|
|
255
337
|
status="failed",
|
|
256
338
|
reason="engine_error",
|
|
257
339
|
completion_reason="engine_exception",
|
|
258
|
-
meta={"engine_exception": type(exc).__name__},
|
|
340
|
+
meta={"engine_exception": type(exc).__name__, **prompt_plan.evidence},
|
|
259
341
|
)
|
|
260
342
|
yield CapabilityResult(
|
|
261
343
|
status=CapabilityStatus.FAILED,
|
|
@@ -267,6 +349,7 @@ class AgentAdapter:
|
|
|
267
349
|
return
|
|
268
350
|
|
|
269
351
|
report = NodeReportBuilder().build(events=events)
|
|
352
|
+
self._apply_prompt_evidence(report=report, evidence=prompt_plan.evidence)
|
|
270
353
|
if issues and getattr(self._services.config, "preflight_mode", "error") == "warn":
|
|
271
354
|
report.meta["preflight_mode"] = "warn"
|
|
272
355
|
report.meta["preflight_issues"] = [self._services.redact_issue(i) for i in issues]
|
|
@@ -306,6 +389,166 @@ class AgentAdapter:
|
|
|
306
389
|
artifacts=list(report.artifacts),
|
|
307
390
|
)
|
|
308
391
|
|
|
392
|
+
def _resolve_prompt_render_plan(self, *, spec: AgentSpec, input: Dict[str, Any]) -> _PromptRenderPlan:
|
|
393
|
+
"""
|
|
394
|
+
解析 AgentSpec + run-level `_runtime_prompt`,生成 prompt 渲染计划。
|
|
395
|
+
|
|
396
|
+
参数:
|
|
397
|
+
- spec:Agent 声明
|
|
398
|
+
- input:run 输入,其中 `_runtime_prompt` 为保留控制面
|
|
399
|
+
|
|
400
|
+
返回:
|
|
401
|
+
- `_PromptRenderPlan`
|
|
402
|
+
|
|
403
|
+
异常:
|
|
404
|
+
- `_InvalidPromptMessages`:控制面非法,调用方应 fail-fast。
|
|
405
|
+
"""
|
|
406
|
+
|
|
407
|
+
envelope_raw = input.get(_RUNTIME_PROMPT_KEY)
|
|
408
|
+
if envelope_raw is None:
|
|
409
|
+
envelope: Dict[str, Any] = {}
|
|
410
|
+
elif isinstance(envelope_raw, dict):
|
|
411
|
+
envelope = dict(envelope_raw)
|
|
412
|
+
else:
|
|
413
|
+
raise _InvalidPromptMessages("_runtime_prompt must be a dict")
|
|
414
|
+
|
|
415
|
+
raw_mode = envelope.get("mode") if "mode" in envelope else getattr(spec, "prompt_render_mode", "structured_task")
|
|
416
|
+
if raw_mode is None:
|
|
417
|
+
raw_mode = "structured_task"
|
|
418
|
+
if not isinstance(raw_mode, str) or not raw_mode.strip():
|
|
419
|
+
raise _InvalidPromptMessages("prompt render mode must be a non-empty string")
|
|
420
|
+
mode = raw_mode.strip()
|
|
421
|
+
if mode not in {"structured_task", "direct_task_text", "precomposed_messages"}:
|
|
422
|
+
raise _InvalidPromptMessages(
|
|
423
|
+
"unsupported prompt render mode; allowed: direct_task_text, precomposed_messages, structured_task"
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
prompt_profile = envelope.get("profile")
|
|
427
|
+
if prompt_profile is None:
|
|
428
|
+
prompt_profile = getattr(spec, "prompt_profile", None)
|
|
429
|
+
if prompt_profile is not None:
|
|
430
|
+
if not isinstance(prompt_profile, str) or not prompt_profile.strip():
|
|
431
|
+
raise _InvalidPromptMessages("prompt profile must be a non-empty string")
|
|
432
|
+
prompt_profile = prompt_profile.strip()
|
|
433
|
+
if prompt_profile not in _ALLOWED_PROMPT_PROFILES:
|
|
434
|
+
allowed = ", ".join(sorted(_ALLOWED_PROMPT_PROFILES))
|
|
435
|
+
raise _InvalidPromptMessages(f"unsupported prompt profile; allowed: {allowed}")
|
|
436
|
+
|
|
437
|
+
trace = envelope.get("trace") if "trace" in envelope else {}
|
|
438
|
+
if trace is None:
|
|
439
|
+
trace = {}
|
|
440
|
+
if not isinstance(trace, dict):
|
|
441
|
+
raise _InvalidPromptMessages("_runtime_prompt.trace must be a dict")
|
|
442
|
+
prompt_hash = trace.get("prompt_hash")
|
|
443
|
+
if prompt_hash is not None:
|
|
444
|
+
if not isinstance(prompt_hash, str) or _PROMPT_HASH_RE.fullmatch(prompt_hash.strip()) is None:
|
|
445
|
+
raise _InvalidPromptMessages("trace.prompt_hash must match sha256:<64 lowercase hex>")
|
|
446
|
+
prompt_hash = prompt_hash.strip()
|
|
447
|
+
composer_version = trace.get("composer_version")
|
|
448
|
+
if composer_version is not None and not isinstance(composer_version, str):
|
|
449
|
+
raise _InvalidPromptMessages("trace.composer_version must be a string")
|
|
450
|
+
|
|
451
|
+
if mode == "structured_task":
|
|
452
|
+
business_input = self._strip_runtime_prompt(input)
|
|
453
|
+
task = self._build_task(spec=spec, input=business_input)
|
|
454
|
+
evidence = self._build_prompt_evidence(
|
|
455
|
+
mode=mode,
|
|
456
|
+
prompt_profile=prompt_profile,
|
|
457
|
+
prompt_hash=prompt_hash or _hash_text(task),
|
|
458
|
+
composer_version=composer_version,
|
|
459
|
+
messages=None,
|
|
460
|
+
)
|
|
461
|
+
return _PromptRenderPlan(
|
|
462
|
+
mode=mode,
|
|
463
|
+
task=task,
|
|
464
|
+
prompt_profile=prompt_profile,
|
|
465
|
+
precomposed_messages=None,
|
|
466
|
+
evidence=evidence,
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
if mode == "direct_task_text":
|
|
470
|
+
task_text = envelope.get("task_text")
|
|
471
|
+
if not isinstance(task_text, str) or not task_text.strip():
|
|
472
|
+
raise _InvalidPromptMessages("_runtime_prompt.task_text must be a non-empty string")
|
|
473
|
+
task = task_text
|
|
474
|
+
evidence = self._build_prompt_evidence(
|
|
475
|
+
mode=mode,
|
|
476
|
+
prompt_profile=prompt_profile,
|
|
477
|
+
prompt_hash=prompt_hash or _hash_text(task),
|
|
478
|
+
composer_version=composer_version,
|
|
479
|
+
messages=None,
|
|
480
|
+
)
|
|
481
|
+
return _PromptRenderPlan(
|
|
482
|
+
mode=mode,
|
|
483
|
+
task=task,
|
|
484
|
+
prompt_profile=prompt_profile,
|
|
485
|
+
precomposed_messages=None,
|
|
486
|
+
evidence=evidence,
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
messages = _validate_precomposed_messages(envelope.get("messages"))
|
|
490
|
+
evidence = self._build_prompt_evidence(
|
|
491
|
+
mode=mode,
|
|
492
|
+
prompt_profile=prompt_profile,
|
|
493
|
+
prompt_hash=prompt_hash or _hash_messages(messages),
|
|
494
|
+
composer_version=composer_version,
|
|
495
|
+
messages=messages,
|
|
496
|
+
)
|
|
497
|
+
return _PromptRenderPlan(
|
|
498
|
+
mode=mode,
|
|
499
|
+
task="",
|
|
500
|
+
prompt_profile=prompt_profile,
|
|
501
|
+
precomposed_messages=messages,
|
|
502
|
+
evidence=evidence,
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
def _build_prompt_evidence(
|
|
506
|
+
self,
|
|
507
|
+
*,
|
|
508
|
+
mode: str,
|
|
509
|
+
prompt_profile: Optional[str],
|
|
510
|
+
prompt_hash: str,
|
|
511
|
+
composer_version: Any,
|
|
512
|
+
messages: Optional[List[Dict[str, Any]]],
|
|
513
|
+
) -> Dict[str, Any]:
|
|
514
|
+
"""
|
|
515
|
+
构造 NodeReport.meta 的 prompt 最小披露摘要。
|
|
516
|
+
|
|
517
|
+
参数:
|
|
518
|
+
- mode:prompt render mode
|
|
519
|
+
- prompt_profile:可选 SDK prompt profile
|
|
520
|
+
- prompt_hash:`sha256:<hex>` 摘要
|
|
521
|
+
- composer_version:可选 composer 版本
|
|
522
|
+
- messages:precomposed messages;仅用于 count/roles 摘要
|
|
523
|
+
"""
|
|
524
|
+
|
|
525
|
+
evidence: Dict[str, Any] = {
|
|
526
|
+
"prompt_render_mode": mode,
|
|
527
|
+
"prompt_hash": prompt_hash,
|
|
528
|
+
}
|
|
529
|
+
if prompt_profile:
|
|
530
|
+
evidence["prompt_profile"] = prompt_profile
|
|
531
|
+
if isinstance(composer_version, str) and composer_version:
|
|
532
|
+
evidence["prompt_composer_version"] = composer_version
|
|
533
|
+
if messages is not None:
|
|
534
|
+
evidence["prompt_messages_count"] = len(messages)
|
|
535
|
+
evidence["prompt_message_roles"] = [str(item["role"]) for item in messages]
|
|
536
|
+
return evidence
|
|
537
|
+
|
|
538
|
+
def _strip_runtime_prompt(self, input: Dict[str, Any]) -> Dict[str, Any]:
|
|
539
|
+
"""从业务 input 中剥离 runtime prompt 控制面。"""
|
|
540
|
+
|
|
541
|
+
if _RUNTIME_PROMPT_KEY not in input:
|
|
542
|
+
return input
|
|
543
|
+
return {k: v for k, v in input.items() if k != _RUNTIME_PROMPT_KEY}
|
|
544
|
+
|
|
545
|
+
def _apply_prompt_evidence(self, *, report: Any, evidence: Dict[str, Any]) -> None:
|
|
546
|
+
"""把 prompt evidence 写入 NodeReport.meta;兼容测试中的 dict fake report。"""
|
|
547
|
+
|
|
548
|
+
meta = getattr(report, "meta", None)
|
|
549
|
+
if isinstance(meta, dict):
|
|
550
|
+
meta.update(evidence)
|
|
551
|
+
|
|
309
552
|
def _build_task(self, *, spec: AgentSpec, input: Dict[str, Any]) -> str:
|
|
310
553
|
"""
|
|
311
554
|
将 AgentSpec + input 转换为 SDK Agent 的 task 文本(结构化拼接)。
|
|
@@ -323,9 +566,10 @@ class AgentAdapter:
|
|
|
323
566
|
if spec.base.description:
|
|
324
567
|
parts.append(f"{_SECTION_TASK}\n{spec.base.description}")
|
|
325
568
|
|
|
326
|
-
|
|
569
|
+
business_input = self._strip_runtime_prompt(input)
|
|
570
|
+
if business_input:
|
|
327
571
|
lines: List[str] = []
|
|
328
|
-
for k, v in
|
|
572
|
+
for k, v in business_input.items():
|
|
329
573
|
if isinstance(v, str):
|
|
330
574
|
lines.append(f"- {k}: {v}")
|
|
331
575
|
else:
|
|
@@ -437,3 +681,48 @@ class AgentAdapter:
|
|
|
437
681
|
if account and domain:
|
|
438
682
|
return f"[{account}:{domain}]"
|
|
439
683
|
return None
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
def _hash_text(text: str) -> str:
|
|
687
|
+
"""生成 prompt 文本的 sha256 摘要。"""
|
|
688
|
+
|
|
689
|
+
return "sha256:" + hashlib.sha256(text.encode("utf-8")).hexdigest()
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
def _hash_messages(messages: List[Dict[str, Any]]) -> str:
|
|
693
|
+
"""对 provider messages 做稳定 JSON canonicalization 后生成摘要。"""
|
|
694
|
+
|
|
695
|
+
canonical = json.dumps(messages, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
|
|
696
|
+
return _hash_text(canonical)
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
def _validate_precomposed_messages(raw: Any) -> List[Dict[str, Any]]:
|
|
700
|
+
"""
|
|
701
|
+
校验 host 提供的最终 provider messages。
|
|
702
|
+
|
|
703
|
+
约束:
|
|
704
|
+
- 只接受非空 list[dict];
|
|
705
|
+
- 每条消息必须有合法 role 与字符串 content;
|
|
706
|
+
- 返回浅拷贝,避免后续执行链路修改调用方输入。
|
|
707
|
+
"""
|
|
708
|
+
|
|
709
|
+
if not isinstance(raw, list):
|
|
710
|
+
raise _InvalidPromptMessages("_runtime_prompt.messages must be a list")
|
|
711
|
+
if not raw:
|
|
712
|
+
raise _InvalidPromptMessages("_runtime_prompt.messages must not be empty")
|
|
713
|
+
|
|
714
|
+
out: List[Dict[str, Any]] = []
|
|
715
|
+
for idx, item in enumerate(raw):
|
|
716
|
+
if not isinstance(item, dict):
|
|
717
|
+
raise _InvalidPromptMessages(f"_runtime_prompt.messages[{idx}] must be a dict")
|
|
718
|
+
role = item.get("role")
|
|
719
|
+
content = item.get("content")
|
|
720
|
+
if not isinstance(role, str) or role not in _ALLOWED_PROMPT_ROLES:
|
|
721
|
+
raise _InvalidPromptMessages(f"_runtime_prompt.messages[{idx}].role is invalid")
|
|
722
|
+
if not isinstance(content, str):
|
|
723
|
+
raise _InvalidPromptMessages(f"_runtime_prompt.messages[{idx}].content must be a string")
|
|
724
|
+
copied = dict(item)
|
|
725
|
+
copied["role"] = role
|
|
726
|
+
copied["content"] = content
|
|
727
|
+
out.append(copied)
|
|
728
|
+
return out
|
|
@@ -66,13 +66,19 @@ class AgentlyBackendConfig:
|
|
|
66
66
|
|
|
67
67
|
requester_factory: AgentlyRequesterFactory
|
|
68
68
|
|
|
69
|
-
def _normalize_usage_payload(
|
|
69
|
+
def _normalize_usage_payload(
|
|
70
|
+
*,
|
|
71
|
+
usage: Any,
|
|
72
|
+
model: Any = None,
|
|
73
|
+
request_id: Any = None,
|
|
74
|
+
provider: Any = None,
|
|
75
|
+
) -> Optional[Dict[str, Any]]:
|
|
70
76
|
"""
|
|
71
77
|
把 provider usage 归一为 capability-runtime 的 `llm_usage` payload 形状。
|
|
72
78
|
|
|
73
79
|
返回:
|
|
74
80
|
- `None`:无法提取任何有效 usage 字段
|
|
75
|
-
- `dict`:`model/input_tokens/output_tokens/total_tokens`
|
|
81
|
+
- `dict`:`model/input_tokens/output_tokens/total_tokens/request_id/provider`
|
|
76
82
|
"""
|
|
77
83
|
|
|
78
84
|
if not isinstance(usage, dict):
|
|
@@ -93,6 +99,8 @@ def _normalize_usage_payload(*, usage: Any, model: Any = None) -> Optional[Dict[
|
|
|
93
99
|
"input_tokens": input_tokens,
|
|
94
100
|
"output_tokens": output_tokens,
|
|
95
101
|
"total_tokens": total_tokens,
|
|
102
|
+
"request_id": request_id.strip() if isinstance(request_id, str) and request_id.strip() else None,
|
|
103
|
+
"provider": provider.strip() if isinstance(provider, str) and provider.strip() else None,
|
|
96
104
|
}
|
|
97
105
|
return payload if any(value is not None for value in payload.values()) else None
|
|
98
106
|
|
|
@@ -120,7 +128,15 @@ def _extract_usage_payload_from_sse_data(data: str) -> Optional[Dict[str, Any]]:
|
|
|
120
128
|
return None
|
|
121
129
|
if not isinstance(obj, dict):
|
|
122
130
|
return None
|
|
123
|
-
|
|
131
|
+
request_id = obj.get("request_id")
|
|
132
|
+
if not (isinstance(request_id, str) and request_id.strip()):
|
|
133
|
+
request_id = obj.get("id")
|
|
134
|
+
return _normalize_usage_payload(
|
|
135
|
+
usage=obj.get("usage"),
|
|
136
|
+
model=obj.get("model"),
|
|
137
|
+
request_id=request_id,
|
|
138
|
+
provider=obj.get("provider"),
|
|
139
|
+
)
|
|
124
140
|
|
|
125
141
|
|
|
126
142
|
def _merge_stream_options_for_usage(value: Any) -> Dict[str, Any]:
|
{capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/protocol/__init__.py
RENAMED
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
"""Protocol 层:纯能力类型定义,不依赖任何上游模块。"""
|
|
4
4
|
|
|
5
|
-
from .agent import AgentIOSchema, AgentSpec
|
|
5
|
+
from .agent import AgentIOSchema, AgentSpec, PromptRenderMode
|
|
6
6
|
from .capability import (
|
|
7
7
|
CapabilityKind,
|
|
8
8
|
CapabilityRef,
|
|
@@ -30,6 +30,7 @@ __all__ = [
|
|
|
30
30
|
"CapabilityResult",
|
|
31
31
|
"AgentSpec",
|
|
32
32
|
"AgentIOSchema",
|
|
33
|
+
"PromptRenderMode",
|
|
33
34
|
"ChatBackendProtocol",
|
|
34
35
|
"WorkflowSpec",
|
|
35
36
|
"Step",
|
{capability_runtime-0.1.0 → capability_runtime-0.1.2}/src/capability_runtime/protocol/agent.py
RENAMED
|
@@ -3,11 +3,14 @@ from __future__ import annotations
|
|
|
3
3
|
"""Agent 元能力声明。"""
|
|
4
4
|
|
|
5
5
|
from dataclasses import dataclass, field
|
|
6
|
-
from typing import Any, Dict, List, Optional
|
|
6
|
+
from typing import Any, Dict, List, Literal, Optional
|
|
7
7
|
|
|
8
8
|
from .capability import CapabilityRef, CapabilitySpec
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
PromptRenderMode = Literal["structured_task", "direct_task_text", "precomposed_messages"]
|
|
12
|
+
|
|
13
|
+
|
|
11
14
|
@dataclass(frozen=True)
|
|
12
15
|
class AgentIOSchema:
|
|
13
16
|
"""
|
|
@@ -36,6 +39,8 @@ class AgentSpec:
|
|
|
36
39
|
- output_schema: 输出 schema(可选)
|
|
37
40
|
- loop_compatible: 是否可被 LoopStep 循环调用
|
|
38
41
|
- llm_config: LLM 覆盖配置
|
|
42
|
+
- prompt_render_mode: prompt 渲染模式(默认 structured_task,保持兼容)
|
|
43
|
+
- prompt_profile: 可选上游 SDK prompt profile(如 generation_direct)
|
|
39
44
|
- prompt_template: 可选的 prompt 模板(支持 {field} 占位符)
|
|
40
45
|
- system_prompt: 可选的“Agent 级 system message”(用于该 Agent 的提示词组织)
|
|
41
46
|
|
|
@@ -58,5 +63,7 @@ class AgentSpec:
|
|
|
58
63
|
output_schema: Optional[AgentIOSchema] = None
|
|
59
64
|
loop_compatible: bool = False
|
|
60
65
|
llm_config: Optional[Dict[str, Any]] = None
|
|
66
|
+
prompt_render_mode: PromptRenderMode = "structured_task"
|
|
67
|
+
prompt_profile: Optional[str] = None
|
|
61
68
|
prompt_template: Optional[str] = None
|
|
62
69
|
system_prompt: Optional[str] = None
|
|
@@ -161,6 +161,8 @@ class NodeReportBuilder:
|
|
|
161
161
|
usage_input_seen = False
|
|
162
162
|
usage_output_seen = False
|
|
163
163
|
usage_total_seen = False
|
|
164
|
+
usage_request_id: Optional[str] = None
|
|
165
|
+
usage_provider: Optional[str] = None
|
|
164
166
|
|
|
165
167
|
def _ensure_tool(call_id: str, *, name: str) -> Dict[str, Any]:
|
|
166
168
|
"""获取/初始化工具调用聚合槽位(以 call_id 为主键)。"""
|
|
@@ -217,6 +219,14 @@ class NodeReportBuilder:
|
|
|
217
219
|
if isinstance(model_name, str) and model_name.strip():
|
|
218
220
|
usage_model = model_name.strip()
|
|
219
221
|
|
|
222
|
+
request_id = usage_summary.get("request_id")
|
|
223
|
+
if isinstance(request_id, str) and request_id.strip():
|
|
224
|
+
usage_request_id = request_id.strip()
|
|
225
|
+
|
|
226
|
+
provider = usage_summary.get("provider")
|
|
227
|
+
if isinstance(provider, str) and provider.strip():
|
|
228
|
+
usage_provider = provider.strip()
|
|
229
|
+
|
|
220
230
|
input_tokens = usage_summary.get("input_tokens")
|
|
221
231
|
if isinstance(input_tokens, int):
|
|
222
232
|
usage_input_total += input_tokens
|
|
@@ -378,12 +388,21 @@ class NodeReportBuilder:
|
|
|
378
388
|
NodeToolCallReport.model_validate(item) for item in tool_calls.values() if isinstance(item, dict)
|
|
379
389
|
]
|
|
380
390
|
usage_report: Optional[NodeUsageReport] = None
|
|
381
|
-
if
|
|
391
|
+
if (
|
|
392
|
+
usage_model is not None
|
|
393
|
+
or usage_input_seen
|
|
394
|
+
or usage_output_seen
|
|
395
|
+
or usage_total_seen
|
|
396
|
+
or usage_request_id is not None
|
|
397
|
+
or usage_provider is not None
|
|
398
|
+
):
|
|
382
399
|
usage_report = NodeUsageReport(
|
|
383
400
|
model=usage_model,
|
|
384
401
|
input_tokens=usage_input_total if usage_input_seen else None,
|
|
385
402
|
output_tokens=usage_output_total if usage_output_seen else None,
|
|
386
403
|
total_tokens=usage_total_total if usage_total_seen else None,
|
|
404
|
+
request_id=usage_request_id,
|
|
405
|
+
provider=usage_provider,
|
|
387
406
|
)
|
|
388
407
|
|
|
389
408
|
report = NodeReport(
|
|
@@ -914,17 +914,30 @@ class Runtime(RuntimeUIEventsMixin):
|
|
|
914
914
|
return []
|
|
915
915
|
return self._sdk.preflight()
|
|
916
916
|
|
|
917
|
-
def create_sdk_agent(
|
|
917
|
+
def create_sdk_agent(
|
|
918
|
+
self,
|
|
919
|
+
*,
|
|
920
|
+
llm_config: Optional[Dict[str, Any]] = None,
|
|
921
|
+
prompt_profile: Optional[str] = None,
|
|
922
|
+
precomposed_messages: Optional[List[Dict[str, Any]]] = None,
|
|
923
|
+
) -> Any:
|
|
918
924
|
"""
|
|
919
925
|
RuntimeServices 协议方法:创建 per-run SDK Agent。
|
|
920
926
|
|
|
921
927
|
参数:
|
|
922
|
-
- llm_config:可选 LLM
|
|
928
|
+
- llm_config:可选 LLM 覆写配置
|
|
929
|
+
- prompt_profile:可选 SDK prompt profile
|
|
930
|
+
- precomposed_messages:可选最终 provider messages 覆写
|
|
923
931
|
"""
|
|
924
932
|
|
|
925
933
|
if self._sdk is None:
|
|
926
934
|
raise RuntimeError("SDK lifecycle is not initialized")
|
|
927
|
-
return self._sdk.create_agent(
|
|
935
|
+
return self._sdk.create_agent(
|
|
936
|
+
custom_tools=list(self._config.custom_tools),
|
|
937
|
+
llm_config=llm_config,
|
|
938
|
+
prompt_profile=prompt_profile,
|
|
939
|
+
precomposed_messages=precomposed_messages,
|
|
940
|
+
)
|
|
928
941
|
|
|
929
942
|
|
|
930
943
|
__all__ = ["Runtime"]
|