blob: f7b7ba658302710f84d2cc75a5e18d8f29d96492 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
//! Logical title failure evidence does not create a physical model attempt.
use crate::{EventLog, StoreError};
use maka_agent_facts::{
diagnostics::TitleFailureCause,
event::{Fact, Invocation},
execution::ModelBinding,
};
use serde::Serialize;
use sqlx::{Connection, SqliteConnection};
use std::time::SystemTime;
#[derive(Serialize)]
struct Failure {
// A non-runtime event must not populate the generated runtime event_session.
opening: Invocation,
recorded_at: SystemTime,
cause: TitleFailureCause,
}
impl EventLog {
pub async fn record_title_failure(
&self,
invocation: Invocation,
cause: TitleFailureCause,
) -> Result<u64, StoreError> {
self.validate_root()?;
cause
.validate()
.map_err(|error| invalid(&error.to_string()))?;
let commits = self.commits.clone();
self.connection.run(move |connection| Box::pin(async move {
let mut tx = connection.begin_with("BEGIN IMMEDIATE").await?;
original_title_binding(&mut tx, &invocation).await?;
let failure = Failure { opening: invocation, recorded_at: SystemTime::now(), cause };
let result = sqlx::query("INSERT INTO event_log(event_id,kind,event_json) VALUES (?,'title_failure',?)")
.bind(uuid::Uuid::new_v4().to_string()).bind(serde_json::to_string(&failure)?)
.execute(&mut *tx).await?;
let sequence = crate::sequence_number(result.last_insert_rowid())?;
tx.commit().await.map_err(StoreError::CommitUnknown)?;
commits.send_replace(sequence);
Ok(sequence)
})).await
}
}
/// Original provenance only; current admission and attempt policy belong to the caller.
pub(crate) async fn original_title_binding(
connection: &mut SqliteConnection,
invocation: &Invocation,
) -> Result<ModelBinding, StoreError> {
let opening = crate::message_sources::first_message_opening(connection, &invocation.session_id)
.await?
.ok_or_else(|| invalid("Session title has no committed user opening"))?;
if opening.event.invocation != *invocation {
return Err(invalid("Session title must use its first user opening"));
}
match opening.event.fact {
Fact::InvocationOpened {
configuration: Some(config),
..
} => config
.model
.ok_or_else(|| invalid("Session title has no frozen model binding")),
_ => Err(invalid("Session title has no frozen configuration")),
}
}
fn invalid(message: &str) -> StoreError {
StoreError::InvalidTransition(message.into())
}