refactor: rename llm worker crate to engine
This commit is contained in:
@@ -8,7 +8,7 @@ autobins = false
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
clap = { version = "4.6.0", features = ["derive"] }
|
||||
llm-worker = { workspace = true }
|
||||
llm-engine = { workspace = true }
|
||||
session-store = { workspace = true }
|
||||
pod-store = { workspace = true }
|
||||
manifest = { workspace = true }
|
||||
|
||||
@@ -2,21 +2,21 @@
|
||||
|
||||
## Role
|
||||
|
||||
`pod` turns an `llm-worker` Worker into a named runtime entity with manifest configuration, scoped tools, session persistence, protocol handling, and Pod metadata integration.
|
||||
`pod` turns an `llm-engine` Engine into a named runtime entity with manifest configuration, scoped tools, session persistence, protocol handling, and Pod metadata integration.
|
||||
|
||||
## Boundaries
|
||||
|
||||
Owns:
|
||||
|
||||
- Pod lifecycle and socket protocol serving
|
||||
- Worker construction around a resolved Manifest
|
||||
- Engine construction around a resolved Manifest
|
||||
- session-store and pod-store coordination
|
||||
- built-in tool registration under scope/policy
|
||||
- spawned-child orchestration hooks
|
||||
|
||||
Does not own:
|
||||
|
||||
- provider-specific wire formats (`provider` / `llm-worker` clients)
|
||||
- provider-specific wire formats (`provider` / `llm-engine` clients)
|
||||
- product CLI parsing (`yoi`)
|
||||
- TUI display authority (`tui`)
|
||||
- current-state storage schema outside Pod metadata (`pod-store`)
|
||||
|
||||
@@ -70,7 +70,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
|
||||
// 5. Extract the assistant's reply from history
|
||||
let history = pod.worker().history();
|
||||
let history = pod.engine().history();
|
||||
if let Some(text) = history
|
||||
.iter()
|
||||
.rev()
|
||||
|
||||
@@ -8,8 +8,8 @@ use std::collections::BTreeMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use llm_worker::Item;
|
||||
use llm_worker::tool::{
|
||||
use llm_engine::Item;
|
||||
use llm_engine::tool::{
|
||||
Tool, ToolDefinition, ToolError, ToolExecutionContext, ToolMeta, ToolOutput,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -308,7 +308,7 @@ fn has_active_workflow_hint(item: &Item) -> bool {
|
||||
|
||||
fn item_system_text(item: &Item) -> Option<String> {
|
||||
match item {
|
||||
Item::Message { role, content, .. } if *role == llm_worker::Role::System => Some(
|
||||
Item::Message { role, content, .. } if *role == llm_engine::Role::System => Some(
|
||||
content
|
||||
.iter()
|
||||
.map(|part| part.as_text())
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! Sync buffer for `session_metrics::Metric` values queued from inside
|
||||
//! Worker callbacks (which run synchronously and cannot themselves
|
||||
//! Engine callbacks (which run synchronously and cannot themselves
|
||||
//! perform `async` store writes).
|
||||
//!
|
||||
//! Pod drains this buffer in `persist_turn` and writes each metric via
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
//! Prune integration — wires the Worker's prune projection to the Pod's
|
||||
//! Prune integration — wires the Engine's prune projection to the Pod's
|
||||
//! usage-history-backed token accounting.
|
||||
//!
|
||||
//! Worker 自身がコンテキスト射影を行う(`worker.rs` の `request_context` 構築
|
||||
//! 直後)。Worker は usage 履歴を知らないので、`min_savings` 判定に使う savings
|
||||
//! Engine 自身がコンテキスト射影を行う(`worker.rs` の `request_context` 構築
|
||||
//! 直後)。Engine は usage 履歴を知らないので、`min_savings` 判定に使う savings
|
||||
//! の見積もりはコールバックで外部から注入する。このモジュールはそのコールバック
|
||||
//! を組み立てて Worker に差し込むための `impl Pod` を提供する。
|
||||
//! を組み立てて Engine に差し込むための `impl Pod` を提供する。
|
||||
//!
|
||||
//! 同じ経路で `PruneObserver` も install し、評価のたびに `prune.fire` /
|
||||
//! `prune.skip` metric を `MetricsTracker` に積む。`Fired` 時は uuid を
|
||||
//! `UsageTracker` にも stash しておき、後続の `LlmUsage` と組で
|
||||
//! `prune.post_request` を吐けるようにする。
|
||||
|
||||
use llm_worker::Item;
|
||||
use llm_worker::llm_client::client::LlmClient;
|
||||
use llm_worker::prune::{
|
||||
use llm_engine::Item;
|
||||
use llm_engine::llm_client::client::LlmClient;
|
||||
use llm_engine::prune::{
|
||||
PruneConfig, PruneDecision, PruneObserver, SavingsEstimator, TokenEstimator,
|
||||
};
|
||||
use session_metrics::Metric;
|
||||
@@ -25,9 +25,9 @@ use crate::compact::token_counter::{
|
||||
};
|
||||
|
||||
impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
/// Enable prune projection on the underlying Worker.
|
||||
/// Enable prune projection on the underlying Engine.
|
||||
///
|
||||
/// Registers the config and token/savings-estimator closures on the Worker.
|
||||
/// Registers the config and token/savings-estimator closures on the Engine.
|
||||
/// The estimators combine persisted [`Pod::usage_history_handle`] records
|
||||
/// with in-flight `UsageTracker` records so multi-request tool loops can
|
||||
/// prune before the surrounding Pod run finishes.
|
||||
@@ -100,7 +100,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
}
|
||||
});
|
||||
|
||||
let worker = self.worker_mut();
|
||||
let worker = self.engine_mut();
|
||||
worker.set_prune_config(Some(config));
|
||||
worker.set_token_estimator(Some(token_estimator));
|
||||
worker.set_savings_estimator(Some(estimator));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Compact / prune 専用のトークン会計補助。
|
||||
//!
|
||||
//! 汎用部分(`prefix_bytes`, `tokens_at`, `total_tokens`, `total_tokens_at`)は
|
||||
//! [`llm_worker::token_counter`] にあり、`UsageRecord` の列と現在の history から
|
||||
//! [`llm_engine::token_counter`] にあり、`UsageRecord` の列と現在の history から
|
||||
//! pure に推定する。本モジュールは compact / prune 固有のロジック
|
||||
//! (`split_for_retained`, `savings_for_prune`)と、Pod 上の公開 API に
|
||||
//! 限定する。
|
||||
@@ -17,12 +17,12 @@
|
||||
//! - 推定の出どころは [`EstimateSource`] で呼び出し側に明示する。
|
||||
//! 課金判断には使えないが、compact / prune の閾値判定には十分な精度
|
||||
|
||||
use llm_worker::llm_client::client::LlmClient;
|
||||
use llm_worker::token_counter::{item_bytes, prefix_bytes, tokens_at};
|
||||
use llm_worker::{Item, UsageRecord};
|
||||
use llm_engine::llm_client::client::LlmClient;
|
||||
use llm_engine::token_counter::{item_bytes, prefix_bytes, tokens_at};
|
||||
use llm_engine::{Item, UsageRecord};
|
||||
use session_store::Store;
|
||||
|
||||
pub use llm_worker::token_counter::{EstimateSource, TokenEstimate};
|
||||
pub use llm_engine::token_counter::{EstimateSource, TokenEstimate};
|
||||
|
||||
use crate::Pod;
|
||||
|
||||
@@ -188,7 +188,7 @@ pub(crate) fn token_estimates_for_prune_impl(
|
||||
|
||||
/// Prune 射影(`ToolResult.content = None`)で節約されるトークン数の推定。
|
||||
///
|
||||
/// `indices` は [`llm_worker::prune::prunable_indices`] が返す候補列を
|
||||
/// `indices` は [`llm_engine::prune::prunable_indices`] が返す候補列を
|
||||
/// 想定する。各候補の content バイト差分を合算し、usage 履歴由来の
|
||||
/// tokens/byte レートでトークン数に換算する。範囲を「丸ごと drop」する
|
||||
/// のではなく、item 自体(summary 等)は残したままの値を返す点が
|
||||
@@ -248,7 +248,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
/// 最後の measurement と、その後に追加された未測定分の byte/4 外挿。
|
||||
pub fn total_tokens(&self) -> TokenEstimate {
|
||||
let usage = self.usage_history();
|
||||
llm_worker::token_counter::total_tokens(self.history(), &usage)
|
||||
llm_engine::token_counter::total_tokens(self.history(), &usage)
|
||||
}
|
||||
|
||||
/// 任意の history index 時点でのプロンプト全長推定。
|
||||
@@ -259,7 +259,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
/// pointer 以降に増えたプロンプト長を測るのに使う。
|
||||
pub fn total_tokens_at(&self, history_len: usize) -> TokenEstimate {
|
||||
let usage = self.usage_history();
|
||||
llm_worker::token_counter::total_tokens_at(self.history(), &usage, history_len)
|
||||
llm_engine::token_counter::total_tokens_at(self.history(), &usage, history_len)
|
||||
}
|
||||
|
||||
/// 末尾から `retained` トークン以上を残すための分割位置。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Tracks per-LLM-request Usage measurements within a Pod run.
|
||||
//!
|
||||
//! Bridge between two sync touchpoints in the Worker lifecycle:
|
||||
//! Bridge between two sync touchpoints in the Engine lifecycle:
|
||||
//!
|
||||
//! - **`pre_llm_request` hook** (async, but synchronously accessed via the
|
||||
//! tracker): captures `history.len()` at the moment a request goes out.
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
use std::sync::Mutex;
|
||||
|
||||
use llm_worker::UsageRecord;
|
||||
use llm_worker::timeline::event::UsageEvent;
|
||||
use llm_engine::UsageRecord;
|
||||
use llm_engine::timeline::event::UsageEvent;
|
||||
|
||||
/// One drained measurement: the underlying `UsageRecord` plus an optional
|
||||
/// `correlation_id` stamped by the prune projection (or any other future
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Compact worker state and the four tools that drive it.
|
||||
//!
|
||||
//! The compact worker is a disposable `Worker` instance spun up by
|
||||
//! The compact worker is a disposable `Engine` instance spun up by
|
||||
//! [`Pod::compact`]. It receives the history to summarise plus a list of
|
||||
//! default reference files (from the session-lifetime `Tracker`) and runs
|
||||
//! a tool-driven LLM loop. The tools here let it:
|
||||
@@ -22,9 +22,9 @@ use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use llm_worker::Item;
|
||||
use llm_worker::interceptor::{Interceptor, PreRequestAction, PreToolAction, ToolCallInfo};
|
||||
use llm_worker::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput, ToolResult};
|
||||
use llm_engine::Item;
|
||||
use llm_engine::interceptor::{Interceptor, PreRequestAction, PreToolAction, ToolCallInfo};
|
||||
use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput, ToolResult};
|
||||
use serde::Deserialize;
|
||||
use tools::ScopedFs;
|
||||
|
||||
@@ -154,7 +154,7 @@ impl Tool for SearchSessionLogTool {
|
||||
async fn execute(
|
||||
&self,
|
||||
input_json: &str,
|
||||
_ctx: llm_worker::tool::ToolExecutionContext,
|
||||
_ctx: llm_engine::tool::ToolExecutionContext,
|
||||
) -> Result<ToolOutput, ToolError> {
|
||||
let params: SearchSessionParams = serde_json::from_str(input_json).map_err(|e| {
|
||||
ToolError::InvalidArgument(format!("invalid search_session_log input: {e}"))
|
||||
@@ -213,7 +213,7 @@ impl Tool for ReadSessionItemsTool {
|
||||
async fn execute(
|
||||
&self,
|
||||
input_json: &str,
|
||||
_ctx: llm_worker::tool::ToolExecutionContext,
|
||||
_ctx: llm_engine::tool::ToolExecutionContext,
|
||||
) -> Result<ToolOutput, ToolError> {
|
||||
let params: ReadSessionParams = serde_json::from_str(input_json).map_err(|e| {
|
||||
ToolError::InvalidArgument(format!("invalid read_session_items input: {e}"))
|
||||
@@ -379,7 +379,7 @@ impl Tool for MarkReadRequiredTool {
|
||||
async fn execute(
|
||||
&self,
|
||||
input_json: &str,
|
||||
_ctx: llm_worker::tool::ToolExecutionContext,
|
||||
_ctx: llm_engine::tool::ToolExecutionContext,
|
||||
) -> Result<ToolOutput, ToolError> {
|
||||
let params: MarkParams = serde_json::from_str(input_json).map_err(|e| {
|
||||
ToolError::InvalidArgument(format!("invalid mark_read_required input: {e}"))
|
||||
@@ -440,7 +440,7 @@ impl Tool for AddReferenceTool {
|
||||
async fn execute(
|
||||
&self,
|
||||
input_json: &str,
|
||||
_ctx: llm_worker::tool::ToolExecutionContext,
|
||||
_ctx: llm_engine::tool::ToolExecutionContext,
|
||||
) -> Result<ToolOutput, ToolError> {
|
||||
let params: ReferenceParams = serde_json::from_str(input_json)
|
||||
.map_err(|e| ToolError::InvalidArgument(format!("invalid add_reference input: {e}")))?;
|
||||
@@ -468,7 +468,7 @@ impl Tool for WriteSummaryTool {
|
||||
async fn execute(
|
||||
&self,
|
||||
input_json: &str,
|
||||
_ctx: llm_worker::tool::ToolExecutionContext,
|
||||
_ctx: llm_engine::tool::ToolExecutionContext,
|
||||
) -> Result<ToolOutput, ToolError> {
|
||||
let params: SummaryParams = serde_json::from_str(input_json)
|
||||
.map_err(|e| ToolError::InvalidArgument(format!("invalid write_summary input: {e}")))?;
|
||||
@@ -623,7 +623,7 @@ impl CompactWorkerInterceptor {
|
||||
impl Interceptor for CompactWorkerInterceptor {
|
||||
async fn pre_llm_request(&self, context: &mut Vec<Item>) -> PreRequestAction {
|
||||
let records = self.usage_tracker.records();
|
||||
let estimate = llm_worker::token_counter::total_tokens(context, &records);
|
||||
let estimate = llm_engine::token_counter::total_tokens(context, &records);
|
||||
if estimate.tokens > self.max_input_tokens {
|
||||
return PreRequestAction::Cancel(format!(
|
||||
"compact worker input occupancy exceeded {} tokens",
|
||||
@@ -673,8 +673,8 @@ mod tests {
|
||||
ScopedFs::new(scope, tmp.to_path_buf())
|
||||
}
|
||||
|
||||
fn make_usage(input: u64) -> llm_worker::timeline::event::UsageEvent {
|
||||
llm_worker::timeline::event::UsageEvent {
|
||||
fn make_usage(input: u64) -> llm_engine::timeline::event::UsageEvent {
|
||||
llm_engine::timeline::event::UsageEvent {
|
||||
input_tokens: Some(input),
|
||||
output_tokens: Some(0),
|
||||
total_tokens: Some(input),
|
||||
|
||||
@@ -2,8 +2,8 @@ use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use llm_worker::WorkerError;
|
||||
use llm_worker::llm_client::client::LlmClient;
|
||||
use llm_engine::EngineError;
|
||||
use llm_engine::llm_client::client::LlmClient;
|
||||
use manifest::TicketFeatureAccessConfig;
|
||||
use pod_store::PodMetadataStore;
|
||||
use session_store::Store;
|
||||
@@ -217,7 +217,7 @@ impl PodController {
|
||||
|
||||
// === 1.5. Direct writer wiring ===
|
||||
//
|
||||
// Worker callbacks fire `on_history_append` for each assistant
|
||||
// Engine callbacks fire `on_history_append` for each assistant
|
||||
// item / tool result that lands in history. With the sync
|
||||
// writer in place, the callback commits each item directly
|
||||
// through a `LogWriterHandle` (no mpsc ferry, no drain task).
|
||||
@@ -228,8 +228,8 @@ impl PodController {
|
||||
pod.attach_log_writer(writer_for_system);
|
||||
pod.wire_history_persistence();
|
||||
|
||||
// === 2. Worker event bridge wiring ===
|
||||
wire_event_bridges_on_worker(&mut pod, &event_tx, &alerter, &in_flight);
|
||||
// === 2. Engine event bridge wiring ===
|
||||
wire_event_bridges_on_engine(&mut pod, &event_tx, &alerter, &in_flight);
|
||||
|
||||
// === 3. Tool registration (builtin / memory / spawn-orchestration) ===
|
||||
let fs_for_view = register_pod_tools(
|
||||
@@ -259,7 +259,7 @@ impl PodController {
|
||||
|
||||
// Materialise pending tool factories so the greeting reflects
|
||||
// the actual registered set instead of a hand-maintained mirror.
|
||||
pod.worker().tool_server_handle().flush_pending();
|
||||
pod.engine().tool_server_handle().flush_pending();
|
||||
|
||||
// === 4. Initial runtime files + PodSharedState + PodHandle +
|
||||
// SocketServer ===
|
||||
@@ -303,7 +303,7 @@ impl PodController {
|
||||
// Clone cancel sender and notification buffer before moving pod
|
||||
// into the controller task so the in-flight turn can be reached
|
||||
// via these handles while pod itself is borrowed by drive_turn.
|
||||
let cancel_tx = pod.worker_mut().cancel_sender();
|
||||
let cancel_tx = pod.engine_mut().cancel_sender();
|
||||
let notify_buffer = pod.notify_buffer_handle();
|
||||
|
||||
tokio::spawn(controller_loop(
|
||||
@@ -326,7 +326,7 @@ impl PodController {
|
||||
}
|
||||
}
|
||||
|
||||
/// Wire the per-event broadcast bridges on the Pod's Worker. Each callback
|
||||
/// Wire the per-event broadcast bridges on the Pod's Engine. Each callback
|
||||
/// re-publishes a worker-level signal as a `protocol::Event` on `event_tx`
|
||||
/// so subscribers (TUI, socket clients) get a single typed stream.
|
||||
///
|
||||
@@ -334,7 +334,7 @@ impl PodController {
|
||||
/// per-item history commit callback so every assistant / tool item
|
||||
/// landing in `worker.history` becomes a singular `LogEntry::AssistantItem`
|
||||
/// / `ToolResult` commit through the sync writer.
|
||||
fn wire_event_bridges_on_worker<C, St>(
|
||||
fn wire_event_bridges_on_engine<C, St>(
|
||||
pod: &mut Pod<C, St>,
|
||||
event_tx: &broadcast::Sender<Event>,
|
||||
alerter: &Alerter,
|
||||
@@ -344,7 +344,7 @@ fn wire_event_bridges_on_worker<C, St>(
|
||||
St: Store + PodMetadataStore + Clone + 'static,
|
||||
{
|
||||
let ai_activity = pod.ai_activity_counter();
|
||||
let worker = pod.worker_mut();
|
||||
let worker = pod.engine_mut();
|
||||
|
||||
let tx = event_tx.clone();
|
||||
worker.on_turn_start(move |turn| {
|
||||
@@ -486,7 +486,7 @@ fn wire_event_bridges_on_worker<C, St>(
|
||||
|
||||
let alerter_for_worker = alerter.clone();
|
||||
worker.on_warning(move |message| {
|
||||
alerter_for_worker.alert(AlertLevel::Warn, AlertSource::Worker, message.to_owned());
|
||||
alerter_for_worker.alert(AlertLevel::Warn, AlertSource::Engine, message.to_owned());
|
||||
});
|
||||
|
||||
// History-append broadcasts (previously `Event::SystemMessage`)
|
||||
@@ -575,7 +575,7 @@ fn is_ticket_orchestrator_role(role: Option<&str>) -> bool {
|
||||
|
||||
/// Register the builtin file-manipulation tools, optional memory tools,
|
||||
/// and the Pod-orchestration tools (SpawnPod + comm) on the Pod's
|
||||
/// Worker. Returns the `ScopedFs` clone used to attach a `PodFsView` to
|
||||
/// Engine. Returns the `ScopedFs` clone used to attach a `PodFsView` to
|
||||
/// the shared state.
|
||||
async fn register_pod_tools<C, St>(
|
||||
pod: &mut Pod<C, St>,
|
||||
@@ -616,13 +616,13 @@ where
|
||||
// a clone for the FS view we attach below, since the tools consume
|
||||
// `fs` itself.
|
||||
let fs_for_view = fs.clone();
|
||||
pod.worker_mut().register_tools(tools::core_builtin_tools(
|
||||
pod.engine_mut().register_tools(tools::core_builtin_tools(
|
||||
fs,
|
||||
tracker.clone(),
|
||||
bash_output_dir,
|
||||
));
|
||||
if feature_config.web.enabled {
|
||||
pod.worker_mut()
|
||||
pod.engine_mut()
|
||||
.register_tools(tools::web_builtin_tools(web_config));
|
||||
}
|
||||
|
||||
@@ -663,7 +663,7 @@ where
|
||||
}
|
||||
|
||||
{
|
||||
let worker = pod.worker_mut();
|
||||
let worker = pod.engine_mut();
|
||||
|
||||
// Memory tools require both explicit feature exposure and memory storage
|
||||
// configuration. This keeps resident-memory config separate from the
|
||||
@@ -777,8 +777,8 @@ async fn controller_loop<C, St>(
|
||||
// Cancellation is meaningful only for an accepted running turn. Clear
|
||||
// idle/stale signals before the status flip; any Cancel/Pause received
|
||||
// after this point is delivered to the turn and must not be discarded by
|
||||
// the Worker at run start.
|
||||
pod.worker_mut().clear_pending_cancel();
|
||||
// the Engine at run start.
|
||||
pod.engine_mut().clear_pending_cancel();
|
||||
set_controller_status(&shared_state, &runtime_dir, &event_tx, PodStatus::Running).await;
|
||||
let parent_originated = run.is_parent_originated();
|
||||
let (new_status, shutdown) = match run {
|
||||
@@ -1211,7 +1211,7 @@ where
|
||||
}
|
||||
(status, shutdown_requested)
|
||||
}
|
||||
Err(PodError::Worker(WorkerError::Cancelled)) if pause_requested => {
|
||||
Err(PodError::Engine(EngineError::Cancelled)) if pause_requested => {
|
||||
// User-initiated Pause. Report the transition to
|
||||
// clients as a normal Paused run-end, and
|
||||
// intentionally skip `PodEvent::Errored` upward:
|
||||
@@ -1405,10 +1405,10 @@ where
|
||||
),
|
||||
};
|
||||
// Tool list reflects whatever `spawn()` ended up registering on the
|
||||
// Worker. Caller must have flushed pending factories first; without
|
||||
// Engine. Caller must have flushed pending factories first; without
|
||||
// a flush the tool table is empty and this returns an empty vec.
|
||||
let tool_names: Vec<String> = pod
|
||||
.worker()
|
||||
.engine()
|
||||
.tool_server_handle()
|
||||
.tool_definitions_sorted()
|
||||
.into_iter()
|
||||
@@ -1428,9 +1428,9 @@ where
|
||||
|
||||
fn worker_error_code(e: &PodError) -> ErrorCode {
|
||||
match e {
|
||||
PodError::Worker(we) => match we {
|
||||
WorkerError::Tool(_) => ErrorCode::ToolError,
|
||||
WorkerError::Client(_) => ErrorCode::ProviderError,
|
||||
PodError::Engine(we) => match we {
|
||||
EngineError::Tool(_) => ErrorCode::ToolError,
|
||||
EngineError::Client(_) => ErrorCode::ProviderError,
|
||||
_ => ErrorCode::Internal,
|
||||
},
|
||||
PodError::Provider(_) => ErrorCode::ProviderError,
|
||||
@@ -1628,7 +1628,7 @@ mod tests {
|
||||
let recv = tokio::spawn(recv_pod_event(listener, Duration::from_secs(2)));
|
||||
|
||||
let pod_future = async {
|
||||
Err::<PodRunResult, _>(PodError::Worker(WorkerError::Aborted(
|
||||
Err::<PodRunResult, _>(PodError::Engine(EngineError::Aborted(
|
||||
"boom from test".into(),
|
||||
)))
|
||||
};
|
||||
@@ -1663,7 +1663,7 @@ mod tests {
|
||||
let listener = UnixListener::bind(&env.parent_socket_path).expect("bind listener");
|
||||
|
||||
let pod_future = async {
|
||||
Err::<PodRunResult, _>(PodError::Worker(WorkerError::Aborted(
|
||||
Err::<PodRunResult, _>(PodError::Engine(EngineError::Aborted(
|
||||
"boom from notify".into(),
|
||||
)))
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ use std::time::Duration;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use client::PodRuntimeCommand;
|
||||
use llm_worker::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput};
|
||||
use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput};
|
||||
use manifest::{Permission, ScopeRule};
|
||||
use pod_store::{PodActiveSegmentRef, PodMetadata, PodMetadataStore, validate_pod_name};
|
||||
use protocol::stream::JsonLineReader;
|
||||
@@ -845,7 +845,7 @@ where
|
||||
async fn execute(
|
||||
&self,
|
||||
_input_json: &str,
|
||||
_ctx: llm_worker::tool::ToolExecutionContext,
|
||||
_ctx: llm_engine::tool::ToolExecutionContext,
|
||||
) -> Result<ToolOutput, ToolError> {
|
||||
let items = self
|
||||
.discovery
|
||||
@@ -872,7 +872,7 @@ where
|
||||
async fn execute(
|
||||
&self,
|
||||
input_json: &str,
|
||||
_ctx: llm_worker::tool::ToolExecutionContext,
|
||||
_ctx: llm_engine::tool::ToolExecutionContext,
|
||||
) -> Result<ToolOutput, ToolError> {
|
||||
let input: PodNameInput = serde_json::from_str(input_json)
|
||||
.map_err(|e| ToolError::InvalidArgument(format!("invalid RestorePod input: {e}")))?;
|
||||
@@ -948,7 +948,7 @@ where
|
||||
async fn execute(
|
||||
&self,
|
||||
input_json: &str,
|
||||
_ctx: llm_worker::tool::ToolExecutionContext,
|
||||
_ctx: llm_engine::tool::ToolExecutionContext,
|
||||
) -> Result<ToolOutput, ToolError> {
|
||||
let input: SendToPeerPodInput = serde_json::from_str(input_json)
|
||||
.map_err(|e| ToolError::InvalidArgument(format!("invalid SendToPeerPod input: {e}")))?;
|
||||
|
||||
+20
-20
@@ -3,11 +3,11 @@
|
||||
//! This module defines the Pod-side feature boundary used to collect
|
||||
//! descriptor metadata, tool contributions, safe hook contributions, background
|
||||
//! task declarations, service declarations, and protocol-backed provider
|
||||
//! startup discovery before installing them into the existing Worker/HookRegistry
|
||||
//! startup discovery before installing them into the existing Engine/HookRegistry
|
||||
//! host surfaces.
|
||||
//!
|
||||
//! The implementation is intentionally host-mediated: tools are installed through
|
||||
//! the normal Worker tool path, hooks are installed through
|
||||
//! the normal Engine tool path, hooks are installed through
|
||||
//! [`crate::hook::HookRegistryBuilder`], and provider output is represented as
|
||||
//! ordinary feature reports/diagnostics instead of a separate authority layer.
|
||||
|
||||
@@ -15,10 +15,10 @@ use std::collections::{HashMap, HashSet};
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
use llm_worker::Worker;
|
||||
use llm_worker::llm_client::client::LlmClient;
|
||||
use llm_worker::state::Mutable;
|
||||
use llm_worker::tool::ToolDefinition;
|
||||
use llm_engine::Engine;
|
||||
use llm_engine::llm_client::client::LlmClient;
|
||||
use llm_engine::state::Mutable;
|
||||
use llm_engine::tool::ToolDefinition;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -167,9 +167,9 @@ impl ProtocolProviderLifecycleDiagnostic {
|
||||
/// Startup-discovered contribution set returned by a protocol-backed provider.
|
||||
///
|
||||
/// Tool definitions are materialized exactly once when registered, then inserted
|
||||
/// into the normal Worker tool path as stable metadata plus executable tool
|
||||
/// into the normal Engine tool path as stable metadata plus executable tool
|
||||
/// handles for the remainder of the run. Execution still flows through the
|
||||
/// Worker, permission, history, and bounded-result machinery.
|
||||
/// Engine, permission, history, and bounded-result machinery.
|
||||
#[derive(Clone)]
|
||||
pub struct ProtocolProviderContribution {
|
||||
declaration: ProtocolProviderDeclaration,
|
||||
@@ -1285,10 +1285,10 @@ impl FeatureRegistryBuilder {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Install modules into the existing Worker tool path and hook builder.
|
||||
pub(crate) fn install_into_worker<C: LlmClient>(
|
||||
/// Install modules into the existing Engine tool path and hook builder.
|
||||
pub(crate) fn install_into_engine<C: LlmClient>(
|
||||
self,
|
||||
worker: &mut Worker<C, Mutable>,
|
||||
worker: &mut Engine<C, Mutable>,
|
||||
hook_builder: &mut HookRegistryBuilder,
|
||||
) -> FeatureRegistryInstallReport {
|
||||
let mut pending_tools = Vec::new();
|
||||
@@ -1485,8 +1485,8 @@ mod tests {
|
||||
use super::*;
|
||||
use async_trait::async_trait;
|
||||
use futures::stream;
|
||||
use llm_worker::llm_client::{ClientError, Request, ResponseStream};
|
||||
use llm_worker::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput};
|
||||
use llm_engine::llm_client::{ClientError, Request, ResponseStream};
|
||||
use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput};
|
||||
use serde_json::json;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
@@ -1511,7 +1511,7 @@ mod tests {
|
||||
async fn execute(
|
||||
&self,
|
||||
_input_json: &str,
|
||||
_ctx: llm_worker::tool::ToolExecutionContext,
|
||||
_ctx: llm_engine::tool::ToolExecutionContext,
|
||||
) -> Result<ToolOutput, ToolError> {
|
||||
Ok(ToolOutput::from("ok".to_string()))
|
||||
}
|
||||
@@ -1695,7 +1695,7 @@ mod tests {
|
||||
let descriptor = FeatureDescriptor::builtin("provider-feature", "Provider feature")
|
||||
.with_protocol_provider(provider.clone());
|
||||
let calls = Arc::new(AtomicUsize::new(0));
|
||||
let mut worker = Worker::new(DummyClient);
|
||||
let mut worker = Engine::new(DummyClient);
|
||||
let mut hook_builder = HookRegistryBuilder::default();
|
||||
|
||||
let report = FeatureRegistryBuilder::new()
|
||||
@@ -1705,7 +1705,7 @@ mod tests {
|
||||
calls: Arc::clone(&calls),
|
||||
state: ProtocolProviderLifecycleState::Ready,
|
||||
})
|
||||
.install_into_worker(&mut worker, &mut hook_builder);
|
||||
.install_into_engine(&mut worker, &mut hook_builder);
|
||||
|
||||
worker.tool_server_handle().flush_pending();
|
||||
let tool_names: Vec<_> = worker
|
||||
@@ -1848,13 +1848,13 @@ mod tests {
|
||||
}
|
||||
|
||||
let calls = Arc::new(AtomicUsize::new(0));
|
||||
let mut worker = Worker::new(DummyClient);
|
||||
let mut worker = Engine::new(DummyClient);
|
||||
let mut hook_builder = HookRegistryBuilder::default();
|
||||
let report = FeatureRegistryBuilder::new()
|
||||
.with_module(StatefulToolFeature {
|
||||
calls: Arc::clone(&calls),
|
||||
})
|
||||
.install_into_worker(&mut worker, &mut hook_builder);
|
||||
.install_into_engine(&mut worker, &mut hook_builder);
|
||||
|
||||
worker.tool_server_handle().flush_pending();
|
||||
let names: Vec<_> = worker
|
||||
@@ -2220,11 +2220,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn builtin_task_feature_installs_through_worker_tool_path() {
|
||||
let mut worker = Worker::new(DummyClient);
|
||||
let mut worker = Engine::new(DummyClient);
|
||||
let mut hook_builder = HookRegistryBuilder::default();
|
||||
let report = FeatureRegistryBuilder::new()
|
||||
.with_module(builtin::task_tools_feature())
|
||||
.install_into_worker(&mut worker, &mut hook_builder);
|
||||
.install_into_engine(&mut worker, &mut hook_builder);
|
||||
|
||||
worker.tool_server_handle().flush_pending();
|
||||
let names: Vec<_> = worker
|
||||
|
||||
@@ -9,7 +9,7 @@ use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use llm_worker::Item;
|
||||
use llm_engine::Item;
|
||||
|
||||
mod store;
|
||||
mod tool_impl;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use llm_worker::Item;
|
||||
use llm_engine::Item;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use llm_worker::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput};
|
||||
use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput};
|
||||
use serde::Deserialize;
|
||||
|
||||
use super::store::{TaskEntry, TaskStatus, TaskStore, render_snapshot, snapshot_overview};
|
||||
@@ -76,7 +76,7 @@ impl Tool for TaskCreateTool {
|
||||
async fn execute(
|
||||
&self,
|
||||
input_json: &str,
|
||||
_ctx: llm_worker::tool::ToolExecutionContext,
|
||||
_ctx: llm_engine::tool::ToolExecutionContext,
|
||||
) -> Result<ToolOutput, ToolError> {
|
||||
let params: TaskCreateParams = serde_json::from_str(input_json)
|
||||
.map_err(|e| ToolError::InvalidArgument(format!("invalid TaskCreate input: {e}")))?;
|
||||
@@ -100,7 +100,7 @@ impl Tool for TaskListTool {
|
||||
async fn execute(
|
||||
&self,
|
||||
input_json: &str,
|
||||
_ctx: llm_worker::tool::ToolExecutionContext,
|
||||
_ctx: llm_engine::tool::ToolExecutionContext,
|
||||
) -> Result<ToolOutput, ToolError> {
|
||||
let _: TaskListParams = serde_json::from_str(input_json)
|
||||
.map_err(|e| ToolError::InvalidArgument(format!("invalid TaskList input: {e}")))?;
|
||||
@@ -117,7 +117,7 @@ impl Tool for TaskGetTool {
|
||||
async fn execute(
|
||||
&self,
|
||||
input_json: &str,
|
||||
_ctx: llm_worker::tool::ToolExecutionContext,
|
||||
_ctx: llm_engine::tool::ToolExecutionContext,
|
||||
) -> Result<ToolOutput, ToolError> {
|
||||
let params: TaskGetParams = serde_json::from_str(input_json)
|
||||
.map_err(|e| ToolError::InvalidArgument(format!("invalid TaskGet input: {e}")))?;
|
||||
@@ -137,7 +137,7 @@ impl Tool for TaskUpdateTool {
|
||||
async fn execute(
|
||||
&self,
|
||||
input_json: &str,
|
||||
_ctx: llm_worker::tool::ToolExecutionContext,
|
||||
_ctx: llm_engine::tool::ToolExecutionContext,
|
||||
) -> Result<ToolOutput, ToolError> {
|
||||
let params: TaskUpdateParams = serde_json::from_str(input_json)
|
||||
.map_err(|e| ToolError::InvalidArgument(format!("invalid TaskUpdate input: {e}")))?;
|
||||
|
||||
@@ -256,7 +256,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn pending_tool_description(
|
||||
pending_tools: &[llm_worker::tool::ToolDefinition],
|
||||
pending_tools: &[llm_engine::tool::ToolDefinition],
|
||||
name: &str,
|
||||
) -> String {
|
||||
pending_tools
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use llm_worker::tool::{
|
||||
use llm_engine::tool::{
|
||||
Tool, ToolDefinition, ToolError, ToolExecutionContext, ToolMeta, ToolOrigin, ToolOutput,
|
||||
};
|
||||
use manifest::McpConfig;
|
||||
|
||||
@@ -20,7 +20,7 @@ use std::time::{Duration, Instant};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use futures_util::{SinkExt, StreamExt};
|
||||
use llm_worker::tool::{
|
||||
use llm_engine::tool::{
|
||||
Tool, ToolDefinition, ToolError, ToolExecutionContext, ToolMeta, ToolOrigin, ToolOutput,
|
||||
};
|
||||
use manifest::plugin::{
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use llm_worker::Item;
|
||||
use llm_engine::Item;
|
||||
use manifest::Scope;
|
||||
use tools::scoped_fs::first_symlink;
|
||||
use tools::{ScopedFs, ToolsError};
|
||||
@@ -369,7 +369,7 @@ fn split_prefix(prefix: &str, cwd: &Path) -> (PathBuf, String, bool) {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use llm_worker::ContentPart;
|
||||
use llm_engine::ContentPart;
|
||||
use manifest::{Permission, Scope, ScopeConfig, ScopeRule};
|
||||
use tempfile::TempDir;
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
//! Pod-layer hook infrastructure
|
||||
//!
|
||||
//! Hooks are the **public** orchestration extension point. They receive
|
||||
//! event-specific context values about each event in the Worker execution loop
|
||||
//! event-specific context values about each event in the Engine execution loop
|
||||
//! and return a safe public control-flow action. Contexts may carry narrow
|
||||
//! host-created handles for approved side effects; hook return values remain
|
||||
//! flow-control decisions only.
|
||||
//!
|
||||
//! Hooks intentionally cannot mutate the Worker's context, history, tool
|
||||
//! Hooks intentionally cannot mutate the Engine's context, history, tool
|
||||
//! call, or tool result. Internal mechanisms that need such access (e.g.
|
||||
//! compaction, notification injection, output truncation) implement
|
||||
//! `llm_worker::Interceptor` directly inside Pod, never via this trait.
|
||||
//! `llm_engine::Interceptor` directly inside Pod, never via this trait.
|
||||
//!
|
||||
//! This separation lets Hooks be exposed safely to user-facing
|
||||
//! extension surfaces (scripting, plugins) in the future without
|
||||
@@ -19,10 +19,10 @@ use std::ops::Deref;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use llm_worker::interceptor::{
|
||||
use llm_engine::interceptor::{
|
||||
PostToolAction, PreRequestAction, PreToolAction, PromptAction, TurnEndAction,
|
||||
};
|
||||
use llm_worker::tool::{ToolOutput, ToolResult};
|
||||
use llm_engine::tool::{ToolOutput, ToolResult};
|
||||
use serde_json::Value;
|
||||
use session_store::{SystemItem, SystemReminder};
|
||||
|
||||
@@ -163,7 +163,7 @@ impl From<HookTurnEndAction> for TurnEndAction {
|
||||
/// event-specific context. The handle queues typed requests; the host drains the
|
||||
/// queue, commits each entry through `LogEntry::SystemItem`, and only then makes
|
||||
/// the matching system message visible to the model. It deliberately exposes no
|
||||
/// raw `llm_worker::Item`, history writer, event sender, `Pod`, `Worker`, or
|
||||
/// raw `llm_engine::Item`, history writer, event sender, `Pod`, `Engine`, or
|
||||
/// notification buffer.
|
||||
pub struct SystemItemAppendHandle {
|
||||
pending: Arc<Mutex<Vec<SystemItem>>>,
|
||||
@@ -202,7 +202,7 @@ pub struct PromptSubmitInfo {
|
||||
|
||||
/// Summary information included in `PreLlmRequest` contexts.
|
||||
pub struct PreRequestInfo {
|
||||
/// Number of items currently in the Worker context.
|
||||
/// Number of items currently in the Engine context.
|
||||
pub item_count: usize,
|
||||
/// Most recently observed `input_tokens` from the LLM provider.
|
||||
/// `None` when the Pod has no compaction state attached, or when
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
#[cfg(test)]
|
||||
use crate::prompt::catalog::PromptCatalog;
|
||||
use llm_worker::Item;
|
||||
use llm_engine::Item;
|
||||
|
||||
/// Build synthetic `Item::ToolResult` items for every unanswered
|
||||
/// `Item::ToolCall` in `history`, preserving order.
|
||||
|
||||
@@ -137,7 +137,7 @@ mod tests {
|
||||
let alerter = Alerter::new(tx);
|
||||
|
||||
for i in 0..(MAX_BUFFERED_ALERTS + 50) {
|
||||
alerter.alert(AlertLevel::Warn, AlertSource::Worker, format!("msg-{i}"));
|
||||
alerter.alert(AlertLevel::Warn, AlertSource::Engine, format!("msg-{i}"));
|
||||
}
|
||||
|
||||
let (snapshot, _rx) = alerter.subscribe_with_snapshot();
|
||||
@@ -153,9 +153,9 @@ mod tests {
|
||||
let (tx, _keep) = broadcast::channel::<Event>(8);
|
||||
let alerter = Alerter::new(tx);
|
||||
|
||||
alerter.alert(AlertLevel::Warn, AlertSource::Worker, "historic".into());
|
||||
alerter.alert(AlertLevel::Warn, AlertSource::Engine, "historic".into());
|
||||
let (snapshot, mut rx) = alerter.subscribe_with_snapshot();
|
||||
alerter.alert(AlertLevel::Error, AlertSource::Worker, "live".into());
|
||||
alerter.alert(AlertLevel::Error, AlertSource::Engine, "live".into());
|
||||
|
||||
assert_eq!(snapshot.len(), 1);
|
||||
assert_eq!(snapshot[0].message, "historic");
|
||||
|
||||
@@ -12,13 +12,13 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use llm_worker::Item;
|
||||
use llm_worker::UsageRecord;
|
||||
use llm_worker::interceptor::{
|
||||
use llm_engine::Item;
|
||||
use llm_engine::UsageRecord;
|
||||
use llm_engine::interceptor::{
|
||||
Interceptor, PostToolAction, PreRequestAction, PreToolAction, PromptAction, ToolCallInfo,
|
||||
ToolResultInfo, TurnEndAction,
|
||||
};
|
||||
use llm_worker::tool::ToolOutput;
|
||||
use llm_engine::tool::ToolOutput;
|
||||
use tracing::info;
|
||||
use tracing::warn;
|
||||
|
||||
@@ -35,7 +35,7 @@ use crate::hook::{
|
||||
use crate::ipc::notify_buffer::{NotifyBuffer, build_system_item};
|
||||
use crate::pod::SystemItemCommitter;
|
||||
use crate::prompt::catalog::PromptCatalog;
|
||||
use llm_worker::token_counter::total_tokens;
|
||||
use llm_engine::token_counter::total_tokens;
|
||||
|
||||
/// Maximum number of bytes copied into `TurnEndInfo::final_text_preview`.
|
||||
const FINAL_TEXT_PREVIEW_LIMIT: usize = 512;
|
||||
@@ -53,7 +53,7 @@ pub(crate) struct PodInterceptor {
|
||||
usage_tracker: Option<Arc<UsageTracker>>,
|
||||
/// Pending-notification buffer drained into `worker.history`
|
||||
/// via [`Self::pending_history_appends`] just before the next LLM
|
||||
/// request. The Worker `extend`s these into its persistent history
|
||||
/// request. The Engine `extend`s these into its persistent history
|
||||
/// so the LLM has a visible trigger for any reaction it commits.
|
||||
pending_notifies: NotifyBuffer,
|
||||
/// Submit-scoped stash of resolver-produced typed system items.
|
||||
@@ -495,14 +495,14 @@ mod tests {
|
||||
.expect("task tool definition");
|
||||
let (meta, tool) = def();
|
||||
ToolCallInfo {
|
||||
call: llm_worker::tool::ToolCall {
|
||||
call: llm_engine::tool::ToolCall {
|
||||
id: "call-id".into(),
|
||||
name: name.into(),
|
||||
input,
|
||||
},
|
||||
meta,
|
||||
tool,
|
||||
context: llm_worker::tool::ToolExecutionContext::new("call-id", "test-batch", 0),
|
||||
context: llm_engine::tool::ToolExecutionContext::new("call-id", "test-batch", 0),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -590,7 +590,7 @@ mod tests {
|
||||
let history = usage_handle_with(ctx_items.len(), 50);
|
||||
let usage_tracker = Arc::new(UsageTracker::new());
|
||||
usage_tracker.note_request(ctx_items.len());
|
||||
usage_tracker.record_usage(&llm_worker::event::UsageEvent {
|
||||
usage_tracker.record_usage(&llm_engine::event::UsageEvent {
|
||||
input_tokens: Some(150),
|
||||
output_tokens: Some(0),
|
||||
total_tokens: Some(150),
|
||||
@@ -656,7 +656,7 @@ mod tests {
|
||||
cache_write_tokens: 0,
|
||||
output_tokens: 0,
|
||||
};
|
||||
let prefix = llm_worker::token_counter::prefix_bytes(&ctx_items);
|
||||
let prefix = llm_engine::token_counter::prefix_bytes(&ctx_items);
|
||||
let delta_bytes = prefix[2].saturating_sub(prefix[1]);
|
||||
let old_projection =
|
||||
11_124 + (delta_bytes as u128 * 11_124_u128 / prefix[1] as u128) as u64;
|
||||
@@ -767,7 +767,7 @@ mod tests {
|
||||
assert!(matches!(
|
||||
&items[0],
|
||||
Item::Message {
|
||||
role: llm_worker::Role::System,
|
||||
role: llm_engine::Role::System,
|
||||
..
|
||||
}
|
||||
));
|
||||
@@ -912,7 +912,7 @@ mod tests {
|
||||
let info = task_tool_call_info("TaskList", serde_json::json!({}));
|
||||
let mut result_info = ToolResultInfo {
|
||||
call: info.call,
|
||||
result: llm_worker::tool::ToolResult::from_output(
|
||||
result: llm_engine::tool::ToolResult::from_output(
|
||||
"call-id",
|
||||
ToolOutput {
|
||||
summary: "ok".into(),
|
||||
@@ -1001,7 +1001,7 @@ mod tests {
|
||||
let mut ctx = ctx_items.clone();
|
||||
let action = interceptor.pre_llm_request(&mut ctx).await;
|
||||
assert!(matches!(action, PreRequestAction::Continue));
|
||||
usage_tracker.record_usage(&llm_worker::event::UsageEvent {
|
||||
usage_tracker.record_usage(&llm_engine::event::UsageEvent {
|
||||
input_tokens: Some(10),
|
||||
output_tokens: Some(0),
|
||||
total_tokens: Some(10),
|
||||
@@ -1017,7 +1017,7 @@ mod tests {
|
||||
other => panic!("expected reminder append, got {other:?}"),
|
||||
};
|
||||
assert_eq!(appended_len, 1);
|
||||
usage_tracker.record_usage(&llm_worker::event::UsageEvent {
|
||||
usage_tracker.record_usage(&llm_engine::event::UsageEvent {
|
||||
input_tokens: Some(11),
|
||||
output_tokens: Some(0),
|
||||
total_tokens: Some(11),
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! Entries are queued here by the Controller (on receipt of the
|
||||
//! corresponding IPC method) and drained by
|
||||
//! `PodInterceptor::pending_history_appends`, which the Worker calls
|
||||
//! `PodInterceptor::pending_history_appends`, which the Engine calls
|
||||
//! at the head of each turn loop iteration. The drain renders each
|
||||
//! pending entry into a typed `SystemItem` (with the `notify_wrapper`
|
||||
//! prompt applied), commits a `LogEntry::SystemItem` per entry through
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use async_trait::async_trait;
|
||||
use llm_worker::llm_client::client::LlmClient;
|
||||
use llm_engine::llm_client::client::LlmClient;
|
||||
use manifest::{ToolPermissionAction, ToolPermissionConfig};
|
||||
use serde_json::Value;
|
||||
use session_store::Store;
|
||||
|
||||
+149
-149
@@ -4,12 +4,12 @@ use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
use arc_swap::ArcSwap;
|
||||
use llm_worker::Item;
|
||||
use llm_worker::llm_client::RequestConfig;
|
||||
use llm_worker::llm_client::client::LlmClient;
|
||||
use llm_worker::llm_client::types::Role;
|
||||
use llm_worker::state::Mutable;
|
||||
use llm_worker::{ToolOutputLimits, UsageRecord, Worker, WorkerError, WorkerResult};
|
||||
use llm_engine::Item;
|
||||
use llm_engine::llm_client::RequestConfig;
|
||||
use llm_engine::llm_client::client::LlmClient;
|
||||
use llm_engine::llm_client::types::Role;
|
||||
use llm_engine::state::Mutable;
|
||||
use llm_engine::{Engine, EngineError, EngineResult, ToolOutputLimits, UsageRecord};
|
||||
use pod_store::{
|
||||
PodActiveSegmentRef, PodMetadata, PodMetadataStore, PodReclaimedChild, PodSpawnedChild,
|
||||
PodSpawnedScopeRule, PodStoreError,
|
||||
@@ -233,12 +233,12 @@ where
|
||||
|
||||
/// An independent agent execution unit.
|
||||
///
|
||||
/// Holds a [`Worker`] directly and persists session state via
|
||||
/// Holds a [`Engine`] directly and persists session state via
|
||||
/// `session-store` functions after each turn.
|
||||
pub struct Pod<C: LlmClient, St: Store> {
|
||||
manifest: PodManifest,
|
||||
/// Always `Some` outside of `run()`/`resume()`.
|
||||
worker: Option<Worker<C, Mutable>>,
|
||||
engine: Option<Engine<C, Mutable>>,
|
||||
store: St,
|
||||
/// Optional write-through hook for name-keyed Pod metadata. Production
|
||||
/// constructors install this from the same FsStore that owns the session
|
||||
@@ -269,7 +269,7 @@ pub struct Pod<C: LlmClient, St: Store> {
|
||||
/// Captures `(history_len, UsageEvent)` pairs during a run; drained
|
||||
/// in `persist_turn` and persisted as `LogEntry::LlmUsage` entries.
|
||||
usage_tracker: Arc<UsageTracker>,
|
||||
/// Sync-side buffer for `Metric` values queued from inside Worker
|
||||
/// Sync-side buffer for `Metric` values queued from inside Engine
|
||||
/// callbacks (currently the prune observer). Drained in `persist_turn`
|
||||
/// and written via `session_metrics::record_metric` alongside
|
||||
/// `LogEntry::LlmUsage`. Always present after construction.
|
||||
@@ -279,7 +279,7 @@ pub struct Pod<C: LlmClient, St: Store> {
|
||||
/// Read by token-accounting APIs (`Pod::total_tokens`, etc.).
|
||||
///
|
||||
/// Wrapped in `Arc<Mutex>` so that callbacks injected into the
|
||||
/// Worker (e.g. the savings estimator used by the prune projection)
|
||||
/// Engine (e.g. the savings estimator used by the prune projection)
|
||||
/// can share the same view via [`Pod::usage_history_handle`].
|
||||
usage_history: Arc<Mutex<Vec<UsageRecord>>>,
|
||||
/// Pod-lifetime file-operation tracker from the builtin `tools`
|
||||
@@ -402,7 +402,7 @@ pub struct Pod<C: LlmClient, St: Store> {
|
||||
/// on disk.
|
||||
sink: SegmentLogSink,
|
||||
/// `true` once `wire_history_persistence` has installed the
|
||||
/// `Worker::on_history_append` callback that commits each appended
|
||||
/// `Engine::on_history_append` callback that commits each appended
|
||||
/// item as a singular `LogEntry::AssistantItem` / `ToolResult`
|
||||
/// directly through the writer. Tests that drive `Pod::new` without
|
||||
/// going through the controller leave this `false`; `persist_turn`
|
||||
@@ -436,12 +436,12 @@ impl<C: LlmClient + Clone + 'static, St: Store + Clone + 'static> Pod<C, St> {
|
||||
// methods using `worker.client()` as fallback when no override
|
||||
// model is configured. system_prompt / request_config / cache_key
|
||||
// are unused on this path, so we deliberately skip copying them.
|
||||
let source_worker = self.worker.as_ref().expect("worker present");
|
||||
let mut worker = Worker::new(source_worker.client().clone());
|
||||
let source_worker = self.engine.as_ref().expect("worker present");
|
||||
let mut worker = Engine::new(source_worker.client().clone());
|
||||
worker.set_history(source_worker.history().to_vec());
|
||||
Self {
|
||||
manifest: self.manifest.clone(),
|
||||
worker: Some(worker),
|
||||
engine: Some(worker),
|
||||
store: self.store.clone(),
|
||||
pod_metadata_writer: None,
|
||||
segment_state: self.segment_state.clone(),
|
||||
@@ -513,7 +513,7 @@ impl<C: LlmClient + Clone + 'static, St: Store + Clone + 'static> Pod<C, St> {
|
||||
self.in_flight = Some(in_flight);
|
||||
}
|
||||
|
||||
/// Wire `Worker::on_history_append` to commit each appended item
|
||||
/// Wire `Engine::on_history_append` to commit each appended item
|
||||
/// directly as a singular `LogEntry::AssistantItem` / `ToolResult`
|
||||
/// through the writer. The controller calls this once per spawned
|
||||
/// Pod after the worker is built; tests that drive `Pod::new` may
|
||||
@@ -528,14 +528,14 @@ impl<C: LlmClient + Clone + 'static, St: Store + Clone + 'static> Pod<C, St> {
|
||||
/// callback would otherwise double-write them.
|
||||
pub fn wire_history_persistence(&mut self) {
|
||||
let writer = self.log_writer_handle();
|
||||
self.worker_mut().on_history_append(move |item| {
|
||||
self.engine_mut().on_history_append(move |item| {
|
||||
if item.is_user_message() {
|
||||
return;
|
||||
}
|
||||
if matches!(
|
||||
item,
|
||||
Item::Message {
|
||||
role: llm_worker::Role::System,
|
||||
role: llm_engine::Role::System,
|
||||
..
|
||||
}
|
||||
) {
|
||||
@@ -548,7 +548,7 @@ impl<C: LlmClient + Clone + 'static, St: Store + Clone + 'static> Pod<C, St> {
|
||||
});
|
||||
if self.manifest.session.record_event_trace {
|
||||
let writer = self.log_writer_handle();
|
||||
self.worker_mut()
|
||||
self.engine_mut()
|
||||
.on_stream_event(move |turn, llm_call, event| {
|
||||
let entry = session_store::TraceEntry {
|
||||
ts: segment_log::now_millis(),
|
||||
@@ -563,7 +563,7 @@ impl<C: LlmClient + Clone + 'static, St: Store + Clone + 'static> Pod<C, St> {
|
||||
}
|
||||
});
|
||||
let writer = self.log_writer_handle();
|
||||
self.worker_mut()
|
||||
self.engine_mut()
|
||||
.on_lifecycle_trace(move |turn, llm_call, label, data| {
|
||||
let entry = session_store::TraceEntry {
|
||||
ts: segment_log::now_millis(),
|
||||
@@ -604,7 +604,7 @@ impl<C: LlmClient + Clone + 'static, St: Store + Clone + 'static> Pod<C, St> {
|
||||
}
|
||||
|
||||
impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
/// Create a new Pod from a pre-built Worker and store.
|
||||
/// Create a new Pod from a pre-built Engine and store.
|
||||
///
|
||||
/// Callers must pre-resolve `cwd` (absolute) and build a [`Scope`]
|
||||
/// — typically via [`Scope::from_config`] when coming from a
|
||||
@@ -616,7 +616,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
/// should parse it themselves and call [`set_system_prompt_template`].
|
||||
pub async fn new(
|
||||
manifest: PodManifest,
|
||||
worker: Worker<C>,
|
||||
worker: Engine<C>,
|
||||
store: St,
|
||||
cwd: PathBuf,
|
||||
scope: Scope,
|
||||
@@ -631,7 +631,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
DelegationScope::from_config(&manifest.delegation_scope).map_err(PodError::Scope)?;
|
||||
let mut pod = Self {
|
||||
manifest,
|
||||
worker: Some(worker),
|
||||
engine: Some(worker),
|
||||
store,
|
||||
pod_metadata_writer: None,
|
||||
segment_state: SegmentState::new(session_id, segment_id, 0),
|
||||
@@ -825,17 +825,17 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
self.sink.clone()
|
||||
}
|
||||
|
||||
/// Direct access to the underlying Worker.
|
||||
pub fn worker(&self) -> &Worker<C, Mutable> {
|
||||
self.worker.as_ref().expect("worker taken during run")
|
||||
/// Direct access to the underlying Engine.
|
||||
pub fn engine(&self) -> &Engine<C, Mutable> {
|
||||
self.engine.as_ref().expect("worker taken during run")
|
||||
}
|
||||
|
||||
/// Mutable access to the underlying Worker.
|
||||
/// Mutable access to the underlying Engine.
|
||||
///
|
||||
/// Use this to register tools, hooks, or subscribers before calling
|
||||
/// [`run`](Self::run).
|
||||
pub fn worker_mut(&mut self) -> &mut Worker<C, Mutable> {
|
||||
self.worker.as_mut().expect("worker taken during run")
|
||||
pub fn engine_mut(&mut self) -> &mut Engine<C, Mutable> {
|
||||
self.engine.as_mut().expect("worker taken during run")
|
||||
}
|
||||
|
||||
/// Install enabled feature modules into the Pod host surfaces.
|
||||
@@ -843,7 +843,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
&mut self,
|
||||
registry: FeatureRegistryBuilder,
|
||||
) -> FeatureRegistryInstallReport {
|
||||
let worker = self.worker.as_mut().expect("worker taken during run");
|
||||
let worker = self.engine.as_mut().expect("worker taken during run");
|
||||
let active_workflow_committer = self.log_writer.clone().map(|writer| {
|
||||
Arc::new(move |entry| writer.commit_log_entry(entry))
|
||||
as active_workflow::LogEntryCommitter
|
||||
@@ -852,7 +852,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
self.active_workflows.clone(),
|
||||
active_workflow_committer,
|
||||
));
|
||||
let report = registry.install_into_worker(worker, &mut self.hook_builder);
|
||||
let report = registry.install_into_engine(worker, &mut self.hook_builder);
|
||||
report
|
||||
}
|
||||
|
||||
@@ -920,10 +920,10 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
.restore_from_history_and_extensions(&state.history, &state.extensions);
|
||||
let mut history = state.history;
|
||||
active_workflow::strip_rehydration_messages(&mut history);
|
||||
self.worker_mut().set_history(history);
|
||||
self.worker_mut().set_request_config(state.config);
|
||||
self.worker_mut().set_turn_count(state.turn_count);
|
||||
self.worker_mut()
|
||||
self.engine_mut().set_history(history);
|
||||
self.engine_mut().set_request_config(state.config);
|
||||
self.engine_mut().set_turn_count(state.turn_count);
|
||||
self.engine_mut()
|
||||
.set_last_run_interrupted(state.last_run_interrupted);
|
||||
self.user_segments = state.user_segments;
|
||||
*self.usage_history.lock().expect("usage_history poisoned") = state.usage_history;
|
||||
@@ -980,9 +980,9 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
self.write_pod_metadata_pending()
|
||||
}
|
||||
|
||||
/// Current history items held by the underlying Worker.
|
||||
/// Current history items held by the underlying Engine.
|
||||
pub fn history(&self) -> &[Item] {
|
||||
self.worker().history()
|
||||
self.engine().history()
|
||||
}
|
||||
|
||||
/// Snapshot of the cumulative LLM Usage measurement timeline.
|
||||
@@ -990,7 +990,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
/// One entry per LLM call. Restored on `restore` and appended in
|
||||
/// `persist_turn`. Used by token-accounting APIs in [`token_counter`].
|
||||
/// Returns a clone since the underlying vector is shared with hooks
|
||||
/// running on the Worker.
|
||||
/// running on the Engine.
|
||||
pub fn usage_history(&self) -> Vec<UsageRecord> {
|
||||
self.usage_history
|
||||
.lock()
|
||||
@@ -1033,7 +1033,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
/// Shared handle to the cumulative Usage history.
|
||||
///
|
||||
/// Callbacks that need live access to the latest measurements (e.g.
|
||||
/// the savings estimator that `attach_prune` installs on the Worker)
|
||||
/// the savings estimator that `attach_prune` installs on the Engine)
|
||||
/// clone this `Arc` and read it at request time. The handle outlives
|
||||
/// any individual run.
|
||||
///
|
||||
@@ -1057,7 +1057,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
|
||||
/// Handle to the synchronous `MetricsTracker` buffer.
|
||||
///
|
||||
/// Worker callbacks (e.g. the prune observer) clone this `Arc` and
|
||||
/// Engine callbacks (e.g. the prune observer) clone this `Arc` and
|
||||
/// `.push(metric)` into it; Pod drains it in `persist_turn` and
|
||||
/// writes each metric via `session_metrics::record_metric`.
|
||||
pub(crate) fn metrics_tracker_handle(
|
||||
@@ -1068,7 +1068,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
|
||||
/// Attach the session-scoped file-operation tracker from the builtin
|
||||
/// `tools` crate. Called by the Controller immediately after it
|
||||
/// registers the builtin tools on the Worker. Overwrites any
|
||||
/// registers the builtin tools on the Engine. Overwrites any
|
||||
/// previously attached tracker.
|
||||
pub fn attach_tracker(&mut self, tracker: tools::Tracker) {
|
||||
self.tracker = Some(tracker);
|
||||
@@ -1227,7 +1227,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
self.hook_builder.add_on_abort(hook);
|
||||
}
|
||||
|
||||
/// Install the hook-based interceptor on the Worker if not already done.
|
||||
/// Install the hook-based interceptor on the Engine if not already done.
|
||||
///
|
||||
/// When either compaction threshold (`threshold` or
|
||||
/// `request_threshold`) is configured in the manifest, allocates
|
||||
@@ -1246,7 +1246,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
.unwrap_or((None, None, manifest::defaults::COMPACT_RETAINED_TOKENS));
|
||||
|
||||
let tracker_for_usage = self.usage_tracker.clone();
|
||||
self.worker_mut().on_usage(move |event| {
|
||||
self.engine_mut().on_usage(move |event| {
|
||||
tracker_for_usage.record_usage(event);
|
||||
});
|
||||
|
||||
@@ -1285,7 +1285,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
self.active_workflows.clone(),
|
||||
)
|
||||
.with_usage_tracker(self.usage_tracker.clone());
|
||||
self.worker_mut().set_interceptor(interceptor);
|
||||
self.engine_mut().set_interceptor(interceptor);
|
||||
self.interceptor_installed = true;
|
||||
}
|
||||
}
|
||||
@@ -1293,7 +1293,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
/// Render the manifest-supplied instruction template exactly once,
|
||||
/// just before the first LLM turn, append the fixed trailing
|
||||
/// section (scope summary + optional AGENTS.md), and hand the
|
||||
/// resulting string to the Worker via `set_system_prompt`.
|
||||
/// resulting string to the Engine via `set_system_prompt`.
|
||||
/// Subsequent invocations are no-ops: the template field is
|
||||
/// consumed with `Option::take()`, so the materialised value
|
||||
/// persists across all later turns and compaction.
|
||||
@@ -1303,10 +1303,10 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
};
|
||||
let alerter = self.alerter.clone();
|
||||
let tool_names: Vec<String> = {
|
||||
let worker = self.worker.as_mut().expect("worker present");
|
||||
let worker = self.engine.as_mut().expect("worker present");
|
||||
// Materialise any pending tool factories so the template sees the
|
||||
// full list of tool names. Redundant with the flush inside
|
||||
// `Worker::lock()`; safe because `flush_pending` is idempotent.
|
||||
// `Engine::lock()`; safe because `flush_pending` is idempotent.
|
||||
worker.tool_server_handle().flush_pending();
|
||||
worker
|
||||
.tool_server_handle()
|
||||
@@ -1386,7 +1386,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
let rendered = template
|
||||
.render(&ctx)
|
||||
.map_err(|source| PodError::SystemPromptRender { source })?;
|
||||
self.worker
|
||||
self.engine
|
||||
.as_mut()
|
||||
.expect("worker present")
|
||||
.set_system_prompt(rendered);
|
||||
@@ -1461,30 +1461,30 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
.expect("usage_history poisoned")
|
||||
.len();
|
||||
EmptyTurnRollbackSnapshot {
|
||||
history_len: self.worker().history().len(),
|
||||
history_len: self.engine().history().len(),
|
||||
user_segments_len: self.user_segments.len(),
|
||||
entries_written: self.segment_state.entries_written(),
|
||||
sink_len: self.sink.len(),
|
||||
pending_attachments,
|
||||
usage_history_len,
|
||||
ai_activity_count: self.ai_activity_counter.load(Ordering::SeqCst),
|
||||
last_run_interrupted: self.worker().last_run_interrupted(),
|
||||
last_run_interrupted: self.engine().last_run_interrupted(),
|
||||
active_workflows: self.active_workflows.snapshot(),
|
||||
}
|
||||
}
|
||||
|
||||
fn should_rollback_empty_turn(
|
||||
&self,
|
||||
result: &Result<WorkerResult, WorkerError>,
|
||||
result: &Result<EngineResult, EngineError>,
|
||||
snapshot: &EmptyTurnRollbackSnapshot,
|
||||
) -> bool {
|
||||
if !matches!(result, Err(WorkerError::Cancelled)) {
|
||||
if !matches!(result, Err(EngineError::Cancelled)) {
|
||||
return false;
|
||||
}
|
||||
if self.ai_activity_counter.load(Ordering::SeqCst) != snapshot.ai_activity_count {
|
||||
return false;
|
||||
}
|
||||
!self.worker().history()[snapshot.history_len..]
|
||||
!self.engine().history()[snapshot.history_len..]
|
||||
.iter()
|
||||
.any(is_ai_materialized_item)
|
||||
}
|
||||
@@ -1493,8 +1493,8 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
&mut self,
|
||||
snapshot: EmptyTurnRollbackSnapshot,
|
||||
) -> Result<(), StoreError> {
|
||||
self.worker_mut().truncate_history(snapshot.history_len);
|
||||
self.worker_mut()
|
||||
self.engine_mut().truncate_history(snapshot.history_len);
|
||||
self.engine_mut()
|
||||
.set_last_run_interrupted(snapshot.last_run_interrupted);
|
||||
self.user_segments.truncate(snapshot.user_segments_len);
|
||||
*self
|
||||
@@ -1523,12 +1523,12 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
///
|
||||
/// `input` is a typed segment list (see [`protocol::Segment`]). The
|
||||
/// Pod flattens it into a single user-message string for the
|
||||
/// underlying Worker, expanding paste content inline, resolving file refs
|
||||
/// underlying Engine, expanding paste content inline, resolving file refs
|
||||
/// into adjacent attachments where possible, and surfacing alerts for
|
||||
/// unresolved refs / unsupported segment kinds.
|
||||
///
|
||||
/// If the between-turns compaction threshold is exceeded mid-run,
|
||||
/// the Worker is aborted, history is compacted, and execution resumes
|
||||
/// the Engine is aborted, history is compacted, and execution resumes
|
||||
/// automatically.
|
||||
pub async fn run(&mut self, input: Vec<Segment>) -> Result<PodRunResult, PodError> {
|
||||
// Validate workflow invocations up front so an invalid slug
|
||||
@@ -1547,7 +1547,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
// `last_run_interrupted` flag; `Pod::resume` reuses the prior
|
||||
// context via a different entry point and never triggers this
|
||||
// path.
|
||||
if self.worker.as_ref().unwrap().last_run_interrupted() {
|
||||
if self.engine.as_ref().unwrap().last_run_interrupted() {
|
||||
self.apply_interrupt_prep()?;
|
||||
}
|
||||
|
||||
@@ -1575,7 +1575,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
// workflow invocations to system messages stashed for the
|
||||
// PodInterceptor to attach right after the user message. File and
|
||||
// Knowledge failures are non-fatal alerts; explicit workflow invocation
|
||||
// failures abort before the Worker sees the turn.
|
||||
// failures abort before the Engine sees the turn.
|
||||
let mut attachments = self.resolve_file_refs(&input);
|
||||
attachments.extend(self.resolve_knowledge_refs(&input));
|
||||
attachments.extend(self.resolve_workflow_invocations(&input)?);
|
||||
@@ -1594,13 +1594,13 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
.expect("pending_attachments poisoned") = attachments;
|
||||
}
|
||||
|
||||
let history_before = self.worker.as_ref().unwrap().history().len();
|
||||
let history_before = self.engine.as_ref().unwrap().history().len();
|
||||
|
||||
// lock → run → unlock
|
||||
let worker = self.worker.take().expect("worker taken during run");
|
||||
let worker = self.engine.take().expect("worker taken during run");
|
||||
let mut locked = worker.lock();
|
||||
let result = locked.run(flattened).await;
|
||||
self.worker = Some(locked.unlock());
|
||||
self.engine = Some(locked.unlock());
|
||||
|
||||
if self.should_rollback_empty_turn(&result, &rollback_snapshot) {
|
||||
self.rollback_empty_turn(rollback_snapshot)?;
|
||||
@@ -1844,11 +1844,11 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
.map_err(PodError::from)?;
|
||||
|
||||
let closures = crate::interrupt_prep::orphan_tool_result_closures(
|
||||
self.worker().history(),
|
||||
self.engine().history(),
|
||||
&tool_result_summary,
|
||||
);
|
||||
if !closures.is_empty() {
|
||||
self.worker_mut().append_history(closures);
|
||||
self.engine_mut().append_history(closures);
|
||||
}
|
||||
self.commit_entry(LogEntry::SystemItem {
|
||||
ts: segment_log::now_millis(),
|
||||
@@ -1856,8 +1856,8 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
body: system_note.clone(),
|
||||
},
|
||||
})?;
|
||||
self.worker_mut()
|
||||
.append_history(std::iter::once(llm_worker::Item::system_message(
|
||||
self.engine_mut()
|
||||
.append_history(std::iter::once(llm_engine::Item::system_message(
|
||||
system_note,
|
||||
)));
|
||||
Ok(())
|
||||
@@ -1871,12 +1871,12 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
/// The explicit `PausedTurnAbandoned` marker preserves durable lifecycle
|
||||
/// semantics without claiming another `run` / `resume` completed.
|
||||
pub fn cancel_paused_turn(&mut self) -> Result<(), PodError> {
|
||||
if !self.worker().last_run_interrupted() {
|
||||
if !self.engine().last_run_interrupted() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.apply_interrupt_prep()?;
|
||||
self.worker_mut().set_last_run_interrupted(false);
|
||||
self.engine_mut().set_last_run_interrupted(false);
|
||||
self.commit_entry(LogEntry::PausedTurnAbandoned {
|
||||
ts: segment_log::now_millis(),
|
||||
})?;
|
||||
@@ -1920,7 +1920,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Flatten a typed segment list into the single string the Worker
|
||||
/// Flatten a typed segment list into the single string the Engine
|
||||
/// receives as the user message, and emit user-facing alerts for
|
||||
/// segments that fall through to placeholder (knowledge / workflow
|
||||
/// refs without a resolver, or unknown variants from a newer client).
|
||||
@@ -1964,7 +1964,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
/// history. The `PodInterceptor::pre_llm_request` drains the
|
||||
/// pending-notification buffer and injects each entry as an
|
||||
/// `Item::system_message` into the per-request context, then the
|
||||
/// Worker's resume path issues the LLM request without a new
|
||||
/// Engine's resume path issues the LLM request without a new
|
||||
/// user turn.
|
||||
pub async fn run_for_notification(
|
||||
&mut self,
|
||||
@@ -1990,12 +1990,12 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
trigger: kind,
|
||||
})?;
|
||||
|
||||
let history_before = self.worker.as_ref().unwrap().history().len();
|
||||
let history_before = self.engine.as_ref().unwrap().history().len();
|
||||
|
||||
let worker = self.worker.take().expect("worker taken during run");
|
||||
let worker = self.engine.take().expect("worker taken during run");
|
||||
let mut locked = worker.lock();
|
||||
let result = locked.resume().await;
|
||||
self.worker = Some(locked.unlock());
|
||||
self.engine = Some(locked.unlock());
|
||||
|
||||
self.handle_worker_result(result, history_before).await
|
||||
}
|
||||
@@ -2004,13 +2004,13 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
pub async fn resume(&mut self) -> Result<PodRunResult, PodError> {
|
||||
self.prepare_for_run().await?;
|
||||
|
||||
let history_before = self.worker.as_ref().unwrap().history().len();
|
||||
let history_before = self.engine.as_ref().unwrap().history().len();
|
||||
|
||||
// lock → resume → unlock
|
||||
let worker = self.worker.take().expect("worker taken during run");
|
||||
let worker = self.engine.take().expect("worker taken during run");
|
||||
let mut locked = worker.lock();
|
||||
let result = locked.resume().await;
|
||||
self.worker = Some(locked.unlock());
|
||||
self.engine = Some(locked.unlock());
|
||||
|
||||
self.handle_worker_result(result, history_before).await
|
||||
}
|
||||
@@ -2025,7 +2025,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
/// calls fall through to entry-count comparison, which auto-forks
|
||||
/// when another writer has appended behind our back.
|
||||
fn ensure_segment_head(&mut self) -> Result<(), PodError> {
|
||||
let w = self.worker.as_ref().unwrap();
|
||||
let w = self.engine.as_ref().unwrap();
|
||||
let loc = self.segment_state.location();
|
||||
let entries_written = self.segment_state.entries_written();
|
||||
if entries_written == 0 {
|
||||
@@ -2090,7 +2090,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle Worker result: always persist the turn first, then if
|
||||
/// Handle Engine result: always persist the turn first, then if
|
||||
/// `Yielded`, perform compaction and resume.
|
||||
///
|
||||
/// Persisting before compaction ensures that if compact fails, the
|
||||
@@ -2098,12 +2098,12 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
/// `Yielded`), so restore remains consistent.
|
||||
async fn handle_worker_result(
|
||||
&mut self,
|
||||
result: Result<WorkerResult, WorkerError>,
|
||||
result: Result<EngineResult, EngineError>,
|
||||
history_before: usize,
|
||||
) -> Result<PodRunResult, PodError> {
|
||||
self.persist_turn(history_before, &result).await?;
|
||||
|
||||
if matches!(result, Ok(WorkerResult::Yielded)) {
|
||||
if matches!(result, Ok(EngineResult::Yielded)) {
|
||||
return self.do_compact_and_resume().await;
|
||||
}
|
||||
|
||||
@@ -2112,7 +2112,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
state.set_just_compacted(false);
|
||||
}
|
||||
}
|
||||
result.map(PodRunResult::from).map_err(PodError::Worker)
|
||||
result.map(PodRunResult::from).map_err(PodError::Engine)
|
||||
}
|
||||
|
||||
/// Perform compaction after a `compact_needed` abort and resume execution.
|
||||
@@ -2218,7 +2218,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
/// Run an explicit user-requested compaction between turns.
|
||||
///
|
||||
/// The controller only calls this while Idle. Paused turns keep their
|
||||
/// interrupted Worker state intact and are intentionally rejected before
|
||||
/// interrupted Engine state intact and are intentionally rejected before
|
||||
/// this method is reached.
|
||||
pub async fn manual_compact(&mut self) -> Result<ManualCompactResult, PodError> {
|
||||
if self.manifest.compaction.is_none() {
|
||||
@@ -2294,7 +2294,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
async fn persist_turn(
|
||||
&mut self,
|
||||
history_before: usize,
|
||||
result: &Result<WorkerResult, WorkerError>,
|
||||
result: &Result<EngineResult, EngineError>,
|
||||
) -> Result<(), StoreError> {
|
||||
// Per-item commits for AssistantItem / ToolResult / SystemItem
|
||||
// entries are expected to have landed synchronously: the
|
||||
@@ -2310,7 +2310,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
// slice from `history_before` inline so the test's
|
||||
// `restore`-style assertions still see entries on disk.
|
||||
if !self.history_persistence_wired {
|
||||
let new_items: Vec<Item> = self.worker.as_ref().unwrap().history()[history_before..]
|
||||
let new_items: Vec<Item> = self.engine.as_ref().unwrap().history()[history_before..]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
@@ -2322,7 +2322,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
if matches!(
|
||||
item,
|
||||
Item::Message {
|
||||
role: llm_worker::Role::System,
|
||||
role: llm_engine::Role::System,
|
||||
..
|
||||
}
|
||||
) {
|
||||
@@ -2333,7 +2333,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
}
|
||||
}
|
||||
|
||||
let turn_count = self.worker.as_ref().unwrap().turn_count();
|
||||
let turn_count = self.engine.as_ref().unwrap().turn_count();
|
||||
self.commit_entry(LogEntry::TurnEnd {
|
||||
ts: segment_log::now_millis(),
|
||||
turn_count,
|
||||
@@ -2392,7 +2392,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
.push(record);
|
||||
}
|
||||
|
||||
let interrupted = self.worker.as_ref().unwrap().last_run_interrupted();
|
||||
let interrupted = self.engine.as_ref().unwrap().last_run_interrupted();
|
||||
match result {
|
||||
Ok(r) => {
|
||||
self.commit_entry(LogEntry::RunCompleted {
|
||||
@@ -2414,10 +2414,10 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
}
|
||||
|
||||
/// Compact the current session by summarising history via a
|
||||
/// disposable Worker, then replacing history with
|
||||
/// disposable Engine, then replacing history with
|
||||
/// `[summary, ...recent_turns]` and creating a new session.
|
||||
///
|
||||
/// The summary Worker uses:
|
||||
/// The summary Engine uses:
|
||||
/// - `compaction.model` from the manifest if configured, or
|
||||
/// - a clone of the main LlmClient via `clone_boxed()`.
|
||||
///
|
||||
@@ -2435,7 +2435,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
// within `retained_tokens`. Item-granular, turn boundaries ignored.
|
||||
let cut = self.split_for_retained(retained_tokens);
|
||||
|
||||
let worker = self.worker.as_ref().expect("worker taken during run");
|
||||
let worker = self.engine.as_ref().expect("worker taken during run");
|
||||
let history = worker.history();
|
||||
let retain_from = cut.index.min(history.len());
|
||||
let mut retained_items = history[retain_from..].to_vec();
|
||||
@@ -2537,7 +2537,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
);
|
||||
}
|
||||
|
||||
// Worker-side state collected by the compact worker's tool calls.
|
||||
// Engine-side state collected by the compact worker's tool calls.
|
||||
let ctx = Arc::new(std::sync::Mutex::new(CompactWorkerContext::with_budget(
|
||||
auto_read_budget,
|
||||
)));
|
||||
@@ -2553,7 +2553,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
.prompts
|
||||
.compact_system()
|
||||
.map_err(PodError::PromptCatalog)?;
|
||||
let mut summary_worker = Worker::new(summary_client).system_prompt(summary_system_prompt);
|
||||
let mut summary_worker = Engine::new(summary_client).system_prompt(summary_system_prompt);
|
||||
summary_worker.set_cache_key(Some(self.segment_id().to_string()));
|
||||
|
||||
// Occupancy-based input-token meter + interceptor. The tracker pairs
|
||||
@@ -2594,8 +2594,8 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
let out = summary_worker
|
||||
.run(summary_input.text)
|
||||
.await
|
||||
.map_err(PodError::Worker)?;
|
||||
let mut locked_worker = out.worker;
|
||||
.map_err(PodError::Engine)?;
|
||||
let mut locked_engine = out.engine;
|
||||
|
||||
// Guard: nudge the worker once more if the expected outputs
|
||||
// (summary, and any auto-read nominations when default refs
|
||||
@@ -2624,7 +2624,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
}
|
||||
};
|
||||
if let Some(prompt) = nudge {
|
||||
let _ = locked_worker.run(prompt).await.map_err(PodError::Worker)?;
|
||||
let _ = locked_engine.run(prompt).await.map_err(PodError::Engine)?;
|
||||
}
|
||||
|
||||
let mut final_ctx = ctx.lock().expect("compact ctx poisoned").clone();
|
||||
@@ -2639,7 +2639,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
{summary_max_tokens}). Rewrite it now with `write_summary`, preserving the \
|
||||
same five sections but making it concise. Target ≈{summary_target_tokens} tokens."
|
||||
);
|
||||
let _ = locked_worker.run(prompt).await.map_err(PodError::Worker)?;
|
||||
let _ = locked_engine.run(prompt).await.map_err(PodError::Engine)?;
|
||||
final_ctx = ctx.lock().expect("compact ctx poisoned").clone();
|
||||
summary_text = final_ctx
|
||||
.summary
|
||||
@@ -2728,7 +2728,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
self.task_feature.snapshot_overview(),
|
||||
task_snapshot_text.clone(),
|
||||
));
|
||||
let result_estimate = llm_worker::token_counter::total_tokens(&new_history, &[]);
|
||||
let result_estimate = llm_engine::token_counter::total_tokens(&new_history, &[]);
|
||||
if result_context_max_tokens > 0 && result_estimate.tokens > result_context_max_tokens {
|
||||
return Err(PodError::CompactResultContextTooLarge {
|
||||
tokens: result_estimate.tokens,
|
||||
@@ -2744,8 +2744,8 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
// { compacted_from }` and reset their view.
|
||||
let new_segment_id = session_store::new_segment_id();
|
||||
let old_loc = self.segment_state.location();
|
||||
let source_turn_count = self.worker.as_ref().unwrap().turn_count();
|
||||
let w = self.worker.as_ref().unwrap();
|
||||
let source_turn_count = self.engine.as_ref().unwrap().turn_count();
|
||||
let w = self.engine.as_ref().unwrap();
|
||||
let entry = LogEntry::SegmentStart {
|
||||
ts: segment_log::now_millis(),
|
||||
session_id: old_loc.session_id,
|
||||
@@ -2798,13 +2798,13 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
self.user_segments.drain(..drop_n);
|
||||
}
|
||||
|
||||
self.worker.as_mut().unwrap().set_history(new_history);
|
||||
self.engine.as_mut().unwrap().set_history(new_history);
|
||||
// Compaction-introduced system messages are part of the new
|
||||
// SegmentStart's history (broadcast above) — clients derive
|
||||
// their blocks from `SegmentStart.history`. No per-item
|
||||
// broadcast is required.
|
||||
let _ = &compact_introduced_system_messages;
|
||||
let worker = self.worker.as_mut().unwrap();
|
||||
let worker = self.engine.as_mut().unwrap();
|
||||
// Anchor the prompt cache at the summary item so that Anthropic
|
||||
// can place a durable `cache_control` breakpoint there — our
|
||||
// compact layout guarantees history[0] is the summary.
|
||||
@@ -2833,7 +2833,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
Ok(new_segment_id)
|
||||
}
|
||||
|
||||
/// Build the LlmClient for the compactor Worker.
|
||||
/// Build the LlmClient for the compactor Engine.
|
||||
///
|
||||
/// Uses `compaction.model` from manifest if set, otherwise clones
|
||||
/// the main client.
|
||||
@@ -2844,11 +2844,11 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
return Ok(client);
|
||||
}
|
||||
}
|
||||
let worker = self.worker.as_ref().expect("worker taken during run");
|
||||
let worker = self.engine.as_ref().expect("worker taken during run");
|
||||
Ok(worker.client().clone_boxed())
|
||||
}
|
||||
|
||||
/// Build the LlmClient for the extract (memory.extract) Worker.
|
||||
/// Build the LlmClient for the extract (memory.extract) Engine.
|
||||
///
|
||||
/// Uses `memory.extract_model` from manifest if set, otherwise clones
|
||||
/// the main client.
|
||||
@@ -2860,7 +2860,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
let client = provider::build_client(m)?;
|
||||
return Ok(client);
|
||||
}
|
||||
let worker = self.worker.as_ref().expect("worker taken during run");
|
||||
let worker = self.engine.as_ref().expect("worker taken during run");
|
||||
Ok(worker.client().clone_boxed())
|
||||
}
|
||||
|
||||
@@ -3027,9 +3027,9 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
}
|
||||
|
||||
let current_history_len = self
|
||||
.worker
|
||||
.engine
|
||||
.as_ref()
|
||||
.expect("worker present")
|
||||
.expect("engine present")
|
||||
.history()
|
||||
.len();
|
||||
if current_history_len <= processed_history_len {
|
||||
@@ -3108,7 +3108,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
None,
|
||||
);
|
||||
|
||||
let items_to_extract = self.worker.as_ref().expect("worker present").history()
|
||||
let items_to_extract = self.engine.as_ref().expect("worker present").history()
|
||||
[processed_history_len..current_history_len]
|
||||
.to_vec();
|
||||
|
||||
@@ -3147,7 +3147,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
return Err(PodError::PromptCatalog(err));
|
||||
}
|
||||
};
|
||||
let mut extract_worker = Worker::new(client).system_prompt(extract_system_prompt);
|
||||
let mut extract_worker = Engine::new(client).system_prompt(extract_system_prompt);
|
||||
extract_worker.set_cache_key(Some(self.segment_id().to_string()));
|
||||
|
||||
extract_worker.set_max_turns(extract_worker_max_turns);
|
||||
@@ -3179,7 +3179,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
Some(extract_audit_base),
|
||||
None,
|
||||
);
|
||||
return Err(PodError::Worker(err));
|
||||
return Err(PodError::Engine(err));
|
||||
}
|
||||
|
||||
let payload = ctx.take_payload().unwrap_or_else(|| {
|
||||
@@ -3271,7 +3271,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
Ok(ExtractDecision::Completed)
|
||||
}
|
||||
|
||||
/// Build the LlmClient for the consolidation (memory.consolidation) Worker.
|
||||
/// Build the LlmClient for the consolidation (memory.consolidation) Engine.
|
||||
///
|
||||
/// Uses `memory.consolidation_model` from manifest if set, otherwise
|
||||
/// clones the main client. Mirrors [`build_extractor_client`].
|
||||
@@ -3283,7 +3283,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
let client = provider::build_client(m)?;
|
||||
return Ok(client);
|
||||
}
|
||||
let worker = self.worker.as_ref().expect("worker taken during run");
|
||||
let worker = self.engine.as_ref().expect("worker taken during run");
|
||||
Ok(worker.client().clone_boxed())
|
||||
}
|
||||
|
||||
@@ -3534,7 +3534,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
return Err(PodError::PromptCatalog(e));
|
||||
}
|
||||
};
|
||||
let mut worker = Worker::new(client).system_prompt(consolidation_system_prompt);
|
||||
let mut worker = Engine::new(client).system_prompt(consolidation_system_prompt);
|
||||
worker.set_cache_key(Some(self.segment_id().to_string()));
|
||||
|
||||
let usage_capture = Arc::new(Mutex::new(None));
|
||||
@@ -3548,7 +3548,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
|
||||
// Memory tools are self-contained — they bypass ScopedFs and write
|
||||
// directly under the workspace via WorkspaceLayout. Resident section
|
||||
// injection is a Pod-level concern; this disposable Worker is built
|
||||
// injection is a Pod-level concern; this disposable Engine is built
|
||||
// without it by construction, in keeping with `docs/plan/memory.md`
|
||||
// §Consolidation のKnowledgeアクセス (agent pulls knowledge through
|
||||
// the search tool instead of via system-prompt residency).
|
||||
@@ -3616,14 +3616,14 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
None,
|
||||
Some(base_consolidation),
|
||||
);
|
||||
Err(PodError::Worker(e))
|
||||
Err(PodError::Engine(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lifecycle_status_for_worker_error(err: &WorkerError) -> memory::audit::WorkerLifecycleStatus {
|
||||
if matches!(err, WorkerError::Cancelled) {
|
||||
fn lifecycle_status_for_worker_error(err: &EngineError) -> memory::audit::WorkerLifecycleStatus {
|
||||
if matches!(err, EngineError::Cancelled) {
|
||||
memory::audit::WorkerLifecycleStatus::Cancelled
|
||||
} else {
|
||||
memory::audit::WorkerLifecycleStatus::Failed
|
||||
@@ -3631,7 +3631,7 @@ fn lifecycle_status_for_worker_error(err: &WorkerError) -> memory::audit::Worker
|
||||
}
|
||||
|
||||
fn usage_audit_from_event(
|
||||
event: &llm_worker::llm_client::event::UsageEvent,
|
||||
event: &llm_engine::llm_client::event::UsageEvent,
|
||||
) -> memory::audit::UsageAudit {
|
||||
memory::audit::UsageAudit {
|
||||
input_tokens: event.input_tokens,
|
||||
@@ -3854,14 +3854,14 @@ where
|
||||
segment_id,
|
||||
)?;
|
||||
|
||||
let mut worker = Worker::new(common.client);
|
||||
let mut worker = Engine::new(common.client);
|
||||
apply_worker_manifest(&mut worker, &manifest.worker);
|
||||
worker.set_cache_key(Some(segment_id.to_string()));
|
||||
let pod_metadata_writer = Some(pod_metadata_writer_for_store(&store));
|
||||
|
||||
let mut pod = Self {
|
||||
manifest,
|
||||
worker: Some(worker),
|
||||
engine: Some(worker),
|
||||
store,
|
||||
pod_metadata_writer,
|
||||
segment_state: SegmentState::new(session_id, segment_id, 0),
|
||||
@@ -3964,14 +3964,14 @@ where
|
||||
segment_id,
|
||||
)?;
|
||||
|
||||
let mut worker = Worker::new(common.client);
|
||||
let mut worker = Engine::new(common.client);
|
||||
apply_worker_manifest(&mut worker, &manifest.worker);
|
||||
worker.set_cache_key(Some(segment_id.to_string()));
|
||||
let pod_metadata_writer = Some(pod_metadata_writer_for_store(&store));
|
||||
|
||||
let mut pod = Self {
|
||||
manifest,
|
||||
worker: Some(worker),
|
||||
engine: Some(worker),
|
||||
store,
|
||||
pod_metadata_writer,
|
||||
segment_state: SegmentState::new(session_id, segment_id, 0),
|
||||
@@ -4087,7 +4087,7 @@ where
|
||||
/// Restore a Pod from an existing session log.
|
||||
///
|
||||
/// Uses the resolved manifest supplied by the caller, seeds a
|
||||
/// fresh Worker from the source session's `RestoredState`, and
|
||||
/// fresh Engine from the source session's `RestoredState`, and
|
||||
/// reuses the same `segment_id` so subsequent turns append to the
|
||||
/// source jsonl as a continuation of the same conversation.
|
||||
///
|
||||
@@ -4171,7 +4171,7 @@ where
|
||||
|
||||
// Build the worker and apply the manifest defaults first, then
|
||||
// overwrite the pieces the session log is authoritative for.
|
||||
let mut worker = Worker::new(common.client);
|
||||
let mut worker = Engine::new(common.client);
|
||||
apply_worker_manifest(&mut worker, &manifest.worker);
|
||||
worker.set_cache_key(Some(segment_id.to_string()));
|
||||
if let Some(ref prompt) = state.system_prompt {
|
||||
@@ -4184,7 +4184,7 @@ where
|
||||
let anchored_on_summary = matches!(
|
||||
state.history.first(),
|
||||
Some(Item::Message {
|
||||
role: llm_worker::Role::System,
|
||||
role: llm_engine::Role::System,
|
||||
..
|
||||
})
|
||||
);
|
||||
@@ -4206,7 +4206,7 @@ where
|
||||
|
||||
let mut pod = Self {
|
||||
manifest,
|
||||
worker: Some(worker),
|
||||
engine: Some(worker),
|
||||
store,
|
||||
pod_metadata_writer,
|
||||
segment_state: SegmentState::new(session_id, segment_id, state.entries_count),
|
||||
@@ -4327,12 +4327,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply worker-level manifest settings to a Worker.
|
||||
/// Apply worker-level manifest settings to a Engine.
|
||||
///
|
||||
/// Note: `system_prompt` is intentionally not applied here. It is a
|
||||
/// minijinja template that is parsed by `Pod::from_manifest` and
|
||||
/// rendered once at first turn in `ensure_system_prompt_materialized`.
|
||||
pub fn apply_worker_manifest<C: LlmClient>(worker: &mut Worker<C>, wm: &WorkerManifest) {
|
||||
pub fn apply_worker_manifest<C: LlmClient>(worker: &mut Engine<C>, wm: &WorkerManifest) {
|
||||
worker.set_request_config(request_config_from_worker_manifest(wm));
|
||||
worker.set_max_turns(wm.max_turns.map(|n| n.get()));
|
||||
worker.set_tool_output_limits(Some(ToolOutputLimits {
|
||||
@@ -4415,15 +4415,15 @@ pub enum ManualCompactResult {
|
||||
Skipped { message: String },
|
||||
}
|
||||
|
||||
impl From<WorkerResult> for PodRunResult {
|
||||
fn from(r: WorkerResult) -> Self {
|
||||
impl From<EngineResult> for PodRunResult {
|
||||
fn from(r: EngineResult) -> Self {
|
||||
match r {
|
||||
WorkerResult::Finished => PodRunResult::Finished,
|
||||
WorkerResult::Paused => PodRunResult::Paused,
|
||||
WorkerResult::LimitReached => PodRunResult::LimitReached,
|
||||
EngineResult::Finished => PodRunResult::Finished,
|
||||
EngineResult::Paused => PodRunResult::Paused,
|
||||
EngineResult::LimitReached => PodRunResult::LimitReached,
|
||||
// Yielded is internal to Pod: it's always caught by
|
||||
// handle_worker_result and never converted to PodRunResult.
|
||||
WorkerResult::Yielded => unreachable!("Yielded never converts to PodRunResult"),
|
||||
EngineResult::Yielded => unreachable!("Yielded never converts to PodRunResult"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4644,9 +4644,9 @@ fn message_overview_entry(idx: usize, item: &Item, max_chars: usize) -> Option<S
|
||||
return None;
|
||||
};
|
||||
let role_label = match role {
|
||||
llm_worker::Role::User => "User",
|
||||
llm_worker::Role::Assistant => "Assistant",
|
||||
llm_worker::Role::System => "System",
|
||||
llm_engine::Role::User => "User",
|
||||
llm_engine::Role::Assistant => "Assistant",
|
||||
llm_engine::Role::System => "System",
|
||||
};
|
||||
let text: String = content
|
||||
.iter()
|
||||
@@ -4785,7 +4785,7 @@ fn preview_segments(segments: &[Segment]) -> String {
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum PodError {
|
||||
#[error(transparent)]
|
||||
Worker(#[from] WorkerError),
|
||||
Engine(#[from] EngineError),
|
||||
|
||||
#[error(transparent)]
|
||||
Store(#[from] StoreError),
|
||||
@@ -5644,19 +5644,19 @@ mod build_summary_prompt_tests {
|
||||
impl LlmClient for NoopClient {
|
||||
async fn stream(
|
||||
&self,
|
||||
_request: llm_worker::llm_client::Request,
|
||||
_request: llm_engine::llm_client::Request,
|
||||
) -> Result<
|
||||
std::pin::Pin<
|
||||
Box<
|
||||
dyn futures::Stream<
|
||||
Item = Result<
|
||||
llm_worker::llm_client::event::Event,
|
||||
llm_worker::llm_client::ClientError,
|
||||
llm_engine::llm_client::event::Event,
|
||||
llm_engine::llm_client::ClientError,
|
||||
>,
|
||||
> + Send,
|
||||
>,
|
||||
>,
|
||||
llm_worker::llm_client::ClientError,
|
||||
llm_engine::llm_client::ClientError,
|
||||
> {
|
||||
Ok(Box::pin(futures::stream::empty()))
|
||||
}
|
||||
@@ -5679,7 +5679,7 @@ mod build_summary_prompt_tests {
|
||||
let cwd = dir.path().join("workspace");
|
||||
std::fs::create_dir_all(&cwd).unwrap();
|
||||
let scope = Scope::writable(&cwd).unwrap();
|
||||
let mut pod = Pod::new(manifest, Worker::new(NoopClient), store, cwd, scope)
|
||||
let mut pod = Pod::new(manifest, Engine::new(NoopClient), store, cwd, scope)
|
||||
.await
|
||||
.unwrap();
|
||||
pod.ensure_segment_head().unwrap();
|
||||
@@ -5794,9 +5794,9 @@ mod build_summary_prompt_tests {
|
||||
.len(),
|
||||
expected_truncate_entries
|
||||
);
|
||||
assert_eq!(pod.worker().history().len(), 1);
|
||||
assert_eq!(pod.engine().history().len(), 1);
|
||||
assert_eq!(
|
||||
pod.worker().history()[0].as_text().unwrap(),
|
||||
pod.engine().history()[0].as_text().unwrap(),
|
||||
"first message"
|
||||
);
|
||||
}
|
||||
@@ -5824,18 +5824,18 @@ mod build_summary_prompt_tests {
|
||||
let cwd = dir.path().join("workspace");
|
||||
std::fs::create_dir_all(&cwd).unwrap();
|
||||
let scope = Scope::writable(&cwd).unwrap();
|
||||
let mut pod = Pod::new(manifest, Worker::new(NoopClient), store, cwd, scope)
|
||||
let mut pod = Pod::new(manifest, Engine::new(NoopClient), store, cwd, scope)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
pod.ensure_segment_head().unwrap();
|
||||
pod.wire_history_persistence();
|
||||
pod.worker_mut()
|
||||
pod.engine_mut()
|
||||
.set_history(vec![Item::tool_call("call-1", "Read", "{}")]);
|
||||
|
||||
pod.apply_interrupt_prep().unwrap();
|
||||
|
||||
let history = pod.worker().history();
|
||||
let history = pod.engine().history();
|
||||
assert_eq!(history.len(), 3);
|
||||
assert!(matches!(history[1], Item::ToolResult { ref call_id, .. } if call_id == "call-1"));
|
||||
assert!(matches!(
|
||||
@@ -5950,7 +5950,7 @@ mod build_summary_prompt_tests {
|
||||
let mut manifest = minimal_manifest_with_skills(vec![]);
|
||||
manifest.memory = memory_config;
|
||||
let scope = Scope::writable(&cwd).unwrap();
|
||||
let mut pod = Pod::new(manifest, Worker::new(NoopClient), store, cwd.clone(), scope)
|
||||
let mut pod = Pod::new(manifest, Engine::new(NoopClient), store, cwd.clone(), scope)
|
||||
.await
|
||||
.unwrap();
|
||||
pod.memory_layout = pod
|
||||
@@ -5975,7 +5975,7 @@ mod build_summary_prompt_tests {
|
||||
.unwrap();
|
||||
pod.set_system_prompt_template(template);
|
||||
pod.ensure_system_prompt_materialized().unwrap();
|
||||
pod.worker().get_system_prompt().unwrap().to_string()
|
||||
pod.engine().get_system_prompt().unwrap().to_string()
|
||||
}
|
||||
|
||||
fn summary_doc(body: &str) -> String {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Central catalog of Pod-level prompt strings.
|
||||
//!
|
||||
//! Prompts that Pod injects into a Worker (compaction system prompt,
|
||||
//! Prompts that Pod injects into a Engine (compaction system prompt,
|
||||
//! notification wrapper, interrupt notes, system-prompt trailing
|
||||
//! sections, AGENTS.md truncation notice, ...) are enumerated by
|
||||
//! [`PodPrompt`] and rendered through a single [`PromptCatalog`]. Direct
|
||||
@@ -59,11 +59,11 @@ const INTERNAL_TOML: &str = include_str!("../../../../resources/prompts/internal
|
||||
/// `resources/prompts/internal.toml`; the build fails otherwise.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum PodPrompt {
|
||||
/// System prompt of the compaction (summary) Worker.
|
||||
/// System prompt of the compaction (summary) Engine.
|
||||
CompactSystem,
|
||||
/// System prompt of the memory extract Worker.
|
||||
/// System prompt of the memory extract Engine.
|
||||
MemoryExtractSystem,
|
||||
/// System prompt of the memory consolidation (integration + tidy) Worker.
|
||||
/// System prompt of the memory consolidation (integration + tidy) Engine.
|
||||
MemoryConsolidationSystem,
|
||||
/// Wrapper around an incoming `Method::Notify` message injected into
|
||||
/// the next LLM request context as a transient system message.
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
//! the rendered body is appended with a fixed trailing section carrying
|
||||
//! the Pod's `Scope` summary, (if present) the project's `AGENTS.md`
|
||||
//! contents, resident memory sections, and conditional Pod-orchestration
|
||||
//! guidance, then the whole string is handed to the Worker via
|
||||
//! guidance, then the whole string is handed to the Engine via
|
||||
//! `set_system_prompt`. Subsequent turns and compactions reuse that
|
||||
//! materialised string verbatim.
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ impl Default for SegmentLogSink {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use llm_worker::llm_client::RequestConfig;
|
||||
use llm_engine::llm_client::RequestConfig;
|
||||
use session_store::segment_log::now_millis;
|
||||
|
||||
fn session_start() -> LogEntry {
|
||||
|
||||
@@ -79,7 +79,7 @@ impl Hook<PostToolCall> for TicketIntakeReadyShutdownHook {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use llm_worker::tool::ToolOutput;
|
||||
use llm_engine::tool::ToolOutput;
|
||||
|
||||
fn tool_result(name: &str, is_error: bool) -> ToolResultSummary {
|
||||
ToolResultSummary {
|
||||
|
||||
@@ -14,8 +14,8 @@ use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use llm_worker::llm_client::types::{ContentPart, Item, Role};
|
||||
use llm_worker::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput};
|
||||
use llm_engine::llm_client::types::{ContentPart, Item, Role};
|
||||
use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput};
|
||||
use protocol::stream::{JsonLineReader, JsonLineWriter};
|
||||
use protocol::{ErrorCode, Event, InvokeKind, Method};
|
||||
use serde::Deserialize;
|
||||
@@ -65,7 +65,7 @@ impl Tool for SendToPodTool {
|
||||
async fn execute(
|
||||
&self,
|
||||
input_json: &str,
|
||||
_ctx: llm_worker::tool::ToolExecutionContext,
|
||||
_ctx: llm_engine::tool::ToolExecutionContext,
|
||||
) -> Result<ToolOutput, ToolError> {
|
||||
let input: SendToPodInput = serde_json::from_str(input_json)
|
||||
.map_err(|e| ToolError::InvalidArgument(format!("invalid SendToPod input: {e}")))?;
|
||||
@@ -130,7 +130,7 @@ impl Tool for ReadPodOutputTool {
|
||||
async fn execute(
|
||||
&self,
|
||||
input_json: &str,
|
||||
_ctx: llm_worker::tool::ToolExecutionContext,
|
||||
_ctx: llm_engine::tool::ToolExecutionContext,
|
||||
) -> Result<ToolOutput, ToolError> {
|
||||
let input: NameInput = serde_json::from_str(input_json)
|
||||
.map_err(|e| ToolError::InvalidArgument(format!("invalid ReadPodOutput input: {e}")))?;
|
||||
@@ -208,7 +208,7 @@ impl Tool for StopPodTool {
|
||||
async fn execute(
|
||||
&self,
|
||||
input_json: &str,
|
||||
_ctx: llm_worker::tool::ToolExecutionContext,
|
||||
_ctx: llm_engine::tool::ToolExecutionContext,
|
||||
) -> Result<ToolOutput, ToolError> {
|
||||
let input: NameInput = serde_json::from_str(input_json)
|
||||
.map_err(|e| ToolError::InvalidArgument(format!("invalid StopPod input: {e}")))?;
|
||||
|
||||
@@ -13,7 +13,7 @@ use std::time::Duration;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use client::PodRuntimeCommand;
|
||||
use llm_worker::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput};
|
||||
use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput};
|
||||
use manifest::{
|
||||
CompactionConfigPartial, DelegationScope, FileUploadLimitsPartial, Permission,
|
||||
PermissionConfigPartial, PodManifest, PodManifestConfig, PodMetaConfig, ProfileDiscovery,
|
||||
@@ -301,7 +301,7 @@ impl Tool for SpawnPodTool {
|
||||
async fn execute(
|
||||
&self,
|
||||
input_json: &str,
|
||||
_ctx: llm_worker::tool::ToolExecutionContext,
|
||||
_ctx: llm_engine::tool::ToolExecutionContext,
|
||||
) -> Result<ToolOutput, ToolError> {
|
||||
let input: SpawnPodInput = serde_json::from_str(input_json)
|
||||
.map_err(|e| ToolError::InvalidArgument(format!("invalid SpawnPod input: {e}")))?;
|
||||
|
||||
@@ -245,7 +245,7 @@ mod tests {
|
||||
use crate::PodStatus;
|
||||
use crate::runtime::dir::RuntimeDir;
|
||||
use crate::spawn::registry::SpawnedPodRegistry;
|
||||
use llm_worker::tool::ToolOutput;
|
||||
use llm_engine::tool::ToolOutput;
|
||||
use pod_store::FsPodStore;
|
||||
use pod_store::PodMetadata;
|
||||
use protocol::stream::{JsonLineReader, JsonLineWriter};
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
//! dependency Knowledge bodies first, then the Workflow body. Resolution is
|
||||
//! strict for explicit user invocations: missing workflows, non-user-invocable
|
||||
//! workflows, and missing Knowledge requirements are returned as errors before
|
||||
//! the turn is handed to the Worker.
|
||||
//! the turn is handed to the Engine.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use llm_worker::Item;
|
||||
use llm_engine::Item;
|
||||
use memory::WorkspaceLayout;
|
||||
use memory::schema::split_frontmatter;
|
||||
use workflow_crate::{Slug, WorkflowRegistry};
|
||||
|
||||
@@ -12,10 +12,10 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use futures::Stream;
|
||||
use llm_worker::Worker;
|
||||
use llm_worker::llm_client::event::{Event as LlmEvent, ResponseStatus, StatusEvent};
|
||||
use llm_worker::llm_client::types::Item;
|
||||
use llm_worker::llm_client::{ClientError, LlmClient, Request};
|
||||
use llm_engine::Engine;
|
||||
use llm_engine::llm_client::event::{Event as LlmEvent, ResponseStatus, StatusEvent};
|
||||
use llm_engine::llm_client::types::Item;
|
||||
use llm_engine::llm_client::{ClientError, LlmClient, Request};
|
||||
use pod_store::{CombinedStore, FsPodStore, PodMetadataStore};
|
||||
use protocol::{Event, Method, RunResult};
|
||||
use session_store::{FsStore, LogEntry, Store};
|
||||
@@ -163,7 +163,7 @@ async fn make_pod_with_manifest(
|
||||
let scope = pod::Scope::writable(&pwd).unwrap();
|
||||
std::mem::forget(pwd_tmp);
|
||||
|
||||
let worker = Worker::new(client);
|
||||
let worker = Engine::new(client);
|
||||
let mut pod = Pod::new(manifest, worker, store, pwd, scope).await.unwrap();
|
||||
pod.enable_pod_metadata_write_through().unwrap();
|
||||
pod
|
||||
@@ -189,7 +189,7 @@ fn drain(rx: &mut broadcast::Receiver<Event>) -> Vec<Event> {
|
||||
/// `SegmentStart.history` carries, by reading the sink mirror directly.
|
||||
fn system_texts_in_sink_session_start(
|
||||
pod: &pod::Pod<
|
||||
impl llm_worker::llm_client::client::LlmClient + Clone + 'static,
|
||||
impl llm_engine::llm_client::client::LlmClient + Clone + 'static,
|
||||
impl session_store::Store + Clone + 'static,
|
||||
>,
|
||||
) -> Vec<String> {
|
||||
@@ -202,7 +202,7 @@ fn system_texts_in_sink_session_start(
|
||||
let item: Item = logged.into();
|
||||
match item {
|
||||
Item::Message {
|
||||
role: llm_worker::Role::System,
|
||||
role: llm_engine::Role::System,
|
||||
content,
|
||||
..
|
||||
} => Some(
|
||||
@@ -462,7 +462,7 @@ async fn mid_turn_compact_success_broadcasts_start_and_done() {
|
||||
pod.run_text("first").await.unwrap();
|
||||
let _ = drain(&mut rx);
|
||||
|
||||
// Second run: pre_llm_request yields immediately, Worker returns
|
||||
// Second run: pre_llm_request yields immediately, Engine returns
|
||||
// Yielded, handle_worker_result routes into do_compact_and_resume.
|
||||
pod.run_text("second").await.unwrap();
|
||||
|
||||
|
||||
@@ -20,9 +20,9 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use futures::Stream;
|
||||
use llm_worker::Worker;
|
||||
use llm_worker::llm_client::event::{Event as LlmEvent, ResponseStatus, StatusEvent};
|
||||
use llm_worker::llm_client::{ClientError, LlmClient, Request};
|
||||
use llm_engine::Engine;
|
||||
use llm_engine::llm_client::event::{Event as LlmEvent, ResponseStatus, StatusEvent};
|
||||
use llm_engine::llm_client::{ClientError, LlmClient, Request};
|
||||
use memory::WorkspaceLayout;
|
||||
use memory::extract::{ExtractedPayload, write_staging};
|
||||
use memory::schema::SourceRef;
|
||||
@@ -169,7 +169,7 @@ async fn make_pod_with(
|
||||
std::mem::forget(store_tmp);
|
||||
|
||||
let scope = pod::Scope::writable(&pwd).unwrap();
|
||||
let worker = Worker::new(client);
|
||||
let worker = Engine::new(client);
|
||||
Pod::new(manifest, worker, store, pwd, scope).await.unwrap()
|
||||
}
|
||||
|
||||
@@ -388,7 +388,7 @@ async fn fires_on_threshold_and_cleans_up_consumed_entries() {
|
||||
write_n_staging(&layout, 2); // threshold is 2 — fires.
|
||||
|
||||
// Sub-worker is given a single text-only response. The consolidation prompt
|
||||
// tells it to call memory tools; the mock skips those, but `Worker::run`
|
||||
// tells it to call memory tools; the mock skips those, but `Engine::run`
|
||||
// returns Ok regardless once the LLM closes with a final text.
|
||||
let client = MockClient::new(vec![done("ok")]);
|
||||
let mut pod = make_pod_with(FILES_THRESHOLD_TOML, pwd.path().to_path_buf(), client).await;
|
||||
|
||||
@@ -4,11 +4,11 @@ use std::sync::{Arc, Mutex};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use futures::{Stream, StreamExt};
|
||||
use llm_worker::Worker;
|
||||
use llm_worker::llm_client::event::{ErrorEvent, Event as LlmEvent, ResponseStatus, StatusEvent};
|
||||
use llm_worker::llm_client::types::Item;
|
||||
use llm_worker::llm_client::{ClientError, LlmClient, Request};
|
||||
use llm_worker::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput};
|
||||
use llm_engine::Engine;
|
||||
use llm_engine::llm_client::event::{ErrorEvent, Event as LlmEvent, ResponseStatus, StatusEvent};
|
||||
use llm_engine::llm_client::types::Item;
|
||||
use llm_engine::llm_client::{ClientError, LlmClient, Request};
|
||||
use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput};
|
||||
use pod_store::{CombinedStore, FsPodStore};
|
||||
use session_store::{FsStore, LogEntry};
|
||||
|
||||
@@ -53,7 +53,7 @@ fn history_from_sink(handle: &PodHandle) -> Vec<Item> {
|
||||
enum MockResponse {
|
||||
/// Emit the events and let the stream terminate naturally.
|
||||
Complete(Vec<LlmEvent>),
|
||||
/// Emit the events and then pend forever so the Worker blocks on
|
||||
/// Emit the events and then pend forever so the Engine blocks on
|
||||
/// `stream.next()` — used to exercise the Cancel/Pause path while a
|
||||
/// turn is actively in flight.
|
||||
Hang(Vec<LlmEvent>),
|
||||
@@ -183,7 +183,7 @@ async fn make_pod_with_pwd_and_manifest(
|
||||
let scope = manifest::Scope::writable(&pwd).unwrap();
|
||||
std::mem::forget(pwd_tmp);
|
||||
|
||||
let worker = Worker::new(client);
|
||||
let worker = Engine::new(client);
|
||||
let pod = Pod::new(manifest, worker, store, pwd.clone(), scope)
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -518,7 +518,7 @@ async fn provider_stream_error_records_run_errored() {
|
||||
!entries.iter().any(|entry| matches!(
|
||||
entry,
|
||||
LogEntry::RunCompleted {
|
||||
result: llm_worker::WorkerResult::Finished,
|
||||
result: llm_engine::EngineResult::Finished,
|
||||
..
|
||||
}
|
||||
)),
|
||||
@@ -854,7 +854,7 @@ async fn run_with_paste_segment_inlines_content_and_emits_typed_user_message() {
|
||||
let echoed = user_input_segments.expect("committed UserInput entry missing");
|
||||
assert_eq!(echoed, segments, "typed segments must round-trip unchanged");
|
||||
|
||||
// The Worker received a single user message whose text is the
|
||||
// The Engine received a single user message whose text is the
|
||||
// flattened body — paste content inlined, no chip label.
|
||||
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
|
||||
let requests = client_for_assert.captured_requests();
|
||||
@@ -1101,7 +1101,7 @@ async fn notify_while_idle_auto_starts_turn_and_injects_system_message() {
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
// The notification must also be persisted into the Worker history
|
||||
// The notification must also be persisted into the Engine history
|
||||
// (and therefore eventually into history.json), per
|
||||
// tickets/notify-history-persist.md.
|
||||
let history = history_from_sink(&handle);
|
||||
@@ -1638,7 +1638,7 @@ impl Tool for HangingTool {
|
||||
async fn execute(
|
||||
&self,
|
||||
_input: &str,
|
||||
_ctx: llm_worker::tool::ToolExecutionContext,
|
||||
_ctx: llm_engine::tool::ToolExecutionContext,
|
||||
) -> Result<ToolOutput, ToolError> {
|
||||
std::future::pending::<()>().await;
|
||||
unreachable!()
|
||||
@@ -1681,7 +1681,7 @@ async fn drain_until<F: FnMut(&Event) -> bool>(
|
||||
#[tokio::test]
|
||||
async fn pause_then_resume_transitions_and_preserves_history_consistency() {
|
||||
// Response 1: hang after opening a text block (no stop / completed),
|
||||
// so the Worker is parked inside the stream read and `cancel_rx`
|
||||
// so the Engine is parked inside the stream read and `cancel_rx`
|
||||
// races it cleanly on Method::Pause.
|
||||
let hang = MockResponse::Hang(vec![
|
||||
LlmEvent::text_block_start(0),
|
||||
@@ -1717,7 +1717,7 @@ async fn pause_then_resume_transitions_and_preserves_history_consistency() {
|
||||
handle.send(Method::Pause).await.unwrap();
|
||||
|
||||
// The controller emits RunEnd { Paused } when the
|
||||
// WorkerError::Cancelled is translated under pause_requested.
|
||||
// EngineError::Cancelled is translated under pause_requested.
|
||||
assert!(
|
||||
drain_until(&mut rx, std::time::Duration::from_secs(2), |e| matches!(
|
||||
e,
|
||||
@@ -1756,9 +1756,9 @@ async fn pause_then_resume_transitions_and_preserves_history_consistency() {
|
||||
.iter()
|
||||
.filter_map(|i| match i {
|
||||
Item::Message { role, .. } => match role {
|
||||
llm_worker::Role::User => Some("user"),
|
||||
llm_worker::Role::Assistant => Some("assistant"),
|
||||
llm_worker::Role::System => Some("system"),
|
||||
llm_engine::Role::User => Some("user"),
|
||||
llm_engine::Role::Assistant => Some("assistant"),
|
||||
llm_engine::Role::System => Some("system"),
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
@@ -1772,13 +1772,13 @@ async fn pause_then_resume_transitions_and_preserves_history_consistency() {
|
||||
.iter()
|
||||
.find_map(|i| match i {
|
||||
Item::Message {
|
||||
role: llm_worker::Role::Assistant,
|
||||
role: llm_engine::Role::Assistant,
|
||||
content,
|
||||
..
|
||||
} => Some(
|
||||
content
|
||||
.iter()
|
||||
.map(|p: &llm_worker::ContentPart| p.as_text().to_owned())
|
||||
.map(|p: &llm_engine::ContentPart| p.as_text().to_owned())
|
||||
.collect::<Vec<_>>()
|
||||
.join(""),
|
||||
),
|
||||
@@ -1797,7 +1797,7 @@ async fn pause_then_resume_transitions_and_preserves_history_consistency() {
|
||||
#[tokio::test]
|
||||
async fn paused_then_run_closes_orphan_tool_use_for_next_request() {
|
||||
// Response 1: emit a tool_use block (complete with stop) targeting
|
||||
// our hanging tool. The Worker commits the ToolCall to history,
|
||||
// our hanging tool. The Engine commits the ToolCall to history,
|
||||
// then parks inside `execute_tools` waiting on the tool — which is
|
||||
// where Method::Pause catches it.
|
||||
let tool_name = "HangyTool";
|
||||
@@ -1821,7 +1821,7 @@ async fn paused_then_run_closes_orphan_tool_use_for_next_request() {
|
||||
let client = MockClient::sequential(vec![first, second]);
|
||||
let client_for_assert = client.clone();
|
||||
let mut pod = make_pod(client).await;
|
||||
pod.worker_mut()
|
||||
pod.engine_mut()
|
||||
.register_tool(hanging_tool_definition(tool_name));
|
||||
let handle = spawn_controller(pod).await;
|
||||
let mut rx = handle.subscribe();
|
||||
@@ -1829,7 +1829,7 @@ async fn paused_then_run_closes_orphan_tool_use_for_next_request() {
|
||||
handle.send(Method::run_text("first")).await.unwrap();
|
||||
|
||||
// Wait for ToolCallDone — the ToolCall is committed to history
|
||||
// right before the Worker enters tool execution and pends.
|
||||
// right before the Engine enters tool execution and pends.
|
||||
assert!(
|
||||
drain_until(&mut rx, std::time::Duration::from_secs(2), |e| matches!(
|
||||
e,
|
||||
@@ -1882,21 +1882,21 @@ async fn paused_then_run_closes_orphan_tool_use_for_next_request() {
|
||||
let mut saw_new_user = false;
|
||||
for item in items {
|
||||
match item {
|
||||
llm_worker::Item::ToolResult {
|
||||
llm_engine::Item::ToolResult {
|
||||
call_id, summary, ..
|
||||
} if call_id == "call_orphan" => {
|
||||
assert_eq!(summary, "[Interrupted by user]");
|
||||
saw_synthetic_tool_result = true;
|
||||
}
|
||||
llm_worker::Item::Message { role, content, .. }
|
||||
if *role == llm_worker::Role::System =>
|
||||
llm_engine::Item::Message { role, content, .. }
|
||||
if *role == llm_engine::Role::System =>
|
||||
{
|
||||
let text: String = content.iter().map(|p| p.as_text()).collect();
|
||||
if text.contains("interrupted by the user") {
|
||||
saw_interruption_note = true;
|
||||
}
|
||||
}
|
||||
llm_worker::Item::Message { role, content, .. } if *role == llm_worker::Role::User => {
|
||||
llm_engine::Item::Message { role, content, .. } if *role == llm_engine::Role::User => {
|
||||
let text: String = content.iter().map(|p| p.as_text()).collect();
|
||||
if text.contains("new request") {
|
||||
saw_new_user = true;
|
||||
@@ -1921,13 +1921,13 @@ async fn paused_then_run_closes_orphan_tool_use_for_next_request() {
|
||||
// Also confirm the closure chain is ordered: tool_result for the
|
||||
// orphan precedes the system note, which precedes the new user
|
||||
// message.
|
||||
let idx = |pred: &dyn Fn(&llm_worker::Item) -> bool| items.iter().position(pred).unwrap();
|
||||
let idx = |pred: &dyn Fn(&llm_engine::Item) -> bool| items.iter().position(pred).unwrap();
|
||||
let tool_result_idx = idx(
|
||||
&|i| matches!(i, llm_worker::Item::ToolResult { call_id, .. } if call_id == "call_orphan"),
|
||||
&|i| matches!(i, llm_engine::Item::ToolResult { call_id, .. } if call_id == "call_orphan"),
|
||||
);
|
||||
let sys_idx = idx(&|i| match i {
|
||||
llm_worker::Item::Message {
|
||||
role: llm_worker::Role::System,
|
||||
llm_engine::Item::Message {
|
||||
role: llm_engine::Role::System,
|
||||
content,
|
||||
..
|
||||
} => content
|
||||
@@ -1938,8 +1938,8 @@ async fn paused_then_run_closes_orphan_tool_use_for_next_request() {
|
||||
_ => false,
|
||||
});
|
||||
let user_idx = idx(&|i| match i {
|
||||
llm_worker::Item::Message {
|
||||
role: llm_worker::Role::User,
|
||||
llm_engine::Item::Message {
|
||||
role: llm_engine::Role::User,
|
||||
content,
|
||||
..
|
||||
} => content
|
||||
@@ -1981,7 +1981,7 @@ async fn paused_cancel_abandons_resume_and_next_input_is_fresh_run() {
|
||||
let client = MockClient::sequential(vec![first, second]);
|
||||
let client_for_assert = client.clone();
|
||||
let mut pod = make_pod(client).await;
|
||||
pod.worker_mut()
|
||||
pod.engine_mut()
|
||||
.register_tool(hanging_tool_definition(tool_name));
|
||||
let handle = spawn_controller(pod).await;
|
||||
let mut rx = handle.subscribe();
|
||||
@@ -2022,7 +2022,7 @@ async fn paused_cancel_abandons_resume_and_next_input_is_fresh_run() {
|
||||
!entries_after_cancel.iter().any(|entry| matches!(
|
||||
entry,
|
||||
LogEntry::RunCompleted {
|
||||
result: llm_worker::WorkerResult::Finished,
|
||||
result: llm_engine::EngineResult::Finished,
|
||||
interrupted: false,
|
||||
..
|
||||
}
|
||||
@@ -2078,7 +2078,7 @@ async fn paused_cancel_abandons_resume_and_next_input_is_fresh_run() {
|
||||
assert!(
|
||||
items.iter().any(|item| matches!(
|
||||
item,
|
||||
llm_worker::Item::ToolResult { call_id, summary, .. }
|
||||
llm_engine::Item::ToolResult { call_id, summary, .. }
|
||||
if call_id == "call_cancelled" && summary == "[Interrupted by user]"
|
||||
)),
|
||||
"paused cancel should close orphan tool_use before future requests: {items:?}"
|
||||
@@ -2086,8 +2086,8 @@ async fn paused_cancel_abandons_resume_and_next_input_is_fresh_run() {
|
||||
assert!(
|
||||
items.iter().any(|item| matches!(
|
||||
item,
|
||||
llm_worker::Item::Message {
|
||||
role: llm_worker::Role::System,
|
||||
llm_engine::Item::Message {
|
||||
role: llm_engine::Role::System,
|
||||
..
|
||||
} if item_text_contains(item, "interrupted by the user")
|
||||
)),
|
||||
@@ -2096,8 +2096,8 @@ async fn paused_cancel_abandons_resume_and_next_input_is_fresh_run() {
|
||||
assert!(
|
||||
items.iter().any(|item| matches!(
|
||||
item,
|
||||
llm_worker::Item::Message {
|
||||
role: llm_worker::Role::User,
|
||||
llm_engine::Item::Message {
|
||||
role: llm_engine::Role::User,
|
||||
..
|
||||
} if item_text_contains(item, "fresh request")
|
||||
)),
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, LazyLock, Mutex};
|
||||
|
||||
use llm_worker::llm_client::types::{ContentPart, Item, Role};
|
||||
use llm_worker::tool::ToolOutput;
|
||||
use llm_engine::llm_client::types::{ContentPart, Item, Role};
|
||||
use llm_engine::tool::ToolOutput;
|
||||
use manifest::{Permission, Scope, ScopeRule, SharedScope};
|
||||
use pod::runtime::dir::{RuntimeDir, SpawnedPodRecord};
|
||||
use pod::runtime::pod_registry::{self, LockFileGuard};
|
||||
|
||||
@@ -21,10 +21,10 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use futures::Stream;
|
||||
use llm_worker::Worker;
|
||||
use llm_worker::llm_client::event::{Event as LlmEvent, ResponseStatus, StatusEvent, UsageEvent};
|
||||
use llm_worker::llm_client::{ClientError, LlmClient, Request};
|
||||
use llm_worker::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput};
|
||||
use llm_engine::Engine;
|
||||
use llm_engine::llm_client::event::{Event as LlmEvent, ResponseStatus, StatusEvent, UsageEvent};
|
||||
use llm_engine::llm_client::{ClientError, LlmClient, Request};
|
||||
use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput};
|
||||
use pod_store::{CombinedStore, FsPodStore};
|
||||
use session_metrics::{DOMAIN, Metric, metrics_from_extensions};
|
||||
use session_store::{FsStore, LogEntry, SegmentId, SessionId, Store, StoreError, TraceEntry};
|
||||
@@ -82,7 +82,7 @@ impl Tool for BigContentTool {
|
||||
async fn execute(
|
||||
&self,
|
||||
_input: &str,
|
||||
_ctx: llm_worker::tool::ToolExecutionContext,
|
||||
_ctx: llm_engine::tool::ToolExecutionContext,
|
||||
) -> Result<ToolOutput, ToolError> {
|
||||
Ok(ToolOutput {
|
||||
summary: self.summary.into(),
|
||||
@@ -187,7 +187,7 @@ async fn make_pod(
|
||||
let pwd = pwd_tmp.path().to_path_buf();
|
||||
let scope = pod::Scope::writable(&pwd).unwrap();
|
||||
|
||||
let mut worker = Worker::new(client);
|
||||
let mut worker = Engine::new(client);
|
||||
worker.register_tool(big_content_tool_definition(tool_name));
|
||||
|
||||
let pod = Pod::new(manifest, worker, store, pwd, scope).await.unwrap();
|
||||
@@ -448,7 +448,7 @@ async fn metric_write_failure_emits_warn_alert_and_does_not_abort_run() {
|
||||
// protected token budget covers the only user message). That is enough to drive
|
||||
// the failure path: at least one metric attempts to write.
|
||||
let client = MockClient::new(vec![text_response_with_cache("hi", 0, 0)]);
|
||||
let worker = Worker::new(client);
|
||||
let worker = Engine::new(client);
|
||||
let mut pod = Pod::new(manifest, worker, store.clone(), pwd, scope)
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -517,7 +517,7 @@ permission = "write"
|
||||
let pwd_tmp = tempfile::tempdir().unwrap();
|
||||
let pwd = pwd_tmp.path().to_path_buf();
|
||||
let scope = pod::Scope::writable(&pwd).unwrap();
|
||||
let worker = Worker::new(client);
|
||||
let worker = Engine::new(client);
|
||||
let mut pod = Pod::new(manifest, worker, store.clone(), pwd, scope)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::path::{Path, PathBuf};
|
||||
use std::sync::{LazyLock, Mutex};
|
||||
|
||||
use client::PodRuntimeCommand;
|
||||
use llm_worker::tool::{ToolError, ToolOutput};
|
||||
use llm_engine::tool::{ToolError, ToolOutput};
|
||||
use manifest::{
|
||||
AuthRef, ModelManifest, Permission, PodManifest, PodManifestConfig, PodMetaConfig, SchemeKind,
|
||||
Scope, ScopeConfig, ScopeRule, SharedScope,
|
||||
|
||||
@@ -5,9 +5,9 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use futures::Stream;
|
||||
use llm_worker::Worker;
|
||||
use llm_worker::llm_client::event::{Event as LlmEvent, ResponseStatus, StatusEvent};
|
||||
use llm_worker::llm_client::{ClientError, LlmClient, Request};
|
||||
use llm_engine::Engine;
|
||||
use llm_engine::llm_client::event::{Event as LlmEvent, ResponseStatus, StatusEvent};
|
||||
use llm_engine::llm_client::{ClientError, LlmClient, Request};
|
||||
use pod_store::{CombinedStore, FsPodStore};
|
||||
use session_store::{FsStore, LogEntry, Store};
|
||||
|
||||
@@ -122,7 +122,7 @@ async fn make_pod_with_body(
|
||||
let loader = PromptLoader::new(Some(user_prompts_tmp.path().to_path_buf()), None);
|
||||
std::mem::forget(user_prompts_tmp);
|
||||
|
||||
let worker = Worker::new(client);
|
||||
let worker = Engine::new(client);
|
||||
let mut pod = Pod::new(manifest, worker, store, pwd.clone(), scope).await?;
|
||||
|
||||
let template = SystemPromptTemplate::parse("$user/test", loader)
|
||||
@@ -154,7 +154,7 @@ async fn template_is_not_materialised_before_first_run() {
|
||||
let client = MockClient::new(vec![single_text_events("ok")]);
|
||||
let (pod, _pwd) = make_pod_with_body("hello", client).await.unwrap();
|
||||
// Before first run, worker still has no system prompt.
|
||||
assert!(pod.worker().get_system_prompt().is_none());
|
||||
assert!(pod.engine().get_system_prompt().is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -168,7 +168,7 @@ async fn materialise_on_first_turn_populates_worker() {
|
||||
.unwrap();
|
||||
pod.run_text("hi").await.unwrap();
|
||||
let rendered = pod
|
||||
.worker()
|
||||
.engine()
|
||||
.get_system_prompt()
|
||||
.expect("system prompt materialised")
|
||||
.to_string();
|
||||
@@ -222,9 +222,9 @@ async fn materialise_runs_only_once_across_turns() {
|
||||
.await
|
||||
.unwrap();
|
||||
pod.run_text("one").await.unwrap();
|
||||
let first = pod.worker().get_system_prompt().unwrap().to_string();
|
||||
let first = pod.engine().get_system_prompt().unwrap().to_string();
|
||||
pod.run_text("two").await.unwrap();
|
||||
let second = pod.worker().get_system_prompt().unwrap().to_string();
|
||||
let second = pod.engine().get_system_prompt().unwrap().to_string();
|
||||
assert_eq!(first, second);
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ async fn agents_md_is_injected_as_trailing_section_when_present() {
|
||||
std::fs::write(pwd.join("AGENTS.md"), "# project rules\nbe kind").unwrap();
|
||||
|
||||
pod.run_text("hi").await.unwrap();
|
||||
let rendered = pod.worker().get_system_prompt().unwrap().to_string();
|
||||
let rendered = pod.engine().get_system_prompt().unwrap().to_string();
|
||||
assert!(rendered.starts_with("BODY"));
|
||||
assert!(rendered.contains("## Project instructions (AGENTS.md)"));
|
||||
assert!(rendered.contains("# project rules"));
|
||||
@@ -247,7 +247,7 @@ async fn agents_md_absent_omits_trailing_section() {
|
||||
let client = MockClient::new(vec![single_text_events("ok")]);
|
||||
let (mut pod, _pwd) = make_pod_with_body("BODY", client).await.unwrap();
|
||||
pod.run_text("hi").await.unwrap();
|
||||
let rendered = pod.worker().get_system_prompt().unwrap().to_string();
|
||||
let rendered = pod.engine().get_system_prompt().unwrap().to_string();
|
||||
assert!(!rendered.contains("## Project instructions"));
|
||||
assert!(!rendered.contains("AGENTS.md"));
|
||||
}
|
||||
@@ -266,7 +266,7 @@ async fn agents_md_not_reread_after_compact() {
|
||||
std::fs::write(&agents_path, "original").unwrap();
|
||||
|
||||
pod.run_text("first").await.unwrap();
|
||||
let before = pod.worker().get_system_prompt().unwrap().to_string();
|
||||
let before = pod.engine().get_system_prompt().unwrap().to_string();
|
||||
assert!(before.contains("original"));
|
||||
pod.run_text("second").await.unwrap();
|
||||
|
||||
@@ -274,12 +274,12 @@ async fn agents_md_not_reread_after_compact() {
|
||||
// system prompt either on a subsequent turn or across compaction.
|
||||
std::fs::write(&agents_path, "mutated").unwrap();
|
||||
pod.compact(0).await.unwrap();
|
||||
let after_compact = pod.worker().get_system_prompt().unwrap().to_string();
|
||||
let after_compact = pod.engine().get_system_prompt().unwrap().to_string();
|
||||
assert!(after_compact.contains("original"));
|
||||
assert!(!after_compact.contains("mutated"));
|
||||
|
||||
pod.run_text("third").await.unwrap();
|
||||
let after_third = pod.worker().get_system_prompt().unwrap().to_string();
|
||||
let after_third = pod.engine().get_system_prompt().unwrap().to_string();
|
||||
assert!(after_third.contains("original"));
|
||||
assert!(!after_third.contains("mutated"));
|
||||
}
|
||||
@@ -329,14 +329,14 @@ async fn compact_preserves_system_prompt() {
|
||||
.unwrap();
|
||||
|
||||
pod.run_text("first").await.unwrap();
|
||||
let before = pod.worker().get_system_prompt().unwrap().to_string();
|
||||
let before = pod.engine().get_system_prompt().unwrap().to_string();
|
||||
pod.run_text("second").await.unwrap();
|
||||
|
||||
pod.compact(0).await.unwrap();
|
||||
|
||||
let after = pod.worker().get_system_prompt().unwrap().to_string();
|
||||
let after = pod.engine().get_system_prompt().unwrap().to_string();
|
||||
assert_eq!(before, after);
|
||||
|
||||
pod.run_text("third").await.unwrap();
|
||||
assert_eq!(pod.worker().get_system_prompt().unwrap(), after.as_str());
|
||||
assert_eq!(pod.engine().get_system_prompt().unwrap(), after.as_str());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user