Improve local evidence search and AI capabilities
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
use crate::app::AppState;
|
||||
use crate::context::RequestContext;
|
||||
use crate::error::WebError;
|
||||
use crate::hermes_tools::{ensure_write_authorized, ToolCallInput};
|
||||
use crate::routes;
|
||||
use axum::http::StatusCode;
|
||||
use serde::Deserialize;
|
||||
use serde_json::{json, Value};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct IndexSettingsArgs {
|
||||
#[serde(default)]
|
||||
include_paths: Vec<String>,
|
||||
#[serde(default)]
|
||||
schedule_mode: Option<String>,
|
||||
#[serde(default)]
|
||||
schedule_time: Option<String>,
|
||||
#[serde(default)]
|
||||
schedule_date: Option<String>,
|
||||
#[serde(default)]
|
||||
run_on_change: Option<bool>,
|
||||
}
|
||||
|
||||
pub async fn index_status(
|
||||
state: &AppState,
|
||||
context: &RequestContext,
|
||||
input: &ToolCallInput,
|
||||
) -> Result<Value, WebError> {
|
||||
let workspace_id = effective_workspace_id(context, input);
|
||||
let root_uri = required_root_uri(
|
||||
context,
|
||||
input,
|
||||
"mnote_index_root_required",
|
||||
"本地索引状态缺少 rootUri",
|
||||
)?;
|
||||
let root_path =
|
||||
routes::ensure_local_workspace_read_access_with_state(state, context, &root_uri)
|
||||
.map_err(|error| error.with_context(context))?;
|
||||
let user_settings = read_user_settings(state, context, &workspace_id, &root_path)?;
|
||||
let effective_settings = routes::effective_local_index_settings_for_root(
|
||||
state.control_plane(),
|
||||
&workspace_id,
|
||||
&root_path,
|
||||
)?;
|
||||
let status = routes::local_index_status_with_settings(
|
||||
&root_path,
|
||||
&root_uri,
|
||||
&workspace_id,
|
||||
&user_settings,
|
||||
&effective_settings,
|
||||
)?;
|
||||
Ok(json!({
|
||||
"ok": true,
|
||||
"schema": "mnote.index.status_result.v1",
|
||||
"result": status
|
||||
}))
|
||||
}
|
||||
|
||||
pub async fn index_refresh(
|
||||
state: &AppState,
|
||||
context: &RequestContext,
|
||||
input: &ToolCallInput,
|
||||
) -> Result<Value, WebError> {
|
||||
let workspace_id = effective_workspace_id(context, input);
|
||||
let root_uri = required_root_uri(
|
||||
context,
|
||||
input,
|
||||
"mnote_index_root_required",
|
||||
"本地索引刷新缺少 rootUri",
|
||||
)?;
|
||||
let root_path =
|
||||
routes::ensure_local_workspace_read_access_with_state(state, context, &root_uri)
|
||||
.map_err(|error| error.with_context(context))?;
|
||||
let effective_settings = routes::effective_local_index_settings_for_root(
|
||||
state.control_plane(),
|
||||
&workspace_id,
|
||||
&root_path,
|
||||
)?;
|
||||
let refreshed = routes::refresh_local_search_index_with_settings(
|
||||
&root_path,
|
||||
&root_uri,
|
||||
&workspace_id,
|
||||
&effective_settings,
|
||||
)?;
|
||||
Ok(json!({
|
||||
"ok": true,
|
||||
"schema": "mnote.index.refresh_result.v1",
|
||||
"index": refreshed
|
||||
}))
|
||||
}
|
||||
|
||||
pub async fn index_update_settings(
|
||||
state: &AppState,
|
||||
context: &RequestContext,
|
||||
input: &ToolCallInput,
|
||||
) -> Result<Value, WebError> {
|
||||
ensure_write_authorized(context, input)?;
|
||||
let workspace_id = effective_workspace_id(context, input);
|
||||
let root_uri = required_root_uri(
|
||||
context,
|
||||
input,
|
||||
"mnote_index_root_required",
|
||||
"本地索引设置缺少 rootUri",
|
||||
)?;
|
||||
let args = parse_settings_args(context, input)?;
|
||||
let root_path =
|
||||
routes::ensure_local_workspace_write_access_with_state(state, context, &root_uri)
|
||||
.map_err(|error| error.with_context(context))?;
|
||||
let actor_id = routes::current_actor_id(state, context).ok_or_else(|| {
|
||||
WebError::new(
|
||||
StatusCode::UNAUTHORIZED,
|
||||
"local_index_settings_auth_required",
|
||||
"本地索引设置需要登录用户",
|
||||
)
|
||||
.with_context(context)
|
||||
})?;
|
||||
if input.dry_run == Some(true) {
|
||||
let planned_settings = routes::preview_user_local_index_settings(
|
||||
state.control_plane(),
|
||||
&actor_id,
|
||||
&workspace_id,
|
||||
&root_path,
|
||||
&args.include_paths,
|
||||
args.schedule_mode.as_deref(),
|
||||
args.schedule_time.as_deref(),
|
||||
args.schedule_date.as_deref(),
|
||||
args.run_on_change,
|
||||
)?;
|
||||
let current_status = index_status(state, context, input).await?;
|
||||
let would_clear_index_files = planned_settings.include_paths.is_empty();
|
||||
return Ok(json!({
|
||||
"ok": true,
|
||||
"schema": "mnote.index.update_settings_plan.v1",
|
||||
"dryRun": true,
|
||||
"plannedSettings": planned_settings,
|
||||
"currentStatus": current_status.get("result").cloned().unwrap_or(Value::Null),
|
||||
"wouldRefresh": true,
|
||||
"wouldClearIndexFiles": would_clear_index_files
|
||||
}));
|
||||
}
|
||||
let settings = routes::write_user_local_index_settings(
|
||||
state.control_plane(),
|
||||
&actor_id,
|
||||
&workspace_id,
|
||||
&root_path,
|
||||
&args.include_paths,
|
||||
args.schedule_mode.as_deref(),
|
||||
args.schedule_time.as_deref(),
|
||||
args.schedule_date.as_deref(),
|
||||
args.run_on_change,
|
||||
)?;
|
||||
let effective_settings = routes::effective_local_index_settings_for_root(
|
||||
state.control_plane(),
|
||||
&workspace_id,
|
||||
&root_path,
|
||||
)?;
|
||||
let refreshed = routes::refresh_local_search_index_with_settings(
|
||||
&root_path,
|
||||
&root_uri,
|
||||
&workspace_id,
|
||||
&effective_settings,
|
||||
)?;
|
||||
let status = routes::local_index_status_with_settings(
|
||||
&root_path,
|
||||
&root_uri,
|
||||
&workspace_id,
|
||||
&settings,
|
||||
&effective_settings,
|
||||
)?;
|
||||
Ok(json!({
|
||||
"ok": true,
|
||||
"schema": "mnote.index.update_settings_result.v1",
|
||||
"settings": settings,
|
||||
"index": refreshed,
|
||||
"result": status
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_settings_args(
|
||||
context: &RequestContext,
|
||||
input: &ToolCallInput,
|
||||
) -> Result<IndexSettingsArgs, WebError> {
|
||||
let args = input.args.clone().unwrap_or_else(|| json!({}));
|
||||
serde_json::from_value::<IndexSettingsArgs>(args).map_err(|error| {
|
||||
WebError::bad_request_code(
|
||||
"mnote_index_settings_payload_invalid",
|
||||
format!("本地索引设置参数无效: {error}"),
|
||||
)
|
||||
.with_context(context)
|
||||
})
|
||||
}
|
||||
|
||||
fn read_user_settings(
|
||||
state: &AppState,
|
||||
context: &RequestContext,
|
||||
workspace_id: &str,
|
||||
root_path: &std::path::Path,
|
||||
) -> Result<routes::LocalIndexSettings, WebError> {
|
||||
if let Some(actor_id) = routes::current_actor_id(state, context) {
|
||||
return routes::read_user_local_index_settings(
|
||||
state.control_plane(),
|
||||
&actor_id,
|
||||
workspace_id,
|
||||
root_path,
|
||||
);
|
||||
}
|
||||
routes::read_local_index_settings_or_default(root_path)
|
||||
}
|
||||
|
||||
fn effective_workspace_id(context: &RequestContext, input: &ToolCallInput) -> String {
|
||||
input
|
||||
.effective_workspace_id()
|
||||
.or_else(|| context.workspace.workspace_id.clone())
|
||||
.unwrap_or_else(|| "default".into())
|
||||
}
|
||||
|
||||
fn required_root_uri(
|
||||
context: &RequestContext,
|
||||
input: &ToolCallInput,
|
||||
code: &'static str,
|
||||
message: &'static str,
|
||||
) -> Result<String, WebError> {
|
||||
input
|
||||
.effective_root_uri()
|
||||
.map(|value| value.trim().to_string())
|
||||
.filter(|value| !value.is_empty())
|
||||
.ok_or_else(|| WebError::bad_request_code(code, message).with_context(context))
|
||||
}
|
||||
Reference in New Issue
Block a user