Checking in progress, though not fully working as expected
This commit is contained in:
@@ -9,7 +9,6 @@ license.workspace = true
|
||||
jemalloc = []
|
||||
local_fs = []
|
||||
test-util = []
|
||||
crash_reporting = ["dep:sentry"]
|
||||
|
||||
[dependencies]
|
||||
async-channel.workspace = true
|
||||
@@ -34,7 +33,6 @@ rayon.workspace = true
|
||||
ignore = "0.4.23"
|
||||
line-span = "0.1.5"
|
||||
log.workspace = true
|
||||
sentry = { workspace = true, optional = true }
|
||||
shellexpand.workspace = true
|
||||
strum.workspace = true
|
||||
strum_macros.workspace = true
|
||||
|
||||
@@ -6,7 +6,6 @@ publish.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[features]
|
||||
crash_reporting = ["dep:sentry", "dep:sentry-log"]
|
||||
integration_tests = []
|
||||
local_fs = []
|
||||
release_bundle = []
|
||||
@@ -35,8 +34,6 @@ serde.workspace = true
|
||||
settings.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde_with.workspace = true
|
||||
sentry = { workspace = true, optional = true }
|
||||
sentry-log = { workspace = true, optional = true }
|
||||
strum.workspace = true
|
||||
strum_macros.workspace = true
|
||||
sysinfo.workspace = true
|
||||
|
||||
@@ -21,8 +21,6 @@ pub struct ChannelConfig {
|
||||
pub telemetry_config: Option<TelemetryConfig>,
|
||||
/// Configuration for autoupdate functionality.
|
||||
pub autoupdate_config: Option<AutoupdateConfig>,
|
||||
/// Configuration for crash reporting.
|
||||
pub crash_reporting_config: Option<CrashReportingConfig>,
|
||||
/// Configuration for statically-bundled MCP OAuth credentials.
|
||||
pub mcp_static_config: Option<McpStaticConfig>,
|
||||
}
|
||||
@@ -130,12 +128,6 @@ pub struct AutoupdateConfig {
|
||||
pub show_autoupdate_menu_items: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct CrashReportingConfig {
|
||||
/// The URL/DSN for sending error logs and crash reports to Sentry.
|
||||
pub sentry_url: Cow<'static, str>,
|
||||
}
|
||||
|
||||
/// Configuration for statically-bundled MCP OAuth credentials.
|
||||
///
|
||||
/// These are credentials for OAuth providers where dynamic client registration
|
||||
|
||||
@@ -48,7 +48,6 @@ impl ChannelState {
|
||||
oz_config: OzConfig::production(),
|
||||
telemetry_config: None,
|
||||
autoupdate_config: None,
|
||||
crash_reporting_config: None,
|
||||
mcp_static_config: None,
|
||||
},
|
||||
}
|
||||
@@ -196,13 +195,6 @@ impl ChannelState {
|
||||
CHANNEL_STATE.lock().config.telemetry_config.is_some()
|
||||
}
|
||||
|
||||
/// Returns whether this build has a crash reporting config and can therefore
|
||||
/// ship crash reports. Builds like OpenWarp intentionally ship with
|
||||
/// `crash_reporting_config: None`, in which case UI that controls crash
|
||||
/// reporting should be hidden since the toggle has no effect.
|
||||
pub fn is_crash_reporting_available() -> bool {
|
||||
CHANNEL_STATE.lock().config.crash_reporting_config.is_some()
|
||||
}
|
||||
|
||||
pub fn releases_base_url() -> Cow<'static, str> {
|
||||
CHANNEL_STATE
|
||||
@@ -346,16 +338,6 @@ impl ChannelState {
|
||||
option_env!("GIT_RELEASE_TAG")
|
||||
}
|
||||
|
||||
pub fn sentry_url() -> Cow<'static, str> {
|
||||
CHANNEL_STATE
|
||||
.lock()
|
||||
.config
|
||||
.crash_reporting_config
|
||||
.as_ref()
|
||||
.map(|crc| crc.sentry_url.clone())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn show_autoupdate_menu_items() -> bool {
|
||||
CHANNEL_STATE
|
||||
.lock()
|
||||
|
||||
@@ -103,25 +103,12 @@ macro_rules! report_if_error {
|
||||
}
|
||||
pub use report_if_error;
|
||||
|
||||
/// Returns whether or not a log entry with the given metadata should be
|
||||
/// ignored by Sentry.
|
||||
#[cfg(feature = "crash_reporting")]
|
||||
pub fn should_ignore_log_for_sentry(md: &log::Metadata) -> bool {
|
||||
// Filter out any Error-level log entries generated by report_error!().
|
||||
// report_error!() utilizes capture_anyhow() to report structured errors
|
||||
// instead of simple string error messages, and we don't want to _also_
|
||||
// report the Error-level log line to Sentry.
|
||||
md.target() == LOG_TARGET && md.level() == log::Level::Error
|
||||
}
|
||||
|
||||
pub trait ErrorExt: RegisteredError + std::error::Error {
|
||||
/// Returns whether or not an error is something that is actionable by our
|
||||
/// engineering team.
|
||||
fn is_actionable(&self) -> bool;
|
||||
|
||||
fn report_error(&self) {
|
||||
#[cfg(feature = "crash_reporting")]
|
||||
sentry::capture_error(self);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,5 @@ impl AnyhowErrorExt for anyhow::Error {
|
||||
}
|
||||
|
||||
fn report_error(&self) {
|
||||
#[cfg(feature = "crash_reporting")]
|
||||
sentry::integrations::anyhow::capture_anyhow(self);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,11 +7,8 @@ pub use overrides::{get_overrides, set_overrides};
|
||||
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Sequence)]
|
||||
pub enum FeatureFlag {
|
||||
Changelog,
|
||||
CocoaSentry,
|
||||
CrashReporting,
|
||||
DebugMode,
|
||||
Autoupdate,
|
||||
LogExpensiveFramesInSentry,
|
||||
WithSandboxTelemetry,
|
||||
RecordAppActiveEvents,
|
||||
|
||||
@@ -934,7 +931,6 @@ pub const LOCAL_FLAGS: &[FeatureFlag] = &[FeatureFlag::LocalClaudeCodexChildHarn
|
||||
/// Features enabled for the development team. The expectation is that, over
|
||||
/// time, these will move on to PREVIEW_FLAGS before being launched.
|
||||
pub const DOGFOOD_FLAGS: &[FeatureFlag] = &[
|
||||
FeatureFlag::LogExpensiveFramesInSentry,
|
||||
FeatureFlag::ToggleBootstrapBlock,
|
||||
FeatureFlag::CreatingSharedSessions,
|
||||
FeatureFlag::RemoveAutosuggestionDuringTabCompletions,
|
||||
@@ -979,7 +975,6 @@ pub const DOGFOOD_FLAGS: &[FeatureFlag] = &[
|
||||
FeatureFlag::PinnedTabs,
|
||||
FeatureFlag::BackgroundComputerUse,
|
||||
FeatureFlag::ContextWindowUsageBreakdown,
|
||||
FeatureFlag::CloudRunners,
|
||||
FeatureFlag::WaitForEventsParentRegistration,
|
||||
];
|
||||
|
||||
@@ -997,7 +992,6 @@ pub const PREVIEW_FLAGS: &[FeatureFlag] = &[
|
||||
pub const RELEASE_FLAGS: &[FeatureFlag] = &[
|
||||
FeatureFlag::Autoupdate,
|
||||
FeatureFlag::Changelog,
|
||||
FeatureFlag::CrashReporting,
|
||||
// Marked text is currently only supported on MacOS.
|
||||
#[cfg(target_os = "macos")]
|
||||
FeatureFlag::ImeMarkedText,
|
||||
|
||||
@@ -18,9 +18,6 @@ dirs.workspace = true
|
||||
log-panics = { version = "2.1.0", features = ["with-backtrace"] }
|
||||
zip = "2.1"
|
||||
|
||||
# Optional crash reporting integration.
|
||||
sentry = { workspace = true, optional = true }
|
||||
sentry-log = { workspace = true, optional = true }
|
||||
|
||||
[target.'cfg(target_family = "wasm")'.dependencies]
|
||||
console_error_panic_hook = { version = "0.1.6" }
|
||||
@@ -33,5 +30,4 @@ galaxy_web_event_bus.workspace = true
|
||||
tempfile.workspace = true
|
||||
|
||||
[features]
|
||||
crash_reporting = ["dep:sentry", "dep:sentry-log", "galaxy_core/crash_reporting"]
|
||||
agent_mode_evals = []
|
||||
|
||||
@@ -475,32 +475,6 @@ fn temp_log_file_path(log_directory: impl AsRef<Path>) -> PathBuf {
|
||||
.join(format!("{channel_logfile_name}.{TEMP_LOG_FILE_SUFFIX}"))
|
||||
}
|
||||
|
||||
#[cfg(feature = "crash_reporting")]
|
||||
fn sentry_log_filter(md: &log::Metadata) -> sentry_log::LogFilter {
|
||||
if galaxy_core::errors::should_ignore_log_for_sentry(md) {
|
||||
return sentry_log::LogFilter::Ignore;
|
||||
}
|
||||
|
||||
match md.target() {
|
||||
// Ignore any log lines that come from the `log_panics` crate.
|
||||
"panic" => sentry_log::LogFilter::Ignore,
|
||||
|
||||
// Filter out spammy INFO-level log lines from wgpu.
|
||||
t if t.starts_with("wgpu_core") || t.starts_with("wgpu_hal") => {
|
||||
sentry_log::LogFilter::Ignore
|
||||
}
|
||||
|
||||
// Filter out the "redraw_frame" logging from breadcrumbs.
|
||||
"galaxyui_core::core::redraw_frame" => sentry_log::LogFilter::Ignore,
|
||||
|
||||
// Filter out logs from the crash-reporting implementation, in case it logs
|
||||
// anything in the process of forwarding logs to Sentry.
|
||||
t if t.starts_with("galaxy::crash_reporting::") => sentry_log::LogFilter::Ignore,
|
||||
|
||||
_ => sentry_log::default_filter(md),
|
||||
}
|
||||
}
|
||||
|
||||
fn init_internal(
|
||||
is_from_crash_recovery_process: bool,
|
||||
is_cli: bool,
|
||||
@@ -610,16 +584,6 @@ fn init_internal(
|
||||
base_logger.format(format_for_terminal_output);
|
||||
}
|
||||
|
||||
#[cfg(feature = "crash_reporting")]
|
||||
{
|
||||
let base_logger = base_logger.build();
|
||||
log::set_max_level(base_logger.filter());
|
||||
let logger = sentry_log::SentryLogger::with_dest(base_logger).filter(sentry_log_filter);
|
||||
log::set_boxed_logger(Box::new(logger))
|
||||
.expect("Should not have already initialized a logger");
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "crash_reporting"))]
|
||||
base_logger.init();
|
||||
|
||||
// If we're logging to a file, initialize the `log_panics` crate, which
|
||||
|
||||
@@ -53,7 +53,6 @@ pub fn main() -> Result<()> {
|
||||
workload_audience_url: None,
|
||||
},
|
||||
telemetry_config: None,
|
||||
crash_reporting_config: None,
|
||||
autoupdate_config: None,
|
||||
mcp_static_config: None,
|
||||
},
|
||||
|
||||
@@ -546,7 +546,7 @@ pub fn test_restore_snapshot_with_settings_page() -> Builder {
|
||||
TestStep::new("Verify settings pane restoration")
|
||||
.add_assertion(assert_pane_title(0, 1, "Settings"))
|
||||
.add_assertion(move |app, window_id| {
|
||||
// Verify the settings view exists and is on the Referrals page.
|
||||
// Verify the settings view exists and is on the About page.
|
||||
let settings_views: Vec<ViewHandle<SettingsView>> = app
|
||||
.views_of_type(window_id)
|
||||
.expect("Settings view must exist");
|
||||
@@ -556,7 +556,7 @@ pub fn test_restore_snapshot_with_settings_page() -> Builder {
|
||||
settings_view.read(app, |view, _| {
|
||||
async_assert_eq!(
|
||||
view.current_settings_section(),
|
||||
SettingsSection::Referrals
|
||||
SettingsSection::About
|
||||
)
|
||||
})
|
||||
}),
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
[package]
|
||||
name = "managed_secrets_wasm"
|
||||
version = "0.1.0"
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
publish.workspace = true
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
galaxy_managed_secrets.workspace = true
|
||||
wasm-bindgen.workspace = true
|
||||
@@ -1,118 +0,0 @@
|
||||
use galaxy_managed_secrets::{ManagedSecretValue, UploadKey, init_envelope};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
/// Called once when the WASM module is instantiated.
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn start() {
|
||||
init_envelope();
|
||||
}
|
||||
|
||||
/// Helper: import keyset and encrypt a secret value.
|
||||
fn do_encrypt(
|
||||
public_key_base64: &str,
|
||||
actor_uid: &str,
|
||||
secret_name: &str,
|
||||
secret: &ManagedSecretValue,
|
||||
) -> Result<String, JsValue> {
|
||||
let upload_key = UploadKey::import_public_keyset(public_key_base64)
|
||||
.map_err(|e| JsValue::from_str(&format!("failed to import public key: {e}")))?;
|
||||
|
||||
upload_key
|
||||
.encrypt_secret(actor_uid, secret_name, secret)
|
||||
.map_err(|e| JsValue::from_str(&format!("encryption failed: {e}")))
|
||||
}
|
||||
|
||||
/// Encrypt a raw secret value.
|
||||
#[wasm_bindgen]
|
||||
pub fn encrypt_raw_secret(
|
||||
public_key_base64: &str,
|
||||
actor_uid: &str,
|
||||
secret_name: &str,
|
||||
secret_value: &str,
|
||||
) -> Result<String, JsValue> {
|
||||
do_encrypt(
|
||||
public_key_base64,
|
||||
actor_uid,
|
||||
secret_name,
|
||||
&ManagedSecretValue::raw_value(secret_value),
|
||||
)
|
||||
}
|
||||
|
||||
/// Encrypt an Anthropic API key secret.
|
||||
#[wasm_bindgen]
|
||||
pub fn encrypt_anthropic_api_key_secret(
|
||||
public_key_base64: &str,
|
||||
actor_uid: &str,
|
||||
secret_name: &str,
|
||||
api_key: &str,
|
||||
) -> Result<String, JsValue> {
|
||||
do_encrypt(
|
||||
public_key_base64,
|
||||
actor_uid,
|
||||
secret_name,
|
||||
&ManagedSecretValue::anthropic_api_key(api_key),
|
||||
)
|
||||
}
|
||||
|
||||
/// Encrypt an Anthropic Bedrock API key secret.
|
||||
#[wasm_bindgen]
|
||||
pub fn encrypt_anthropic_bedrock_api_key_secret(
|
||||
public_key_base64: &str,
|
||||
actor_uid: &str,
|
||||
secret_name: &str,
|
||||
aws_bearer_token_bedrock: &str,
|
||||
aws_region: &str,
|
||||
) -> Result<String, JsValue> {
|
||||
do_encrypt(
|
||||
public_key_base64,
|
||||
actor_uid,
|
||||
secret_name,
|
||||
&ManagedSecretValue::anthropic_bedrock_api_key(aws_bearer_token_bedrock, aws_region),
|
||||
)
|
||||
}
|
||||
|
||||
/// Encrypt an Anthropic Bedrock access key secret.
|
||||
///
|
||||
/// `aws_session_token` is optional and may be `None` for persistent IAM credentials
|
||||
/// that do not require a session token.
|
||||
#[wasm_bindgen]
|
||||
pub fn encrypt_anthropic_bedrock_access_key_secret(
|
||||
public_key_base64: &str,
|
||||
actor_uid: &str,
|
||||
secret_name: &str,
|
||||
aws_access_key_id: &str,
|
||||
aws_secret_access_key: &str,
|
||||
aws_session_token: Option<String>,
|
||||
aws_region: &str,
|
||||
) -> Result<String, JsValue> {
|
||||
do_encrypt(
|
||||
public_key_base64,
|
||||
actor_uid,
|
||||
secret_name,
|
||||
&ManagedSecretValue::anthropic_bedrock_access_key(
|
||||
aws_access_key_id,
|
||||
aws_secret_access_key,
|
||||
aws_session_token,
|
||||
aws_region,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
/// Encrypt an OpenAI API key secret.
|
||||
///
|
||||
/// `base_url` is optional; when `None`, the harness uses the provider's default endpoint.
|
||||
#[wasm_bindgen]
|
||||
pub fn encrypt_openai_api_key_secret(
|
||||
public_key_base64: &str,
|
||||
actor_uid: &str,
|
||||
secret_name: &str,
|
||||
api_key: &str,
|
||||
base_url: Option<String>,
|
||||
) -> Result<String, JsValue> {
|
||||
do_encrypt(
|
||||
public_key_base64,
|
||||
actor_uid,
|
||||
secret_name,
|
||||
&ManagedSecretValue::openai_api_key(api_key, base_url),
|
||||
)
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
[package]
|
||||
name = "warp_multi_agent_client"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
authors.workspace = true
|
||||
publish.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[features]
|
||||
agent_mode_evals = ["warp_server_client/agent_mode_evals"]
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
base64.workspace = true
|
||||
cfg-if.workspace = true
|
||||
futures.workspace = true
|
||||
prost.workspace = true
|
||||
reqwest-eventsource.workspace = true
|
||||
thiserror.workspace = true
|
||||
tracing.workspace = true
|
||||
tracing-futures.workspace = true
|
||||
warp_core.workspace = true
|
||||
warp_multi_agent_api.workspace = true
|
||||
warp_server_client.workspace = true
|
||||
@@ -1,170 +0,0 @@
|
||||
use base64::Engine as _;
|
||||
use base64::prelude::BASE64_URL_SAFE;
|
||||
use futures::StreamExt as _;
|
||||
use prost::Message as _;
|
||||
use tracing_futures::Instrument as _;
|
||||
use warp_core::channel::ChannelState;
|
||||
#[cfg(feature = "agent_mode_evals")]
|
||||
use warp_server_client::base_client::EVAL_USER_ID_HEADER;
|
||||
use warp_server_client::base_client::{AmbientHeaderPolicy, BaseClient};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("Failed to authenticate multi-agent request")]
|
||||
Authentication(#[source] anyhow::Error),
|
||||
|
||||
#[error("Failed to resolve ambient headers for multi-agent request")]
|
||||
AmbientHeaders(#[source] anyhow::Error),
|
||||
|
||||
#[error("Failed to decode base64 multi-agent response event")]
|
||||
Base64Decode(#[source] base64::DecodeError),
|
||||
|
||||
#[error("Failed to decode protobuf multi-agent response event")]
|
||||
ProtobufDecode(#[source] prost::DecodeError),
|
||||
|
||||
#[error("Multi-agent eventsource stream failed: {0:?}")]
|
||||
EventSource(Box<reqwest_eventsource::Error>),
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_family = "wasm")] {
|
||||
/// A multi-agent response event stream without an unnecessary `Send` bound on WASM.
|
||||
pub type OutputStream = futures::stream::LocalBoxStream<
|
||||
'static,
|
||||
Result<warp_multi_agent_api::ResponseEvent, Error>,
|
||||
>;
|
||||
} else {
|
||||
/// A multi-agent response event stream that can be sent between native threads.
|
||||
pub type OutputStream = futures::stream::BoxStream<
|
||||
'static,
|
||||
Result<warp_multi_agent_api::ResponseEvent, Error>,
|
||||
>;
|
||||
}
|
||||
}
|
||||
|
||||
/// Opens a decoded multi-agent response event stream.
|
||||
pub async fn generate_multi_agent_output(
|
||||
client: &BaseClient,
|
||||
request: &warp_multi_agent_api::Request,
|
||||
) -> Result<OutputStream, Error> {
|
||||
let auth_token = client
|
||||
.get_or_refresh_access_token()
|
||||
.await
|
||||
.map_err(Error::Authentication)?;
|
||||
let is_passive = is_passive_suggestion_request(request);
|
||||
let url = endpoint_url(is_passive);
|
||||
|
||||
let mut request_builder = client
|
||||
.http_client()
|
||||
.post(url)
|
||||
.proto(request)
|
||||
.prevent_sleep("Agent Mode request in-progress");
|
||||
if let Some(token) = auth_token.as_bearer_token() {
|
||||
request_builder = request_builder.bearer_auth(token);
|
||||
}
|
||||
|
||||
for (name, value) in client
|
||||
.ambient_headers(ambient_policy(is_passive))
|
||||
.await
|
||||
.map_err(Error::AmbientHeaders)?
|
||||
{
|
||||
request_builder = request_builder.header(name, value);
|
||||
}
|
||||
|
||||
#[cfg(feature = "agent_mode_evals")]
|
||||
if let Some(eval_user_id) = client.eval_user_id() {
|
||||
request_builder = request_builder.header(EVAL_USER_ID_HEADER, eval_user_id.to_string());
|
||||
}
|
||||
|
||||
let raw_stream = client.wrap_eventsource_with_iap_detection(request_builder.eventsource());
|
||||
let output_stream = raw_stream.filter_map(|event| async {
|
||||
match event {
|
||||
Ok(reqwest_eventsource::Event::Message(message_event)) => {
|
||||
Some(decode_response_event(&message_event.data))
|
||||
}
|
||||
Ok(reqwest_eventsource::Event::Open) => None,
|
||||
Err(error) => Some(Err(Error::EventSource(Box::new(error)))),
|
||||
}
|
||||
});
|
||||
|
||||
// Once we get the init event, add some identifiers to the trace span.
|
||||
let output_stream = output_stream.inspect(|event| {
|
||||
if let Ok(event) = &event {
|
||||
match &event.r#type {
|
||||
Some(warp_multi_agent_api::response_event::Type::Init(init)) => {
|
||||
tracing::info!("StreamInit");
|
||||
tracing::Span::current().record("conversation_id", &init.conversation_id);
|
||||
tracing::Span::current().record("request_id", &init.request_id);
|
||||
tracing::Span::current().record("run_id", &init.run_id);
|
||||
}
|
||||
Some(warp_multi_agent_api::response_event::Type::Finished(_finished)) => {
|
||||
tracing::info!("StreamFinished");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
// Wrap the output stream with a trace span.
|
||||
let output_stream = output_stream.instrument(tracing::info_span!(
|
||||
"generate_multi_agent_output",
|
||||
tags.cloud_agent = true,
|
||||
conversation_id = tracing::field::Empty,
|
||||
request_id = tracing::field::Empty,
|
||||
run_id = tracing::field::Empty,
|
||||
));
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_family = "wasm")] {
|
||||
Ok(output_stream.boxed_local())
|
||||
} else {
|
||||
Ok(output_stream.boxed())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_passive_suggestion_request(request: &warp_multi_agent_api::Request) -> bool {
|
||||
request.input.as_ref().is_some_and(|input| {
|
||||
matches!(
|
||||
input.r#type,
|
||||
Some(warp_multi_agent_api::request::input::Type::GeneratePassiveSuggestions(_))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn endpoint_url(is_passive: bool) -> String {
|
||||
format!(
|
||||
"{}/{}/{}",
|
||||
ChannelState::server_root_url(),
|
||||
if cfg!(feature = "agent_mode_evals") {
|
||||
"agent-mode-evals"
|
||||
} else {
|
||||
"ai"
|
||||
},
|
||||
if is_passive {
|
||||
"passive-suggestions"
|
||||
} else {
|
||||
"multi-agent"
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn ambient_policy(is_passive: bool) -> AmbientHeaderPolicy {
|
||||
if is_passive {
|
||||
// Passive suggestions read from the main conversation, but cannot modify it.
|
||||
AmbientHeaderPolicy::omit_all()
|
||||
} else {
|
||||
AmbientHeaderPolicy::workload_only()
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_response_event(data: &str) -> Result<warp_multi_agent_api::ResponseEvent, Error> {
|
||||
let decoded_data = BASE64_URL_SAFE
|
||||
.decode(data.trim_matches('"'))
|
||||
.map_err(Error::Base64Decode)?;
|
||||
warp_multi_agent_api::ResponseEvent::decode(decoded_data.as_slice())
|
||||
.map_err(Error::ProtobufDecode)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "lib_tests.rs"]
|
||||
mod tests;
|
||||
@@ -1,77 +0,0 @@
|
||||
use base64::Engine as _;
|
||||
use base64::prelude::BASE64_URL_SAFE;
|
||||
use prost::Message as _;
|
||||
use warp_server_client::base_client::AmbientHeaderPolicy;
|
||||
|
||||
use super::{
|
||||
Error, ambient_policy, decode_response_event, endpoint_url, is_passive_suggestion_request,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn detects_passive_suggestion_requests() {
|
||||
let regular = warp_multi_agent_api::Request::default();
|
||||
let passive = warp_multi_agent_api::Request {
|
||||
input: Some(warp_multi_agent_api::request::Input {
|
||||
r#type: Some(
|
||||
warp_multi_agent_api::request::input::Type::GeneratePassiveSuggestions(
|
||||
Default::default(),
|
||||
),
|
||||
),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
assert!(!is_passive_suggestion_request(®ular));
|
||||
assert!(is_passive_suggestion_request(&passive));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn routes_regular_and_passive_requests_to_distinct_endpoints() {
|
||||
let prefix = if cfg!(feature = "agent_mode_evals") {
|
||||
"agent-mode-evals"
|
||||
} else {
|
||||
"ai"
|
||||
};
|
||||
|
||||
assert!(endpoint_url(false).ends_with(&format!("/{prefix}/multi-agent")));
|
||||
assert!(endpoint_url(true).ends_with(&format!("/{prefix}/passive-suggestions")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn selects_endpoint_specific_ambient_header_policies() {
|
||||
assert_eq!(ambient_policy(false), AmbientHeaderPolicy::workload_only());
|
||||
assert_eq!(ambient_policy(true), AmbientHeaderPolicy::omit_all());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decodes_quoted_base64_protobuf_response_event() {
|
||||
let expected = warp_multi_agent_api::ResponseEvent::default();
|
||||
let encoded = BASE64_URL_SAFE.encode(expected.encode_to_vec());
|
||||
|
||||
let decoded = decode_response_event(&format!("\"{encoded}\"")).unwrap();
|
||||
|
||||
assert_eq!(decoded, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn distinguishes_base64_and_protobuf_decode_errors() {
|
||||
assert!(matches!(
|
||||
decode_response_event("%"),
|
||||
Err(Error::Base64Decode(_))
|
||||
));
|
||||
|
||||
let invalid_protobuf = BASE64_URL_SAFE.encode([0xff]);
|
||||
assert!(matches!(
|
||||
decode_response_event(&invalid_protobuf),
|
||||
Err(Error::ProtobufDecode(_))
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
#[test]
|
||||
fn native_output_stream_is_send() {
|
||||
fn assert_send<T: Send>() {}
|
||||
|
||||
assert_send::<super::OutputStream>();
|
||||
}
|
||||
@@ -19,7 +19,6 @@ fn main() -> Result<()> {
|
||||
server_config: WarpServerConfig::production(),
|
||||
oz_config: OzConfig::production(),
|
||||
telemetry_config: None,
|
||||
crash_reporting_config: None,
|
||||
autoupdate_config: None,
|
||||
mcp_static_config: None,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user