theway-core 0.1.21

theway core — stateful agent runtime + harness (Agent loop, skills, prompt templates, sessions, compaction) on top of theway-llm-provider.
Documentation
use theway_contract::extension::{
    ExtensionActionKind, ExtensionHookClass, ExtensionLifecycleEvent,
};
use tokio_util::sync::CancellationToken;

use crate::agent::model_request::NormalizedModelRequestDraft;
use crate::agent::runtime_extensions::ValidatedRuntimeExtensionResult;

use super::{HarnessRuntimeExtensions, is_host_consumed_action, parse_follow_up};

impl HarnessRuntimeExtensions {
    pub(in crate::agent::assembly) async fn before_model_request(
        &self,
        mut request: NormalizedModelRequestDraft,
        model_max_tokens: u32,
        cancel: CancellationToken,
    ) -> NormalizedModelRequestDraft {
        self.model_context.apply_to_request(&mut request);
        if !self.port.has_request_hook(
            ExtensionLifecycleEvent::BeforeModelRequest,
            ExtensionHookClass::Transform,
        ) {
            return request;
        }

        let Ok(invocation) = self.invocation(
            ExtensionLifecycleEvent::BeforeModelRequest,
            ExtensionHookClass::Transform,
            serde_json::json!({"request": request}),
            cancel.is_cancelled(),
        ) else {
            return request;
        };
        let Ok(ValidatedRuntimeExtensionResult::Transform(result)) =
            self.guarded(self.port.dispatch_request(invocation)).await
        else {
            return request;
        };
        if result.actions().iter().any(|action| {
            !matches!(
                action.kind,
                ExtensionActionKind::ReplaceModelRequest | ExtensionActionKind::EnqueueFollowUp
            ) && !is_host_consumed_action(action.kind)
        }) {
            return request;
        }

        let replacement = result
            .actions()
            .iter()
            .find(|action| action.kind == ExtensionActionKind::ReplaceModelRequest)
            .map(|action| {
                action
                    .payload
                    .get("request")
                    .cloned()
                    .ok_or(())
                    .and_then(|value| serde_json::from_value(value).map_err(|_| ()))
            })
            .transpose();
        let Ok(replacement) = replacement else {
            return request;
        };
        let accepted = replacement.unwrap_or_else(|| request.clone());
        if accepted
            .validate_replacement(&request, model_max_tokens)
            .is_err()
        {
            return request;
        }

        let follow_ups = result
            .actions()
            .iter()
            .filter(|action| action.kind == ExtensionActionKind::EnqueueFollowUp)
            .map(|action| parse_follow_up(&action.payload))
            .collect::<Option<Vec<_>>>();
        let Some(follow_ups) = follow_ups else {
            return request;
        };
        if self.enqueue_follow_ups(follow_ups).is_err() {
            return request;
        }
        accepted
    }
}