swarm-engine-core 0.1.6

Core types and orchestration for SwarmEngine
Documentation
//! BatchInvoker - Batch LLM 推論の抽象化
//!
//! # 型の定義場所
//!
//! Batch関連の型は以下のモジュールで定義されています:
//!
//! - **正規定義 (`manager.rs`)**: `BatchDecisionRequest`, `WorkerDecisionRequest`, `ManagerId`
//! - **本モジュール (`batch.rs`)**: `BatchInvoker` trait, `BatchInvokeResult`,
//!   `BatchInvokeError`, `DecisionResponse`
//!
//! 本モジュールは `manager.rs` の型を re-export しており、
//! 利用者はどちらからも import 可能です。

use std::collections::HashMap;

use crate::actions::ActionDef;
use crate::exploration::{DependencyGraph, SelectResult};
use crate::extensions::Extensions;
use crate::types::WorkerId;

// Re-export from manager.rs (正規定義はそちら)
pub use super::manager::{BatchDecisionRequest, ManagerId, WorkerDecisionRequest};

/// Batch LLM 推論の抽象化
///
/// Manager の BatchDecisionRequest を受け取り、各 Worker への DecisionResponse を返す。
/// vLLM / Ollama / OpenAI 等のバックエンドに応じた実装を LLM 層で提供する。
///
/// # 設計
///
/// ```text
/// Orchestrator
//////     ├─ Manager.prepare_batch() → BatchDecisionRequest
//////     ├─ BatchInvoker.invoke(request, extensions) → BatchInvokeResult  ← ここを抽象化
//////     └─ Manager.finalize_batch(responses) → ManagementDecision
/// ```
///
/// # Extensions によるランタイム設定
///
/// `extensions` には以下の設定が含まれる可能性があります:
/// - `LlmConfig`: LLM プロバイダ、モデル、temperature 等
/// - `ActionsConfig`: 利用可能な Action 一覧
/// - `BatchProcessorConfig`: 並列処理設定等
///
/// 実装は必要に応じてこれらを `extensions.get::<T>()` で取得します。
///
/// # 同期 vs 非同期
///
/// 同期インターフェース(`invoke`)を提供。
/// 非同期が必要な場合は実装側で `block_on` 等を使用する。
pub trait BatchInvoker: Send + Sync {
    /// Batch リクエストを処理
    ///
    /// # Arguments
    /// - `request`: Manager が生成した Batch リクエスト
    /// - `extensions`: ランタイム設定(LlmConfig, ActionsConfig 等)
    fn invoke(&self, request: BatchDecisionRequest, extensions: &Extensions) -> BatchInvokeResult;

    /// タスクとアクション一覧からアクション依存グラフを生成
    ///
    /// Swarm の Ticks 開始前に呼び出され、アクション間の依存関係を計画する。
    /// LLM を使用して動的に依存グラフを生成する。
    ///
    /// # Arguments
    /// - `task`: タスク説明(SwarmTask.description)
    /// - `actions`: 利用可能なアクション定義の一覧
    /// - `hint`: `LearnedDependencyProvider.select()` の結果(LoRA、vote_count 等を含む)
    ///
    /// # Returns
    /// - `Some(DependencyGraph)`: 依存グラフが生成された場合
    /// - `None`: 依存グラフ生成をスキップする場合(デフォルト)
    ///
    /// # Example
    ///
    /// ```ignore
    /// let graph = invoker.plan_dependencies(
    ///     "Find the auth handler in src/",
    ///     &actions,  // Vec<ActionDef>
    ///     Some(&select_result),  // SelectResult from LearnedDependencyProvider
    /// );
    /// // => Some(DependencyGraph { edges: [Grep -> Read], start: [Grep], terminal: [Read] })
    /// ```
    fn plan_dependencies(
        &self,
        _task: &str,
        _actions: &[ActionDef],
        _hint: Option<&SelectResult>,
    ) -> Option<DependencyGraph> {
        None
    }

    /// プロセッサ名(デバッグ/ログ用)
    fn name(&self) -> &str;

    /// ヘルスチェック
    fn is_healthy(&self) -> bool {
        true
    }
}

/// Batch 推論結果
pub type BatchInvokeResult = Vec<(WorkerId, Result<DecisionResponse, BatchInvokeError>)>;

/// Batch 推論エラー
#[derive(Debug, Clone, thiserror::Error)]
pub enum BatchInvokeError {
    /// 一時的エラー(リトライ可能)
    #[error("Batch invoke error (transient): {0}")]
    Transient(String),

    /// 恒久的エラー(リトライ不可)
    #[error("Batch invoke error: {0}")]
    Permanent(String),
}

impl BatchInvokeError {
    pub fn transient(message: impl Into<String>) -> Self {
        Self::Transient(message.into())
    }

    pub fn permanent(message: impl Into<String>) -> Self {
        Self::Permanent(message.into())
    }

    pub fn is_transient(&self) -> bool {
        matches!(self, Self::Transient(_))
    }

    pub fn message(&self) -> &str {
        match self {
            Self::Transient(msg) => msg,
            Self::Permanent(msg) => msg,
        }
    }
}

impl From<crate::error::SwarmError> for BatchInvokeError {
    fn from(err: crate::error::SwarmError) -> Self {
        if err.is_transient() {
            Self::Transient(err.message())
        } else {
            Self::Permanent(err.message())
        }
    }
}

impl From<BatchInvokeError> for crate::error::SwarmError {
    fn from(err: BatchInvokeError) -> Self {
        match err {
            BatchInvokeError::Transient(message) => {
                crate::error::SwarmError::LlmTransient { message }
            }
            BatchInvokeError::Permanent(message) => {
                crate::error::SwarmError::LlmPermanent { message }
            }
        }
    }
}

/// LLM からの判断レスポンス
#[derive(Debug, Clone)]
pub struct DecisionResponse {
    /// 選択されたツール/Action
    pub tool: String,
    /// ターゲット
    pub target: String,
    /// 引数
    pub args: HashMap<String, String>,
    /// 推論理由
    pub reasoning: Option<String>,
    /// 信頼度 (0.0 - 1.0)
    pub confidence: f64,
    /// 送信したプロンプト(デバッグ/Snapshot用)
    pub prompt: Option<String>,
    /// LLM からの生レスポンス(デバッグ/Snapshot用)
    pub raw_response: Option<String>,
}

impl Default for DecisionResponse {
    fn default() -> Self {
        Self {
            tool: String::new(),
            target: String::new(),
            args: HashMap::new(),
            reasoning: None,
            confidence: 0.0,
            prompt: None,
            raw_response: None,
        }
    }
}