feat(pi): add first-class Coding Agent support

This commit is contained in:
SaladDay
2026-08-03 09:21:23 +00:00
parent ac764b4818
commit 9d71e355da
159 changed files with 39663 additions and 632 deletions
+56
View File
@@ -181,6 +181,7 @@ impl StreamCheckService {
}
AppType::OpenClaw => Self::extract_openclaw_base_url(provider),
AppType::Hermes => Self::extract_hermes_base_url(provider),
AppType::Pi => Self::extract_pi_base_url(provider),
AppType::ClaudeDesktop => ClaudeAdapter::new()
.extract_base_url(provider)
.map_err(|e| AppError::Message(format!("Failed to extract base_url: {e}"))),
@@ -323,6 +324,31 @@ impl StreamCheckService {
})
}
/// Pi endpoint inheritance is owned by the pinned composer. Reachability
/// checks deliberately consume its first effective model instead of
/// reimplementing provider/model fallback rules.
fn extract_pi_base_url(provider: &Provider) -> Result<String, AppError> {
let config: crate::pi_config::model::PiManagedProviderConfig =
serde_json::from_value(provider.settings_config.clone()).map_err(|error| {
AppError::InvalidInput(format!(
"Pi provider '{}' is not a managed native configuration: {error}",
provider.id
))
})?;
let composition =
crate::pi_config::native::compose_managed_pi_provider(&provider.id, &config)?;
composition
.models
.first()
.map(|model| model.base_url.clone())
.ok_or_else(|| {
AppError::InvalidInput(format!(
"Pi provider '{}' has no effective models",
provider.id
))
})
}
/// OpenCode: `{ npm, options: { baseURL, apiKey }, ... }`
///
/// 用户未显式填 `options.baseURL` 时,按 `npm`AI SDK 包)回退到包自带默认端点。
@@ -501,6 +527,36 @@ mod tests {
);
}
#[test]
fn pi_reachability_uses_composer_effective_model_endpoint() {
let provider = make_provider(serde_json::json!({
"name": "Pi",
"api": "openai-responses",
"baseUrl": "https://provider.example/v1",
"apiKey": "literal",
"models": [{
"id": "model",
"name": "Model",
"baseUrl": "https://model.example/custom",
"reasoning": false,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 128000,
"maxTokens": 8192
}]
}));
assert_eq!(
StreamCheckService::resolve_base_url(&AppType::Pi, &provider).unwrap(),
"https://model.example/custom"
);
}
#[test]
fn test_resolve_base_url_uses_explicit_url_or_errors_when_missing() {
// 有显式 base_url → 直接用