first pass of merging in warp (doesn't build)
This commit is contained in:
+42
-13
@@ -4,24 +4,23 @@ pub mod util;
|
||||
#[cfg_attr(target_family = "wasm", path = "wasm.rs")]
|
||||
mod imp;
|
||||
|
||||
use crate::tab_configs::{TabConfig, TabConfigError};
|
||||
use crate::themes::theme::GalaxyThemeConfig;
|
||||
use crate::{
|
||||
launch_configs::launch_config::LaunchConfig, themes::theme::ThemeKind,
|
||||
workflows::workflow::Workflow,
|
||||
};
|
||||
use galaxy_core::ui::theme::GalaxyTheme;
|
||||
use galaxyui::{Entity, ModelContext, SingletonEntity};
|
||||
use lazy_static::lazy_static;
|
||||
#[cfg(feature = "local_fs")]
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) use imp::load_tab_configs;
|
||||
#[cfg(feature = "local_fs")]
|
||||
pub use imp::load_workflows;
|
||||
pub use imp::{load_launch_configs, load_theme_configs};
|
||||
use lazy_static::lazy_static;
|
||||
use galaxy_core::ui::theme::WarpTheme;
|
||||
use warpui::{Entity, ModelContext, SingletonEntity};
|
||||
|
||||
use crate::ai::custom_model_routers::{CustomModelRouter, ModelConfigError};
|
||||
use crate::launch_configs::launch_config::LaunchConfig;
|
||||
use crate::tab_configs::{TabConfig, TabConfigError};
|
||||
use crate::themes::theme::{ThemeKind, WarpThemeConfig};
|
||||
use crate::workflows::workflow::Workflow;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref LAUNCH_CONFIG_COMMENT: String = format!(
|
||||
@@ -63,6 +62,12 @@ pub enum GalaxyConfigUpdateEvent {
|
||||
/// Emitted when one or more tab config files failed to parse.
|
||||
#[cfg_attr(target_family = "wasm", allow(dead_code))]
|
||||
TabConfigErrors(Vec<TabConfigError>),
|
||||
/// The local `custom_model_routers/` custom model routers were created, modified, or deleted.
|
||||
#[cfg_attr(not(feature = "local_fs"), allow(dead_code))]
|
||||
ModelConfigs,
|
||||
/// Emitted when one or more `custom_model_routers/` files failed to parse.
|
||||
#[cfg_attr(target_family = "wasm", allow(dead_code))]
|
||||
ModelConfigErrors(Vec<ModelConfigError>),
|
||||
/// The settings file (`settings.toml`) was created, modified, or deleted.
|
||||
#[cfg_attr(not(feature = "local_fs"), expect(dead_code))]
|
||||
Settings,
|
||||
@@ -89,6 +94,12 @@ pub struct GalaxyConfig {
|
||||
tab_config_errors: Vec<TabConfigError>,
|
||||
theme_config: GalaxyThemeConfig,
|
||||
local_user_workflows: Vec<Workflow>,
|
||||
/// User-defined custom model routers loaded from `~/.warp/custom_model_routers/`.
|
||||
#[cfg_attr(target_family = "wasm", allow(dead_code))]
|
||||
custom_model_routers: Vec<CustomModelRouter>,
|
||||
/// Errors for `custom_model_routers/` files that failed to parse.
|
||||
#[cfg_attr(target_family = "wasm", allow(dead_code))]
|
||||
custom_model_router_errors: Vec<ModelConfigError>,
|
||||
}
|
||||
|
||||
/// Platform-independent parts of GalaxyConfig.
|
||||
@@ -120,7 +131,19 @@ impl GalaxyConfig {
|
||||
&self.local_user_workflows
|
||||
}
|
||||
|
||||
/// Saving the newly created launch configuration to the GalaxyConfig that we currently
|
||||
/// The local (YAML-sourced) custom model routers.
|
||||
#[cfg_attr(target_family = "wasm", allow(dead_code))]
|
||||
pub fn custom_model_routers(&self) -> &Vec<CustomModelRouter> {
|
||||
&self.custom_model_routers
|
||||
}
|
||||
|
||||
/// Parse errors for `custom_model_routers/` files that failed to load.
|
||||
#[cfg_attr(target_family = "wasm", allow(dead_code))]
|
||||
pub fn custom_model_router_errors(&self) -> &Vec<ModelConfigError> {
|
||||
&self.custom_model_router_errors
|
||||
}
|
||||
|
||||
/// Saving the newly created launch configuration to the WarpConfig that we currently
|
||||
/// have.
|
||||
pub fn append_launch_config(
|
||||
&mut self,
|
||||
@@ -189,11 +212,17 @@ pub fn launch_configs_dir() -> PathBuf {
|
||||
}
|
||||
|
||||
/// Returns the path to the directory containing the user's tab configs.
|
||||
#[cfg_attr(target_family = "wasm", expect(dead_code))]
|
||||
pub fn tab_configs_dir() -> PathBuf {
|
||||
base_dir().join("tab_configs")
|
||||
}
|
||||
|
||||
/// Returns the path to the directory containing the user's custom model router
|
||||
/// configs (`~/.warp/custom_model_routers/`). Each file defines a single router.
|
||||
#[cfg_attr(target_family = "wasm", expect(dead_code))]
|
||||
pub fn custom_model_routers_dir() -> PathBuf {
|
||||
base_dir().join("custom_model_routers")
|
||||
}
|
||||
|
||||
/// Returns the path to the directory containing the built-in default tab configs.
|
||||
/// These are shipped with Warp and user-editable (Warp does not overwrite modifications).
|
||||
#[cfg_attr(target_family = "wasm", expect(dead_code))]
|
||||
@@ -400,5 +429,5 @@ impl Entity for GalaxyConfig {
|
||||
impl SingletonEntity for GalaxyConfig {}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "mod_test.rs"]
|
||||
#[path = "mod_tests.rs"]
|
||||
mod tests;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use super::*;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
use super::*;
|
||||
use crate::launch_configs::launch_config::PaneTemplateType;
|
||||
use crate::tab_configs::render_tab_config;
|
||||
use crate::tab_configs::tab_config::{generated_worktree_repo_dir, TabConfigPaneType};
|
||||
+116
-13
@@ -1,13 +1,23 @@
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::{fs, io};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use galaxyui::{ModelContext, SingletonEntity};
|
||||
use itertools::Itertools;
|
||||
use repo_metadata::RepositoryUpdate;
|
||||
use galaxyui::{ModelContext, ModelHandle, SingletonEntity};
|
||||
|
||||
use super::util::{
|
||||
for_each_dir_entry, has_name, is_config_file, parse_model_config_dir_entry,
|
||||
parse_multi_launch_config_dir_entry, parse_multi_workflow_dir_entry,
|
||||
parse_single_theme_dir_entry, parse_tab_config_dir_entry,
|
||||
};
|
||||
use super::{
|
||||
custom_model_routers_dir, launch_configs_dir, tab_configs_dir, themes_dir, workflows_dir,
|
||||
WarpConfigUpdateEvent, LAUNCH_CONFIG_COMMENT,
|
||||
};
|
||||
use crate::ai::custom_model_routers::{CustomModelRouter, ModelConfigError};
|
||||
use crate::features::FeatureFlag;
|
||||
use crate::galaxy_managed_paths_watcher::{
|
||||
repository_update_touches_path, repository_update_touches_prefix, GalaxyManagedPathsWatcher,
|
||||
@@ -18,16 +28,7 @@ use crate::tab_configs::{TabConfig, TabConfigError};
|
||||
use crate::themes::theme::GalaxyThemeConfig;
|
||||
use crate::workflows::workflow::Workflow;
|
||||
|
||||
use super::util::{
|
||||
for_each_dir_entry, has_name, is_config_file, parse_multi_launch_config_dir_entry,
|
||||
parse_multi_workflow_dir_entry, parse_single_theme_dir_entry, parse_tab_config_dir_entry,
|
||||
};
|
||||
use super::{
|
||||
launch_configs_dir, tab_configs_dir, themes_dir, workflows_dir, GalaxyConfigUpdateEvent,
|
||||
LAUNCH_CONFIG_COMMENT,
|
||||
};
|
||||
|
||||
impl super::GalaxyConfig {
|
||||
impl super::WarpConfig {
|
||||
pub fn new(ctx: &mut ModelContext<Self>) -> Self {
|
||||
// Load launch configs, and workflows from disk asynchronously on a background
|
||||
// thread.
|
||||
@@ -62,6 +63,19 @@ impl super::GalaxyConfig {
|
||||
ctx.emit(GalaxyConfigUpdateEvent::LocalUserWorkflows);
|
||||
},
|
||||
);
|
||||
if FeatureFlag::CustomModelRouters.is_enabled() {
|
||||
let _ = ctx.spawn(
|
||||
async move { load_model_configs(&custom_model_routers_dir()) },
|
||||
|me, (models, errors), ctx| {
|
||||
me.custom_model_routers = models;
|
||||
me.custom_model_router_errors = errors;
|
||||
ctx.emit(WarpConfigUpdateEvent::ModelConfigs);
|
||||
// Don't emit ModelConfigErrors on startup — like tab configs,
|
||||
// the error toast should only appear when the user saves a
|
||||
// file, not on app restart.
|
||||
},
|
||||
);
|
||||
}
|
||||
ctx.subscribe_to_model(
|
||||
&GalaxyManagedPathsWatcher::handle(ctx),
|
||||
Self::handle_warp_managed_paths_event,
|
||||
@@ -75,7 +89,8 @@ impl super::GalaxyConfig {
|
||||
|
||||
fn handle_warp_managed_paths_event(
|
||||
&mut self,
|
||||
event: &GalaxyManagedPathsWatcherEvent,
|
||||
_: ModelHandle<WarpManagedPathsWatcher>,
|
||||
event: &WarpManagedPathsWatcherEvent,
|
||||
ctx: &mut ModelContext<Self>,
|
||||
) {
|
||||
let GalaxyManagedPathsWatcherEvent::FilesChanged(update) = event;
|
||||
@@ -128,6 +143,23 @@ impl super::GalaxyConfig {
|
||||
);
|
||||
}
|
||||
|
||||
if FeatureFlag::CustomModelRouters.is_enabled()
|
||||
&& update_touches_dir(update, &custom_model_routers_dir())
|
||||
{
|
||||
let dir_path = custom_model_routers_dir();
|
||||
let _ = ctx.spawn(
|
||||
async move { load_model_configs(&dir_path) },
|
||||
|me, (models, errors), ctx| {
|
||||
me.custom_model_routers = models;
|
||||
me.custom_model_router_errors = errors.clone();
|
||||
ctx.emit(WarpConfigUpdateEvent::ModelConfigs);
|
||||
if !errors.is_empty() {
|
||||
ctx.emit(WarpConfigUpdateEvent::ModelConfigErrors(errors));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if FeatureFlag::SettingsFile.is_enabled()
|
||||
&& update_touches_path(update, &crate::settings::user_preferences_toml_file_path())
|
||||
{
|
||||
@@ -135,6 +167,53 @@ impl super::GalaxyConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes a custom model router to disk as a YAML file.
|
||||
///
|
||||
/// When `existing_path` is provided (editing) the file at that path is
|
||||
/// overwritten; otherwise a new file is created under
|
||||
/// `custom_model_routers_dir()`. The file name is derived from `name` by
|
||||
/// lowercasing and replacing non-alphanumeric characters (except `-`) with
|
||||
/// `_`. If the candidate path already exists, a numeric suffix is appended
|
||||
/// (`_2`, `_3`, …) until a free slot is found. Returns the path written to.
|
||||
#[cfg(feature = "local_fs")]
|
||||
pub fn save_custom_model_router(
|
||||
name: &str,
|
||||
yaml: &str,
|
||||
existing_path: Option<&std::path::Path>,
|
||||
) -> anyhow::Result<std::path::PathBuf> {
|
||||
let dir = custom_model_routers_dir();
|
||||
std::fs::create_dir_all(&dir)
|
||||
.map_err(|e| anyhow::anyhow!("could not create custom_model_routers dir: {e}"))?;
|
||||
let path = if let Some(p) = existing_path {
|
||||
p.to_path_buf()
|
||||
} else {
|
||||
let sanitized = name
|
||||
.to_lowercase()
|
||||
.replace(|c: char| !c.is_alphanumeric() && c != '-', "_");
|
||||
let candidate = dir.join(format!("{sanitized}.yaml"));
|
||||
if candidate.exists() {
|
||||
(2..)
|
||||
.map(|n| dir.join(format!("{sanitized}_{n}.yaml")))
|
||||
.find(|p| !p.exists())
|
||||
.expect("infinite iterator always finds a free slot")
|
||||
} else {
|
||||
candidate
|
||||
}
|
||||
};
|
||||
std::fs::write(&path, yaml)
|
||||
.map_err(|e| anyhow::anyhow!("could not write router file: {e}"))?;
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
/// Deletes a custom model router file from disk.
|
||||
/// The filesystem watcher in [`Self::handle_warp_managed_paths_event`] will
|
||||
/// pick up the deletion and reload `custom_model_routers`.
|
||||
#[cfg(feature = "local_fs")]
|
||||
pub fn delete_custom_model_router(source_path: &std::path::Path) -> anyhow::Result<()> {
|
||||
std::fs::remove_file(source_path)
|
||||
.map_err(|e| anyhow::anyhow!("could not delete router file: {e}"))
|
||||
}
|
||||
|
||||
/// This method takes a file name candidate (appends .yaml if missing) and a LaunchConfig as
|
||||
/// arguments. It saves the file and returns the filename used if successful.
|
||||
#[cfg(feature = "local_fs")]
|
||||
@@ -191,6 +270,30 @@ pub fn load_launch_configs(launch_config_path: &Path) -> Vec<LaunchConfig> {
|
||||
.collect_vec()
|
||||
}
|
||||
|
||||
/// Loads custom model routers from the config directory at `dir_path`
|
||||
/// (`~/.warp/custom_model_routers/`), where each file defines a single router.
|
||||
/// Returns the parsed routers (sorted by display name) and any per-file
|
||||
/// parse/validation errors. If the directory does not exist, returns empty vecs.
|
||||
pub fn load_model_configs(dir_path: &Path) -> (Vec<CustomModelRouter>, Vec<ModelConfigError>) {
|
||||
let results = for_each_dir_entry(dir_path, parse_model_config_dir_entry);
|
||||
let mut models = Vec::new();
|
||||
let mut errors = Vec::new();
|
||||
for result in results {
|
||||
match result {
|
||||
Ok(model) => models.push(model),
|
||||
Err(error) => errors.push(error),
|
||||
}
|
||||
}
|
||||
models.sort_by(|a, b| {
|
||||
let a_name = a.info.display_name.to_lowercase();
|
||||
let b_name = b.info.display_name.to_lowercase();
|
||||
a_name
|
||||
.cmp(&b_name)
|
||||
.then_with(|| a.info.display_name.cmp(&b.info.display_name))
|
||||
});
|
||||
(models, errors)
|
||||
}
|
||||
|
||||
/// Loads all tab configs from `tab_config_path`. Each tab config is an individual TOML file.
|
||||
///
|
||||
/// Returns successfully parsed configs and any errors for files that failed to parse.
|
||||
|
||||
@@ -9,6 +9,9 @@ use itertools::Itertools;
|
||||
use serde::de::DeserializeOwned;
|
||||
use walkdir::{DirEntry, WalkDir};
|
||||
|
||||
use crate::ai::custom_model_routers::{
|
||||
parse_model_config_yaml, CustomModelRouter, ModelConfigError,
|
||||
};
|
||||
use crate::launch_configs::launch_config::LaunchConfig;
|
||||
use crate::tab_configs::{TabConfig, TabConfigError};
|
||||
use crate::themes::theme::{GalaxyTheme, GalaxyThemeConfig, ThemeKind};
|
||||
@@ -186,6 +189,28 @@ pub(super) fn parse_tab_config_dir_entry(
|
||||
)
|
||||
}
|
||||
|
||||
/// Parses a `DirEntry` as a single custom model router (one router per YAML file).
|
||||
/// Returns `None` for non-config files, otherwise the parsed router or a
|
||||
/// [`ModelConfigError`] describing the read/parse/validation failure.
|
||||
pub(super) fn parse_model_config_dir_entry(
|
||||
item: &DirEntry,
|
||||
) -> Option<Result<CustomModelRouter, ModelConfigError>> {
|
||||
let file_name = get_file_name(item)?;
|
||||
if !is_config_file(&file_name) {
|
||||
return None;
|
||||
}
|
||||
let make_error = |message: String| ModelConfigError {
|
||||
file_name: file_name.clone(),
|
||||
file_path: item.path().into(),
|
||||
error_message: message,
|
||||
};
|
||||
let contents = match fs::read_to_string(item.path()) {
|
||||
Ok(contents) => contents,
|
||||
Err(e) => return Some(Err(make_error(e.to_string()))),
|
||||
};
|
||||
Some(parse_model_config_yaml(&contents, Some(item.path())).map_err(make_error))
|
||||
}
|
||||
|
||||
/// Runs the given function on each `DirEntry` within the `Path`. If the path is not a directory,
|
||||
/// an empty `Vector` is returned. It works recursively, covering directories within a given path.
|
||||
pub(super) fn for_each_dir_entry<F, T>(path: &Path, dir_entry_fn: F) -> Vec<T>
|
||||
|
||||
@@ -3,17 +3,15 @@ use std::path::Path;
|
||||
use galaxyui::ModelContext;
|
||||
|
||||
use crate::launch_configs::launch_config::LaunchConfig;
|
||||
use crate::themes::theme::GalaxyThemeConfig;
|
||||
use crate::tab_configs::{TabConfig, TabConfigError};
|
||||
use crate::themes::theme::WarpThemeConfig;
|
||||
use crate::workflows::workflow::Workflow;
|
||||
|
||||
impl super::GalaxyConfig {
|
||||
pub fn new(_ctx: &mut ModelContext<Self>) -> Self {
|
||||
Self {
|
||||
launch_configs: Default::default(),
|
||||
tab_configs: Default::default(),
|
||||
tab_config_errors: Default::default(),
|
||||
theme_config: GalaxyThemeConfig::new(),
|
||||
local_user_workflows: Default::default(),
|
||||
theme_config: WarpThemeConfig::new(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,3 +37,10 @@ pub fn load_launch_configs(_launch_config_path: &Path) -> Vec<LaunchConfig> {
|
||||
// launch configs from any path.
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Loads all tab configs relative to the `tab_config_path`.
|
||||
pub(crate) fn load_tab_configs(_tab_config_path: &Path) -> (Vec<TabConfig>, Vec<TabConfigError>) {
|
||||
// There's no local filesystem for wasm, so we'll never be able to retrieve
|
||||
// tab configs from any path.
|
||||
Default::default()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user