feat(providers): Moonshot AI provider + extract shared OpenAI-compat helpers
- Add MoonshotProvider and MoonshotCodeProvider (OpenAI-compatible)
- Extract `fetch_openai_models()` and `build_openai_llm()` shared functions,
deduplicating model-listing and client construction across OpenAI,
OpenRouter, DeepSeek, LM Studio, and Z.AI providers
- Add `lists_models` field to `ProviderUiMeta` — drives frontend model
picker dynamically instead of hardcoded `type_id` list
- Port DeepSeek, LM Studio, OpenRouter model listing to `fetch_openai_models`
- Set `lists_models: true` on Z.AI provider (missed in prior pass)
feat(mcp): named key placeholders {SECRET:name}/{ENV:name} in connector URLs
- `apply_key_placeholder` now accepts an `env` map alongside `api_key`
- Named tokens resolve from the connector's described `env[]` fields,
with `{SECRET:name}` falling back to `api_key` for backward compat
- Replace `substitute_secret_tokens` with `substitute_named_tokens`
- Unresolved tokens are left in place (visible misconfig) rather than
silently producing a wrong URL
fix(ui): connector detail hides generic API key when schema has secret
fix(ui): model picker uses `lists_models` from provider types endpoint
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
pub mod anthropic;
|
||||
pub mod deepseek;
|
||||
pub mod lm_studio;
|
||||
pub mod moonshot;
|
||||
pub mod ollama;
|
||||
pub mod openai;
|
||||
pub mod openrouter;
|
||||
@@ -10,7 +11,13 @@ pub mod zai;
|
||||
pub use crate::provider::ServiceType;
|
||||
pub use core_api::provider::RemoteLlmModelInfo;
|
||||
|
||||
use core_api::provider::{ApiProvider, LlmModelRecord};
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
|
||||
use core_api::provider::{ApiProvider, BuiltLlmClient, LlmModelRecord, LlmProviderRecord};
|
||||
|
||||
use crate::chatbot::openai::OpenAiClient;
|
||||
|
||||
/// Computes the `extra_params` an OpenAI-compatible client should be built with,
|
||||
/// given a model's stored `extra_params` and its selected reasoning value. The
|
||||
@@ -37,3 +44,54 @@ pub(crate) fn extra_with_reasoning(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetches the OpenAI-style `GET {base_url}/models` catalog shared by most
|
||||
/// OpenAI-compatible providers. Returns the raw per-model JSON objects from
|
||||
/// the `data` envelope so each provider can map and enrich them with its own
|
||||
/// heuristics. `api_key` is sent as a bearer token when present (local
|
||||
/// providers pass `None`); `who` is the display name used in error messages.
|
||||
pub(crate) async fn fetch_openai_models(
|
||||
http: &reqwest::Client,
|
||||
base_url: &str,
|
||||
api_key: Option<&str>,
|
||||
who: &str,
|
||||
) -> Result<Vec<serde_json::Value>> {
|
||||
let url = format!("{}/models", base_url.trim_end_matches('/'));
|
||||
let mut req = http.get(&url);
|
||||
if let Some(key) = api_key {
|
||||
req = req.bearer_auth(key);
|
||||
}
|
||||
let resp: serde_json::Value = req
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| anyhow!("{who} request failed: {e}"))?
|
||||
.error_for_status()
|
||||
.map_err(|e| anyhow!("{who} error response: {e}"))?
|
||||
.json()
|
||||
.await
|
||||
.map_err(|e| anyhow!("{who} response parse failed: {e}"))?;
|
||||
|
||||
resp["data"]
|
||||
.as_array()
|
||||
.cloned()
|
||||
.ok_or_else(|| anyhow!("unexpected {who} response shape"))
|
||||
}
|
||||
|
||||
/// Builds an `OpenAiClient` for an OpenAI-compatible provider: requires the
|
||||
/// provider record's `api_key` and merges the model's stored `extra_params`
|
||||
/// with the provider-translated reasoning fragment (see `extra_with_reasoning`).
|
||||
pub(crate) fn build_openai_llm(
|
||||
provider: &dyn ApiProvider,
|
||||
base_url: &str,
|
||||
record: &LlmProviderRecord,
|
||||
model: &LlmModelRecord,
|
||||
prompt_cache: bool,
|
||||
) -> Result<BuiltLlmClient> {
|
||||
let key = record.api_key.as_deref()
|
||||
.with_context(|| format!("provider '{}': api_key required for {}", record.name, provider.type_id()))?;
|
||||
let extra = extra_with_reasoning(provider, model);
|
||||
Ok(BuiltLlmClient {
|
||||
client: Arc::new(OpenAiClient::new(base_url, key, extra, prompt_cache)),
|
||||
prompt_cache,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user