pub struct ModelCall { /* private fields */ }Expand description
One completion.
Implementations§
Source§impl ModelCall
impl ModelCall
Sourcepub const DEFAULT_MAX_OUTPUT_TOKENS: u32 = 16_384
pub const DEFAULT_MAX_OUTPUT_TOKENS: u32 = 16_384
Per-call output ceiling used when the caller does not set one.
Sized for reasoning models, whose thinking tokens count against the
same limit as the answer: at 4096 a model reasoning at medium effort
regularly spends the whole allowance thinking and returns a truncated
turn with no answer in it. A role that needs a tighter bound declares
max_tokens; the spend ceilings, not this default, bound cost.
Sourcepub fn new(
provider: Arc<dyn ModelProvider>,
model: ModelId,
prompt: Value,
) -> Self
pub fn new( provider: Arc<dyn ModelProvider>, model: ModelId, prompt: Value, ) -> Self
A completion from this provider.
Sourcepub fn with_tools(
self,
tools: impl IntoIterator<Item = ToolDeclaration>,
) -> Self
pub fn with_tools( self, tools: impl IntoIterator<Item = ToolDeclaration>, ) -> Self
Tell the model which tools it may ask for.
What comes back is a request, not an action: the chosen name is matched against the operator’s grants exactly — never resolved to a near neighbour — and the arguments stay untrusted until they pass the sink’s field-provenance rules. Declaring is telling; authorizing is separate.
Declare only what is granted. Offering the model a tool the manifest does not grant produces a call that is refused after the model has been paid for choosing it, and teaches nobody anything.
Sourcepub fn continuing(self, transcript: &Transcript) -> Self
pub fn continuing(self, transcript: &Transcript) -> Self
Continue a tool loop: show the model what its tools answered, with the provider state its last turn left.
The prompt stays what it was. A continuation is the same question with more known, so re-stating it would change the effect key and make each turn of a loop a different call for replay purposes.
The transcript’s label travels with it: the sink judges it beside the
prompt’s, its relayed outputs are read by the sink’s content rules, and
the answer is labelled with it. That is why a continuation can only be
handed over as a Transcript — a bare list of outputs would reach the
provider outside every gate.
Sourcepub fn with_compaction(self, compaction: Compaction) -> Self
pub fn with_compaction(self, compaction: Compaction) -> Self
Ask the provider to compact the conversation once its input reaches
compaction.trigger_tokens: the earlier turns are replaced by a
summary inside the provider’s continuation, which the next call sends
back, and Completion::compacted says it happened.
Anthropic and OpenAI Responses compact; every other wire refuses the
call rather than letting the conversation run into its window. The
instruction (/system) is never compacted. Part of the effect key:
the provider is asked something else.
Sourcepub fn streaming_to(self, observer: Arc<dyn ModelStreamObserver>) -> Self
pub fn streaming_to(self, observer: Arc<dyn ModelStreamObserver>) -> Self
Observe visible model text as it arrives during live execution.
Sourcepub fn stopped_by(self, stop: Stop) -> Self
pub fn stopped_by(self, stop: Stop) -> Self
End this call on stop, while it streams, as a recorded completion of
what had arrived (Completion::stopped).
Stopping ends the bill where the stream is rather than at the answer’s end. A buffered call, and a call that declares a schema, run to their end. The signal is not effect identity; replay returns the recorded completion.
Sourcepub const fn with_max_output_tokens(self, max_output_tokens: u32) -> Self
pub const fn with_max_output_tokens(self, max_output_tokens: u32) -> Self
Bound how many tokens this call may generate.
Sourcepub const fn with_max_input_tokens(self, max_input_tokens: u32) -> Self
pub const fn with_max_input_tokens(self, max_input_tokens: u32) -> Self
Bound how many tokens this call may send, cached ones included.
Input cannot be bounded before the call — it is whatever the conversation has grown to — so this is held against the usage the provider reports: a completion that sent more is a failed call, billed as reported. That keeps the per-call figure a tenant’s spend quota reserves a bound on every answer a run goes on to use. Not part of the effect key: it changes which answers are accepted, not what the provider is asked.
Sourcepub const fn with_reasoning_effort(self, effort: ReasoningEffort) -> Self
pub const fn with_reasoning_effort(self, effort: ReasoningEffort) -> Self
Request an explicit reasoning depth.
Sourcepub const fn with_reasoning_summary(self) -> Self
pub const fn with_reasoning_summary(self) -> Self
Ask the provider for a readable summary of the model’s reasoning.
It arrives as Completion::reasoning and, live, as
ModelStreamEvent::ReasoningDelta. On Anthropic it turns adaptive
thinking on for a model that would not otherwise think. Part of the
effect key: the provider is asked something else.
Sourcepub const fn with_pricing(self, pricing: Pricing) -> Self
pub const fn with_pricing(self, pricing: Pricing) -> Self
Price this call’s usage, so its spend carries money as well as tokens.
Applied at the effect boundary to every usage the call reports — the answer’s and a metered failure’s alike — so a money ceiling counts a stream that died after generating exactly as it counts one that finished. Not part of the effect key: a price changes what the run is billed, not what the provider is asked.
Sourcepub const fn with_max_sensitivity(self, s: Sensitivity) -> Self
pub const fn with_max_sensitivity(self, s: Sensitivity) -> Self
The highest sensitivity this model may be shown.
The control that matters for a hosted model: a prompt assembled from a secret is an exfiltration whether or not anyone meant it.
It is also the floor of the completion’s own label — see
with_output_sensitivity for why the
two are one decision.
Sourcepub const fn with_output_sensitivity(self, s: Sensitivity) -> Self
pub const fn with_output_sensitivity(self, s: Sensitivity) -> Self
Declare the completion’s sensitivity floor, above the derived one.
The derived floor is max_sensitivity,
always: a caller who raised the egress ceiling to show the model a
confidential prompt has told the runtime what class of data the answer
was derived from, and a completion labelled below its own prompt is a
laundering primitive — ask the model to restate the secret and read it
back a level down. So this setter can only raise the floor; a value
below the ceiling is kept and simply loses to it at
Effect::output_sensitivity, where the two are joined in one place
rather than compensated for by hand at every call site.
pub const fn with_retry(self, r: RetryPolicy) -> Self
Sourcepub fn with_media<'a>(
self,
media: Arc<dyn BlobStore>,
artifacts: impl IntoIterator<Item = &'a FetchedMedia>,
) -> Self
pub fn with_media<'a>( self, media: Arc<dyn BlobStore>, artifacts: impl IntoIterator<Item = &'a FetchedMedia>, ) -> Self
Permit these exact FetchedMedia artifacts
to materialize from this blob store immediately before live dispatch.
The prompt and effect key contain only media digests. Strict replay does
not execute perform, so it reads neither blob storage nor the network.
A prompt marker without a matching digest/type grant is refused;
knowing another case’s digest is not authority to read that blob.
Model output remains journaled for replay and may itself reproduce media
content; digest-only input storage is not an output-redaction promise.
Sourcepub fn expecting(self, schema: Value) -> Self
pub fn expecting(self, schema: Value) -> Self
Require the answer to conform to a JSON Schema.
The schema goes into the effect key, which is the point: editing a schema changes the effect, so a replayed run reports divergence instead of quietly reading back an answer shaped to different rules. A schema that lived outside the key would let today’s shape re-interpret last year’s stored answer.
Enforcement happens at the provider, during generation, where a
constraint can prevent a malformed answer rather than reject one already
paid for. What this crate adds on top is the parse — see
Completion::structured.
Trait Implementations§
Source§impl Effect for ModelCall
impl Effect for ModelCall
Source§fn mutates(&self) -> bool
fn mutates(&self) -> bool
A completion does not change the world.
Said plainly because it is the one outward call in this crate where that
is true, and it is what makes retrying a rate-limit sane. It is not free
— see spend — but a second completion does not move money twice.
Source§fn protected_fields(&self) -> &[ProtectedField]
fn protected_fields(&self) -> &[ProtectedField]
/system when the prompt has an instruction.
A model reads its instruction and its data as the same undifferentiated
text, so text arriving as data that reads like a directive is obeyed
like one. Every other control here bounds what the model may then do;
this is the only one that asks who was allowed to give the order.
Untrusted material belongs in messages.
Source§fn output_sensitivity(&self) -> Sensitivity
fn output_sensitivity(&self) -> Sensitivity
Never below max_sensitivity.
A sink’s result inherits the class of what was sent into it: a caller
who raised the egress ceiling to get a confidential prompt out has
declared what the answer derives from, and returning that answer at a
lower label would make one round trip through a model a release nobody
authorized. with_output_sensitivity still raises the floor further;
nothing lowers it below the ceiling.
Source§fn carried(&self) -> Option<Tainted<Value>>
fn carried(&self) -> Option<Tainted<Value>>
The relayed tool outputs, under the transcript’s label.
The provider continuation is not offered to content rules: it is the model’s own earlier output, often encrypted, and its label is already in the transcript’s.
Source§fn outbound_bytes(&self) -> u64
fn outbound_bytes(&self) -> u64
Everything the provider is sent, not only the prompt the sink gate binds.
The prompt of a tool-loop turn can be two bytes while the request carries every declared tool, every earlier tool result, the provider’s continuation state and each granted image — and all of it leaves the plane. So the figure is the canonical size of every provider-visible input (the same set the effect key holds) plus each referenced media artifact at its encoded size, since the marker in the prompt is a few dozen bytes standing in for megabytes.
Source§fn trust(&self) -> Trust
fn trust(&self) -> Trust
Model output is untrusted, and this is the case the rule was written for.
A completion is a plausible-sounding string produced from whatever was in the context window — including anything untrusted that got there. It is the canonical prompt-injection carrier.
Source§fn source(&self) -> SourceId
fn source(&self) -> SourceId
Which model answered — model:{provider}/{model} — the same spelling
the live stream label has always carried, now derived in one place so
the terminal completion and the stream cannot name one answer two ways.
Source§type Output = Completion
type Output = Completion
Source§fn gen_ai_operation(&self) -> Option<&'static str>
fn gen_ai_operation(&self) -> Option<&'static str>
Source§fn descriptor(&self) -> EffectDescriptor
fn descriptor(&self) -> EffectDescriptor
Source§fn recovery(&self) -> Recovery
fn recovery(&self) -> Recovery
InDoubt failure. The two are the
same situation reached from different directions.Source§fn retry(&self) -> RetryPolicy
fn retry(&self) -> RetryPolicy
Source§fn max_sensitivity(&self) -> Sensitivity
fn max_sensitivity(&self) -> Sensitivity
Source§fn sink_arguments(&self) -> Option<&Value>
fn sink_arguments(&self) -> Option<&Value>
Source§fn rebind(&mut self, arguments: Value) -> bool
fn rebind(&mut self, arguments: Value) -> bool
arguments in place of the ones this effect was built over,
where it can — what a declared redaction at a sink needs. Read moreSource§fn rebind_carried(&mut self, carried: Value) -> bool
fn rebind_carried(&mut self, carried: Value) -> bool
Source§fn gen_ai_request(&self) -> Option<GenAiRequest>
fn gen_ai_request(&self) -> Option<GenAiRequest>
GenAI
span attributes. Read moreSource§fn gen_ai_response(&self, output: &Completion) -> Option<GenAiResponse>
fn gen_ai_response(&self, output: &Completion) -> Option<GenAiResponse>
Source§fn perform<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Completion, EffectError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn perform<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Completion, EffectError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§fn attach(&mut self, _provenance: &Provenance)
fn attach(&mut self, _provenance: &Provenance)
Source§fn retries_landed(&self) -> bool
fn retries_landed(&self) -> bool
Source§fn credential_binding(&self) -> Option<CredentialBinding>
fn credential_binding(&self) -> Option<CredentialBinding>
Auto Trait Implementations§
impl !RefUnwindSafe for ModelCall
impl !UnwindSafe for ModelCall
impl Freeze for ModelCall
impl Send for ModelCall
impl Sync for ModelCall
impl Unpin for ModelCall
impl UnsafeUnpin for ModelCall
Blanket Implementations§
Source§impl<E> AnyEffect for Ewhere
E: Effect,
impl<E> AnyEffect for Ewhere
E: Effect,
Source§fn spend_erased(&self, output: &Value) -> Spend
fn spend_erased(&self, output: &Value) -> Spend
Round-trips the output to ask the typed effect what it cost.
A type that cannot be deserialized from its own serialization is already
broken for replay — Effect::Output requires the round-trip, and the
journal reconstructs every output that way. Charging zero here is the
same answer replay would reach, and the defect surfaces where it belongs.
Source§fn gen_ai_response_erased(&self, output: &Value) -> Option<GenAiResponse>
fn gen_ai_response_erased(&self, output: &Value) -> Option<GenAiResponse>
Round-trips the output, for the reason
spend_erased does.
fn descriptor(&self) -> EffectDescriptor
fn attach_erased(&mut self, provenance: &Provenance)
fn gen_ai_operation(&self) -> Option<&'static str>
fn mutates(&self) -> bool
fn recovery(&self) -> Recovery
fn retry(&self) -> RetryPolicy
fn retries_landed(&self) -> bool
fn max_sensitivity(&self) -> Sensitivity
fn sink_arguments(&self) -> Option<&Value>
fn rebind_erased(&mut self, arguments: Value) -> bool
fn carried(&self) -> Option<Tainted<Value>>
fn rebind_carried_erased(&mut self, carried: Value) -> bool
fn outbound_bytes(&self) -> u64
fn credential_binding(&self) -> Option<CredentialBinding>
fn protected_fields(&self) -> &[ProtectedField]
fn delegation_depth(&self) -> Option<usize>
fn source(&self) -> SourceId
fn trust(&self) -> Trust
fn output_sensitivity(&self) -> Sensitivity
fn gen_ai_request(&self) -> Option<GenAiRequest>
fn perform_erased<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Value, EffectError>> + Send + 'async_trait>>where
'life0: 'async_trait,
E: 'async_trait,
fn reconcile_erased<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Reconciliation<Value>, EffectError>> + Send + 'async_trait>>where
'life0: 'async_trait,
E: 'async_trait,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<E> BuildsEffect<E> for Ewhere
E: Effect,
impl<E> BuildsEffect<E> for Ewhere
E: Effect,
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more