Legg til orchestration-validering i maskinrommet (oppgave 24.1)

Ny node_kind 'orchestration' med strukturert metadata-validering:
- trigger.event valideres mot kjent liste (node.created, edge.created,
  communication.ended, node.published, scheduled.due, manual)
- trigger.conditions må være objekt hvis satt
- executor valideres mot script/bot/dream
- intelligence og effort valideres som heltall 1-3
- compiled valideres som boolean
- pipeline valideres som array

Valideringen kjører i både create_node og update_node, identisk
mønster som validate_collection_traits og validate_ai_preset_metadata.

Ref: docs/concepts/orkestrering.md
This commit is contained in:
vegard 2026-03-18 16:43:11 +00:00
parent 77e3d59b46
commit 6dabdcf8ec
2 changed files with 117 additions and 2 deletions

View file

@ -102,6 +102,21 @@ fn validate_collection_traits(
Ok(())
}
/// Gyldige trigger-events for orchestration-noder.
/// Ref: docs/concepts/orkestrering.md § 5 "Kjente trigger-events"
const VALID_TRIGGER_EVENTS: &[&str] = &[
"node.created",
"edge.created",
"communication.ended",
"node.published",
"scheduled.due",
"manual",
];
/// Gyldige executor-verdier for orchestration-noder.
/// Ref: docs/concepts/orkestrering.md § 4 "Tre utførelsesnivåer"
const VALID_EXECUTORS: &[&str] = &["script", "bot", "dream"];
/// Gyldige modellprofiler for AI-presets.
/// Ref: docs/infra/ai_gateway.md § "Modellprofiler"
const VALID_MODEL_PROFILES: &[&str] = &["flash", "standard"];
@ -188,6 +203,101 @@ fn validate_ai_preset_metadata(
Ok(())
}
/// Validerer metadata for `node_kind == "orchestration"`.
///
/// Påkrevde felter i metadata:
/// - `trigger` (objekt med `event` og valgfri `conditions`)
/// - `trigger.event` (string, må finnes i VALID_TRIGGER_EVENTS)
/// - `executor` (string, må finnes i VALID_EXECUTORS)
///
/// Valgfrie felter:
/// - `trigger.conditions` (objekt — fri filtrering)
/// - `intelligence` (heltall 13)
/// - `effort` (heltall 13)
/// - `compiled` (boolean — om scriptet er kompilert fra AI-modus)
/// - `pipeline` (array — sekvens av steg)
///
/// Ref: docs/concepts/orkestrering.md
fn validate_orchestration_metadata(
node_kind: &str,
metadata: &serde_json::Value,
) -> Result<(), String> {
if node_kind != "orchestration" {
return Ok(());
}
// Påkrevd: trigger (objekt)
let trigger = metadata
.get("trigger")
.ok_or("orchestration krever metadata.trigger")?;
let trigger_obj = trigger
.as_object()
.ok_or("metadata.trigger må være et objekt")?;
// Påkrevd: trigger.event
match trigger_obj.get("event").and_then(|v| v.as_str()) {
None | Some("") => {
return Err("orchestration krever metadata.trigger.event (ikke-tom streng)".into())
}
Some(ev) if !VALID_TRIGGER_EVENTS.contains(&ev) => {
return Err(format!(
"Ukjent trigger-event: '{ev}'. Gyldige verdier: {VALID_TRIGGER_EVENTS:?}"
));
}
_ => {}
}
// Valgfri: trigger.conditions (må være objekt hvis satt)
if let Some(conditions) = trigger_obj.get("conditions") {
if !conditions.is_object() {
return Err("metadata.trigger.conditions må være et objekt".into());
}
}
// Påkrevd: executor
match metadata.get("executor").and_then(|v| v.as_str()) {
None => return Err("orchestration krever metadata.executor".into()),
Some(ex) if !VALID_EXECUTORS.contains(&ex) => {
return Err(format!(
"Ugyldig executor: '{ex}'. Gyldige verdier: {VALID_EXECUTORS:?}"
));
}
_ => {}
}
// Valgfri: intelligence (13)
if let Some(val) = metadata.get("intelligence") {
match val.as_i64() {
Some(n) if (1..=3).contains(&n) => {}
_ => return Err("metadata.intelligence må være et heltall 13".into()),
}
}
// Valgfri: effort (13)
if let Some(val) = metadata.get("effort") {
match val.as_i64() {
Some(n) if (1..=3).contains(&n) => {}
_ => return Err("metadata.effort må være et heltall 13".into()),
}
}
// Valgfri: compiled (boolean)
if let Some(val) = metadata.get("compiled") {
if !val.is_boolean() {
return Err("metadata.compiled må være en boolean".into());
}
}
// Valgfri: pipeline (array)
if let Some(val) = metadata.get("pipeline") {
if !val.is_array() {
return Err("metadata.pipeline må være et array".into());
}
}
Ok(())
}
#[derive(Serialize)]
pub struct ErrorResponse {
pub error: String,
@ -517,6 +627,9 @@ pub async fn create_node(
// -- Valider metadata for AI-presets (oppgave 18.1) --
validate_ai_preset_metadata(&node_kind, &metadata).map_err(|e| bad_request(&e))?;
// -- Valider metadata for orchestration-noder (oppgave 24.1) --
validate_orchestration_metadata(&node_kind, &metadata).map_err(|e| bad_request(&e))?;
// -- Kontekstbasert identitet (oppgave 8.2) --
// Hvis context_id er satt, sjekk om brukeren har et alias som er
// deltaker i kommunikasjonsnoden. I så fall brukes aliaset som created_by.
@ -1051,6 +1164,9 @@ pub async fn update_node(
// -- Valider metadata for AI-presets (oppgave 18.1) --
validate_ai_preset_metadata(&node_kind, &metadata).map_err(|e| bad_request(&e))?;
// -- Valider metadata for orchestration-noder (oppgave 24.1) --
validate_orchestration_metadata(&node_kind, &metadata).map_err(|e| bad_request(&e))?;
// -- Beskytt model_profile for egendefinerte AI-presets (oppgave 18.6) --
// Kun admin/owner på en samling kan endre model_profile. Vanlige brukere
// som eier et custom preset kan redigere alt annet (prompt, icon, farge osv.)

View file

@ -316,8 +316,7 @@ Ref: `docs/concepts/orkestrering.md`. Orkestreringsnoder (`node_kind: 'orchestra
med fritekst-instruksjoner som boten utfører via function calling. Strukturerte triggere,
automatisk eskalering av intelligens ved feil, kompilering av velprøvde mønstre.
- [~] 24.1 Orchestration node-type: legg til `orchestration` i maskinrommets node-validering. Metadata-skjema: `trigger` (event + conditions), `executor`, `intelligence`, `effort`, `compiled`, `pipeline`. Valider trigger-events mot kjent liste.
> Påbegynt: 2026-03-18T16:35
- [x] 24.1 Orchestration node-type: legg til `orchestration` i maskinrommets node-validering. Metadata-skjema: `trigger` (event + conditions), `executor`, `intelligence`, `effort`, `compiled`, `pipeline`. Valider trigger-events mot kjent liste.
- [ ] 24.2 Trigger-evaluering i portvokteren: ved node/edge-events, sjekk om noen `orchestration`-noder matcher triggeren. Effektiv lookup (indeksert på `metadata.trigger.event`). Ingen LLM-kall for trigger-matching.
- [ ] 24.3 Bot-utførelse: utvid `synops-respond` (eller nytt `synops-orchestrate`) til å ta orchestration-node som input. Bygg prompt med trigger-kontekst + instruksjoner + tilgjengelige verktøy. Function calling for hvert steg. Logg i `orchestration_log`.
- [ ] 24.4 Auto-eskalering: start med lavt intelligens/effort-nivå. Ved feil (tool_error, ambiguous_instruction) bump automatisk til neste nivå og retry. Maks 3 nivåer.