praval.models

Provider-neutral model contracts for Praval.

These types describe model input, output, tools, events, and capabilities without binding the rest of Praval to one provider’s wire format.

Classes

AudioResponse(**data)

Provider-neutral transcription or synthesized-audio response.

ContentPart(**data)

A single multimodal content part.

EmbeddingRequest(**data)

Provider-neutral embedding request.

EmbeddingResponse(**data)

Provider-neutral embedding response.

ModelEvent(**data)

A streaming model event.

ModelMessage(**data)

A provider-neutral conversation message.

ModelRequest(**data)

Provider-neutral model request.

ModelResponse(**data)

Provider-neutral model response.

ProviderAdapter(*args, **kwargs)

Protocol implemented by provider-neutral adapters.

ProviderCapabilities(**data)

Capabilities exposed by a provider or a provider/model pair.

ProviderProfile(**data)

A registered provider/model profile.

ReasoningConfig(**data)

Reasoning controls for providers that support them.

SpeechRequest(**data)

Provider-neutral request to synthesize speech from text.

StructuredOutputConfig(**data)

Structured output request configuration.

ToolCall(**data)

A model-requested tool invocation.

ToolResult(**data)

A result returned from a tool invocation.

ToolSpec(**data)

Provider-neutral tool declaration.

TranscriptionRequest(**data)

Provider-neutral request to transcribe an audio file or byte payload.

Usage(**data)

Provider-neutral token usage.

class praval.models.AudioResponse(**data)[source]

Bases: BaseModel

Provider-neutral transcription or synthesized-audio response.

Parameters:
  • text (str | None)

  • data (bytes | None)

  • provider (str | None)

  • model (str | None)

  • format (str | None)

  • mime_type (str | None)

  • raw (Any)

  • metadata (Dict[str, Any])

model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}

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

text: str | None
data: bytes | None
provider: str | None
model: str | None
format: str | None
mime_type: str | None
raw: Any
metadata: Dict[str, Any]
class praval.models.ContentKind(*values)[source]

Bases: str, Enum

Sensitive content category represented by a safe reference.

PROMPT = 'prompt'
RESPONSE = 'response'
CONTEXT = 'context'
TOOL_ARGUMENTS = 'tool_arguments'
TOOL_RESULT = 'tool_result'
RETRIEVED_DOCUMENT = 'retrieved_document'
MEDIA = 'media'
JUDGE_EVIDENCE = 'judge_evidence'
OTHER = 'other'
class praval.models.ContentPart(**data)[source]

Bases: BaseModel

A single multimodal content part.

Parameters:
  • type (str)

  • text (str | None)

  • data (str | None)

  • url (str | None)

  • mime_type (str | None)

  • metadata (Dict[str, Any])

  • extra_data (Any)

model_config: ClassVar[ConfigDict] = {'extra': 'allow'}

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

type: str
text: str | None
data: str | None
url: str | None
mime_type: str | None
metadata: Dict[str, Any]
classmethod text_part(text)[source]

Create a text content part.

Return type:

ContentPart

Parameters:

text (str)

classmethod image_url(url, mime_type=None)[source]

Create an image URL content part.

Return type:

ContentPart

Parameters:
  • url (str)

  • mime_type (str | None)

classmethod image_base64(data, mime_type='image/png')[source]

Create an inline base64 image content part.

Return type:

ContentPart

Parameters:
  • data (str)

  • mime_type (str)

classmethod audio_url(url, mime_type=None)[source]

Create an audio URL content part.

Return type:

ContentPart

Parameters:
  • url (str)

  • mime_type (str | None)

classmethod audio_base64(data, mime_type='audio/wav')[source]

Create an inline base64 audio content part.

Return type:

ContentPart

Parameters:
  • data (str)

  • mime_type (str)

classmethod video_url(url, mime_type=None)[source]

Create a video URL content part.

Return type:

ContentPart

Parameters:
  • url (str)

  • mime_type (str | None)

classmethod video_base64(data, mime_type='video/mp4')[source]

Create an inline base64 video content part.

Return type:

ContentPart

Parameters:
  • data (str)

  • mime_type (str)

classmethod file_url(url, mime_type='application/octet-stream', *, name=None)[source]

Create a remote file content part.

Return type:

ContentPart

Parameters:
  • url (str)

  • mime_type (str)

  • name (str | None)

classmethod file_data(data, mime_type, *, name=None)[source]

Create an inline file content part.

Return type:

ContentPart

Parameters:
  • data (str)

  • mime_type (str)

  • name (str | None)

class praval.models.ContentReference(**data)[source]

Bases: _ObservationModel

Safe identity for content that is not embedded in an observation.

Parameters:
  • kind (ContentKind)

  • sha256 (str)

  • size_bytes (int)

  • reference (str | None)

  • media_type (str | None)

kind: ContentKind
sha256: str
size_bytes: int
reference: str | None
media_type: str | None
model_config: ClassVar[ConfigDict] = {'extra': 'forbid', 'frozen': True}

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

class praval.models.EmbeddingRequest(**data)[source]

Bases: BaseModel

Provider-neutral embedding request.

Parameters:
  • inputs (List[Any])

  • provider (str | None)

  • model (str | None)

  • dimensions (int | None)

  • provider_options (Dict[str, Any])

  • metadata (Dict[str, Any])

model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}

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

inputs: List[Any]
provider: str | None
model: str | None
dimensions: int | None
provider_options: Dict[str, Any]
metadata: Dict[str, Any]
class praval.models.EmbeddingResponse(**data)[source]

Bases: BaseModel

Provider-neutral embedding response.

Parameters:
  • embeddings (List[List[float]])

  • provider (str | None)

  • model (str | None)

  • dimensions (int | None)

  • raw (Any)

  • metadata (Dict[str, Any])

model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}

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

embeddings: List[List[float]]
provider: str | None
model: str | None
dimensions: int | None
raw: Any
metadata: Dict[str, Any]
class praval.models.ExecutionObservation(**data)[source]

Bases: _ObservationModel

Versioned facts for one completed agent invocation or workflow.

The schema contains identities and bounded metadata only. Raw prompts, responses, tool payloads, retrieved documents, media, and judge evidence are represented through ContentReference values.

Parameters:
schema_version: Literal[1]
observation_id: str
run_id: str
kind: ObservationKind
conversation_id: str | None
response_id: str | None
agent_id: str | None
agent_name: str | None
workflow_id: str | None
workflow_name: str | None
started_at: datetime
ended_at: datetime
duration_ms: float
status: ObservationStatus
error_type: str | None
terminal_outcome: str | None
provider: str | None
model: str | None
request_mode: str | None
usage: TokenUsageObservation | None
tool_calls: tuple[ToolCallObservation, ...]
retries: tuple[RetryObservation, ...]
hitl_decisions: tuple[HITLDecisionObservation, ...]
handoffs: tuple[ReefHandoffObservation, ...]
content_references: tuple[ContentReference, ...]
privacy: ObservationPrivacy
trace_id: str | None
span_id: str | None
classmethod require_utc(value)[source]

Require timezone-aware timestamps and normalize them to UTC.

Return type:

datetime

Parameters:

value (datetime)

validate_contract()[source]

Validate cross-field identity, timing, error, and trace invariants.

Return type:

ExecutionObservation

model_config: ClassVar[ConfigDict] = {'extra': 'forbid', 'frozen': True}

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

class praval.models.HITLDecisionObservation(**data)[source]

Bases: _ObservationModel

Metadata-only human-in-the-loop decision.

Parameters:
  • decision_id (str)

  • decision (Literal['requested', 'approved', 'rejected', 'edited', 'timeout', 'cancelled'])

  • tool_name (str | None)

  • reviewer_type (str | None)

decision_id: str
decision: Literal['requested', 'approved', 'rejected', 'edited', 'timeout', 'cancelled']
tool_name: str | None
reviewer_type: str | None
model_config: ClassVar[ConfigDict] = {'extra': 'forbid', 'frozen': True}

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

class praval.models.ModelEvent(**data)[source]

Bases: BaseModel

A streaming model event.

Parameters:
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}

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

type: str
delta: str
response: ModelResponse | None
tool_call: ToolCall | None
tool_result: ToolResult | None
usage: Usage | None
metadata: Dict[str, Any]
class praval.models.ModelMessage(**data)[source]

Bases: BaseModel

A provider-neutral conversation message.

Parameters:
  • role (str)

  • content (Any)

  • name (str | None)

  • tool_call_id (str | None)

  • metadata (Dict[str, Any])

  • extra_data (Any)

model_config: ClassVar[ConfigDict] = {'extra': 'allow'}

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

role: str
content: Any
name: str | None
tool_call_id: str | None
metadata: Dict[str, Any]
class praval.models.ModelRequest(**data)[source]

Bases: BaseModel

Provider-neutral model request.

Parameters:
  • messages (List[ModelMessage])

  • provider (str | None)

  • model (str | None)

  • tools (List[ToolSpec])

  • temperature (float | None)

  • max_output_tokens (int | None)

  • max_tool_rounds (int | None)

  • stream (bool)

  • response_schema (StructuredOutputConfig | None)

  • reasoning (ReasoningConfig | None)

  • provider_options (Dict[str, Any])

  • stream_options (Dict[str, Any])

  • timeout (float | None)

  • metadata (Dict[str, Any])

  • hitl_context (Dict[str, Any] | None)

model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}

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

messages: List[ModelMessage]
provider: str | None
model: str | None
tools: List[ToolSpec]
temperature: float | None
max_output_tokens: int | None
max_tool_rounds: int | None
stream: bool
response_schema: StructuredOutputConfig | None
reasoning: ReasoningConfig | None
provider_options: Dict[str, Any]
stream_options: Dict[str, Any]
timeout: float | None
metadata: Dict[str, Any]
hitl_context: Dict[str, Any] | None
class praval.models.ModelResponse(**data)[source]

Bases: BaseModel

Provider-neutral model response.

Parameters:
  • content (str)

  • provider (str | None)

  • model (str | None)

  • messages (List[ModelMessage])

  • tool_calls (List[ToolCall])

  • usage (Usage | None)

  • finish_reason (str | None)

  • raw (Any)

  • metadata (Dict[str, Any])

model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}

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

content: str
provider: str | None
model: str | None
messages: List[ModelMessage]
tool_calls: List[ToolCall]
usage: Usage | None
finish_reason: str | None
raw: Any
metadata: Dict[str, Any]
property text: str

Return response content as text.

class praval.models.NoOpObservationRecorder[source]

Bases: object

Default recorder used when no observation consumer is configured.

record(observation)[source]

Discard an observation without side effects.

Return type:

None

Parameters:

observation (ExecutionObservation)

class praval.models.ObservationFactStatus(*values)[source]

Bases: str, Enum

Status shared by bounded child facts such as tools and handoffs.

OK = 'ok'
ERROR = 'error'
CANCELLED = 'cancelled'
TIMEOUT = 'timeout'
REJECTED = 'rejected'
class praval.models.ObservationKind(*values)[source]

Bases: str, Enum

Execution boundary represented by an observation.

AGENT = 'agent'
WORKFLOW = 'workflow'
class praval.models.ObservationPrivacy(**data)[source]

Bases: _ObservationModel

Privacy policy applied to all content facts in an observation.

Parameters:
  • mode (PrivacyMode)

  • content_captured (bool)

  • redaction_applied (bool)

  • byte_limit (int)

mode: PrivacyMode
content_captured: bool
redaction_applied: bool
byte_limit: int
validate_capture_policy()[source]

Keep metadata-only observations free from captured content.

Return type:

ObservationPrivacy

model_config: ClassVar[ConfigDict] = {'extra': 'forbid', 'frozen': True}

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

class praval.models.ObservationRecorder(*args, **kwargs)[source]

Bases: Protocol

Structural interface for consumers of completed observations.

record(observation)[source]

Record one completed execution observation.

Return type:

None

Parameters:

observation (ExecutionObservation)

__init__(*args, **kwargs)
class praval.models.ObservationStatus(*values)[source]

Bases: str, Enum

Terminal status of an observed execution.

OK = 'ok'
ERROR = 'error'
CANCELLED = 'cancelled'
TIMEOUT = 'timeout'
class praval.models.PrivacyMode(*values)[source]

Bases: str, Enum

Content handling policy applied before an observation is recorded.

METADATA_ONLY = 'metadata_only'
REDACTED = 'redacted'
FULL = 'full'
class praval.models.ProviderAdapter(*args, **kwargs)[source]

Bases: Protocol

Protocol implemented by provider-neutral adapters.

provider_name: str
capabilities: ProviderCapabilities
invoke(request)[source]

Execute a non-streaming model request.

Return type:

ModelResponse

Parameters:

request (ModelRequest)

stream(request)[source]

Execute a streaming model request.

Return type:

Iterator[ModelEvent]

Parameters:

request (ModelRequest)

async ainvoke(request)[source]

Execute a non-streaming request asynchronously.

Return type:

ModelResponse

Parameters:

request (ModelRequest)

astream(request)[source]

Execute a streaming model request asynchronously.

Return type:

AsyncIterator[ModelEvent]

Parameters:

request (ModelRequest)

close()[source]

Release provider resources.

Return type:

None

__init__(*args, **kwargs)
class praval.models.ProviderCapabilities(**data)[source]

Bases: BaseModel

Capabilities exposed by a provider or a provider/model pair.

Parameters:
  • text (bool)

  • chat_completions (bool)

  • responses_api (bool)

  • tools (bool)

  • streaming (bool)

  • native_streaming (bool)

  • tool_streaming (bool)

  • structured_outputs (bool)

  • json_schema_mode (str | None)

  • multimodal (bool)

  • image_input (bool)

  • file_input (bool)

  • audio_input (bool)

  • video_input (bool)

  • audio_transcription (bool)

  • speech_generation (bool)

  • reasoning (bool)

  • reasoning_effort (bool)

  • reasoning_budget (bool)

  • embeddings (bool)

  • local (bool)

  • server_tools (bool)

  • mcp (bool)

  • computer_use (bool)

text: bool
chat_completions: bool
responses_api: bool
tools: bool
streaming: bool
native_streaming: bool
tool_streaming: bool
structured_outputs: bool
json_schema_mode: str | None
multimodal: bool
image_input: bool
file_input: bool
audio_input: bool
video_input: bool
audio_transcription: bool
speech_generation: bool
reasoning: bool
reasoning_effort: bool
reasoning_budget: bool
embeddings: bool
local: bool
server_tools: bool
mcp: bool
computer_use: bool
supports(capability)[source]

Return whether a named capability is enabled.

Return type:

bool

Parameters:

capability (str)

model_config: ClassVar[ConfigDict] = {}

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

class praval.models.ProviderProfile(**data)[source]

Bases: BaseModel

A registered provider/model profile.

Parameters:
  • provider (str)

  • model (str)

  • display_name (str | None)

  • capabilities (ProviderCapabilities)

  • default (bool)

  • endpoint (str | None)

  • local_preset (str | None)

  • context_window (int | None)

  • max_output_tokens (int | None)

  • default_parameters (Dict[str, Any])

  • unsupported_combinations (List[Dict[str, Any]])

  • downgrade_policy (str)

  • notes (str)

provider: str
model: str
display_name: str | None
capabilities: ProviderCapabilities
default: bool
endpoint: str | None
local_preset: str | None
context_window: int | None
max_output_tokens: int | None
default_parameters: Dict[str, Any]
unsupported_combinations: List[Dict[str, Any]]
downgrade_policy: str
notes: str
model_config: ClassVar[ConfigDict] = {}

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

class praval.models.ReasoningConfig(**data)[source]

Bases: BaseModel

Reasoning controls for providers that support them.

Parameters:
  • effort (str | None)

  • summary (str | None)

  • encrypted (bool)

  • budget_tokens (int | None)

  • mode (str | None)

  • display (str | None)

effort: str | None
summary: str | None
encrypted: bool
budget_tokens: int | None
mode: str | None
display: str | None
model_config: ClassVar[ConfigDict] = {}

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

class praval.models.ReefHandoffObservation(**data)[source]

Bases: _ObservationModel

Metadata-only facts about one Reef handoff.

Parameters:
  • handoff_id (str)

  • source_agent_id (str | None)

  • target_agent_id (str)

  • spore_id (str | None)

  • channel (str | None)

  • status (ObservationFactStatus)

  • duration_ms (float | None)

  • error_type (str | None)

handoff_id: str
source_agent_id: str | None
target_agent_id: str
spore_id: str | None
channel: str | None
status: ObservationFactStatus
duration_ms: float | None
error_type: str | None
model_config: ClassVar[ConfigDict] = {'extra': 'forbid', 'frozen': True}

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

class praval.models.RetryObservation(**data)[source]

Bases: _ObservationModel

One bounded retry decision made during execution.

Parameters:
  • attempt (int)

  • operation (str)

  • reason_type (str | None)

  • backoff_ms (float)

attempt: int
operation: str
reason_type: str | None
backoff_ms: float
model_config: ClassVar[ConfigDict] = {'extra': 'forbid', 'frozen': True}

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

class praval.models.SpeechRequest(**data)[source]

Bases: BaseModel

Provider-neutral request to synthesize speech from text.

Parameters:
  • input (str)

  • provider (str | None)

  • model (str | None)

  • voice (str)

  • response_format (str)

  • speed (float)

  • instructions (str | None)

  • provider_options (Dict[str, Any])

  • timeout (float | None)

  • metadata (Dict[str, Any])

model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}

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

input: str
provider: str | None
model: str | None
voice: str
response_format: str
speed: float
instructions: str | None
provider_options: Dict[str, Any]
timeout: float | None
metadata: Dict[str, Any]
class praval.models.StructuredOutputConfig(**data)[source]

Bases: BaseModel

Structured output request configuration.

Parameters:
  • schema (Dict[str, Any] | None)

  • name (str | None)

  • strict (bool)

model_config: ClassVar[ConfigDict] = {'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}

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

json_schema: Dict[str, Any] | None
name: str | None
strict: bool
class praval.models.ToolCall(**data)[source]

Bases: BaseModel

A model-requested tool invocation.

Parameters:
  • id (str)

  • name (str)

  • arguments (Dict[str, Any])

  • raw (Any)

  • extra_data (Any)

model_config: ClassVar[ConfigDict] = {'extra': 'allow'}

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

id: str
name: str
arguments: Dict[str, Any]
raw: Any
class praval.models.ToolResult(**data)[source]

Bases: BaseModel

A result returned from a tool invocation.

Parameters:
  • tool_call_id (str)

  • name (str)

  • content (str)

  • is_error (bool)

  • metadata (Dict[str, Any])

  • extra_data (Any)

model_config: ClassVar[ConfigDict] = {'extra': 'allow'}

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

tool_call_id: str
name: str
content: str
is_error: bool
metadata: Dict[str, Any]
class praval.models.ToolSpec(**data)[source]

Bases: BaseModel

Provider-neutral tool declaration.

Parameters:
  • name (str)

  • description (str)

  • parameters (Dict[str, Any])

  • strict (bool)

  • requires_approval (bool)

  • risk_level (str)

  • approval_reason (str)

  • metadata (Dict[str, Any])

  • extra_data (Any)

model_config: ClassVar[ConfigDict] = {'extra': 'allow'}

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

name: str
description: str
parameters: Dict[str, Any]
strict: bool
requires_approval: bool
risk_level: str
approval_reason: str
metadata: Dict[str, Any]
class praval.models.TranscriptionRequest(**data)[source]

Bases: BaseModel

Provider-neutral request to transcribe an audio file or byte payload.

Parameters:
  • audio (Any)

  • provider (str | None)

  • model (str | None)

  • filename (str | None)

  • mime_type (str | None)

  • language (str | None)

  • prompt (str | None)

  • response_format (str)

  • temperature (float | None)

  • provider_options (Dict[str, Any])

  • timeout (float | None)

  • metadata (Dict[str, Any])

model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}

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

audio: Any
provider: str | None
model: str | None
filename: str | None
mime_type: str | None
language: str | None
prompt: str | None
response_format: str
temperature: float | None
provider_options: Dict[str, Any]
timeout: float | None
metadata: Dict[str, Any]
class praval.models.TokenUsageObservation(**data)[source]

Bases: _ObservationModel

Bounded provider-neutral model token usage.

Parameters:
  • input_tokens (int)

  • output_tokens (int)

  • reasoning_tokens (int)

  • cache_read_tokens (int)

  • cache_write_tokens (int)

  • total_tokens (int)

input_tokens: int
output_tokens: int
reasoning_tokens: int
cache_read_tokens: int
cache_write_tokens: int
total_tokens: int
validate_total()[source]

Reject totals smaller than the directly billed token classes.

Return type:

TokenUsageObservation

model_config: ClassVar[ConfigDict] = {'extra': 'forbid', 'frozen': True}

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

class praval.models.Usage(**data)[source]

Bases: BaseModel

Provider-neutral token usage.

Parameters:
  • input_tokens (int)

  • output_tokens (int)

  • total_tokens (int)

  • reasoning_tokens (int)

input_tokens: int
output_tokens: int
total_tokens: int
reasoning_tokens: int
model_config: ClassVar[ConfigDict] = {}

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

class praval.models.ToolCallObservation(**data)[source]

Bases: _ObservationModel

Metadata-only facts about one tool invocation.

Parameters:
  • tool_call_id (str)

  • name (str)

  • status (ObservationFactStatus)

  • duration_ms (float)

  • error_type (str | None)

tool_call_id: str
name: str
status: ObservationFactStatus
duration_ms: float
error_type: str | None
model_config: ClassVar[ConfigDict] = {'extra': 'forbid', 'frozen': True}

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