feat: add MCP/skill tool protocol, tasks/tools pages, chat UX overhaul
Tools page: skill & MCP server management with [[skill:]]/[[mcp:]] protocol (up to 3 rounds). New Tasks and Tools pages. Chat: session snapshots, empty-session reuse, per-message model_id, unified JSON prediction suggestions. Settings: upload size limit, prediction options, model enable/disable. Schema additions with ensure_column migrations. Add FEATURES/ARCHITECTURE docs.
This commit is contained in:
@@ -5,5 +5,5 @@ pub mod types;
|
||||
|
||||
pub use error::{EngineError, Result};
|
||||
pub use manager::EngineManager;
|
||||
pub use remote::{stream_chat_remote, RemoteConfig};
|
||||
pub use remote::{chat_remote, stream_chat_remote, RemoteConfig};
|
||||
pub use types::{ChatMessage, ChatRequest, ChatStreamEvent, EngineConfig, EngineStatus};
|
||||
@@ -271,6 +271,11 @@ pub(crate) fn sse_text_stream(
|
||||
.or_else(|| value.get("content").and_then(|v| v.as_str()));
|
||||
if let Some(text) = content {
|
||||
yield Ok(ChatStreamEvent::Text(text.to_string()));
|
||||
} else if let Some(reasoning) = value
|
||||
.pointer("/choices/0/delta/reasoning_content")
|
||||
.and_then(|v| v.as_str())
|
||||
{
|
||||
yield Ok(ChatStreamEvent::Reasoning(reasoning.to_string()));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
|
||||
@@ -33,6 +33,32 @@ pub async fn stream_chat_remote(
|
||||
Ok(sse_text_stream(response.bytes_stream()))
|
||||
}
|
||||
|
||||
/// 调用远程 OpenAI 兼容 API 并返回完整的非流式文本。
|
||||
pub async fn chat_remote(cfg: &RemoteConfig, req: ChatRequest) -> Result<String> {
|
||||
let base = normalize_base(&cfg.base_url);
|
||||
let url = format!("{base}/chat/completions");
|
||||
let client = reqwest::Client::new();
|
||||
let mut builder = client.post(&url).json(&req);
|
||||
if let Some(key) = &cfg.api_key {
|
||||
if !key.trim().is_empty() {
|
||||
builder = builder.bearer_auth(key);
|
||||
}
|
||||
}
|
||||
let response = builder.send().await?;
|
||||
let status = response.status();
|
||||
if !status.is_success() {
|
||||
let body = response.text().await.unwrap_or_default();
|
||||
return Err(EngineError::EngineHttp(status, body));
|
||||
}
|
||||
let value: serde_json::Value = response.json().await?;
|
||||
let text = value
|
||||
.pointer("/choices/0/message/content")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string();
|
||||
Ok(text)
|
||||
}
|
||||
|
||||
/// 把用户填写的 base url 归一化为 `<scheme>://<host>/v1` 形式。
|
||||
fn normalize_base(base: &str) -> String {
|
||||
let mut s = base.trim().trim_end_matches('/').to_string();
|
||||
|
||||
@@ -55,6 +55,8 @@ impl Serialize for ChatMessage {
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ChatStreamEvent {
|
||||
Text(String),
|
||||
/// 推理模型(如 Qwen3.5/DeepSeek-R1)的思考过程增量文本
|
||||
Reasoning(String),
|
||||
Usage {
|
||||
prompt_tokens: u32,
|
||||
completion_tokens: u32,
|
||||
|
||||
Reference in New Issue
Block a user