blob: 098fbdcd4682fe862fc44c801e2c238ae77e994f [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
//! Reusable route choices. A route freezes the selected values in its original
//! assignment; editing this catalog never changes an accepted task.
use super::*;
use maka_agent_api::workhub::presets::*;
impl Hub {
fn presets_key(&self) -> String {
format!("{}presets", self.agent.0.prefix)
}
pub(super) async fn presets(&self) -> std::result::Result<Presets, String> {
let record = self
.agent
.0
.store
.plugin_data(&namespace(), &self.presets_key())
.await
.map_err(|_| "workhub_store_unavailable")?;
let revision = record.as_ref().map(|r| r.revision);
let entries = match record.map(|r| r.data) {
None | Some(Data::Deleted) => vec![],
Some(Data::Present(value)) => {
serde_json::from_value(value).map_err(|_| "workhub_presets_invalid")?
}
};
Ok(Presets { revision, entries })
}
/// Resolve reusable intent only for a new route; accepted assignments keep
/// their original exact selection independently of later preset edits.
pub(super) async fn resolve_preset(
&self,
id: &str,
) -> std::result::Result<(Preset, crate::ModelSelection), String> {
let preset = self
.presets()
.await?
.entries
.into_iter()
.find(|preset| preset.id == id)
.ok_or("workhub_preset_missing")?;
if !preset.enabled {
return Err("workhub_preset_disabled".into());
}
self.tasks
.workers
.get(&preset.worker)
.and_then(Option::as_ref)
.ok_or("workhub_worker_unavailable")?;
let public_guard = self.agent.0.catalog.snapshot.read().await;
let snapshot = public_guard
.as_ref()
.map_err(|_| "workhub_configuration_unavailable")?
.clone();
let connection = self
.agent
.0
.configuration
.catalog()
.await
.map_err(|_| "workhub_configuration_unavailable")?
.connections
.into_iter()
.find(|connection| {
connection.connection_id == preset.model.connection_id && connection.enabled
})
.ok_or("workhub_preset_connection_unavailable")?;
drop(public_guard);
if maka_providers::definition(&connection.provider, snapshot).is_err() {
return Err("workhub_preset_connection_unavailable".into());
}
if !connection
.enabled_model_ids
.contains(&preset.model.model_id)
{
return Err("workhub_preset_model_unavailable".into());
}
let selection = crate::ModelSelection {
connection_id: preset.model.connection_id.clone(),
revision: connection.revision,
model_id: preset.model.model_id.clone(),
};
Ok((preset, selection))
}
pub(super) async fn save_presets(&self, input: Presets) -> std::result::Result<Result, String> {
let mut ids = std::collections::BTreeSet::new();
if input.entries.iter().any(|p| {
p.id.is_empty()
|| !ids.insert(&p.id)
|| !p
.id
.bytes()
.all(|b| b.is_ascii_alphanumeric() || b"._:-".contains(&b))
|| p.name.trim().is_empty()
|| !self.tasks.workers.contains_key(&p.worker)
|| p.tools.iter().any(|s| s.is_empty())
|| p.tools
.iter()
.collect::<std::collections::BTreeSet<_>>()
.len()
!= p.tools.len()
|| maka_agent_facts::configuration::validation::target(&p.model).is_err()
}) {
return Ok(Result::Invalid);
}
let committed = self
.agent
.0
.store
.plugin_data_batch(
&namespace(),
vec![Mutation {
key: self.presets_key(),
expected_revision: input.revision,
data: Data::Present(
serde_json::to_value(&input.entries)
.map_err(|_| "workhub_encoding_failed")?,
),
}],
)
.await;
match committed {
Ok(rows) => Ok(Result::Saved {
snapshot: Presets {
revision: Some(rows[0].revision),
entries: input.entries,
},
}),
Err(maka_event_log::StoreError::RevisionConflict { .. }) => Ok(Result::Conflict {
snapshot: self.presets().await?,
}),
Err(_) => Err("workhub_presets_commit_unknown".into()),
}
}
}