1
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
use std::{error::Error, fmt, sync::Arc};
|
||||
|
||||
#[cfg(test)]
|
||||
use std::{collections::HashMap, sync::Mutex};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
error::Error,
|
||||
fmt,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use module_ai::{AiTaskService, InMemoryAiTaskStore};
|
||||
use module_auth::{
|
||||
@@ -11,14 +13,16 @@ use module_auth::{
|
||||
use module_runtime::RuntimeSnapshotRecord;
|
||||
#[cfg(test)]
|
||||
use module_runtime::{SAVE_SNAPSHOT_VERSION, format_utc_micros};
|
||||
use platform_agent::MockLangChainRustAgentExecutor;
|
||||
use platform_auth::{
|
||||
AccessTokenClaims, AccessTokenClaimsInput, AuthProvider, BindingStatus, JwtConfig, JwtError,
|
||||
RefreshCookieConfig, RefreshCookieError, RefreshCookieSameSite, SmsAuthConfig, SmsAuthProvider,
|
||||
SmsAuthProviderKind, SmsProviderError, WechatProvider, sign_access_token, verify_access_token,
|
||||
};
|
||||
use platform_llm::{LlmClient, LlmConfig, LlmError};
|
||||
use platform_llm::{LlmClient, LlmConfig, LlmError, LlmProvider};
|
||||
use platform_oss::{OssClient, OssConfig, OssError};
|
||||
use serde_json::Value;
|
||||
use shared_contracts::creative_agent::CreativeAgentSessionSnapshot;
|
||||
use spacetime_client::{SpacetimeClient, SpacetimeClientConfig, SpacetimeClientError};
|
||||
use time::OffsetDateTime;
|
||||
use tracing::{info, warn};
|
||||
@@ -51,11 +55,22 @@ pub struct AppState {
|
||||
ai_task_service: AiTaskService,
|
||||
spacetime_client: SpacetimeClient,
|
||||
llm_client: Option<LlmClient>,
|
||||
creative_agent_gpt5_client: Option<LlmClient>,
|
||||
creative_agent_executor: Arc<MockLangChainRustAgentExecutor>,
|
||||
// Phase 1 任务 E 的 creative session facade 暂存在 api-server。
|
||||
// creative_agent_* 表由任务 D 收口后,这里只保留读写 facade。
|
||||
creative_agent_sessions: Arc<Mutex<HashMap<String, CreativeAgentSessionRuntimeRecord>>>,
|
||||
#[cfg(test)]
|
||||
// 测试环境允许在未启动 SpacetimeDB 时,用内存快照兜底当前 runtime story 回归链。
|
||||
test_runtime_snapshot_store: Arc<Mutex<HashMap<String, RuntimeSnapshotRecord>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct CreativeAgentSessionRuntimeRecord {
|
||||
owner_user_id: String,
|
||||
snapshot: CreativeAgentSessionSnapshot,
|
||||
}
|
||||
|
||||
// 后台管理员运行态独立于普通玩家登录体系,只从环境变量构造。
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AdminRuntime {
|
||||
@@ -167,6 +182,7 @@ impl AppState {
|
||||
procedure_timeout: config.spacetime_procedure_timeout,
|
||||
});
|
||||
let llm_client = build_llm_client(&config)?;
|
||||
let creative_agent_gpt5_client = build_creative_agent_gpt5_client(&config)?;
|
||||
|
||||
Ok(Self {
|
||||
config,
|
||||
@@ -185,6 +201,9 @@ impl AppState {
|
||||
ai_task_service,
|
||||
spacetime_client,
|
||||
llm_client,
|
||||
creative_agent_gpt5_client,
|
||||
creative_agent_executor: Arc::new(MockLangChainRustAgentExecutor),
|
||||
creative_agent_sessions: Arc::new(Mutex::new(HashMap::new())),
|
||||
#[cfg(test)]
|
||||
test_runtime_snapshot_store: Arc::new(Mutex::new(HashMap::new())),
|
||||
})
|
||||
@@ -336,6 +355,44 @@ impl AppState {
|
||||
self.llm_client.as_ref()
|
||||
}
|
||||
|
||||
pub fn creative_agent_gpt5_client(&self) -> Option<&LlmClient> {
|
||||
self.creative_agent_gpt5_client.as_ref()
|
||||
}
|
||||
|
||||
pub fn creative_agent_executor(&self) -> Arc<MockLangChainRustAgentExecutor> {
|
||||
self.creative_agent_executor.clone()
|
||||
}
|
||||
|
||||
pub fn get_creative_agent_session(
|
||||
&self,
|
||||
session_id: &str,
|
||||
owner_user_id: &str,
|
||||
) -> Option<CreativeAgentSessionSnapshot> {
|
||||
self.creative_agent_sessions
|
||||
.lock()
|
||||
.expect("creative agent session store should lock")
|
||||
.get(session_id)
|
||||
.filter(|record| record.owner_user_id == owner_user_id)
|
||||
.map(|record| record.snapshot.clone())
|
||||
}
|
||||
|
||||
pub fn put_creative_agent_session(
|
||||
&self,
|
||||
owner_user_id: String,
|
||||
session: CreativeAgentSessionSnapshot,
|
||||
) {
|
||||
self.creative_agent_sessions
|
||||
.lock()
|
||||
.expect("creative agent session store should lock")
|
||||
.insert(
|
||||
session.session_id.clone(),
|
||||
CreativeAgentSessionRuntimeRecord {
|
||||
owner_user_id,
|
||||
snapshot: session,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub async fn get_runtime_snapshot_record(
|
||||
&self,
|
||||
user_id: String,
|
||||
@@ -710,6 +767,31 @@ fn build_llm_client(config: &AppConfig) -> Result<Option<LlmClient>, AppStateIni
|
||||
Ok(Some(LlmClient::new(llm_config)?))
|
||||
}
|
||||
|
||||
fn build_creative_agent_gpt5_client(
|
||||
config: &AppConfig,
|
||||
) -> Result<Option<LlmClient>, AppStateInitError> {
|
||||
let Some(api_key) = config
|
||||
.apimart_api_key
|
||||
.as_ref()
|
||||
.map(|value| value.trim())
|
||||
.filter(|value| !value.is_empty())
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let llm_config = LlmConfig::new(
|
||||
LlmProvider::OpenAiCompatible,
|
||||
config.apimart_base_url.clone(),
|
||||
api_key.to_string(),
|
||||
platform_agent::CREATIVE_AGENT_GPT5_MODEL.to_string(),
|
||||
config.apimart_image_request_timeout_ms,
|
||||
0,
|
||||
config.llm_retry_backoff_ms,
|
||||
)?;
|
||||
|
||||
Ok(Some(LlmClient::new(llm_config)?))
|
||||
}
|
||||
|
||||
// 只有在用户名和密码都已配置时才启用后台,避免半配置状态暴露伪入口。
|
||||
fn build_admin_runtime(
|
||||
config: &AppConfig,
|
||||
@@ -783,5 +865,28 @@ mod tests {
|
||||
let state = AppState::new(AppConfig::default()).expect("state should build");
|
||||
|
||||
assert!(state.llm_client().is_none());
|
||||
assert!(state.creative_agent_gpt5_client().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn app_state_builds_creative_agent_gpt5_client_from_apimart_settings() {
|
||||
let mut config = AppConfig::default();
|
||||
config.llm_api_key = None;
|
||||
config.apimart_base_url = "https://api.apimart.test/v1".to_string();
|
||||
config.apimart_api_key = Some("apimart-key".to_string());
|
||||
|
||||
let state = AppState::new(config).expect("state should build");
|
||||
let client = state
|
||||
.creative_agent_gpt5_client()
|
||||
.expect("creative agent gpt5 client should exist");
|
||||
|
||||
assert_eq!(
|
||||
client.config().model(),
|
||||
platform_agent::CREATIVE_AGENT_GPT5_MODEL
|
||||
);
|
||||
assert_eq!(
|
||||
client.config().responses_url(),
|
||||
"https://api.apimart.test/v1/responses"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user