use std::sync::Arc;
use std::time::Duration;
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use nexo_auth::resolver::CredentialStores;
use nexo_auth::{AgentCredentialResolver, Channel};
use nexo_broker::AnyBroker;
use nexo_llm::ToolDef;
use serde_json::Value;
use tokio_util::sync::CancellationToken;
use crate::error::PollerError;
use crate::PollerRunner;
#[async_trait]
pub trait Poller: Send + Sync + 'static {
fn kind(&self) -> &'static str;
fn description(&self) -> &'static str {
""
}
fn validate(&self, _config: &Value) -> Result<(), PollerError> {
Ok(())
}
async fn tick(&self, ctx: &PollContext) -> Result<TickOutcome, PollerError>;
fn custom_tools(&self) -> Vec<CustomToolSpec> {
Vec::new()
}
}
pub struct CustomToolSpec {
pub def: ToolDef,
pub handler: Arc<dyn CustomToolHandler>,
}
#[async_trait]
pub trait CustomToolHandler: Send + Sync + 'static {
async fn call(&self, runner: Arc<PollerRunner>, args: Value) -> anyhow::Result<Value>;
}
pub struct PollContext {
pub job_id: String,
pub agent_id: String,
pub kind: &'static str,
pub credentials: Arc<AgentCredentialResolver>,
pub stores: Option<Arc<CredentialStores>>,
pub broker: AnyBroker,
pub now: DateTime<Utc>,
pub cursor: Option<Vec<u8>>,
pub config: Value,
pub interval_hint: Duration,
pub cancel: CancellationToken,
pub llm_registry: Option<Arc<nexo_llm::LlmRegistry>>,
pub llm_config: Option<Arc<nexo_config::LlmConfig>>,
}
#[derive(Debug, Default)]
pub struct TickOutcome {
pub items_seen: u32,
pub items_dispatched: u32,
pub deliver: Vec<OutboundDelivery>,
pub next_cursor: Option<Vec<u8>>,
pub next_interval_hint: Option<Duration>,
}
#[derive(Debug, Clone)]
pub struct OutboundDelivery {
pub channel: Channel,
pub recipient: String,
pub payload: Value,
}
#[cfg(test)]
mod tests {
use super::*;
fn assert_send_sync<T: Send + Sync>() {}
#[test]
fn poll_context_is_send_sync() {
assert_send_sync::<PollContext>();
}
#[test]
fn tick_outcome_default_is_empty() {
let o = TickOutcome::default();
assert_eq!(o.items_seen, 0);
assert_eq!(o.items_dispatched, 0);
assert!(o.deliver.is_empty());
assert!(o.next_cursor.is_none());
assert!(o.next_interval_hint.is_none());
}
}