Skip to content

Commit 4e227f5

Browse files
committed
fix: harness policy
1 parent 8d7c11b commit 4e227f5

38 files changed

Lines changed: 1731 additions & 1225 deletions

File tree

nix/packages/xiuxian-core-rs.nix

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,19 @@ python3Packages.buildPythonPackage {
114114
export RUST_LANG_PROJECT_HARNESS_REV="$(cargo_lock_git_rev "https://github.com/tao3k/rust-lang-project-harness")"
115115
export LANCE_REV="$(cargo_lock_git_rev "https://github.com/lancedb/lance.git")"
116116
export LITELLM_RS_REV="$(cargo_lock_git_rev "https://github.com/majiayu000/litellm-rs")"
117+
export LITCHI_REV="$(cargo_lock_git_rev "https://github.com/DevExzh/litchi.git")"
117118
118119
cat > .cargo/git-sources.toml <<EOF
119120
[source."git+https://github.com/majiayu000/litellm-rs?rev=''${LITELLM_RS_REV}"]
120121
git = "https://github.com/majiayu000/litellm-rs"
121122
rev = "''${LITELLM_RS_REV}"
122123
replace-with = "vendored-sources"
123124
125+
[source."git+https://github.com/DevExzh/litchi.git?rev=''${LITCHI_REV}"]
126+
git = "https://github.com/DevExzh/litchi.git"
127+
rev = "''${LITCHI_REV}"
128+
replace-with = "vendored-sources"
129+
124130
[source."git+https://github.com/J-F-Liu/lopdf?rev=''${LOPDF_REV}"]
125131
git = "https://github.com/J-F-Liu/lopdf"
126132
rev = "''${LOPDF_REV}"

packages/rust/crates/xiuxian-julia-runtime/tests/unit/lib/wendao.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,10 @@ fn link_graph_rerank_provider_binding_uses_runtime_route_and_launch_config() {
173173
assert_eq!(binding.selector.capability_id.0, JULIA_RERANK_CAPABILITY_ID);
174174
assert_eq!(binding.endpoint.route.as_deref(), Some("/custom-rerank"));
175175
assert_eq!(
176-
binding.launch.unwrap_or_else(|| panic!("launch config missing")).launcher_path,
176+
binding
177+
.launch
178+
.unwrap_or_else(|| panic!("launch config missing"))
179+
.launcher_path,
177180
DEFAULT_JULIA_SEARCH_LAUNCHER_PATH
178181
);
179182
}

packages/rust/crates/xiuxian-llm/src/model_routing/attachment.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,11 @@ pub struct WendaoAttachmentRouteConfig {
5454
#[derive(Debug, Clone, PartialEq, Eq)]
5555
pub struct WendaoAttachmentRouteInput {
5656
/// Gateway task kind.
57-
pub task_kind: String,
57+
pub task_kind: WendaoRouteTaskKind,
5858
/// Source modality.
5959
pub modality: String,
6060
/// Source kind.
61-
pub source_kind: String,
61+
pub source_kind: WendaoRouteSourceKind,
6262
/// Precision tier required by Gateway.
6363
pub precision_tier: String,
6464
/// Privacy tier used by route policy.
@@ -139,9 +139,9 @@ pub fn wendao_image_extract_route_config_with_model_routing_config(
139139
#[must_use]
140140
pub fn wendao_attachment_route_intent(input: &WendaoAttachmentRouteInput) -> WendaoRouteIntent {
141141
WendaoRouteIntent {
142-
task_kind: WendaoRouteTaskKind::new(input.task_kind.clone()),
142+
task_kind: input.task_kind.clone(),
143143
modality: input.modality.clone(),
144-
source_kind: WendaoRouteSourceKind::new(input.source_kind.clone()),
144+
source_kind: input.source_kind.clone(),
145145
precision_tier: input.precision_tier.clone(),
146146
privacy_tier: input.privacy_tier.clone(),
147147
latency_budget_ms: input.latency_budget_ms,
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
//! vLLM-SR route-decision probe client.
2+
3+
use reqwest::header::AUTHORIZATION;
4+
use serde_json::json;
5+
6+
use super::constants::VLLM_SR_AUTO_MODEL;
7+
use super::route_helpers::normalize_base_url;
8+
use super::types::{WendaoModelDecision, WendaoRouteIntent};
9+
10+
/// OpenAI-compatible vLLM-SR route-decision probe client.
11+
#[derive(Clone)]
12+
pub struct VllmSrRouteDecisionClient {
13+
base_url: String,
14+
bearer_token: Option<String>,
15+
http: reqwest::Client,
16+
}
17+
18+
impl VllmSrRouteDecisionClient {
19+
/// Build a route decision client.
20+
#[must_use]
21+
pub fn new(base_url: impl Into<String>) -> Self {
22+
Self::with_http_client(base_url, reqwest::Client::new())
23+
}
24+
25+
/// Build a route decision client with an injected HTTP client.
26+
#[must_use]
27+
pub fn with_http_client(base_url: impl Into<String>, http: reqwest::Client) -> Self {
28+
let base_url = base_url.into();
29+
Self {
30+
base_url: normalize_base_url(base_url.as_str()),
31+
bearer_token: None,
32+
http,
33+
}
34+
}
35+
36+
/// Attach an optional bearer token for vLLM-SR deployments that require it.
37+
#[must_use]
38+
pub fn with_bearer_token(mut self, bearer_token: impl Into<String>) -> Self {
39+
let bearer_token = bearer_token.into();
40+
self.bearer_token = (!bearer_token.trim().is_empty()).then_some(bearer_token);
41+
self
42+
}
43+
44+
/// Return the normalized vLLM-SR base URL.
45+
#[must_use]
46+
pub fn base_url(&self) -> &str {
47+
self.base_url.as_str()
48+
}
49+
50+
/// Obtain a route decision through vLLM-SR's OpenAI-compatible data plane.
51+
///
52+
/// # Errors
53+
///
54+
/// Returns an error when the HTTP request fails, vLLM-SR returns a
55+
/// non-success status, or the response does not include a selected model.
56+
pub async fn decide(
57+
&self,
58+
intent: &WendaoRouteIntent,
59+
selected_provider: &str,
60+
selected_backend_profile: &str,
61+
) -> Result<WendaoModelDecision, String> {
62+
let endpoint = format!("{}/v1/chat/completions", self.base_url);
63+
let prompt = serde_json::to_string(intent)
64+
.map_err(|error| format!("serialize Wendao route intent: {error}"))?;
65+
let payload = json!({
66+
"model": VLLM_SR_AUTO_MODEL,
67+
"messages": [
68+
{
69+
"role": "system",
70+
"content": "Route this Wendao task. The response body is ignored; Wendao reads vLLM-SR routing headers."
71+
},
72+
{
73+
"role": "user",
74+
"content": prompt
75+
}
76+
],
77+
"temperature": 0,
78+
"max_tokens": 1,
79+
"stream": false
80+
});
81+
82+
let mut request = self.http.post(endpoint.as_str()).json(&payload);
83+
if let Some(token) = self.bearer_token.as_deref() {
84+
request = request.header(AUTHORIZATION, format!("Bearer {token}"));
85+
}
86+
let response = request
87+
.send()
88+
.await
89+
.map_err(|error| format!("call vLLM-SR route probe `{endpoint}`: {error}"))?;
90+
let status = response.status();
91+
let headers = response.headers().clone();
92+
let body = response
93+
.text()
94+
.await
95+
.map_err(|error| format!("read vLLM-SR route probe response body: {error}"))?;
96+
if !status.is_success() {
97+
return Err(format!(
98+
"vLLM-SR route probe `{endpoint}` returned {status}: {body}"
99+
));
100+
}
101+
WendaoModelDecision::from_vllm_sr_response_parts(
102+
&headers,
103+
&body,
104+
selected_provider,
105+
selected_backend_profile,
106+
)
107+
}
108+
}

packages/rust/crates/xiuxian-llm/src/model_routing/config.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use super::{
1313
WENDAO_CHAT_ROUTE_MODEL_ENV, WENDAO_CHAT_ROUTE_PROVIDER_ENV,
1414
WENDAO_IMAGE_EXTRACT_ROUTE_BACKEND_PROFILE_ENV, WENDAO_IMAGE_EXTRACT_ROUTE_MODEL_ENV,
1515
WENDAO_IMAGE_EXTRACT_ROUTE_PROVIDER_ENV, WENDAO_MODEL_ROUTING_MODE_ENV,
16-
WENDAO_VLLM_SR_BASE_URL_ENV,
16+
WENDAO_VLLM_SR_BASE_URL_ENV, WendaoModelRoutingMode,
1717
};
1818

1919
/// System-level Wendao model-routing defaults shipped by `xiuxian-llm`.
@@ -25,7 +25,7 @@ pub const WENDAO_MODEL_ROUTING_SYSTEM_DEFAULT_TOML: &str =
2525
pub struct WendaoModelRoutingTomlConfig {
2626
/// Routing mode, for example `deterministic` or `vllm-sr`.
2727
#[serde(default)]
28-
pub mode: Option<String>,
28+
pub mode: Option<WendaoModelRoutingMode>,
2929
/// vLLM-SR base URL used when vLLM-SR mode is enabled.
3030
#[serde(default)]
3131
pub vllm_sr_base_url: Option<String>,
@@ -154,7 +154,7 @@ fn toml_value_for_env_key(
154154
) -> Option<String> {
155155
let config = config?;
156156
let raw = match key {
157-
WENDAO_MODEL_ROUTING_MODE_ENV => config.mode.as_deref(),
157+
WENDAO_MODEL_ROUTING_MODE_ENV => config.mode.map(WendaoModelRoutingMode::as_str),
158158
WENDAO_VLLM_SR_BASE_URL_ENV => config.vllm_sr_base_url.as_deref(),
159159
WENDAO_CHAT_ROUTE_PROVIDER_ENV => route_provider(&config.chat, config),
160160
WENDAO_CHAT_ROUTE_MODEL_ENV => config.chat.model.as_deref(),
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//! Flight metadata emission for model route intents and decisions.
2+
3+
use super::constants::{
4+
WENDAO_ROUTE_ID_HEADER, WENDAO_ROUTE_MODALITY_HEADER, WENDAO_ROUTE_PRECISION_TIER_HEADER,
5+
WENDAO_ROUTE_SELECTED_BACKEND_PROFILE_HEADER, WENDAO_ROUTE_SELECTED_MODEL_HEADER,
6+
WENDAO_ROUTE_SELECTED_PROVIDER_HEADER, WENDAO_ROUTE_TASK_KIND_HEADER,
7+
};
8+
use super::types::{WendaoModelDecision, WendaoRouteIntent};
9+
10+
/// Emit stable Flight metadata pairs for a route intent and decision.
11+
#[must_use]
12+
pub fn wendao_model_route_metadata(
13+
intent: &WendaoRouteIntent,
14+
decision: &WendaoModelDecision,
15+
) -> Vec<(&'static str, String)> {
16+
vec![
17+
(WENDAO_ROUTE_ID_HEADER, decision.route_id.clone()),
18+
(
19+
WENDAO_ROUTE_TASK_KIND_HEADER,
20+
intent.task_kind.as_str().to_owned(),
21+
),
22+
(WENDAO_ROUTE_MODALITY_HEADER, intent.modality.clone()),
23+
(
24+
WENDAO_ROUTE_SELECTED_PROVIDER_HEADER,
25+
decision.selected_provider.clone(),
26+
),
27+
(
28+
WENDAO_ROUTE_SELECTED_MODEL_HEADER,
29+
decision.selected_model.clone(),
30+
),
31+
(
32+
WENDAO_ROUTE_SELECTED_BACKEND_PROFILE_HEADER,
33+
decision.selected_backend_profile.clone(),
34+
),
35+
(
36+
WENDAO_ROUTE_PRECISION_TIER_HEADER,
37+
intent.precision_tier.clone(),
38+
),
39+
]
40+
}

0 commit comments

Comments
 (0)