mechaharness.harness.base

Harness architecture hierarchy.

AbstractHarness is an agent: one run() is a finite lifecycle of inference calls. Families specialize prompting, tool-call interpretation, and termination. The shared loop emits EventLog records, prices cost, and checks tool grants. Inference is injected via Strategy — harnesses never talk to providers directly.

class mechaharness.harness.base.AbstractHarness(inference: Completer, tools: ToolRegistry | None = None, *, config: HarnessConfig, event_log: TypeAliasForwardRef('mechaharness.core.events.EventLog') | None = None, access: TypeAliasForwardRef('mechaharness.core.access.AccessControl') | AccessPolicy | None = None, cost: TypeAliasForwardRef('mechaharness.core.access.CostAccountant') | None = None, environment: InferenceEnvironment | None = None, agent_id: str | None = None, parent_agent_id: str | None = None, subagent_tools: bool = False)[source]

Bases: Completer

Base harness: inject a completer + tools, subclasses define the loop policy.

__init__(inference: Completer, tools: ToolRegistry | None = None, *, config: HarnessConfig, event_log: TypeAliasForwardRef('mechaharness.core.events.EventLog') | None = None, access: TypeAliasForwardRef('mechaharness.core.access.AccessControl') | AccessPolicy | None = None, cost: TypeAliasForwardRef('mechaharness.core.access.CostAccountant') | None = None, environment: InferenceEnvironment | None = None, agent_id: str | None = None, parent_agent_id: str | None = None, subagent_tools: bool = False) → None[source]
access_policy() → AccessPolicy[source]

Tool grants. Models default to none; harnesses override.

build_request(messages: list[ChatMessage]) → CompletionRequest[source]

Hook: families can reshape messages / tool schemas per model quirks.

capability_profile() → CapabilityProfile[source]

Skills (and therefore inference cost) of this completer.

async complete(request: CompletionRequest) → CompletionResponse[source]

Treat this harness as a completer (same shape as a raw model).

async execute_tools(tool_calls: list[ToolCall], *, run_id: str, cost: CostReport) → list[ToolResult][source]
family: str = 'abstract'
final_text(message: ChatMessage) → str | None[source]

Hook: families may extract a shorter answer from the last message.

interpret_tool_calls(message: ChatMessage, *, turn: int) → list[ToolCall][source]

Hook: families may parse tool calls from free-form text.

async run(user_input: str, *, history: list[ChatMessage] | None = None) → HarnessResult[source]

Run one agent lifecycle for user_input and return the result.

Emits EventLog records, prices inference/tools via CostAccountant, and enforces tool grants through AccessControl.

should_continue_without_tools(message: ChatMessage) → bool[source]
abstractmethod should_stop(message: ChatMessage, finish_reason: str | None) → bool[source]

Return True when the harness should end the loop.

async stream_events(user_input: str, *, history: list[ChatMessage] | None = None) → AsyncIterator[TypeAliasForwardRef('mechaharness.core.events.Event')][source]
tool_result_message(result: ToolResult) → ChatMessage[source]

Hook: families may encode tool results differently.

class mechaharness.harness.base.HarnessConfig(*, model: str, system_prompt: str | None = None, max_turns: int = 8, temperature: float | None = None, max_tokens: int | None = None, extra: dict[str, ~typing.Any]=<factory>)[source]

Bases: BaseModel

Per-run knobs for a harness (model, turn budget, sampling).

extra: dict[str, Any]
max_tokens: int | None
max_turns: int
model: str
model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

system_prompt: str | None
temperature: float | None
class mechaharness.harness.base.HarnessResult(*, final_text: str | None, messages: list[~mechaharness.core.types.ChatMessage], turns: int, events: list[~mechaharness.core.events.Event] = <factory>, cost: ~mechaharness.core.access.CostReport = <factory>)[source]

Bases: BaseModel

Outcome of AbstractHarness.run: final text, transcript, events, cost.

cost: CostReport
events: list[Event]
final_text: str | None
messages: list[ChatMessage]
model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

turns: int