fix: minor compatibility adjustments for server crate integration

This commit is contained in:
Sisyphus
2026-04-01 21:26:06 +09:00
parent 48e36d422a
commit 49151afe69
12 changed files with 45 additions and 31 deletions

View File

@@ -1,5 +1,5 @@
use crate::error::ApiError; use crate::error::ApiError;
use crate::providers::claw_provider::{self, ClawApiClient, AuthSource}; use crate::providers::claw_provider::{self, AuthSource, ClawApiClient};
use crate::providers::openai_compat::{self, OpenAiCompatClient, OpenAiCompatConfig}; use crate::providers::openai_compat::{self, OpenAiCompatClient, OpenAiCompatConfig};
use crate::providers::{self, Provider, ProviderKind}; use crate::providers::{self, Provider, ProviderKind};
use crate::types::{MessageRequest, MessageResponse, StreamEvent}; use crate::types::{MessageRequest, MessageResponse, StreamEvent};

View File

@@ -9,7 +9,7 @@ pub use client::{
resolve_startup_auth_source, MessageStream, OAuthTokenSet, ProviderClient, resolve_startup_auth_source, MessageStream, OAuthTokenSet, ProviderClient,
}; };
pub use error::ApiError; pub use error::ApiError;
pub use providers::claw_provider::{ClawApiClient, ClawApiClient as ApiClient, AuthSource}; pub use providers::claw_provider::{AuthSource, ClawApiClient, ClawApiClient as ApiClient};
pub use providers::openai_compat::{OpenAiCompatClient, OpenAiCompatConfig}; pub use providers::openai_compat::{OpenAiCompatClient, OpenAiCompatConfig};
pub use providers::{ pub use providers::{
detect_provider_kind, max_tokens_for_model, resolve_model_alias, ProviderKind, detect_provider_kind, max_tokens_for_model, resolve_model_alias, ProviderKind,

View File

@@ -652,7 +652,7 @@ mod tests {
use super::{ use super::{
now_unix_timestamp, oauth_token_is_expired, resolve_saved_oauth_token, now_unix_timestamp, oauth_token_is_expired, resolve_saved_oauth_token,
resolve_startup_auth_source, ClawApiClient, AuthSource, OAuthTokenSet, resolve_startup_auth_source, AuthSource, ClawApiClient, OAuthTokenSet,
}; };
use crate::types::{ContentBlockDelta, MessageRequest}; use crate::types::{ContentBlockDelta, MessageRequest};

View File

@@ -290,8 +290,7 @@ async fn live_stream_smoke_test() {
let client = ApiClient::from_env().expect("ANTHROPIC_API_KEY must be set"); let client = ApiClient::from_env().expect("ANTHROPIC_API_KEY must be set");
let mut stream = client let mut stream = client
.stream_message(&MessageRequest { .stream_message(&MessageRequest {
model: std::env::var("CLAW_MODEL") model: std::env::var("CLAW_MODEL").unwrap_or_else(|_| "claude-sonnet-4-6".to_string()),
.unwrap_or_else(|_| "claude-sonnet-4-6".to_string()),
max_tokens: 32, max_tokens: 32,
messages: vec![InputMessage::user_text( messages: vec![InputMessage::user_text(
"Reply with exactly: hello from rust", "Reply with exactly: hello from rust",

View File

@@ -386,8 +386,7 @@ mod tests {
let root = temp_dir(); let root = temp_dir();
fs::create_dir_all(&root).expect("create root"); fs::create_dir_all(&root).expect("create root");
fs::write(root.join("CLAW.md"), "custom guidance\n").expect("write existing claw md"); fs::write(root.join("CLAW.md"), "custom guidance\n").expect("write existing claw md");
fs::write(root.join(".gitignore"), ".claw/settings.local.json\n") fs::write(root.join(".gitignore"), ".claw/settings.local.json\n").expect("write gitignore");
.expect("write gitignore");
let first = initialize_repo(&root).expect("first init should succeed"); let first = initialize_repo(&root).expect("first init should succeed");
assert!(first assert!(first

View File

@@ -16,7 +16,7 @@ use std::thread;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use api::{ use api::{
resolve_startup_auth_source, ClawApiClient, AuthSource, ContentBlockDelta, InputContentBlock, resolve_startup_auth_source, AuthSource, ClawApiClient, ContentBlockDelta, InputContentBlock,
InputMessage, MessageRequest, MessageResponse, OutputContentBlock, InputMessage, MessageRequest, MessageResponse, OutputContentBlock,
StreamEvent as ApiStreamEvent, ToolChoice, ToolDefinition, ToolResultContentBlock, StreamEvent as ApiStreamEvent, ToolChoice, ToolDefinition, ToolResultContentBlock,
}; };
@@ -2037,8 +2037,7 @@ fn render_memory_report() -> Result<String, Box<dyn std::error::Error>> {
if project_context.instruction_files.is_empty() { if project_context.instruction_files.is_empty() {
lines.push("Discovered files".to_string()); lines.push("Discovered files".to_string());
lines.push( lines.push(
" No CLAW instruction files discovered in the current directory ancestry." " No CLAW instruction files discovered in the current directory ancestry.".to_string(),
.to_string(),
); );
} else { } else {
lines.push("Discovered files".to_string()); lines.push("Discovered files".to_string());
@@ -2790,7 +2789,8 @@ fn build_runtime(
allowed_tools: Option<AllowedToolSet>, allowed_tools: Option<AllowedToolSet>,
permission_mode: PermissionMode, permission_mode: PermissionMode,
progress_reporter: Option<InternalPromptProgressReporter>, progress_reporter: Option<InternalPromptProgressReporter>,
) -> Result<ConversationRuntime<DefaultRuntimeClient, CliToolExecutor>, Box<dyn std::error::Error>> { ) -> Result<ConversationRuntime<DefaultRuntimeClient, CliToolExecutor>, Box<dyn std::error::Error>>
{
let (feature_config, tool_registry) = build_runtime_plugin_state()?; let (feature_config, tool_registry) = build_runtime_plugin_state()?;
Ok(ConversationRuntime::new_with_features( Ok(ConversationRuntime::new_with_features(
session, session,

View File

@@ -24,8 +24,8 @@ pub use compact::{
get_compact_continuation_message, should_compact, CompactionConfig, CompactionResult, get_compact_continuation_message, should_compact, CompactionConfig, CompactionResult,
}; };
pub use config::{ pub use config::{
ConfigEntry, ConfigError, ConfigLoader, ConfigSource, McpManagedProxyServerConfig, ConfigEntry, ConfigError, ConfigLoader, ConfigSource, McpConfigCollection,
McpConfigCollection, McpOAuthConfig, McpRemoteServerConfig, McpSdkServerConfig, McpManagedProxyServerConfig, McpOAuthConfig, McpRemoteServerConfig, McpSdkServerConfig,
McpServerConfig, McpStdioServerConfig, McpTransport, McpWebSocketServerConfig, OAuthConfig, McpServerConfig, McpStdioServerConfig, McpTransport, McpWebSocketServerConfig, OAuthConfig,
ResolvedPermissionMode, RuntimeConfig, RuntimeFeatureConfig, RuntimeHookConfig, ResolvedPermissionMode, RuntimeConfig, RuntimeFeatureConfig, RuntimeHookConfig,
RuntimePluginConfig, ScopedMcpServerConfig, CLAW_SETTINGS_SCHEMA_NAME, RuntimePluginConfig, ScopedMcpServerConfig, CLAW_SETTINGS_SCHEMA_NAME,
@@ -45,7 +45,7 @@ pub use mcp::{
scoped_mcp_config_hash, unwrap_ccr_proxy_url, scoped_mcp_config_hash, unwrap_ccr_proxy_url,
}; };
pub use mcp_client::{ pub use mcp_client::{
McpManagedProxyTransport, McpClientAuth, McpClientBootstrap, McpClientTransport, McpClientAuth, McpClientBootstrap, McpClientTransport, McpManagedProxyTransport,
McpRemoteTransport, McpSdkTransport, McpStdioTransport, McpRemoteTransport, McpSdkTransport, McpStdioTransport,
}; };
pub use mcp_stdio::{ pub use mcp_stdio::{

View File

@@ -97,12 +97,10 @@ impl McpClientTransport {
McpServerConfig::Sdk(config) => Self::Sdk(McpSdkTransport { McpServerConfig::Sdk(config) => Self::Sdk(McpSdkTransport {
name: config.name.clone(), name: config.name.clone(),
}), }),
McpServerConfig::ManagedProxy(config) => { McpServerConfig::ManagedProxy(config) => Self::ManagedProxy(McpManagedProxyTransport {
Self::ManagedProxy(McpManagedProxyTransport { url: config.url.clone(),
url: config.url.clone(), id: config.id.clone(),
id: config.id.clone(), }),
})
}
} }
} }
} }

View File

@@ -1163,8 +1163,12 @@ mod tests {
} }
fn cleanup_script(script_path: &Path) { fn cleanup_script(script_path: &Path) {
fs::remove_file(script_path).expect("cleanup script"); if let Err(error) = fs::remove_file(script_path) {
fs::remove_dir_all(script_path.parent().expect("script parent")).expect("cleanup dir"); assert_eq!(error.kind(), std::io::ErrorKind::NotFound, "cleanup script");
}
if let Err(error) = fs::remove_dir_all(script_path.parent().expect("script parent")) {
assert_eq!(error.kind(), std::io::ErrorKind::NotFound, "cleanup dir");
}
} }
fn manager_server_config( fn manager_server_config(

View File

@@ -3,10 +3,13 @@ use std::fmt::{Display, Formatter};
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
use serde::{Deserialize, Serialize};
use crate::json::{JsonError, JsonValue}; use crate::json::{JsonError, JsonValue};
use crate::usage::TokenUsage; use crate::usage::TokenUsage;
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum MessageRole { pub enum MessageRole {
System, System,
User, User,
@@ -14,7 +17,8 @@ pub enum MessageRole {
Tool, Tool,
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ContentBlock { pub enum ContentBlock {
Text { Text {
text: String, text: String,
@@ -32,14 +36,14 @@ pub enum ContentBlock {
}, },
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ConversationMessage { pub struct ConversationMessage {
pub role: MessageRole, pub role: MessageRole,
pub blocks: Vec<ContentBlock>, pub blocks: Vec<ContentBlock>,
pub usage: Option<TokenUsage>, pub usage: Option<TokenUsage>,
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Session { pub struct Session {
pub version: u32, pub version: u32,
pub messages: Vec<ConversationMessage>, pub messages: Vec<ConversationMessage>,

View File

@@ -1,4 +1,5 @@
use crate::session::Session; use crate::session::Session;
use serde::{Deserialize, Serialize};
const DEFAULT_INPUT_COST_PER_MILLION: f64 = 15.0; const DEFAULT_INPUT_COST_PER_MILLION: f64 = 15.0;
const DEFAULT_OUTPUT_COST_PER_MILLION: f64 = 75.0; const DEFAULT_OUTPUT_COST_PER_MILLION: f64 = 75.0;
@@ -25,7 +26,7 @@ impl ModelPricing {
} }
} }
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] #[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct TokenUsage { pub struct TokenUsage {
pub input_tokens: u32, pub input_tokens: u32,
pub output_tokens: u32, pub output_tokens: u32,

View File

@@ -91,7 +91,10 @@ impl GlobalToolRegistry {
Ok(Self { plugin_tools }) Ok(Self { plugin_tools })
} }
pub fn normalize_allowed_tools(&self, values: &[String]) -> Result<Option<BTreeSet<String>>, String> { pub fn normalize_allowed_tools(
&self,
values: &[String],
) -> Result<Option<BTreeSet<String>>, String> {
if values.is_empty() { if values.is_empty() {
return Ok(None); return Ok(None);
} }
@@ -100,7 +103,11 @@ impl GlobalToolRegistry {
let canonical_names = builtin_specs let canonical_names = builtin_specs
.iter() .iter()
.map(|spec| spec.name.to_string()) .map(|spec| spec.name.to_string())
.chain(self.plugin_tools.iter().map(|tool| tool.definition().name.clone())) .chain(
self.plugin_tools
.iter()
.map(|tool| tool.definition().name.clone()),
)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let mut name_map = canonical_names let mut name_map = canonical_names
.iter() .iter()
@@ -151,7 +158,8 @@ impl GlobalToolRegistry {
.plugin_tools .plugin_tools
.iter() .iter()
.filter(|tool| { .filter(|tool| {
allowed_tools.is_none_or(|allowed| allowed.contains(tool.definition().name.as_str())) allowed_tools
.is_none_or(|allowed| allowed.contains(tool.definition().name.as_str()))
}) })
.map(|tool| ToolDefinition { .map(|tool| ToolDefinition {
name: tool.definition().name.clone(), name: tool.definition().name.clone(),
@@ -174,7 +182,8 @@ impl GlobalToolRegistry {
.plugin_tools .plugin_tools
.iter() .iter()
.filter(|tool| { .filter(|tool| {
allowed_tools.is_none_or(|allowed| allowed.contains(tool.definition().name.as_str())) allowed_tools
.is_none_or(|allowed| allowed.contains(tool.definition().name.as_str()))
}) })
.map(|tool| { .map(|tool| {
( (