4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
95 lines
3.0 KiB
Python
95 lines
3.0 KiB
Python
"""Structured prompt blocks rendered at the provider boundary."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Iterable, Mapping
|
|
from dataclasses import dataclass, field
|
|
from typing import Any, Literal
|
|
|
|
type PromptBlockKind = Literal["system", "rule", "context", "conversation", "tool", "user"]
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class PromptBlock:
|
|
"""One model-visible prompt block with optional provenance metadata."""
|
|
|
|
id: str
|
|
content: str
|
|
kind: PromptBlockKind = "context"
|
|
title: str | None = None
|
|
priority: int = 0
|
|
provenance: str | None = None
|
|
token_estimate: int | None = None
|
|
metadata: Mapping[str, Any] = field(default_factory=dict)
|
|
include_title: bool = False
|
|
|
|
def render(self) -> str:
|
|
"""Render this block without changing its body text."""
|
|
if not self.content:
|
|
return ""
|
|
if self.include_title and self.title:
|
|
return f"--- {self.title} ---\n{self.content}"
|
|
return self.content
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class PromptEnvelope:
|
|
"""Ordered collection of structured prompt blocks.
|
|
|
|
The envelope keeps block identity, kind, priority, provenance, and token
|
|
estimates available to tests and future trimming policy while still
|
|
rendering to the same prompt strings current providers accept.
|
|
"""
|
|
|
|
blocks: tuple[PromptBlock, ...] = ()
|
|
separator: str = "\n\n"
|
|
metadata: Mapping[str, Any] = field(default_factory=dict)
|
|
|
|
@classmethod
|
|
def from_text(
|
|
cls,
|
|
content: str,
|
|
*,
|
|
block_id: str = "prompt",
|
|
kind: PromptBlockKind = "system",
|
|
metadata: Mapping[str, Any] | None = None,
|
|
) -> PromptEnvelope:
|
|
"""Wrap an existing string prompt in a single structured block."""
|
|
return cls(
|
|
blocks=(PromptBlock(id=block_id, content=content, kind=kind),),
|
|
metadata=dict(metadata or {}),
|
|
)
|
|
|
|
@classmethod
|
|
def from_blocks(
|
|
cls,
|
|
blocks: Iterable[PromptBlock],
|
|
*,
|
|
separator: str = "\n\n",
|
|
metadata: Mapping[str, Any] | None = None,
|
|
) -> PromptEnvelope:
|
|
return cls(
|
|
blocks=tuple(blocks),
|
|
separator=separator,
|
|
metadata=dict(metadata or {}),
|
|
)
|
|
|
|
def block(self, block_id: str) -> PromptBlock | None:
|
|
"""Return the block with ``block_id`` if present."""
|
|
return next((block for block in self.blocks if block.id == block_id), None)
|
|
|
|
def require_block(self, block_id: str) -> PromptBlock:
|
|
"""Return a block by id, raising a clear error when it is absent."""
|
|
block = self.block(block_id)
|
|
if block is None:
|
|
raise KeyError(f"PromptEnvelope block not found: {block_id}")
|
|
return block
|
|
|
|
def render(self) -> str:
|
|
"""Render all non-empty blocks in order."""
|
|
rendered = [block.render() for block in self.blocks]
|
|
return self.separator.join(text for text in rendered if text)
|
|
|
|
|
|
__all__ = ["PromptBlock", "PromptEnvelope"]
|