use std::collections::{BTreeMap, BTreeSet}; use std::fmt; use std::future::Future; use std::pin::Pin; use std::sync::Arc; use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; use crate::contract::{ validate_description, validate_function_name, validate_identifier, validate_metadata, }; macro_rules! provider_id { ($name:ident, $label:literal) => { #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)] #[serde(transparent)] pub struct $name(String); impl $name { pub fn try_new(value: impl Into) -> Result { let value = value.into(); validate_identifier(&value, $label).map_err(ProviderError::from_contract)?; Ok(Self(value)) } pub fn as_str(&self) -> &str { &self.0 } } impl TryFrom for $name { type Error = ProviderError; fn try_from(value: String) -> Result { Self::try_new(value) } } impl<'de> Deserialize<'de> for $name { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { let value = String::deserialize(deserializer)?; Self::try_new(value).map_err(serde::de::Error::custom) } } impl fmt::Display for $name { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str(&self.0) } } }; } provider_id!(ProviderInstanceId, "provider instance id"); provider_id!(ProviderProtocolId, "provider protocol id"); #[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)] #[serde(rename_all = "kebab-case")] pub enum ProviderCapability { Streaming, FunctionTools, RequiredToolChoice, ImageInput, WebSearch, ReasoningEffort, TextVerbosity, } #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields, rename_all = "camelCase")] pub struct ProviderDescriptor { instance_id: ProviderInstanceId, protocol_id: ProviderProtocolId, display_name: String, capabilities: BTreeSet, metadata: Value, } impl ProviderDescriptor { pub fn try_new( instance_id: ProviderInstanceId, protocol_id: ProviderProtocolId, capabilities: impl IntoIterator, ) -> Result { let descriptor = Self { display_name: instance_id.as_str().to_string(), instance_id, protocol_id, capabilities: capabilities.into_iter().collect(), metadata: Value::Object(Map::new()), }; descriptor.validate()?; Ok(descriptor) } pub fn with_metadata(mut self, metadata: Value) -> Result { validate_metadata(&metadata, "provider metadata").map_err(ProviderError::from_contract)?; self.metadata = metadata; Ok(self) } pub fn with_display_name( mut self, display_name: impl Into, ) -> Result { self.display_name = display_name.into(); self.validate()?; Ok(self) } pub fn instance_id(&self) -> &ProviderInstanceId { &self.instance_id } pub fn protocol_id(&self) -> &ProviderProtocolId { &self.protocol_id } pub fn display_name(&self) -> &str { &self.display_name } pub fn capabilities(&self) -> &BTreeSet { &self.capabilities } pub fn supports(&self, capability: ProviderCapability) -> bool { self.capabilities.contains(&capability) } pub fn metadata(&self) -> &Value { &self.metadata } fn validate(&self) -> Result<(), ProviderError> { validate_description(&self.display_name, "provider display name") .map_err(ProviderError::from_contract)?; validate_metadata(&self.metadata, "provider metadata") .map_err(ProviderError::from_contract)?; if self.supports(ProviderCapability::RequiredToolChoice) && !self.supports(ProviderCapability::FunctionTools) { return Err(ProviderError::invalid( "required-tool-choice capability 依赖 function-tools capability", )); } Ok(()) } } #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "kebab-case")] pub enum ProviderRole { System, User, Assistant, Tool, } #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[serde( deny_unknown_fields, tag = "type", rename_all = "kebab-case", rename_all_fields = "camelCase" )] pub enum ProviderContentPart { Text { text: String, }, Image { source: Value, }, ToolResult { tool_call_id: String, output: Value, is_error: bool, }, } impl ProviderContentPart { pub fn text(text: impl Into) -> Result { let text = text.into(); validate_non_empty_text(&text, "message text")?; Ok(Self::Text { text }) } pub fn image(source: Value) -> Result { if !source.is_object() { return Err(ProviderError::invalid("image source 必须是 JSON object")); } Ok(Self::Image { source }) } pub fn tool_result( tool_call_id: impl Into, output: Value, is_error: bool, ) -> Result { let tool_call_id = tool_call_id.into(); validate_identifier(&tool_call_id, "tool call id").map_err(ProviderError::from_contract)?; Ok(Self::ToolResult { tool_call_id, output, is_error, }) } fn validate(&self) -> Result<(), ProviderError> { match self { Self::Text { text } => validate_non_empty_text(text, "message text"), Self::Image { source } if !source.is_object() => { Err(ProviderError::invalid("image source 必须是 JSON object")) } Self::ToolResult { tool_call_id, .. } => { validate_identifier(tool_call_id, "tool call id") .map_err(ProviderError::from_contract) } Self::Image { .. } => Ok(()), } } } #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields, rename_all = "camelCase")] pub struct ProviderMessage { role: ProviderRole, content: Vec, } impl ProviderMessage { pub fn try_new( role: ProviderRole, content: impl IntoIterator, ) -> Result { let message = Self { role, content: content.into_iter().collect(), }; message.validate()?; Ok(message) } pub fn role(&self) -> ProviderRole { self.role } pub fn content(&self) -> &[ProviderContentPart] { &self.content } fn validate(&self) -> Result<(), ProviderError> { if self.content.is_empty() { return Err(ProviderError::invalid("provider message content 不能为空")); } for part in &self.content { part.validate()?; } Ok(()) } } #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields, rename_all = "camelCase")] pub struct ProviderToolDefinition { name: String, description: String, input_schema: Value, strict: bool, } impl ProviderToolDefinition { pub fn try_new( name: impl Into, description: impl Into, input_schema: Value, ) -> Result { let tool = Self { name: name.into(), description: description.into(), input_schema, strict: false, }; tool.validate()?; Ok(tool) } pub fn name(&self) -> &str { &self.name } pub fn description(&self) -> &str { &self.description } pub fn input_schema(&self) -> &Value { &self.input_schema } pub fn with_strict(mut self, strict: bool) -> Self { self.strict = strict; self } pub fn strict(&self) -> bool { self.strict } fn validate(&self) -> Result<(), ProviderError> { validate_function_name(&self.name).map_err(ProviderError::from_contract)?; validate_description(&self.description, "provider tool description") .map_err(ProviderError::from_contract)?; if self.input_schema.get("type").and_then(Value::as_str) != Some("object") { return Err(ProviderError::invalid( "provider tool inputSchema.type 必须为 object", )); } Ok(()) } } #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] #[serde(tag = "type", content = "name", rename_all = "kebab-case")] pub enum ProviderToolChoice { Auto, None, Required, Specific(String), } #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "kebab-case")] pub enum ProviderReasoningEffort { Minimal, Low, Medium, High, XHigh, Max, } #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "kebab-case")] pub enum ProviderTextVerbosity { Low, Medium, High, } #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields, rename_all = "camelCase")] pub struct ProviderRequest { request_id: String, model: Option, messages: Vec, max_output_tokens: Option, request_timeout_ms: Option, tools: Vec, tool_choice: ProviderToolChoice, web_search: bool, reasoning_effort: Option, text_verbosity: Option, metadata: Value, } impl ProviderRequest { pub fn try_new( request_id: impl Into, messages: impl IntoIterator, ) -> Result { let request = Self { request_id: request_id.into(), model: None, messages: messages.into_iter().collect(), max_output_tokens: None, request_timeout_ms: None, tools: Vec::new(), tool_choice: ProviderToolChoice::Auto, web_search: false, reasoning_effort: None, text_verbosity: None, metadata: Value::Object(Map::new()), }; request.validate()?; Ok(request) } pub fn with_tools( mut self, tools: impl IntoIterator, tool_choice: ProviderToolChoice, ) -> Result { self.tools = tools.into_iter().collect(); self.tool_choice = tool_choice; self.validate()?; Ok(self) } pub fn with_model(mut self, model: impl Into) -> Result { self.model = Some(model.into()); self.validate()?; Ok(self) } pub fn with_max_output_tokens(mut self, max_output_tokens: u32) -> Result { self.max_output_tokens = Some(max_output_tokens); self.validate()?; Ok(self) } pub fn with_request_timeout_ms( mut self, request_timeout_ms: u64, ) -> Result { self.request_timeout_ms = Some(request_timeout_ms); self.validate()?; Ok(self) } pub fn with_web_search(mut self, enabled: bool) -> Self { self.web_search = enabled; self } pub fn with_reasoning_effort(mut self, effort: ProviderReasoningEffort) -> Self { self.reasoning_effort = Some(effort); self } pub fn with_text_verbosity(mut self, verbosity: ProviderTextVerbosity) -> Self { self.text_verbosity = Some(verbosity); self } pub fn with_metadata(mut self, metadata: Value) -> Result { validate_metadata(&metadata, "provider request metadata") .map_err(ProviderError::from_contract)?; self.metadata = metadata; Ok(self) } pub fn request_id(&self) -> &str { &self.request_id } pub fn model(&self) -> Option<&str> { self.model.as_deref() } pub fn messages(&self) -> &[ProviderMessage] { &self.messages } pub fn max_output_tokens(&self) -> Option { self.max_output_tokens } pub fn request_timeout_ms(&self) -> Option { self.request_timeout_ms } pub fn tools(&self) -> &[ProviderToolDefinition] { &self.tools } pub fn tool_choice(&self) -> &ProviderToolChoice { &self.tool_choice } pub fn web_search(&self) -> bool { self.web_search } pub fn reasoning_effort(&self) -> Option { self.reasoning_effort } pub fn text_verbosity(&self) -> Option { self.text_verbosity } pub fn metadata(&self) -> &Value { &self.metadata } pub fn required_capabilities(&self) -> BTreeSet { let mut required = BTreeSet::new(); if !self.tools.is_empty() { required.insert(ProviderCapability::FunctionTools); } if matches!( self.tool_choice, ProviderToolChoice::Required | ProviderToolChoice::Specific(_) ) { required.insert(ProviderCapability::FunctionTools); required.insert(ProviderCapability::RequiredToolChoice); } if self .messages .iter() .flat_map(|message| message.content.iter()) .any(|part| matches!(part, ProviderContentPart::ToolResult { .. })) { required.insert(ProviderCapability::FunctionTools); } if self .messages .iter() .flat_map(|message| message.content.iter()) .any(|part| matches!(part, ProviderContentPart::Image { .. })) { required.insert(ProviderCapability::ImageInput); } if self.web_search { required.insert(ProviderCapability::WebSearch); } if self.reasoning_effort.is_some() { required.insert(ProviderCapability::ReasoningEffort); } if self.text_verbosity.is_some() { required.insert(ProviderCapability::TextVerbosity); } required } fn validate(&self) -> Result<(), ProviderError> { validate_identifier(&self.request_id, "provider request id") .map_err(ProviderError::from_contract)?; if self .model .as_ref() .is_some_and(|model| model.trim().is_empty()) { return Err(ProviderError::invalid("provider request model 不能为空")); } if self.max_output_tokens == Some(0) { return Err(ProviderError::invalid( "provider request maxOutputTokens 必须大于 0", )); } if self.request_timeout_ms == Some(0) { return Err(ProviderError::invalid( "provider request requestTimeoutMs 必须大于 0", )); } if self.messages.is_empty() { return Err(ProviderError::invalid("provider request messages 不能为空")); } for message in &self.messages { message.validate()?; } let mut tool_names = BTreeSet::new(); for tool in &self.tools { tool.validate()?; if !tool_names.insert(tool.name.as_str()) { return Err(ProviderError::invalid(format!( "provider tool name 重复:{}", tool.name ))); } } match &self.tool_choice { ProviderToolChoice::Auto | ProviderToolChoice::None => {} ProviderToolChoice::Required if self.tools.is_empty() => { return Err(ProviderError::invalid( "required tool choice 需要至少一个 function tool", )); } ProviderToolChoice::Specific(name) => { validate_function_name(name).map_err(ProviderError::from_contract)?; if !tool_names.contains(name.as_str()) { return Err(ProviderError::invalid(format!( "specific tool choice 引用了未知工具:{name}" ))); } } ProviderToolChoice::Required => {} } validate_metadata(&self.metadata, "provider request metadata") .map_err(ProviderError::from_contract) } } #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields, rename_all = "camelCase")] pub struct ProviderToolCall { id: String, name: String, arguments: String, } impl ProviderToolCall { pub fn try_new( id: impl Into, name: impl Into, arguments: impl Into, ) -> Result { let call = Self { id: id.into(), name: name.into(), arguments: arguments.into(), }; validate_identifier(&call.id, "provider tool call id") .map_err(ProviderError::from_contract)?; validate_function_name(&call.name).map_err(ProviderError::from_contract)?; Ok(call) } pub fn id(&self) -> &str { &self.id } pub fn name(&self) -> &str { &self.name } pub fn arguments(&self) -> &str { &self.arguments } } #[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(deny_unknown_fields, rename_all = "camelCase")] pub struct ProviderUsage { input_tokens: u64, output_tokens: u64, total_tokens: u64, } impl ProviderUsage { pub fn new(input_tokens: u64, output_tokens: u64, total_tokens: u64) -> Self { Self { input_tokens, output_tokens, total_tokens, } } pub fn input_tokens(&self) -> u64 { self.input_tokens } pub fn output_tokens(&self) -> u64 { self.output_tokens } pub fn total_tokens(&self) -> u64 { self.total_tokens } } #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields, rename_all = "camelCase")] pub struct ProviderResponse { request_id: String, model: String, response_id: Option, content: Vec, tool_calls: Vec, finish_reason: Option, usage: Option, } impl ProviderResponse { pub fn try_new( request_id: impl Into, model: impl Into, content: impl IntoIterator, tool_calls: impl IntoIterator, ) -> Result { let response = Self { request_id: request_id.into(), model: model.into(), response_id: None, content: content.into_iter().collect(), tool_calls: tool_calls.into_iter().collect(), finish_reason: None, usage: None, }; validate_identifier(&response.request_id, "provider response request id") .map_err(ProviderError::from_contract)?; validate_non_empty_text(&response.model, "provider response model")?; Ok(response) } pub fn with_finish_reason(mut self, reason: impl Into) -> Result { let reason = reason.into(); validate_non_empty_text(&reason, "provider finish reason")?; self.finish_reason = Some(reason); Ok(self) } pub fn with_response_id( mut self, response_id: impl Into, ) -> Result { let response_id = response_id.into(); validate_non_empty_text(&response_id, "provider response id")?; self.response_id = Some(response_id); Ok(self) } pub fn with_usage(mut self, usage: ProviderUsage) -> Self { self.usage = Some(usage); self } pub fn request_id(&self) -> &str { &self.request_id } pub fn model(&self) -> &str { &self.model } pub fn response_id(&self) -> Option<&str> { self.response_id.as_deref() } pub fn content(&self) -> &[ProviderContentPart] { &self.content } pub fn tool_calls(&self) -> &[ProviderToolCall] { &self.tool_calls } pub fn finish_reason(&self) -> Option<&str> { self.finish_reason.as_deref() } pub fn usage(&self) -> Option { self.usage } fn validate_for_request( &self, request_id: &str, declared_tools: &BTreeSet, ) -> Result<(), ProviderError> { if self.request_id != request_id { return Err(ProviderError::invalid( "provider response request id 与请求不匹配", )); } validate_non_empty_text(&self.model, "provider response model")?; if self.content.is_empty() && self.tool_calls.is_empty() { return Err(ProviderError::invalid( "provider response 必须包含 content 或 toolCalls", )); } for part in &self.content { part.validate()?; } let mut call_ids = BTreeSet::new(); for call in &self.tool_calls { validate_identifier(&call.id, "provider tool call id") .map_err(ProviderError::from_contract)?; validate_function_name(&call.name).map_err(ProviderError::from_contract)?; if !call_ids.insert(call.id.as_str()) { return Err(ProviderError::invalid(format!( "provider tool call id 重复:{}", call.id ))); } if !declared_tools.contains(&call.name) { return Err(ProviderError::invalid(format!( "provider response 返回了未声明工具:{}", call.name ))); } } Ok(()) } } #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[serde( deny_unknown_fields, tag = "type", rename_all = "kebab-case", rename_all_fields = "camelCase" )] pub enum ProviderStreamEvent { TextDelta { accumulated_text: String, delta_text: String, finish_reason: Option, }, ToolCallDelta { tool_call_id: String, arguments_delta: String, }, Usage { usage: ProviderUsage, }, } pub trait ProviderStreamSink { fn emit(&mut self, event: ProviderStreamEvent) -> Result<(), ProviderError>; } pub type ProviderFuture<'a, T> = Pin + Send + 'a>>; pub type ProviderStreamFuture<'a, T> = Pin + 'a>>; pub trait ProviderAdapter: Send + Sync { fn descriptor(&self) -> &ProviderDescriptor; fn invoke( &self, request: ProviderRequest, ) -> ProviderFuture<'_, Result>; fn stream<'a>( &'a self, request: ProviderRequest, sink: Box, ) -> ProviderStreamFuture<'a, Result>; } #[derive(Clone, Debug, Eq, PartialEq)] pub struct ProviderTarget { instance_id: ProviderInstanceId, protocol_id: ProviderProtocolId, } impl ProviderTarget { pub fn new(instance_id: ProviderInstanceId, protocol_id: ProviderProtocolId) -> Self { Self { instance_id, protocol_id, } } pub fn instance_id(&self) -> &ProviderInstanceId { &self.instance_id } pub fn protocol_id(&self) -> &ProviderProtocolId { &self.protocol_id } } #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum ProviderErrorKind { InvalidContract, InvalidConfig, InvalidRequest, DuplicateInstanceId, UnknownInstanceId, ProtocolMismatch, CapabilityMismatch, Timeout, Connectivity, Upstream, StreamUnavailable, EmptyResponse, Transport, Deserialize, StreamSink, } #[derive(Clone, Debug, Eq, PartialEq)] pub struct ProviderError { kind: ProviderErrorKind, detail: String, attempts: Option, status_code: Option, } impl ProviderError { pub fn invalid_config(detail: impl Into) -> Self { Self::new(ProviderErrorKind::InvalidConfig, detail) } pub fn invalid_request(detail: impl Into) -> Self { Self::new(ProviderErrorKind::InvalidRequest, detail) } pub fn timeout(attempts: u32, detail: impl Into) -> Self { Self::new(ProviderErrorKind::Timeout, detail).with_attempts(attempts) } pub fn connectivity(attempts: u32, detail: impl Into) -> Self { Self::new(ProviderErrorKind::Connectivity, detail).with_attempts(attempts) } pub fn upstream(status_code: u16, detail: impl Into) -> Self { Self::new(ProviderErrorKind::Upstream, detail).with_status_code(status_code) } pub fn stream_unavailable(detail: impl Into) -> Self { Self::new(ProviderErrorKind::StreamUnavailable, detail) } pub fn empty_response(detail: impl Into) -> Self { Self::new(ProviderErrorKind::EmptyResponse, detail) } pub fn transport(detail: impl Into) -> Self { Self::new(ProviderErrorKind::Transport, detail) } pub fn deserialize(detail: impl Into) -> Self { Self::new(ProviderErrorKind::Deserialize, detail) } pub fn stream_sink(detail: impl Into) -> Self { Self::new(ProviderErrorKind::StreamSink, detail) } fn new(kind: ProviderErrorKind, detail: impl Into) -> Self { Self { kind, detail: detail.into(), attempts: None, status_code: None, } } fn with_attempts(mut self, attempts: u32) -> Self { self.attempts = Some(attempts); self } fn with_status_code(mut self, status_code: u16) -> Self { self.status_code = Some(status_code); self } fn invalid(detail: impl Into) -> Self { Self::new(ProviderErrorKind::InvalidContract, detail) } fn from_contract(error: crate::ContractError) -> Self { Self::invalid(error.to_string()) } pub fn kind(&self) -> ProviderErrorKind { self.kind } pub fn detail(&self) -> &str { &self.detail } pub fn attempts(&self) -> Option { self.attempts } pub fn status_code(&self) -> Option { self.status_code } } impl fmt::Display for ProviderError { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str(&self.detail) } } impl std::error::Error for ProviderError {} struct RegisteredProvider { descriptor: ProviderDescriptor, adapter: Arc, } #[derive(Default)] pub struct ProviderRegistry { providers: BTreeMap, } impl ProviderRegistry { pub fn new() -> Self { Self::default() } pub fn try_new( adapters: impl IntoIterator>, ) -> Result { let mut registry = Self::new(); for adapter in adapters { registry.register(adapter)?; } Ok(registry) } pub fn register(&mut self, adapter: Arc) -> Result<(), ProviderError> { let descriptor = adapter.descriptor().clone(); descriptor.validate()?; if self.providers.contains_key(descriptor.instance_id()) { return Err(ProviderError::new( ProviderErrorKind::DuplicateInstanceId, format!("provider instance id 重复:{}", descriptor.instance_id()), )); } self.providers.insert( descriptor.instance_id.clone(), RegisteredProvider { descriptor, adapter, }, ); Ok(()) } pub fn len(&self) -> usize { self.providers.len() } pub fn is_empty(&self) -> bool { self.providers.is_empty() } pub fn descriptor( &self, instance_id: &ProviderInstanceId, ) -> Result<&ProviderDescriptor, ProviderError> { self.providers .get(instance_id) .map(|entry| &entry.descriptor) .ok_or_else(|| { ProviderError::new( ProviderErrorKind::UnknownInstanceId, format!("未知 provider instance:{instance_id}"), ) }) } pub fn adapter( &self, instance_id: &ProviderInstanceId, ) -> Result, ProviderError> { self.providers .get(instance_id) .map(|entry| Arc::clone(&entry.adapter)) .ok_or_else(|| { ProviderError::new( ProviderErrorKind::UnknownInstanceId, format!("未知 provider instance:{instance_id}"), ) }) } pub fn invoke( &self, target: &ProviderTarget, request: ProviderRequest, ) -> ProviderFuture<'static, Result> { let adapter = match self.resolve(target, &request, false) { Ok(adapter) => adapter, Err(error) => return Box::pin(async move { Err(error) }), }; let request_id = request.request_id.clone(); let declared_tools = request .tools .iter() .map(|tool| tool.name.clone()) .collect::>(); Box::pin(async move { let response = adapter.invoke(request).await?; response.validate_for_request(&request_id, &declared_tools)?; Ok(response) }) } pub fn stream<'a>( &'a self, target: &ProviderTarget, request: ProviderRequest, sink: Box, ) -> ProviderStreamFuture<'a, Result> { let adapter = match self.resolve(target, &request, true) { Ok(adapter) => adapter, Err(error) => return Box::pin(async move { Err(error) }), }; let request_id = request.request_id.clone(); let declared_tools = request .tools .iter() .map(|tool| tool.name.clone()) .collect::>(); Box::pin(async move { let response = adapter.stream(request, sink).await?; response.validate_for_request(&request_id, &declared_tools)?; Ok(response) }) } fn resolve( &self, target: &ProviderTarget, request: &ProviderRequest, streaming: bool, ) -> Result, ProviderError> { request.validate()?; let entry = self.providers.get(target.instance_id()).ok_or_else(|| { ProviderError::new( ProviderErrorKind::UnknownInstanceId, format!("未知 provider instance:{}", target.instance_id()), ) })?; if entry.descriptor.protocol_id() != target.protocol_id() { return Err(ProviderError::new( ProviderErrorKind::ProtocolMismatch, format!( "provider instance {} 的 protocol 不匹配", target.instance_id() ), )); } let mut required = request.required_capabilities(); if streaming { required.insert(ProviderCapability::Streaming); } let missing = required .difference(entry.descriptor.capabilities()) .copied() .collect::>(); if !missing.is_empty() { return Err(ProviderError::new( ProviderErrorKind::CapabilityMismatch, format!( "provider instance {} 缺少能力:{missing:?}", target.instance_id() ), )); } Ok(Arc::clone(&entry.adapter)) } } fn validate_non_empty_text(value: &str, field: &str) -> Result<(), ProviderError> { if value.trim().is_empty() { Err(ProviderError::invalid(format!("{field} 不能为空"))) } else { Ok(()) } }