Files
freedak f7a720204a Update: 将子项目从 submodule 转为完整内容
- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用
- 添加所有子项目的完整源代码
- 保留原始 .git 为 .git.bak 备份
2026-07-04 19:20:46 +08:00

171 lines
6.3 KiB
Rust

use std::path::PathBuf;
use std::sync::Arc;
use nomifun_ai_agent::AcpSessionSyncService;
use nomifun_ai_agent::AcpSkillManager;
use nomifun_ai_agent::factory::{AgentFactoryDeps, build_agent_factory};
use nomifun_ai_agent::registry::AgentRegistry;
use nomifun_ai_agent::types::BuildTaskOptions;
use nomifun_common::{AgentType, ProviderWithModel, encrypt_string};
use nomifun_db::{
CreateProviderParams, IAcpSessionRepository, IProviderRepository, SqliteAcpSessionRepository,
SqliteAgentMetadataRepository, SqliteProviderRepository, SqliteRemoteAgentRepository, init_database_memory,
};
fn test_encryption_key() -> [u8; 32] {
[0xABu8; 32]
}
async fn setup() -> (
Arc<dyn IProviderRepository>,
Arc<SqliteRemoteAgentRepository>,
Arc<AgentRegistry>,
Arc<AcpSessionSyncService>,
) {
let db = init_database_memory().await.unwrap();
let pool = db.pool().clone();
let provider_repo: Arc<dyn IProviderRepository> = Arc::new(SqliteProviderRepository::new(pool.clone()));
let remote_agent_repo = Arc::new(SqliteRemoteAgentRepository::new(pool.clone()));
let metadata_repo = Arc::new(SqliteAgentMetadataRepository::new(pool.clone()));
let registry = AgentRegistry::new(metadata_repo);
registry.hydrate().await.unwrap();
let session_repo: Arc<dyn IAcpSessionRepository> = Arc::new(SqliteAcpSessionRepository::new(pool));
let acp_agent_service = AcpSessionSyncService::new(session_repo);
(provider_repo, remote_agent_repo, registry, acp_agent_service)
}
async fn insert_test_provider(repo: &dyn IProviderRepository, id: &str, platform: &str) {
let key = test_encryption_key();
let encrypted_api_key = encrypt_string("sk-test-key-12345", &key).unwrap();
repo.create(CreateProviderParams {
id: Some(id),
platform,
name: "Test Provider",
base_url: "https://api.example.com/v1",
api_key_encrypted: &encrypted_api_key,
models: r#"["gpt-4o","gpt-5.4"]"#,
enabled: true,
capabilities: "[]",
context_limit: None,
model_protocols: None,
model_enabled: None,
model_health: None,
bedrock_config: None,
is_full_url: false,
})
.await
.unwrap();
}
fn make_factory(
provider_repo: Arc<dyn IProviderRepository>,
remote_agent_repo: Arc<SqliteRemoteAgentRepository>,
agent_registry: Arc<AgentRegistry>,
acp_agent_service: Arc<AcpSessionSyncService>,
) -> nomifun_ai_agent::task_manager::AgentFactory {
let tmp = tempfile::TempDir::new().unwrap();
let skill_paths = Arc::new(nomifun_extension::resolve_skill_paths(tmp.path(), tmp.path()));
build_agent_factory(AgentFactoryDeps {
cron_sink_factory: None,
gateway_mcp_config: None,
open_mcp_config: None,
computer_mcp_config: None,
browser_mcp_config: None,
client_prefs: None,
settings_repo: None,
companion_prompt: None,
companion_skill_sink: None,
skill_manager: AcpSkillManager::new(skill_paths),
remote_agent_repo,
provider_repo,
encryption_key: test_encryption_key(),
agent_registry,
acp_agent_service,
data_dir: PathBuf::from("/tmp/nomi-test"),
work_dir: PathBuf::from("/tmp/nomi-test"),
backend_binary_path: Arc::new(PathBuf::from("/tmp/nomi-test/nomicore")),
guide_mcp_config: None,
requirement_mcp_config: None,
knowledge_mcp_config: None,
mcp_server_repo: None,
requirement_sink: None,
companion_sink: None,
knowledge_retrieval: None,
knowledge_writeback: None,
})
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn nomi_factory_returns_error_for_missing_provider() {
let (provider_repo, remote_agent_repo, agent_registry, acp_agent_service) = setup().await;
let factory = make_factory(provider_repo, remote_agent_repo, agent_registry, acp_agent_service);
let options = BuildTaskOptions {
agent_type: AgentType::Nomi,
workspace: String::new(),
model: ProviderWithModel {
provider_id: "nonexistent-provider".into(),
model: "gpt-4o".into(),
use_model: None,
},
conversation_id: "conv-test-1".into(),
extra: serde_json::json!({}),
};
let result = factory(options).await;
match result {
Ok(_) => panic!("Expected error for missing provider, got Ok"),
Err(e) => {
let err_msg = e.to_string();
assert!(
err_msg.contains("not found"),
"Expected 'not found' error, got: {err_msg}"
);
}
}
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn nomi_factory_resolves_provider_from_db() {
let (provider_repo, remote_agent_repo, agent_registry, acp_agent_service) = setup().await;
insert_test_provider(&*provider_repo, "prov-001", "openai").await;
let factory = make_factory(provider_repo, remote_agent_repo, agent_registry, acp_agent_service);
let options = BuildTaskOptions {
agent_type: AgentType::Nomi,
workspace: "/tmp/test-workspace".into(),
model: ProviderWithModel {
provider_id: "prov-001".into(),
model: "gpt-4o".into(),
use_model: None,
},
conversation_id: "conv-test-2".into(),
extra: serde_json::json!({ "max_tokens": 2048 }),
};
let result = factory(options).await;
assert!(result.is_ok(), "Expected Ok, got: {:?}", result.err());
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn nomi_factory_respects_use_model_override() {
let (provider_repo, remote_agent_repo, agent_registry, acp_agent_service) = setup().await;
insert_test_provider(&*provider_repo, "prov-002", "openai").await;
let factory = make_factory(provider_repo, remote_agent_repo, agent_registry, acp_agent_service);
let options = BuildTaskOptions {
agent_type: AgentType::Nomi,
workspace: "/tmp/test-workspace".into(),
model: ProviderWithModel {
provider_id: "prov-002".into(),
model: "gpt-4o".into(),
use_model: Some("gpt-5.4".into()),
},
conversation_id: "conv-test-3".into(),
extra: serde_json::json!({}),
};
let result = factory(options).await;
assert!(result.is_ok(), "Expected Ok, got: {:?}", result.err());
}