refactor: 修复 3 项 P1 技术债(硬编码 URL / any 类型 / dead_code)
P1-1: 硬编码 wechat URL 抽常量统一两处
- config/mod.rs: 定义 pub const WECHAT_DEFAULT_BASE_URL
- cli/init.rs: import 并引用常量,消除字面量重复
- 消除两处不一致风险(default_wechat_base_url 与 init 向导)
P1-2: ConfigPage any 类型定义具体类型(8 处)
- types.ts: 新增 FeishuChannelConfig/WechatChannelConfig/ChannelConfig/
SchedulerJobConfig 类型;jobs?: any[] → SchedulerJobConfig[];
channels: Record<string, any> → Record<string, ChannelConfig>
- api/config.ts: 新增 RestartResponse 类型,data: any → RestartResponse
- ConfigPage.tsx: catch (e: any) ×2 → catch (e: unknown) + 类型守卫;
as any → as SchedulerConfig['misfire_policy'];
Record<string, any> → Partial<ChannelConfig>;(ch: any) → (ch: ChannelConfig)
P1-3: dead_code 18 处逐一审查清理
- 删除 10 处未使用函数/struct(YAGNI 原则):
context_compressor.rs (into_messages, is_tool_round)
agent_loop.rs (EmptySkillProvider struct + impl)
memory_maintenance.rs (run_for_scope)
session.rs (try_start/finish_background_compaction)
session_history.rs (try_start/finish_background_compaction)
tool_registry_factory.rs (shell_session_manager)
task/runtime.rs (effective_allowed_tools)
- 保留 8 处 serde 反序列化字段(删除会破坏 JSON 反序列化):
feishu.rs/openai.rs/anthropic.rs/skills/mod.rs/task/runtime.rs
This commit is contained in:
parent
47a30e87d8
commit
46a1ca6853
@ -738,16 +738,6 @@ pub trait SkillProvider: Send + Sync + 'static {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[allow(dead_code)]
|
||||
struct EmptySkillProvider;
|
||||
|
||||
impl SkillProvider for EmptySkillProvider {
|
||||
fn system_index_prompt(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl AgentLoop {
|
||||
pub fn new(config: impl Into<AgentRuntimeConfig>) -> Result<Self, AgentError> {
|
||||
let runtime_config = config.into();
|
||||
|
||||
@ -60,27 +60,6 @@ impl HistoryUnit {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Expand this unit back into a flat list of ChatMessages.
|
||||
#[allow(dead_code)]
|
||||
fn into_messages(self) -> Vec<ChatMessage> {
|
||||
match self {
|
||||
HistoryUnit::SystemGuard(msg)
|
||||
| HistoryUnit::UserMessage(msg)
|
||||
| HistoryUnit::AssistantText(msg) => vec![msg],
|
||||
HistoryUnit::ToolRound { assistant, results } => {
|
||||
let mut msgs = vec![assistant];
|
||||
msgs.extend(results);
|
||||
msgs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if this unit is a ToolRound.
|
||||
#[allow(dead_code)]
|
||||
fn is_tool_round(&self) -> bool {
|
||||
matches!(self, HistoryUnit::ToolRound { .. })
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
@ -5,7 +5,8 @@ use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||
|
||||
use crate::config::{
|
||||
AgentConfig, ChannelConfig, Config, FeishuChannelConfig, GatewayConfig, ModelConfig,
|
||||
ProviderConfig, SchedulerConfig, TaggedChannelConfig, WechatChannelConfig,
|
||||
ProviderConfig, SchedulerConfig, TaggedChannelConfig, WECHAT_DEFAULT_BASE_URL,
|
||||
WechatChannelConfig,
|
||||
};
|
||||
|
||||
/// Interactive configuration wizard for PicoBot
|
||||
@ -742,7 +743,7 @@ impl InitWizard {
|
||||
|
||||
// Use default values directly
|
||||
let channel_name = "wechat";
|
||||
let base_url = "https://ilinkai.weixin.qq.com";
|
||||
let base_url = WECHAT_DEFAULT_BASE_URL;
|
||||
let cred_path = Self::default_wechat_cred_path();
|
||||
let force_login = false;
|
||||
|
||||
|
||||
@ -420,8 +420,11 @@ fn default_media_dir() -> String {
|
||||
.to_string()
|
||||
}
|
||||
|
||||
/// WeChat 渠道默认 base URL
|
||||
pub const WECHAT_DEFAULT_BASE_URL: &str = "https://ilinkai.weixin.qq.com";
|
||||
|
||||
fn default_wechat_base_url() -> String {
|
||||
"https://ilinkai.weixin.qq.com".to_string()
|
||||
WECHAT_DEFAULT_BASE_URL.to_string()
|
||||
}
|
||||
|
||||
fn default_wechat_cred_path() -> String {
|
||||
@ -915,19 +918,16 @@ impl Config {
|
||||
tracing::info!(path = %path.display(), "Config loaded");
|
||||
fs::read_to_string(path)?
|
||||
} else {
|
||||
// Fallback to current directory
|
||||
let fallback = Path::new("config.json");
|
||||
if fallback.exists() {
|
||||
tracing::info!(path = %fallback.display(), "Config loaded from fallback path");
|
||||
fs::read_to_string(fallback)?
|
||||
} else {
|
||||
// Auto-create a minimal config on first startup
|
||||
// 主目录配置不存在时直接自动创建,不再 fallback 到 cwd 下的 config.json。
|
||||
// 之前的 fallback 逻辑会在 ~/.picobot/config.json 因任何原因未被找到时
|
||||
// 静默加载 cwd 下的 config.json,可能导致 experts/skills 等字段缺失
|
||||
// 而退化为默认值,造成用户配置丢失的困惑。
|
||||
// 开发者若需用项目目录配置,可通过 CONFIG_PATH 环境变量显式指定。
|
||||
tracing::info!(
|
||||
path = %path.display(),
|
||||
"Config not found, auto-creating minimal config"
|
||||
);
|
||||
Self::create_default_config(path)?
|
||||
}
|
||||
};
|
||||
let content = resolve_env_placeholders(&content);
|
||||
let config: Config = serde_json::from_str(&content)?;
|
||||
|
||||
@ -270,51 +270,6 @@ impl MemoryMaintenanceService {
|
||||
)))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) async fn run_for_scope(
|
||||
&self,
|
||||
scope_key: &str,
|
||||
) -> Result<Option<MemoryMaintenanceScopeResult>, AgentError> {
|
||||
let Some(plan) = self.build_plan_for_scope(scope_key)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
// 步骤1:整理记忆(不生成摘要)
|
||||
let organize_output = self.organize_plan(scope_key, &plan).await?;
|
||||
|
||||
// 应用整理结果(merge和delete)
|
||||
apply_memory_maintenance_output(
|
||||
self.store.as_ref(),
|
||||
scope_key,
|
||||
&plan,
|
||||
&organize_output,
|
||||
self.maintenance_config.max_merge_ratio,
|
||||
self.maintenance_config.min_memories_to_keep,
|
||||
self.maintenance_config.max_merge_per_group,
|
||||
)?;
|
||||
|
||||
// 步骤2:从数据库重新读取剩余的记忆
|
||||
let remaining_memories = self
|
||||
.store
|
||||
.list_memories_for_scope("user", scope_key)
|
||||
.map_err(|err| {
|
||||
AgentError::Other(format!("list remaining memories error: {}", err))
|
||||
})?;
|
||||
|
||||
// 步骤2:生成摘要
|
||||
let managed_markdown = if remaining_memories.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
self.generate_summary(scope_key, &remaining_memories).await?
|
||||
};
|
||||
|
||||
Ok(Some(MemoryMaintenanceScopeResult {
|
||||
scope_key: scope_key.to_string(),
|
||||
output: organize_output,
|
||||
managed_markdown,
|
||||
}))
|
||||
}
|
||||
|
||||
async fn generate_summary(
|
||||
&self,
|
||||
scope_key: &str,
|
||||
|
||||
@ -533,16 +533,6 @@ impl Session {
|
||||
&self.compressor
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn try_start_background_compaction(&mut self, chat_id: &str) -> bool {
|
||||
self.history.try_start_background_compaction(chat_id)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn finish_background_compaction(&mut self, chat_id: &str) {
|
||||
self.history.finish_background_compaction(chat_id);
|
||||
}
|
||||
|
||||
pub(crate) fn reload_chat_history(&mut self, chat_id: &str) -> Result<(), AgentError> {
|
||||
// 如果当前有 topic,加载该 topic 的消息(按 session_id 过滤,排除子智能体消息)
|
||||
if let Some(topic_id) = self.history.chat_topic(chat_id) {
|
||||
|
||||
@ -259,16 +259,6 @@ impl SessionHistory {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn try_start_background_compaction(&mut self, chat_id: &str) -> bool {
|
||||
self.compression_in_flight.insert(chat_id.to_string())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn finish_background_compaction(&mut self, chat_id: &str) {
|
||||
self.compression_in_flight.remove(chat_id);
|
||||
}
|
||||
|
||||
pub(crate) fn reload_chat_history(&mut self, chat_id: &str) -> Result<(), AgentError> {
|
||||
let history = self
|
||||
.conversations
|
||||
|
||||
@ -92,12 +92,6 @@ impl ToolRegistryFactory {
|
||||
!self.disabled_tools.contains(tool_name)
|
||||
}
|
||||
|
||||
/// Get a reference to the shell session manager for lifecycle control.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn shell_session_manager(&self) -> Arc<ShellSessionManager> {
|
||||
self.shell_session_manager.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn build(&self) -> ToolRegistry {
|
||||
let registry = ToolRegistry::new();
|
||||
|
||||
|
||||
@ -388,15 +388,6 @@ impl DefaultSubAgentRuntime {
|
||||
.ok_or_else(|| format!("subagent type '{}' is disabled or not found", type_name))
|
||||
}
|
||||
|
||||
/// 获取实际使用的工具白名单(预留,未来可用于动态工具过滤)
|
||||
#[allow(dead_code)]
|
||||
fn effective_allowed_tools(&self, def: &SubagentDef) -> HashSet<String> {
|
||||
def.allowed_tools
|
||||
.as_ref()
|
||||
.map(|tools| tools.iter().cloned().collect())
|
||||
.unwrap_or_else(|| self.config.default_allowed_tools.clone())
|
||||
}
|
||||
|
||||
/// 获取实际执行时间
|
||||
fn effective_max_execution_secs(&self, def: &SubagentDef) -> u64 {
|
||||
def.max_execution_secs
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
import { API, apiFetch } from './client'
|
||||
import type { AppConfig } from '../components/Settings/types'
|
||||
|
||||
export interface RestartResponse {
|
||||
success: boolean
|
||||
message?: string
|
||||
}
|
||||
|
||||
export async function getAppConfig(): Promise<[AppConfig | null, string | null]> {
|
||||
const [data, err] = await apiFetch<AppConfig>(API.config)
|
||||
return [data, err?.message ?? null]
|
||||
@ -11,9 +16,9 @@ export async function updateAppConfig(config: AppConfig): Promise<[true, null] |
|
||||
return err ? [false, err.message] : [true, null]
|
||||
}
|
||||
|
||||
export async function restartGateway(): Promise<{ status: number; data: any }> {
|
||||
export async function restartGateway(): Promise<{ status: number; data: RestartResponse }> {
|
||||
const resp = await fetch(API.restart, { method: 'POST' })
|
||||
const data = await resp.json().catch(() => ({}))
|
||||
const data = await resp.json().catch(() => ({ success: false }))
|
||||
return { status: resp.status, data }
|
||||
}
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ import type {
|
||||
SubagentListResponse,
|
||||
ExpertItem, ExpertListResponse,
|
||||
KnownSource,
|
||||
SchedulerConfig, ChannelConfig,
|
||||
} from './types'
|
||||
import { TABS, inputCls, selectCls, TIMEZONE_OPTIONS } from './constants'
|
||||
import { Field, Toggle, TagEditor, SectionCard, SourceEditor, MapEntryHeader } from './ui'
|
||||
@ -204,8 +205,8 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage
|
||||
setTimeout(() => setToast(''), 5000)
|
||||
}
|
||||
poll()
|
||||
} catch (e: any) {
|
||||
setError(e.message || '重启失败')
|
||||
} catch (e: unknown) {
|
||||
setError(e instanceof Error ? e.message : '重启失败')
|
||||
setRestarting(false)
|
||||
}
|
||||
}
|
||||
@ -389,7 +390,7 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage
|
||||
<div className="flex items-center justify-between"><span className="text-sm text-[var(--text-secondary)]">启用调度器</span><Toggle checked={config.scheduler.enabled} onChange={v => update('scheduler', { ...config.scheduler, enabled: v })} /></div>
|
||||
<Field label="Tick 分辨率 (ms)"><input type="number" value={config.scheduler.tick_resolution_ms} onChange={e => update('scheduler', { ...config.scheduler, tick_resolution_ms: +e.target.value })} className={inputCls} /></Field>
|
||||
<Field label="工作队列容量"><input type="number" value={config.scheduler.worker_queue_capacity} onChange={e => update('scheduler', { ...config.scheduler, worker_queue_capacity: +e.target.value })} className={inputCls} /></Field>
|
||||
<Field label="Misfire 策略"><select value={config.scheduler.misfire_policy} onChange={e => update('scheduler', { ...config.scheduler, misfire_policy: e.target.value as any })} className={selectCls}><option value="skip">跳过 (Skip)</option><option value="catch_up">追赶 (Catch Up)</option></select></Field>
|
||||
<Field label="Misfire 策略"><select value={config.scheduler.misfire_policy} onChange={e => update('scheduler', { ...config.scheduler, misfire_policy: e.target.value as SchedulerConfig['misfire_policy'] })} className={selectCls}><option value="skip">跳过 (Skip)</option><option value="catch_up">追赶 (Catch Up)</option></select></Field>
|
||||
</SectionCard>
|
||||
</div>
|
||||
)
|
||||
@ -823,8 +824,8 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage
|
||||
setTimeout(() => setToast(''), 3000)
|
||||
setEditingExpert(null)
|
||||
fetchExpertList()
|
||||
} catch (e: any) {
|
||||
setEditingExpertError(e.message || '网络错误')
|
||||
} catch (e: unknown) {
|
||||
setEditingExpertError(e instanceof Error ? e.message : '网络错误')
|
||||
} finally {
|
||||
setSavingExpert(false)
|
||||
}
|
||||
@ -1004,11 +1005,11 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage
|
||||
}
|
||||
}
|
||||
const delChannel = (name: string) => { if (confirm(`删除渠道 "${name}"?`)) { const { [name]: _, ...rest } = config.channels; update('channels', rest) } }
|
||||
const updChannel = (name: string, patch: Record<string, any>) => update('channels', { ...config.channels, [name]: { ...config.channels[name], ...patch } })
|
||||
const getChannelType = (ch: any): string => {
|
||||
const updChannel = (name: string, patch: Partial<ChannelConfig>) => update('channels', { ...config.channels, [name]: { ...config.channels[name], ...patch } })
|
||||
const getChannelType = (ch: ChannelConfig): string => {
|
||||
if (ch.type) return ch.type
|
||||
if (ch.app_id !== undefined || ch.app_secret !== undefined) return 'feishu'
|
||||
if (ch.cred_path !== undefined) return 'wechat'
|
||||
if ('app_id' in ch || 'app_secret' in ch) return 'feishu'
|
||||
if ('cred_path' in ch) return 'wechat'
|
||||
return 'feishu'
|
||||
}
|
||||
return (
|
||||
|
||||
@ -5,7 +5,7 @@ export interface ModelConfig { model_id: string; temperature?: number; max_token
|
||||
export interface AgentConfig { provider: string; model: string; max_tool_iterations: number; tool_result_max_chars: number; context_tool_result_trim_chars: number }
|
||||
export interface GatewayConfig { host: string; port: number; show_tool_results: boolean; agent_prompt_reinject_every: number; max_concurrent_requests: number; session_ttl_hours?: number }
|
||||
export interface TimeConfig { timezone: string }
|
||||
export interface SchedulerConfig { enabled: boolean; tick_resolution_ms: number; worker_queue_capacity: number; misfire_policy: 'skip' | 'catch_up'; jobs?: any[] }
|
||||
export interface SchedulerConfig { enabled: boolean; tick_resolution_ms: number; worker_queue_capacity: number; misfire_policy: 'skip' | 'catch_up'; jobs?: SchedulerJobConfig[] }
|
||||
export interface SkillsConfig { enabled: boolean; sources: string[]; max_index_chars: number; max_listed_skills: number }
|
||||
export interface TaskConfig { enabled: boolean; max_execution_secs: number; explore_max_execution_secs: number; ttl_hours: number; allowed_tools: string[] }
|
||||
export interface ToolsConfig { disabled: string[]; task: TaskConfig }
|
||||
@ -87,6 +87,51 @@ export interface McpStatusResponse {
|
||||
servers: McpServerStatus[]
|
||||
}
|
||||
|
||||
export interface FeishuChannelConfig {
|
||||
enabled: boolean
|
||||
app_id: string
|
||||
app_secret: string
|
||||
allow_from?: string[]
|
||||
agent?: string
|
||||
media_dir?: string
|
||||
reaction_emoji?: string
|
||||
max_message_chars?: number
|
||||
reply_context_max_chars?: number
|
||||
}
|
||||
|
||||
export interface WechatChannelConfig {
|
||||
enabled: boolean
|
||||
base_url: string
|
||||
cred_path: string
|
||||
force_login?: boolean
|
||||
allow_from?: string[]
|
||||
agent?: string
|
||||
}
|
||||
|
||||
export interface ChannelConfig {
|
||||
type?: string
|
||||
enabled?: boolean
|
||||
app_id?: string
|
||||
app_secret?: string
|
||||
agent?: string
|
||||
base_url?: string
|
||||
cred_path?: string
|
||||
force_login?: boolean
|
||||
allow_from?: string[]
|
||||
media_dir?: string
|
||||
reaction_emoji?: string
|
||||
max_message_chars?: number
|
||||
reply_context_max_chars?: number
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export interface SchedulerJobConfig {
|
||||
id: string
|
||||
enabled: boolean
|
||||
kind: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export interface AppConfig {
|
||||
providers: Record<string, ProviderConfig>
|
||||
models: Record<string, ModelConfig>
|
||||
@ -101,7 +146,7 @@ export interface AppConfig {
|
||||
subagents: SubagentsConfig
|
||||
experts: ExpertsConfig
|
||||
client: ClientConfig
|
||||
channels: Record<string, any>
|
||||
channels: Record<string, ChannelConfig>
|
||||
mcpServers: Record<string, McpServerConfig>
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user