feat(server): refactor copilot (#14892)

#### PR Dependency Tree


* **PR #14892** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)
This commit is contained in:
DarkSky
2026-05-04 00:36:47 +08:00
committed by GitHub
parent fa8f1a096c
commit d64f368623
239 changed files with 35859 additions and 16777 deletions
@@ -0,0 +1,455 @@
use llm_adapter::{
backend::{
BackendConfig, BackendError, DefaultHttpClient, dispatch_embedding_request, dispatch_rerank_request,
dispatch_structured_request, resolve_attachment_reference_plan, resolve_request_intent,
},
core::{EmbeddingResponse, ImageResponse, RerankResponse, StructuredResponse},
router::{
PreparedChatRoute, PreparedEmbeddingRoute, PreparedImageRoute, PreparedRerankRoute, PreparedStructuredRoute,
dispatch_embedding_with_fallback, dispatch_image_with_fallback, dispatch_prepared_chat_with_fallback,
dispatch_rerank_with_fallback, dispatch_structured_with_fallback, prepared_chat_routes_from_serializable,
prepared_embedding_routes_from_serializable, prepared_image_routes_from_serializable,
prepared_rerank_routes_from_serializable, prepared_structured_routes_from_serializable,
serializable_prepared_routes_from_str,
},
};
use napi::{Env, Result, Task, bindgen_prelude::AsyncTask};
use crate::llm::{
LlmDispatchPayload, LlmEmbeddingDispatchPayload, LlmPreparedImageDispatchRoutePayload, LlmRerankDispatchPayload,
LlmStructuredDispatchPayload, apply_request_middlewares, apply_structured_request_middlewares,
core::contracts::LlmImageRequestContract, map_backend_error, map_json_error, parse_embedding_protocol,
parse_protocol, parse_rerank_protocol, parse_structured_protocol,
};
pub struct AsyncLlmStructuredDispatchTask {
pub(crate) protocol: String,
pub(crate) backend_config_json: String,
pub(crate) request_json: String,
}
pub struct AsyncLlmStructuredDispatchPreparedTask {
pub(crate) routes_json: String,
}
pub struct AsyncLlmDispatchPreparedTask {
pub(crate) routes_json: String,
}
#[napi]
impl Task for AsyncLlmDispatchPreparedTask {
type Output = String;
type JsValue = String;
fn compute(&mut self) -> Result<Self::Output> {
let routes = parse_prepared_dispatch_routes(&self.routes_json)?;
let (provider_id, response) =
dispatch_prepared_with_fallback(&DefaultHttpClient::default(), &routes).map_err(map_backend_error)?;
serde_json::to_string(&serde_json::json!({
"provider_id": provider_id,
"response": response,
}))
.map_err(map_json_error)
}
fn resolve(&mut self, _: Env, output: Self::Output) -> Result<Self::JsValue> {
Ok(output)
}
}
#[napi]
impl Task for AsyncLlmStructuredDispatchTask {
type Output = String;
type JsValue = String;
fn compute(&mut self) -> Result<Self::Output> {
let protocol = parse_structured_protocol(&self.protocol)?;
let config: BackendConfig = serde_json::from_str(&self.backend_config_json).map_err(map_json_error)?;
let payload: LlmStructuredDispatchPayload = serde_json::from_str(&self.request_json).map_err(map_json_error)?;
let request =
apply_structured_request_middlewares(payload.request, &payload.middleware, protocol, config.request_layer)?;
let response = dispatch_structured_request(&DefaultHttpClient::default(), &config, protocol, &request)
.map_err(map_backend_error)?;
serde_json::to_string(&response).map_err(map_json_error)
}
fn resolve(&mut self, _: Env, output: Self::Output) -> Result<Self::JsValue> {
Ok(output)
}
}
#[napi]
impl Task for AsyncLlmStructuredDispatchPreparedTask {
type Output = String;
type JsValue = String;
fn compute(&mut self) -> Result<Self::Output> {
let (provider_id, response) = dispatch_prepared_structured_routes(&self.routes_json)?;
serde_json::to_string(&serde_json::json!({
"provider_id": provider_id,
"response": response,
}))
.map_err(map_json_error)
}
fn resolve(&mut self, _: Env, output: Self::Output) -> Result<Self::JsValue> {
Ok(output)
}
}
pub struct AsyncLlmEmbeddingDispatchTask {
pub(crate) protocol: String,
pub(crate) backend_config_json: String,
pub(crate) request_json: String,
}
pub struct AsyncLlmEmbeddingDispatchPreparedTask {
pub(crate) routes_json: String,
}
pub struct AsyncLlmImageDispatchPreparedTask {
pub(crate) routes_json: String,
}
#[napi]
impl Task for AsyncLlmEmbeddingDispatchTask {
type Output = String;
type JsValue = String;
fn compute(&mut self) -> Result<Self::Output> {
let protocol = parse_embedding_protocol(&self.protocol)?;
let config: BackendConfig = serde_json::from_str(&self.backend_config_json).map_err(map_json_error)?;
let payload: LlmEmbeddingDispatchPayload = serde_json::from_str(&self.request_json).map_err(map_json_error)?;
let response = dispatch_embedding_request(&DefaultHttpClient::default(), &config, protocol, &payload.request)
.map_err(map_backend_error)?;
serde_json::to_string(&response).map_err(map_json_error)
}
fn resolve(&mut self, _: Env, output: Self::Output) -> Result<Self::JsValue> {
Ok(output)
}
}
#[napi]
impl Task for AsyncLlmEmbeddingDispatchPreparedTask {
type Output = String;
type JsValue = String;
fn compute(&mut self) -> Result<Self::Output> {
let routes = parse_prepared_embedding_routes(&self.routes_json)?;
let (provider_id, response) =
dispatch_prepared_embedding_with_fallback(&DefaultHttpClient::default(), &routes).map_err(map_backend_error)?;
serde_json::to_string(&serde_json::json!({
"provider_id": provider_id,
"response": response,
}))
.map_err(map_json_error)
}
fn resolve(&mut self, _: Env, output: Self::Output) -> Result<Self::JsValue> {
Ok(output)
}
}
#[napi]
impl Task for AsyncLlmImageDispatchPreparedTask {
type Output = String;
type JsValue = String;
fn compute(&mut self) -> Result<Self::Output> {
let routes = parse_prepared_image_routes(&self.routes_json)?;
let (provider_id, response) =
dispatch_image_with_fallback(&DefaultHttpClient::default(), &routes).map_err(map_backend_error)?;
serde_json::to_string(&serde_json::json!({
"provider_id": provider_id,
"response": response,
}))
.map_err(map_json_error)
}
fn resolve(&mut self, _: Env, output: Self::Output) -> Result<Self::JsValue> {
Ok(output)
}
}
pub struct AsyncLlmRerankDispatchTask {
pub(crate) protocol: String,
pub(crate) backend_config_json: String,
pub(crate) request_json: String,
}
pub struct AsyncLlmRerankDispatchPreparedTask {
pub(crate) routes_json: String,
}
#[napi]
impl Task for AsyncLlmRerankDispatchTask {
type Output = String;
type JsValue = String;
fn compute(&mut self) -> Result<Self::Output> {
let protocol = parse_rerank_protocol(&self.protocol)?;
let config: BackendConfig = serde_json::from_str(&self.backend_config_json).map_err(map_json_error)?;
let payload: LlmRerankDispatchPayload = serde_json::from_str(&self.request_json).map_err(map_json_error)?;
let response = dispatch_rerank_request(&DefaultHttpClient::default(), &config, protocol, &payload.request)
.map_err(map_backend_error)?;
serde_json::to_string(&response).map_err(map_json_error)
}
fn resolve(&mut self, _: Env, output: Self::Output) -> Result<Self::JsValue> {
Ok(output)
}
}
#[napi]
impl Task for AsyncLlmRerankDispatchPreparedTask {
type Output = String;
type JsValue = String;
fn compute(&mut self) -> Result<Self::Output> {
let routes = parse_prepared_rerank_routes(&self.routes_json)?;
let (provider_id, response) =
dispatch_prepared_rerank_with_fallback(&DefaultHttpClient::default(), &routes).map_err(map_backend_error)?;
serde_json::to_string(&serde_json::json!({
"provider_id": provider_id,
"response": response,
}))
.map_err(map_json_error)
}
fn resolve(&mut self, _: Env, output: Self::Output) -> Result<Self::JsValue> {
Ok(output)
}
}
pub(crate) fn parse_prepared_chat_routes_with_middleware(
routes_json: &str,
) -> Result<Vec<(PreparedChatRoute, crate::llm::LlmMiddlewarePayload)>> {
let payload = serializable_prepared_routes_from_str::<LlmDispatchPayload>(routes_json).map_err(map_backend_error)?;
let middleware = payload
.iter()
.map(|route| route.request.middleware.clone())
.collect::<Vec<_>>();
let routes = prepared_chat_routes_from_serializable(payload, |request, protocol, request_layer| {
apply_request_middlewares(request.request, &request.middleware, protocol, request_layer).map_err(|error| {
BackendError::InvalidRequest {
field: "middleware.request",
message: error.reason.clone(),
}
})
})
.map_err(map_backend_error)?;
Ok(routes.into_iter().zip(middleware).collect())
}
pub(crate) fn parse_prepared_chat_routes_without_middleware(
routes_json: &str,
) -> Result<Vec<(PreparedChatRoute, crate::llm::LlmMiddlewarePayload)>> {
let payload = serializable_prepared_routes_from_str::<LlmDispatchPayload>(routes_json).map_err(map_backend_error)?;
let middleware = payload
.iter()
.map(|route| route.request.middleware.clone())
.collect::<Vec<_>>();
let routes =
prepared_chat_routes_from_serializable(payload, |request, _protocol, _request_layer| Ok(request.request))
.map_err(map_backend_error)?;
Ok(routes.into_iter().zip(middleware).collect())
}
fn parse_prepared_dispatch_routes(routes_json: &str) -> Result<Vec<PreparedChatRoute>> {
Ok(
parse_prepared_chat_routes_with_middleware(routes_json)?
.into_iter()
.map(|(route, _)| route)
.collect(),
)
}
fn parse_prepared_structured_routes(routes_json: &str) -> Result<Vec<PreparedStructuredRoute>> {
let payload =
serializable_prepared_routes_from_str::<LlmStructuredDispatchPayload>(routes_json).map_err(map_backend_error)?;
prepared_structured_routes_from_serializable(payload, |request, protocol, request_layer| {
apply_structured_request_middlewares(request.request, &request.middleware, protocol, request_layer).map_err(
|error| BackendError::InvalidRequest {
field: "middleware.request",
message: error.reason.clone(),
},
)
})
.map_err(map_backend_error)
}
pub(crate) fn dispatch_prepared_structured_routes(routes_json: &str) -> Result<(String, StructuredResponse)> {
let routes = parse_prepared_structured_routes(routes_json)?;
dispatch_prepared_structured_with_fallback(&DefaultHttpClient::default(), &routes).map_err(map_backend_error)
}
pub(crate) fn dispatch_prepared_image_route_payloads(
payload: Vec<LlmPreparedImageDispatchRoutePayload>,
) -> Result<(String, ImageResponse)> {
let routes = prepared_image_routes_from_payload(payload)?;
dispatch_image_with_fallback(&DefaultHttpClient::default(), &routes).map_err(map_backend_error)
}
fn parse_prepared_embedding_routes(routes_json: &str) -> Result<Vec<PreparedEmbeddingRoute>> {
let payload =
serializable_prepared_routes_from_str::<LlmEmbeddingDispatchPayload>(routes_json).map_err(map_backend_error)?;
prepared_embedding_routes_from_serializable(payload, |request| Ok(request.request)).map_err(map_backend_error)
}
fn parse_prepared_rerank_routes(routes_json: &str) -> Result<Vec<PreparedRerankRoute>> {
let payload =
serializable_prepared_routes_from_str::<LlmRerankDispatchPayload>(routes_json).map_err(map_backend_error)?;
prepared_rerank_routes_from_serializable(payload, |request| Ok(request.request)).map_err(map_backend_error)
}
fn parse_prepared_image_routes(routes_json: &str) -> Result<Vec<PreparedImageRoute>> {
let payload =
serializable_prepared_routes_from_str::<LlmImageRequestContract>(routes_json).map_err(map_backend_error)?;
prepared_image_routes_from_payload(payload)
}
fn prepared_image_routes_from_payload(
payload: Vec<LlmPreparedImageDispatchRoutePayload>,
) -> Result<Vec<PreparedImageRoute>> {
prepared_image_routes_from_serializable(payload, |request| {
request
.try_into()
.map_err(|error: napi::Error| BackendError::InvalidRequest {
field: "request",
message: error.reason.clone(),
})
})
.map_err(map_backend_error)
}
fn dispatch_prepared_with_fallback(
client: &dyn llm_adapter::backend::BackendHttpClient,
routes: &[PreparedChatRoute],
) -> std::result::Result<(String, llm_adapter::core::CoreResponse), llm_adapter::backend::BackendError> {
dispatch_prepared_chat_with_fallback(client, routes)
}
fn dispatch_prepared_structured_with_fallback(
client: &dyn llm_adapter::backend::BackendHttpClient,
routes: &[PreparedStructuredRoute],
) -> std::result::Result<(String, StructuredResponse), llm_adapter::backend::BackendError> {
dispatch_structured_with_fallback(client, routes)
}
fn dispatch_prepared_embedding_with_fallback(
client: &dyn llm_adapter::backend::BackendHttpClient,
routes: &[PreparedEmbeddingRoute],
) -> std::result::Result<(String, EmbeddingResponse), llm_adapter::backend::BackendError> {
dispatch_embedding_with_fallback(client, routes)
}
fn dispatch_prepared_rerank_with_fallback(
client: &dyn llm_adapter::backend::BackendHttpClient,
routes: &[PreparedRerankRoute],
) -> std::result::Result<(String, RerankResponse), llm_adapter::backend::BackendError> {
dispatch_rerank_with_fallback(client, routes)
}
#[napi(catch_unwind)]
pub fn llm_dispatch_prepared(routes_json: String) -> AsyncTask<AsyncLlmDispatchPreparedTask> {
AsyncTask::new(AsyncLlmDispatchPreparedTask { routes_json })
}
#[napi(catch_unwind)]
pub fn llm_structured_dispatch(
protocol: String,
backend_config_json: String,
request_json: String,
) -> AsyncTask<AsyncLlmStructuredDispatchTask> {
AsyncTask::new(AsyncLlmStructuredDispatchTask {
protocol,
backend_config_json,
request_json,
})
}
#[napi(catch_unwind)]
pub fn llm_structured_dispatch_prepared(routes_json: String) -> AsyncTask<AsyncLlmStructuredDispatchPreparedTask> {
AsyncTask::new(AsyncLlmStructuredDispatchPreparedTask { routes_json })
}
#[napi(catch_unwind)]
pub fn llm_embedding_dispatch(
protocol: String,
backend_config_json: String,
request_json: String,
) -> AsyncTask<AsyncLlmEmbeddingDispatchTask> {
AsyncTask::new(AsyncLlmEmbeddingDispatchTask {
protocol,
backend_config_json,
request_json,
})
}
#[napi(catch_unwind)]
pub fn llm_embedding_dispatch_prepared(routes_json: String) -> AsyncTask<AsyncLlmEmbeddingDispatchPreparedTask> {
AsyncTask::new(AsyncLlmEmbeddingDispatchPreparedTask { routes_json })
}
#[napi(catch_unwind)]
pub fn llm_image_dispatch_prepared(routes_json: String) -> AsyncTask<AsyncLlmImageDispatchPreparedTask> {
AsyncTask::new(AsyncLlmImageDispatchPreparedTask { routes_json })
}
#[napi(catch_unwind)]
pub fn llm_rerank_dispatch(
protocol: String,
backend_config_json: String,
request_json: String,
) -> AsyncTask<AsyncLlmRerankDispatchTask> {
AsyncTask::new(AsyncLlmRerankDispatchTask {
protocol,
backend_config_json,
request_json,
})
}
#[napi(catch_unwind)]
pub fn llm_rerank_dispatch_prepared(routes_json: String) -> AsyncTask<AsyncLlmRerankDispatchPreparedTask> {
AsyncTask::new(AsyncLlmRerankDispatchPreparedTask { routes_json })
}
#[napi(catch_unwind)]
pub fn llm_plan_attachment_reference(
protocol: String,
backend_config_json: String,
source_json: String,
) -> Result<String> {
let protocol = parse_protocol(&protocol)?;
let config: BackendConfig = serde_json::from_str(&backend_config_json).map_err(map_json_error)?;
let source: serde_json::Value = serde_json::from_str(&source_json).map_err(map_json_error)?;
let plan = resolve_attachment_reference_plan(&config, &protocol, &source).map_err(map_backend_error)?;
serde_json::to_string(&plan).map_err(map_json_error)
}
#[napi(catch_unwind)]
pub fn llm_resolve_request_intent(
protocol: String,
backend_config_json: String,
intent_json: String,
) -> Result<String> {
let protocol = parse_protocol(&protocol)?;
let config: BackendConfig = serde_json::from_str(&backend_config_json).map_err(map_json_error)?;
let intent: llm_adapter::backend::RequestIntent = serde_json::from_str(&intent_json).map_err(map_json_error)?;
let resolved = resolve_request_intent(&config, &protocol, intent).map_err(map_backend_error)?;
serde_json::to_string(&resolved).map_err(map_json_error)
}
@@ -0,0 +1,113 @@
#[cfg(test)]
use llm_adapter::middleware::RequestMiddleware;
#[cfg(test)]
use llm_adapter::middleware::resolve_request_chain as adapter_resolve_request_chain;
use llm_adapter::{
backend::{BackendError, BackendRequestLayer, ChatProtocol, EmbeddingProtocol, RerankProtocol, StructuredProtocol},
core::{CoreRequest, StructuredRequest},
middleware::{
StreamMiddleware, apply_request_middleware_names, apply_structured_request_middleware_names,
resolve_stream_middleware_chain,
},
};
use napi::{Error, Result, Status};
use crate::llm::LlmMiddlewarePayload;
pub(crate) fn apply_request_middlewares(
request: CoreRequest,
middleware: &LlmMiddlewarePayload,
protocol: ChatProtocol,
request_layer: Option<BackendRequestLayer>,
) -> Result<CoreRequest> {
apply_request_middleware_names(
request,
&middleware.request,
&middleware.config,
protocol,
request_layer,
)
.map_err(map_backend_parse_error)
}
pub(crate) fn apply_structured_request_middlewares(
request: StructuredRequest,
middleware: &LlmMiddlewarePayload,
protocol: StructuredProtocol,
request_layer: Option<BackendRequestLayer>,
) -> Result<StructuredRequest> {
apply_structured_request_middleware_names(
request,
&middleware.request,
&middleware.config,
protocol,
request_layer,
)
.map_err(map_backend_parse_error)
}
#[cfg(test)]
pub(crate) fn resolve_request_chain(
request: &[String],
protocol: ChatProtocol,
request_layer: Option<BackendRequestLayer>,
) -> Result<Vec<RequestMiddleware>> {
adapter_resolve_request_chain(request, protocol, request_layer).map_err(map_backend_parse_error)
}
pub(crate) fn resolve_stream_chain(stream: &[String]) -> Result<Vec<StreamMiddleware>> {
resolve_stream_middleware_chain(stream).map_err(map_backend_parse_error)
}
pub(crate) fn parse_protocol(protocol: &str) -> Result<ChatProtocol> {
protocol.parse().map_err(map_backend_parse_error)
}
pub(crate) fn parse_structured_protocol(protocol: &str) -> Result<StructuredProtocol> {
protocol.parse().map_err(map_backend_parse_error)
}
pub(crate) fn parse_embedding_protocol(protocol: &str) -> Result<EmbeddingProtocol> {
protocol.parse().map_err(map_backend_parse_error)
}
pub(crate) fn parse_rerank_protocol(protocol: &str) -> Result<RerankProtocol> {
protocol.parse().map_err(map_backend_parse_error)
}
fn map_backend_parse_error(error: BackendError) -> Error {
Error::new(Status::InvalidArg, error.to_string())
}
pub(crate) fn backend_transport_error(message: impl Into<String>) -> BackendError {
BackendError::Transport {
message: message.into(),
}
}
pub(crate) fn map_json_error(error: serde_json::Error) -> Error {
Error::new(Status::InvalidArg, format!("Invalid JSON payload: {error}"))
}
pub(crate) fn map_backend_error(error: BackendError) -> Error {
match error {
BackendError::InvalidRequest { message, .. } => Error::new(Status::InvalidArg, message),
BackendError::Timeout { message } => Error::new(Status::GenericFailure, format!("llm_timeout: {message}")),
other => Error::new(Status::GenericFailure, other.to_string()),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_preserve_backend_timeout_semantics() {
let error = map_backend_error(BackendError::Timeout {
message: "request timed out".to_string(),
});
assert_eq!(error.status, Status::GenericFailure);
assert_eq!(error.reason, "llm_timeout: request timed out");
}
}
@@ -0,0 +1,27 @@
mod dispatch;
mod middleware;
mod payload;
#[cfg(test)]
pub(crate) use dispatch::AsyncLlmDispatchPreparedTask;
pub(crate) use dispatch::{
dispatch_prepared_image_route_payloads, dispatch_prepared_structured_routes,
parse_prepared_chat_routes_with_middleware, parse_prepared_chat_routes_without_middleware,
};
pub use dispatch::{
llm_dispatch_prepared, llm_embedding_dispatch, llm_embedding_dispatch_prepared, llm_image_dispatch_prepared,
llm_plan_attachment_reference, llm_rerank_dispatch, llm_rerank_dispatch_prepared, llm_resolve_request_intent,
llm_structured_dispatch, llm_structured_dispatch_prepared,
};
pub(crate) use llm_adapter::middleware::StreamPipeline;
#[cfg(test)]
pub(crate) use middleware::resolve_request_chain;
pub(crate) use middleware::{
apply_request_middlewares, apply_structured_request_middlewares, backend_transport_error, map_backend_error,
map_json_error, parse_embedding_protocol, parse_protocol, parse_rerank_protocol, parse_structured_protocol,
resolve_stream_chain,
};
pub(crate) use payload::{
LlmDispatchPayload, LlmEmbeddingDispatchPayload, LlmMiddlewarePayload, LlmPreparedImageDispatchRoutePayload,
LlmRerankDispatchPayload, LlmRoutedBackendPayload, LlmStructuredDispatchPayload,
};
@@ -0,0 +1,214 @@
use llm_adapter::{
backend::BackendConfig,
core::{CoreRequest, EmbeddingRequest, RerankRequest, StructuredRequest},
middleware::MiddlewareConfig,
router::SerializablePreparedRoute,
};
use serde::{Deserialize, Serialize};
use crate::llm::core::contracts::{
LlmEmbeddingRequestContract, LlmImageRequestContract, LlmRequestContract, LlmRerankRequestContract,
LlmStructuredRequestContract,
};
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
#[serde(default)]
pub(crate) struct LlmMiddlewarePayload {
pub(crate) request: Vec<String>,
pub(crate) stream: Vec<String>,
pub(crate) config: MiddlewareConfig,
}
impl LlmMiddlewarePayload {
fn is_empty(&self) -> bool {
self.request.is_empty()
&& self.stream.is_empty()
&& self.config.additional_properties_policy == MiddlewareConfig::default().additional_properties_policy
&& self.config.property_format_policy == MiddlewareConfig::default().property_format_policy
&& self.config.property_min_length_policy == MiddlewareConfig::default().property_min_length_policy
&& self.config.array_min_items_policy == MiddlewareConfig::default().array_min_items_policy
&& self.config.array_max_items_policy == MiddlewareConfig::default().array_max_items_policy
&& self.config.max_tokens_cap.is_none()
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(try_from = "LlmRequestContract")]
pub(crate) struct LlmDispatchPayload {
#[serde(flatten)]
pub(crate) request: CoreRequest,
#[serde(default, skip_serializing_if = "LlmMiddlewarePayload::is_empty")]
pub(crate) middleware: LlmMiddlewarePayload,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub(crate) struct LlmRoutedBackendPayload {
pub(crate) provider_id: String,
pub(crate) protocol: String,
pub(crate) model: String,
#[serde(alias = "backendConfig")]
pub(crate) config: BackendConfig,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(try_from = "LlmStructuredRequestContract")]
pub(crate) struct LlmStructuredDispatchPayload {
#[serde(flatten)]
pub(crate) request: StructuredRequest,
#[serde(default, skip_serializing_if = "LlmMiddlewarePayload::is_empty")]
pub(crate) middleware: LlmMiddlewarePayload,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(from = "LlmEmbeddingRequestContract")]
pub(crate) struct LlmEmbeddingDispatchPayload {
pub(crate) request: EmbeddingRequest,
}
impl From<LlmEmbeddingRequestContract> for LlmEmbeddingDispatchPayload {
fn from(request: LlmEmbeddingRequestContract) -> Self {
Self {
request: request.into(),
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(from = "LlmRerankRequestContract")]
pub(crate) struct LlmRerankDispatchPayload {
#[serde(flatten)]
pub(crate) request: RerankRequest,
}
impl From<LlmRerankRequestContract> for LlmRerankDispatchPayload {
fn from(request: LlmRerankRequestContract) -> Self {
Self {
request: request.into(),
}
}
}
pub(crate) type LlmPreparedImageDispatchRoutePayload = SerializablePreparedRoute<LlmImageRequestContract>;
#[cfg(test)]
mod tests {
use llm_adapter::router::SerializablePreparedRoute;
use super::{
LlmDispatchPayload, LlmPreparedImageDispatchRoutePayload, LlmRerankDispatchPayload, LlmStructuredDispatchPayload,
};
#[test]
fn prepared_chat_route_payload_deserializes_nested_request() {
let payload = serde_json::from_value::<Vec<SerializablePreparedRoute<LlmDispatchPayload>>>(serde_json::json!([
{
"provider_id": "openai-primary",
"protocol": "openai_chat",
"model": "gpt-5-mini",
"config": {
"base_url": "https://api.openai.com",
"auth_token": "test-key"
},
"request": {
"model": "gpt-5-mini",
"messages": [
{
"role": "user",
"content": [{ "type": "text", "text": "hello" }]
}
]
}
}
]))
.expect("prepared chat route payload should deserialize");
assert_eq!(payload[0].model, "gpt-5-mini");
assert_eq!(payload[0].request.request.model, "gpt-5-mini");
}
#[test]
fn prepared_structured_route_payload_deserializes_nested_request() {
let payload =
serde_json::from_value::<Vec<SerializablePreparedRoute<LlmStructuredDispatchPayload>>>(serde_json::json!([
{
"provider_id": "openai-primary",
"protocol": "openai_responses",
"model": "gpt-5-mini",
"config": {
"base_url": "https://api.openai.com",
"auth_token": "test-key"
},
"request": {
"model": "gpt-5-mini",
"messages": [
{
"role": "user",
"content": [{ "type": "text", "text": "hello" }]
}
],
"schema": {
"type": "object",
"properties": {
"summary": { "type": "string" }
},
"required": ["summary"]
}
}
}
]))
.expect("prepared structured route payload should deserialize");
assert_eq!(payload[0].model, "gpt-5-mini");
assert_eq!(payload[0].request.request.model, "gpt-5-mini");
}
#[test]
fn prepared_rerank_route_payload_deserializes_nested_request() {
let payload =
serde_json::from_value::<Vec<SerializablePreparedRoute<LlmRerankDispatchPayload>>>(serde_json::json!([
{
"provider_id": "openai-primary",
"protocol": "openai_chat",
"model": "gpt-5-mini",
"config": {
"base_url": "https://api.openai.com",
"auth_token": "test-key"
},
"request": {
"model": "gpt-5-mini",
"query": "hello",
"candidates": [{ "text": "world" }]
}
}
]))
.expect("prepared rerank route payload should deserialize");
assert_eq!(payload[0].model, "gpt-5-mini");
assert_eq!(payload[0].request.request.model, "gpt-5-mini");
}
#[test]
fn prepared_image_route_payload_deserializes_nested_request() {
let payload = serde_json::from_value::<Vec<LlmPreparedImageDispatchRoutePayload>>(serde_json::json!([
{
"provider_id": "openai-primary",
"protocol": "openai_images",
"model": "gpt-image-1",
"config": {
"base_url": "https://api.openai.com",
"auth_token": "test-key",
"request_layer": "openai_images"
},
"request": {
"model": "gpt-image-1",
"prompt": "draw",
"operation": "generate"
}
}
]))
.expect("prepared image route payload should deserialize");
assert_eq!(payload[0].model, "gpt-image-1");
assert_eq!(payload[0].request.prompt, "draw");
}
}