fix coordinator compilation

This commit is contained in:
Timur Gordon
2025-11-14 00:35:26 +01:00
parent 84545f0d75
commit 94a66d9af4
15 changed files with 397 additions and 459 deletions

View File

@@ -11,10 +11,13 @@ use std::hash::{Hash, Hasher};
use tokio::sync::{Mutex, Semaphore};
use crate::{
clients::{Destination, MyceliumClient, MyceliumTransport, SupervisorClient, SupervisorHub},
models::{Job, JobStatus, Message, MessageStatus, ScriptType, TransportStatus},
models::{Job, JobStatus, Message, MessageStatus, TransportStatus},
service::AppService,
};
use hero_supervisor_openrpc_client::{
SupervisorClient,
transports::{Destination, MyceliumClient, MyceliumTransport, SupervisorHub},
};
use tracing::{error, info};
#[derive(Clone)]
@@ -197,44 +200,64 @@ async fn deliver_one(
// Load message
let msg: Message = service.load_message(context_id, caller_id, id).await?;
// Embedded job id (if any)
let job_id_opt: Option<u32> = msg.job.first().map(|j| j.id);
// Determine routing from FlowNode.supervisor_url if available
let supervisor_url = if !msg.nodes.is_empty() {
// Use FlowNode routing (new architecture)
msg.nodes[0].supervisor_url.clone()
} else {
// Fallback: get first available runner (legacy)
let runners = service.scan_runners(context_id).await?;
let Some(runner) = runners.into_iter().next() else {
let log = format!(
"No runners available in context {} for message {}",
context_id, msg_key
);
let _ = service
.append_message_logs(context_id, caller_id, id, vec![log.clone()])
.await;
let _ = service
.update_message_status(context_id, caller_id, id, MessageStatus::Error)
.await;
return Err(log.into());
};
// Build URL from runner
if !runner.pubkey.trim().is_empty() {
format!("mycelium://{}", runner.pubkey)
} else {
format!("http://{}", runner.address)
}
};
// Determine routing script_type
let desired: ScriptType = determine_script_type(&msg);
// Discover runners and select a matching one
let runners = service.scan_runners(context_id).await?;
let Some(runner) = runners.into_iter().find(|r| r.script_type == desired) else {
let log = format!(
"No runner with script_type {:?} available in context {} for message {}",
desired, context_id, msg_key
);
let _ = service
.append_message_logs(context_id, caller_id, id, vec![log.clone()])
.await;
let _ = service
.update_message_status(context_id, caller_id, id, MessageStatus::Error)
.await;
return Err(log.into());
// Parse supervisor_url to determine destination
// Format: "mycelium://<pubkey>" or "http://<address>" or just "<address>"
let dest = if supervisor_url.starts_with("mycelium://") {
let pubkey = supervisor_url.strip_prefix("mycelium://").unwrap_or("");
Destination::Pk(pubkey.to_string())
} else {
// Extract address (strip http:// or https:// if present)
let address_str = supervisor_url
.strip_prefix("http://")
.or_else(|| supervisor_url.strip_prefix("https://"))
.unwrap_or(&supervisor_url);
// Parse IP address (strip port if present)
let ip_str = address_str.split(':').next().unwrap_or(address_str);
let ip_addr = ip_str.parse().unwrap_or_else(|_| {
// Default to localhost if parsing fails
std::net::IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1))
});
Destination::Ip(ip_addr)
};
// Build SupervisorClient
let dest = if !runner.pubkey.trim().is_empty() {
Destination::Pk(runner.pubkey.clone())
} else {
Destination::Ip(runner.address)
};
// Keep clones for poller usage
let dest_for_poller = dest.clone();
let topic_for_poller = cfg.topic.clone();
let secret_for_poller = runner.secret.clone();
let client = cache
.get_or_create(
sup_hub.clone(),
dest.clone(),
cfg.topic.clone(),
runner.secret.clone(),
None, // TODO: Get secret from runner or config
)
.await;
@@ -244,11 +267,44 @@ async fn deliver_one(
// Send via the new client API
// The transport handles message correlation internally
let _result = if method == "job.run" {
let job_result = if method == "job.run" {
if let Some(j) = msg.job.first() {
// Use typed job_run method
let job = serde_json::from_value(job_to_json(j)?)?;
client.job_run(job, None).await?;
let result = client.job_run(job, None).await;
// Update node status based on result
if !msg.nodes.is_empty() {
let node_id = msg.nodes[0].id;
let flow_id = msg.flow_id;
match &result {
Ok(_) => {
// Job completed successfully
let _ = service
.update_node_status_unchecked(
context_id,
flow_id,
node_id,
crate::dag::NodeStatus::Completed,
)
.await;
}
Err(_) => {
// Job failed
let _ = service
.update_node_status_unchecked(
context_id,
flow_id,
node_id,
crate::dag::NodeStatus::Failed,
)
.await;
}
}
}
result?;
serde_json::Value::Null
} else {
// Generic call - not supported in new API, would need custom implementation
@@ -277,19 +333,16 @@ async fn deliver_one(
.update_message_status(context_id, caller_id, id, MessageStatus::Acknowledged)
.await?;
// For job.run, mark the job as dispatched
// Log job completion
if method == "job.run" {
if let Some(job_id) = msg.job.first().map(|j| j.id) {
let _ = service
.update_job_status_unchecked(context_id, caller_id, job_id, JobStatus::Dispatched)
.await;
if let Some(job_id) = msg.job.first().map(|j| j.id.parse::<u32>().unwrap_or(0)) {
let _ = service
.append_message_logs(
context_id,
caller_id,
id,
vec![format!(
"Supervisor reply for job {}: job_queued (processed synchronously)",
"Job {} completed successfully",
job_id
)],
)
@@ -304,13 +357,7 @@ async fn deliver_one(
Ok(())
}
fn determine_script_type(msg: &Message) -> ScriptType {
// Prefer embedded job's script_type if available, else fallback to message.message_type
match msg.job.first() {
Some(j) => j.script_type.clone(),
None => msg.message_type.clone(),
}
}
// Removed determine_executor - routing now based on FlowNode.supervisor_url
fn build_params(msg: &Message) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> {
// Minimal mapping: