Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
4314e00
- impl config commands
laststylebender14 Feb 4, 2025
c3d80cf
- show error instead of raising the error for config insert
laststylebender14 Feb 4, 2025
6c51592
- pick model from confi but fallback to env.
laststylebender14 Feb 4, 2025
b388171
- lint changes
laststylebender14 Feb 4, 2025
9b6695c
- lint changes
laststylebender14 Feb 4, 2025
b2d51d6
- fix test
laststylebender14 Feb 4, 2025
5e64919
- use primary_model method
laststylebender14 Feb 4, 2025
f96c030
- improve error
laststylebender14 Feb 4, 2025
2c92aad
- make commands typesafe
laststylebender14 Feb 4, 2025
d805b55
- simplify parse method
laststylebender14 Feb 4, 2025
466c992
- add unit tests
laststylebender14 Feb 4, 2025
567bf08
- clean up config
laststylebender14 Feb 4, 2025
3ac21de
- fmt changes
laststylebender14 Feb 4, 2025
e593f3d
- lint changes
laststylebender14 Feb 4, 2025
cab32c1
- use better variant names
laststylebender14 Feb 4, 2025
8e1f723
- drop un-necessary test
laststylebender14 Feb 4, 2025
425df7b
- lint fmt changes
laststylebender14 Feb 4, 2025
215f745
- remove models from command
laststylebender14 Feb 4, 2025
5b01b2f
- update model name on set command.
laststylebender14 Feb 4, 2025
264cc20
- use similar style as /info command
laststylebender14 Feb 5, 2025
c3a4b2b
- format errors with StatusDisplay::failed
laststylebender14 Feb 5, 2025
427f337
- format the it like /info
laststylebender14 Feb 5, 2025
25f2118
[autofix.ci] apply automated fixes
autofix-ci[bot] Feb 5, 2025
df44fce
Merge branch 'main' of https://github.com/antinomyhq/forge into feat/…
tusharmath Feb 6, 2025
b7bc85c
feat: implement Info conversion for Config and remove display string …
tusharmath Feb 6, 2025
81da849
feat: refactor configuration handling and introduce error management
tusharmath Feb 6, 2025
4fe51f5
refactor: clean up imports and formatting across multiple files
tusharmath Feb 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 0 additions & 106 deletions crates/forge_domain/src/command.rs

This file was deleted.

2 changes: 0 additions & 2 deletions crates/forge_domain/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ mod arena;
mod chat_request;
mod chat_response;
mod chat_stream_ext;
mod command;
mod config;
mod context;
mod conversation;
Expand Down Expand Up @@ -35,7 +34,6 @@ pub use agent::*;
pub use chat_request::*;
pub use chat_response::*;
pub use chat_stream_ext::*;
pub use command::*;
pub use config::*;
pub use context::*;
pub use conversation::*;
Expand Down
4 changes: 2 additions & 2 deletions crates/forge_domain/src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ impl Parameters {
pub struct ModelId(String);

impl ModelId {
pub fn new(id: &str) -> Self {
Self(id.to_string())
pub fn new<T: Into<String>>(id: T) -> Self {
Self(id.into())
}
}

Expand Down
3 changes: 2 additions & 1 deletion crates/forge_main/src/completer/command.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use forge_domain::Command;
use reedline::{Completer, Span, Suggestion};

use crate::model::Command;

#[derive(Default)]
pub struct CommandCompleter;

Expand Down
168 changes: 168 additions & 0 deletions crates/forge_main/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
use std::collections::HashMap;
use std::fmt::{Display, Formatter};

use forge_domain::Environment;

use crate::info::Info;
use crate::model::ConfigKey;

impl From<&Config> for Info {
fn from(config: &Config) -> Self {
let mut info = Info::new().add_title("Configuration");
if config.is_empty() {
info = info.add_item("Status", "No configurations set");
} else {
let mut configs: Vec<_> = config.values.iter().collect();
configs.sort_by(|a, b| a.0.as_str().cmp(b.0.as_str())); // Sort by key string
for (key, value) in configs {
info = info.add_item(key.as_str(), value.as_str());
}
}
info
}
}

/// Custom error type for configuration-related errors
#[derive(Debug, thiserror::Error)]
pub enum ConfigError {
#[error("Model name cannot be empty")]
EmptyModelName,
#[error("Tool timeout must be greater than zero")]
NonPositiveTimeout,
#[error("Failed to parse timeout value: {0}")]
MalformedTimeout(String),
}

/// Represents configuration values with their specific types
#[derive(Debug, Clone)]
pub enum ConfigValue {
/// Model identifier string
Model(String),
/// Tool timeout in seconds
ToolTimeout(u32),
}

impl ConfigValue {
/// Returns the string representation of the configuration value
pub fn as_str(&self) -> String {
match self {
ConfigValue::Model(model) => model.clone(),
ConfigValue::ToolTimeout(timeout) => timeout.to_string(),
}
}

/// Creates a new ConfigValue from a key-value pair
pub fn from_key_value(key: &ConfigKey, value: &str) -> Result<Self, ConfigError> {
match key {
ConfigKey::PrimaryModel | ConfigKey::SecondaryModel => {
if value.trim().is_empty() {
Err(ConfigError::EmptyModelName)
} else {
Ok(ConfigValue::Model(value.to_string()))
}
}
ConfigKey::ToolTimeout => match value.parse::<u32>() {
Ok(0) => Err(ConfigError::NonPositiveTimeout),
Ok(timeout) => Ok(ConfigValue::ToolTimeout(timeout)),
Err(_) => Err(ConfigError::MalformedTimeout(value.to_string())),
},
}
}
}

impl Display for ConfigValue {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}

/// Main configuration structure holding all config values
#[derive(Default)]
pub struct Config {
values: HashMap<ConfigKey, ConfigValue>,
}

impl From<&Environment> for Config {
fn from(env: &Environment) -> Self {
let mut config = Config::default();
// No need to handle errors here as we control the input values
let _ = config.insert(&ConfigKey::PrimaryModel, &env.large_model_id);
let _ = config.insert(&ConfigKey::SecondaryModel, &env.small_model_id);
let _ = config.insert(&ConfigKey::ToolTimeout, "20");
config
}
}

impl Config {
/// Returns the primary model configuration if set
pub fn primary_model(&self) -> Option<String> {
self.get_model(&ConfigKey::PrimaryModel)
}

/// Helper method to get model configuration
fn get_model(&self, key: &ConfigKey) -> Option<String> {
self.values.get(key).and_then(|v| match v {
ConfigValue::Model(m) => Some(m.clone()),
_ => None,
})
}

/// Gets a configuration value by key string
pub fn get(&self, key: &ConfigKey) -> Option<String> {
self.values.get(key).map(|v| v.as_str())
}

/// Inserts a new configuration value
pub fn insert(&mut self, key: &ConfigKey, value: &str) -> Result<(), ConfigError> {
let config_value = ConfigValue::from_key_value(key, value)?;
self.values.insert(key.clone(), config_value);
Ok(())
}

/// Checks if the configuration is empty
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_config_basic() {
let mut config = Config::default();
assert!(config.is_empty());

// Test setting and getting values
config.insert(&ConfigKey::PrimaryModel, "gpt-4").unwrap();
assert_eq!(config.get(&ConfigKey::PrimaryModel).unwrap(), "gpt-4");

config.insert(&ConfigKey::ToolTimeout, "30").unwrap();
assert_eq!(config.get(&ConfigKey::ToolTimeout).unwrap(), "30");

// Test type-safe accessors
assert_eq!(config.primary_model().unwrap(), "gpt-4");

// Test overwriting values
config
.insert(&ConfigKey::PrimaryModel, "gpt-3.5-turbo")
.unwrap();
assert_eq!(config.primary_model().unwrap(), "gpt-3.5-turbo");

// Test getting non-existent key
assert!(config.get(&ConfigKey::SecondaryModel).is_none());

// Test invalid operations
assert!(matches!(
config
.insert(&ConfigKey::ToolTimeout, "invalid")
.unwrap_err(),
ConfigError::MalformedTimeout(_)
));
assert!(matches!(
config.insert(&ConfigKey::ToolTimeout, "0").unwrap_err(),
ConfigError::NonPositiveTimeout
));
}
}
15 changes: 15 additions & 0 deletions crates/forge_main/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use thiserror::Error;

#[derive(Debug, Error)]
pub enum Error {
#[error("Missing command parameter: {0}")]
MissingParameter(String),

#[error("Unsupported command parameter: {0}")]
UnsupportedParameter(String),

#[error("Invalid argument: {0}")]
MissingParameterValue(String),
}

pub type Result<A> = std::result::Result<A, Error>;
3 changes: 2 additions & 1 deletion crates/forge_main/src/input.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use std::path::PathBuf;

use async_trait::async_trait;
use forge_domain::{Command, Environment, Usage, UserInput};
use forge_domain::{Environment, Usage};
use tokio::fs;

use crate::console::CONSOLE;
use crate::editor::{ForgeEditor, ReadResult};
use crate::model::{Command, UserInput};
use crate::prompt::ForgePrompt;
use crate::status::StatusDisplay;

Expand Down
2 changes: 2 additions & 0 deletions crates/forge_main/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
mod banner;
mod cli;
mod completer;
mod config;
mod console;
mod editor;
mod error;
mod info;
mod input;
mod log;
Expand Down
Loading