1
This commit is contained in:
@@ -9,8 +9,7 @@ use axum::{
|
||||
response::Response,
|
||||
};
|
||||
use platform_auth::{
|
||||
AccessTokenClaims, AuthProvider, BindingStatus, read_refresh_session_token,
|
||||
verify_access_token,
|
||||
AccessTokenClaims, AuthProvider, BindingStatus, read_refresh_session_token, verify_access_token,
|
||||
};
|
||||
use serde_json::{Value, json};
|
||||
use time::OffsetDateTime;
|
||||
@@ -237,11 +236,11 @@ mod tests {
|
||||
INTERNAL_API_SECRET_HEADER, INTERNAL_AUTH_USER_ID_HEADER, RefreshSessionToken,
|
||||
extract_bearer_token, try_build_internal_forwarded_claims,
|
||||
};
|
||||
use crate::{config::AppConfig, state::AppState};
|
||||
use axum::{
|
||||
http::{HeaderMap, HeaderValue, StatusCode, header::AUTHORIZATION},
|
||||
response::IntoResponse,
|
||||
};
|
||||
use crate::{config::AppConfig, state::AppState};
|
||||
|
||||
#[test]
|
||||
fn extract_bearer_token_accepts_standard_header() {
|
||||
|
||||
@@ -9,8 +9,8 @@ use shared_contracts::big_fish::{
|
||||
BigFishActionResponse, BigFishAgentMessageResponse, BigFishAnchorItemResponse,
|
||||
BigFishAnchorPackResponse, BigFishAssetCoverageResponse, BigFishAssetSlotResponse,
|
||||
BigFishBackgroundBlueprintResponse, BigFishGameDraftResponse, BigFishLevelBlueprintResponse,
|
||||
BigFishRuntimeEntityResponse, BigFishRuntimeParamsResponse, BigFishRuntimeSnapshotResponse,
|
||||
BigFishRunResponse, BigFishSessionResponse, BigFishSessionSnapshotResponse,
|
||||
BigFishRunResponse, BigFishRuntimeEntityResponse, BigFishRuntimeParamsResponse,
|
||||
BigFishRuntimeSnapshotResponse, BigFishSessionResponse, BigFishSessionSnapshotResponse,
|
||||
BigFishVector2Response, CreateBigFishSessionRequest, ExecuteBigFishActionRequest,
|
||||
SendBigFishMessageRequest, SubmitBigFishInputRequest,
|
||||
};
|
||||
@@ -58,7 +58,9 @@ pub async fn create_big_fish_session(
|
||||
created_at_micros: current_utc_micros(),
|
||||
})
|
||||
.await
|
||||
.map_err(|error| big_fish_error_response(&request_context, map_big_fish_client_error(error)))?;
|
||||
.map_err(|error| {
|
||||
big_fish_error_response(&request_context, map_big_fish_client_error(error))
|
||||
})?;
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
@@ -80,7 +82,9 @@ pub async fn get_big_fish_session(
|
||||
.spacetime_client()
|
||||
.get_big_fish_session(session_id, authenticated.claims().user_id().to_string())
|
||||
.await
|
||||
.map_err(|error| big_fish_error_response(&request_context, map_big_fish_client_error(error)))?;
|
||||
.map_err(|error| {
|
||||
big_fish_error_response(&request_context, map_big_fish_client_error(error))
|
||||
})?;
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
@@ -128,7 +132,9 @@ pub async fn submit_big_fish_message(
|
||||
submitted_at_micros: current_utc_micros(),
|
||||
})
|
||||
.await
|
||||
.map_err(|error| big_fish_error_response(&request_context, map_big_fish_client_error(error)))?;
|
||||
.map_err(|error| {
|
||||
big_fish_error_response(&request_context, map_big_fish_client_error(error))
|
||||
})?;
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
@@ -168,7 +174,9 @@ pub async fn stream_big_fish_message(
|
||||
submitted_at_micros: current_utc_micros(),
|
||||
})
|
||||
.await
|
||||
.map_err(|error| big_fish_error_response(&request_context, map_big_fish_client_error(error)))?;
|
||||
.map_err(|error| {
|
||||
big_fish_error_response(&request_context, map_big_fish_client_error(error))
|
||||
})?;
|
||||
|
||||
let session_response = map_big_fish_session_response(session);
|
||||
let reply_text = session_response
|
||||
@@ -176,9 +184,24 @@ pub async fn stream_big_fish_message(
|
||||
.clone()
|
||||
.unwrap_or_else(|| "锚点已更新。".to_string());
|
||||
let mut sse_body = String::new();
|
||||
append_sse_event(&request_context, &mut sse_body, "reply_delta", &json!({ "text": reply_text }))?;
|
||||
append_sse_event(&request_context, &mut sse_body, "session", &json!({ "session": session_response }))?;
|
||||
append_sse_event(&request_context, &mut sse_body, "done", &json!({ "ok": true }))?;
|
||||
append_sse_event(
|
||||
&request_context,
|
||||
&mut sse_body,
|
||||
"reply_delta",
|
||||
&json!({ "text": reply_text }),
|
||||
)?;
|
||||
append_sse_event(
|
||||
&request_context,
|
||||
&mut sse_body,
|
||||
"session",
|
||||
&json!({ "session": session_response }),
|
||||
)?;
|
||||
append_sse_event(
|
||||
&request_context,
|
||||
&mut sse_body,
|
||||
"done",
|
||||
&json!({ "ok": true }),
|
||||
)?;
|
||||
Ok(build_event_stream_response(sse_body))
|
||||
}
|
||||
|
||||
@@ -288,7 +311,9 @@ pub async fn start_big_fish_run(
|
||||
started_at_micros: current_utc_micros(),
|
||||
})
|
||||
.await
|
||||
.map_err(|error| big_fish_error_response(&request_context, map_big_fish_client_error(error)))?;
|
||||
.map_err(|error| {
|
||||
big_fish_error_response(&request_context, map_big_fish_client_error(error))
|
||||
})?;
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
@@ -310,7 +335,9 @@ pub async fn get_big_fish_run(
|
||||
.spacetime_client()
|
||||
.get_big_fish_run(run_id, authenticated.claims().user_id().to_string())
|
||||
.await
|
||||
.map_err(|error| big_fish_error_response(&request_context, map_big_fish_client_error(error)))?;
|
||||
.map_err(|error| {
|
||||
big_fish_error_response(&request_context, map_big_fish_client_error(error))
|
||||
})?;
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
@@ -348,7 +375,9 @@ pub async fn submit_big_fish_input(
|
||||
submitted_at_micros: current_utc_micros(),
|
||||
})
|
||||
.await
|
||||
.map_err(|error| big_fish_error_response(&request_context, map_big_fish_client_error(error)))?;
|
||||
.map_err(|error| {
|
||||
big_fish_error_response(&request_context, map_big_fish_client_error(error))
|
||||
})?;
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
@@ -383,7 +412,9 @@ fn map_big_fish_session_response(session: BigFishSessionRecord) -> BigFishSessio
|
||||
}
|
||||
}
|
||||
|
||||
fn map_big_fish_anchor_pack_response(anchor_pack: BigFishAnchorPackRecord) -> BigFishAnchorPackResponse {
|
||||
fn map_big_fish_anchor_pack_response(
|
||||
anchor_pack: BigFishAnchorPackRecord,
|
||||
) -> BigFishAnchorPackResponse {
|
||||
BigFishAnchorPackResponse {
|
||||
gameplay_promise: map_big_fish_anchor_item_response(anchor_pack.gameplay_promise),
|
||||
ecology_visual_theme: map_big_fish_anchor_item_response(anchor_pack.ecology_visual_theme),
|
||||
@@ -417,7 +448,9 @@ fn map_big_fish_draft_response(draft: BigFishGameDraftRecord) -> BigFishGameDraf
|
||||
}
|
||||
}
|
||||
|
||||
fn map_big_fish_level_response(level: BigFishLevelBlueprintRecord) -> BigFishLevelBlueprintResponse {
|
||||
fn map_big_fish_level_response(
|
||||
level: BigFishLevelBlueprintRecord,
|
||||
) -> BigFishLevelBlueprintResponse {
|
||||
BigFishLevelBlueprintResponse {
|
||||
level: level.level,
|
||||
name: level.name,
|
||||
@@ -528,7 +561,9 @@ fn map_big_fish_runtime_response(run: BigFishRuntimeRecord) -> BigFishRuntimeSna
|
||||
}
|
||||
}
|
||||
|
||||
fn map_big_fish_entity_response(entity: BigFishRuntimeEntityRecord) -> BigFishRuntimeEntityResponse {
|
||||
fn map_big_fish_entity_response(
|
||||
entity: BigFishRuntimeEntityRecord,
|
||||
) -> BigFishRuntimeEntityResponse {
|
||||
BigFishRuntimeEntityResponse {
|
||||
entity_id: entity.entity_id,
|
||||
level: entity.level,
|
||||
@@ -547,7 +582,8 @@ fn map_big_fish_vector_response(vector: BigFishVector2Record) -> BigFishVector2R
|
||||
|
||||
fn build_big_fish_welcome_text(seed_text: &str) -> String {
|
||||
if seed_text.trim().is_empty() {
|
||||
return "我会先帮你确定大鱼吃小鱼的核心锚点。可以从主题生态、成长阶梯或风险节奏开始。".to_string();
|
||||
return "我会先帮你确定大鱼吃小鱼的核心锚点。可以从主题生态、成长阶梯或风险节奏开始。"
|
||||
.to_string();
|
||||
}
|
||||
"我已经收到你的玩法起点,会先把它整理成锚点并准备结果页草稿。".to_string()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{env, net::SocketAddr};
|
||||
use std::{env, fs, net::SocketAddr, path::PathBuf};
|
||||
|
||||
use platform_llm::{
|
||||
DEFAULT_ARK_BASE_URL, DEFAULT_MAX_RETRIES, DEFAULT_REQUEST_TIMEOUT_MS,
|
||||
@@ -7,6 +7,7 @@ use platform_llm::{
|
||||
|
||||
const DEFAULT_LLM_MODEL: &str = "doubao-1-5-pro-32k-character-250715";
|
||||
const DEFAULT_INTERNAL_API_SECRET: &str = "genarrative-dev-internal-bridge";
|
||||
const SPACETIME_LOCAL_CONFIG_FILE: &str = "spacetime.local.json";
|
||||
|
||||
// 集中管理 api-server 的启动配置,避免入口层直接散落环境变量解析逻辑。
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -123,6 +124,10 @@ impl AppConfig {
|
||||
pub fn from_env() -> Self {
|
||||
let mut config = Self::default();
|
||||
|
||||
if let Some(local_spacetime_database) = read_local_spacetime_database() {
|
||||
config.spacetime_database = local_spacetime_database;
|
||||
}
|
||||
|
||||
if let Ok(bind_host) = env::var("GENARRATIVE_API_HOST")
|
||||
&& !bind_host.trim().is_empty()
|
||||
{
|
||||
@@ -141,8 +146,7 @@ impl AppConfig {
|
||||
config.log_filter = log_filter;
|
||||
}
|
||||
|
||||
config.internal_api_secret =
|
||||
read_first_non_empty_env(&["GENARRATIVE_INTERNAL_API_SECRET"]);
|
||||
config.internal_api_secret = read_first_non_empty_env(&["GENARRATIVE_INTERNAL_API_SECRET"]);
|
||||
|
||||
if let Some(jwt_issuer) =
|
||||
read_first_non_empty_env(&["GENARRATIVE_JWT_ISSUER", "JWT_ISSUER"])
|
||||
@@ -361,6 +365,33 @@ fn read_first_non_empty_env(keys: &[&str]) -> Option<String> {
|
||||
})
|
||||
}
|
||||
|
||||
fn read_local_spacetime_database() -> Option<String> {
|
||||
let config_path = find_upward_file(SPACETIME_LOCAL_CONFIG_FILE)?;
|
||||
let raw_text = fs::read_to_string(config_path).ok()?;
|
||||
let parsed = serde_json::from_str::<serde_json::Value>(&raw_text).ok()?;
|
||||
parsed
|
||||
.get("database")
|
||||
.and_then(|value| value.as_str())
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(ToOwned::to_owned)
|
||||
}
|
||||
|
||||
fn find_upward_file(file_name: &str) -> Option<PathBuf> {
|
||||
let mut current_dir = env::current_dir().ok()?;
|
||||
|
||||
loop {
|
||||
let candidate = current_dir.join(file_name);
|
||||
if candidate.is_file() {
|
||||
return Some(candidate);
|
||||
}
|
||||
|
||||
if !current_dir.pop() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_first_duration_seconds_env(keys: &[&str]) -> Option<u64> {
|
||||
keys.iter().find_map(|key| {
|
||||
env::var(key)
|
||||
|
||||
@@ -30,15 +30,16 @@ use shared_contracts::{
|
||||
SwapPuzzlePiecesRequest,
|
||||
},
|
||||
puzzle_works::{
|
||||
PuzzleWorkDetailResponse, PuzzleWorkMutationResponse, PuzzleWorkProfileResponse,
|
||||
PuzzleWorkSummaryResponse, PuzzleWorksResponse, PutPuzzleWorkRequest,
|
||||
PutPuzzleWorkRequest, PuzzleWorkDetailResponse, PuzzleWorkMutationResponse,
|
||||
PuzzleWorkProfileResponse, PuzzleWorkSummaryResponse, PuzzleWorksResponse,
|
||||
},
|
||||
};
|
||||
use shared_kernel::build_prefixed_uuid_id;
|
||||
use spacetime_client::{
|
||||
PuzzleAgentMessageRecord, PuzzleAgentMessageSubmitRecordInput, PuzzleAgentSessionCreateRecordInput,
|
||||
PuzzleAgentSessionRecord, PuzzleAgentSuggestedActionRecord, PuzzleAnchorItemRecord,
|
||||
PuzzleAnchorPackRecord, PuzzleCreatorIntentRecord, PuzzleGeneratedImageCandidateRecord,
|
||||
PuzzleAgentMessageRecord, PuzzleAgentMessageSubmitRecordInput,
|
||||
PuzzleAgentSessionCreateRecordInput, PuzzleAgentSessionRecord,
|
||||
PuzzleAgentSuggestedActionRecord, PuzzleAnchorItemRecord, PuzzleAnchorPackRecord,
|
||||
PuzzleCreatorIntentRecord, PuzzleGeneratedImageCandidateRecord,
|
||||
PuzzleGeneratedImagesSaveRecordInput, PuzzlePublishRecordInput, PuzzleResultDraftRecord,
|
||||
PuzzleResultPreviewBlockerRecord, PuzzleResultPreviewFindingRecord, PuzzleResultPreviewRecord,
|
||||
PuzzleRunDragRecordInput, PuzzleRunRecord, PuzzleRunStartRecordInput, PuzzleRunSwapRecordInput,
|
||||
@@ -47,11 +48,8 @@ use spacetime_client::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
api_response::json_success_body,
|
||||
auth::AuthenticatedAccessToken,
|
||||
http_error::AppError,
|
||||
request_context::RequestContext,
|
||||
state::AppState,
|
||||
api_response::json_success_body, auth::AuthenticatedAccessToken, http_error::AppError,
|
||||
request_context::RequestContext, state::AppState,
|
||||
};
|
||||
|
||||
const PUZZLE_AGENT_API_BASE_PROVIDER: &str = "puzzle-agent";
|
||||
@@ -110,14 +108,16 @@ pub async fn get_puzzle_agent_session(
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
Extension(authenticated): Extension<AuthenticatedAccessToken>,
|
||||
) -> Result<Json<Value>, Response> {
|
||||
ensure_non_empty(&request_context, PUZZLE_AGENT_API_BASE_PROVIDER, &session_id, "sessionId")?;
|
||||
ensure_non_empty(
|
||||
&request_context,
|
||||
PUZZLE_AGENT_API_BASE_PROVIDER,
|
||||
&session_id,
|
||||
"sessionId",
|
||||
)?;
|
||||
|
||||
let session = state
|
||||
.spacetime_client()
|
||||
.get_puzzle_agent_session(
|
||||
session_id,
|
||||
authenticated.claims().user_id().to_string(),
|
||||
)
|
||||
.get_puzzle_agent_session(session_id, authenticated.claims().user_id().to_string())
|
||||
.await
|
||||
.map_err(|error| {
|
||||
puzzle_error_response(
|
||||
@@ -152,7 +152,12 @@ pub async fn submit_puzzle_agent_message(
|
||||
})),
|
||||
)
|
||||
})?;
|
||||
ensure_non_empty(&request_context, PUZZLE_AGENT_API_BASE_PROVIDER, &session_id, "sessionId")?;
|
||||
ensure_non_empty(
|
||||
&request_context,
|
||||
PUZZLE_AGENT_API_BASE_PROVIDER,
|
||||
&session_id,
|
||||
"sessionId",
|
||||
)?;
|
||||
|
||||
let client_message_id = payload.client_message_id.trim().to_string();
|
||||
let message_text = payload.text.trim().to_string();
|
||||
@@ -207,7 +212,12 @@ pub async fn stream_puzzle_agent_message(
|
||||
})),
|
||||
)
|
||||
})?;
|
||||
ensure_non_empty(&request_context, PUZZLE_AGENT_API_BASE_PROVIDER, &session_id, "sessionId")?;
|
||||
ensure_non_empty(
|
||||
&request_context,
|
||||
PUZZLE_AGENT_API_BASE_PROVIDER,
|
||||
&session_id,
|
||||
"sessionId",
|
||||
)?;
|
||||
|
||||
let session = state
|
||||
.spacetime_client()
|
||||
@@ -245,7 +255,12 @@ pub async fn stream_puzzle_agent_message(
|
||||
"session",
|
||||
&json!({ "session": session_response }),
|
||||
)?;
|
||||
append_sse_event(&request_context, &mut sse_body, "done", &json!({ "ok": true }))?;
|
||||
append_sse_event(
|
||||
&request_context,
|
||||
&mut sse_body,
|
||||
"done",
|
||||
&json!({ "ok": true }),
|
||||
)?;
|
||||
Ok(build_event_stream_response(sse_body))
|
||||
}
|
||||
|
||||
@@ -266,7 +281,12 @@ pub async fn execute_puzzle_agent_action(
|
||||
})),
|
||||
)
|
||||
})?;
|
||||
ensure_non_empty(&request_context, PUZZLE_AGENT_API_BASE_PROVIDER, &session_id, "sessionId")?;
|
||||
ensure_non_empty(
|
||||
&request_context,
|
||||
PUZZLE_AGENT_API_BASE_PROVIDER,
|
||||
&session_id,
|
||||
"sessionId",
|
||||
)?;
|
||||
|
||||
let owner_user_id = authenticated.claims().user_id().to_string();
|
||||
let now = current_utc_micros();
|
||||
@@ -327,11 +347,11 @@ pub async fn execute_puzzle_agent_action(
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.map_err(|error| {
|
||||
SpacetimeClientError::Runtime(format!(
|
||||
"拼图候选图序列化失败:{error}"
|
||||
))
|
||||
});
|
||||
.map_err(|error| {
|
||||
SpacetimeClientError::Runtime(format!(
|
||||
"拼图候选图序列化失败:{error}"
|
||||
))
|
||||
});
|
||||
match candidates_json {
|
||||
Ok(candidates_json) => {
|
||||
state
|
||||
@@ -497,7 +517,12 @@ pub async fn get_puzzle_work_detail(
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
Extension(_authenticated): Extension<AuthenticatedAccessToken>,
|
||||
) -> Result<Json<Value>, Response> {
|
||||
ensure_non_empty(&request_context, PUZZLE_WORKS_PROVIDER, &profile_id, "profileId")?;
|
||||
ensure_non_empty(
|
||||
&request_context,
|
||||
PUZZLE_WORKS_PROVIDER,
|
||||
&profile_id,
|
||||
"profileId",
|
||||
)?;
|
||||
|
||||
let item = state
|
||||
.spacetime_client()
|
||||
@@ -536,7 +561,12 @@ pub async fn put_puzzle_work(
|
||||
})),
|
||||
)
|
||||
})?;
|
||||
ensure_non_empty(&request_context, PUZZLE_WORKS_PROVIDER, &profile_id, "profileId")?;
|
||||
ensure_non_empty(
|
||||
&request_context,
|
||||
PUZZLE_WORKS_PROVIDER,
|
||||
&profile_id,
|
||||
"profileId",
|
||||
)?;
|
||||
|
||||
let item = state
|
||||
.spacetime_client()
|
||||
@@ -599,7 +629,12 @@ pub async fn get_puzzle_gallery_detail(
|
||||
AxumPath(profile_id): AxumPath<String>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
) -> Result<Json<Value>, Response> {
|
||||
ensure_non_empty(&request_context, PUZZLE_GALLERY_PROVIDER, &profile_id, "profileId")?;
|
||||
ensure_non_empty(
|
||||
&request_context,
|
||||
PUZZLE_GALLERY_PROVIDER,
|
||||
&profile_id,
|
||||
"profileId",
|
||||
)?;
|
||||
|
||||
let item = state
|
||||
.spacetime_client()
|
||||
@@ -637,7 +672,12 @@ pub async fn start_puzzle_run(
|
||||
})),
|
||||
)
|
||||
})?;
|
||||
ensure_non_empty(&request_context, PUZZLE_RUNTIME_PROVIDER, &payload.profile_id, "profileId")?;
|
||||
ensure_non_empty(
|
||||
&request_context,
|
||||
PUZZLE_RUNTIME_PROVIDER,
|
||||
&payload.profile_id,
|
||||
"profileId",
|
||||
)?;
|
||||
|
||||
let run = state
|
||||
.spacetime_client()
|
||||
@@ -767,7 +807,12 @@ pub async fn drag_puzzle_piece_or_group(
|
||||
)
|
||||
})?;
|
||||
ensure_non_empty(&request_context, PUZZLE_RUNTIME_PROVIDER, &run_id, "runId")?;
|
||||
ensure_non_empty(&request_context, PUZZLE_RUNTIME_PROVIDER, &payload.piece_id, "pieceId")?;
|
||||
ensure_non_empty(
|
||||
&request_context,
|
||||
PUZZLE_RUNTIME_PROVIDER,
|
||||
&payload.piece_id,
|
||||
"pieceId",
|
||||
)?;
|
||||
|
||||
let run = state
|
||||
.spacetime_client()
|
||||
@@ -850,12 +895,16 @@ fn map_puzzle_agent_session_response(
|
||||
.into_iter()
|
||||
.map(map_puzzle_suggested_action_response)
|
||||
.collect(),
|
||||
result_preview: session.result_preview.map(map_puzzle_result_preview_response),
|
||||
result_preview: session
|
||||
.result_preview
|
||||
.map(map_puzzle_result_preview_response),
|
||||
updated_at: session.updated_at,
|
||||
}
|
||||
}
|
||||
|
||||
fn map_puzzle_anchor_pack_response(anchor_pack: PuzzleAnchorPackRecord) -> PuzzleAnchorPackResponse {
|
||||
fn map_puzzle_anchor_pack_response(
|
||||
anchor_pack: PuzzleAnchorPackRecord,
|
||||
) -> PuzzleAnchorPackResponse {
|
||||
PuzzleAnchorPackResponse {
|
||||
theme_promise: map_puzzle_anchor_item_response(anchor_pack.theme_promise),
|
||||
visual_subject: map_puzzle_anchor_item_response(anchor_pack.visual_subject),
|
||||
@@ -1098,8 +1147,7 @@ fn resolve_author_display_name(
|
||||
|
||||
fn build_puzzle_welcome_text(seed_text: &str) -> String {
|
||||
if seed_text.trim().is_empty() {
|
||||
return "先告诉我你想做一张什么样的拼图图像,我会帮你收束成可发布的题材锚点。"
|
||||
.to_string();
|
||||
return "先告诉我你想做一张什么样的拼图图像,我会帮你收束成可发布的题材锚点。".to_string();
|
||||
}
|
||||
|
||||
"我先接住你的画面灵感,再一起把它收束成正式拼图关卡。".to_string()
|
||||
@@ -1124,11 +1172,7 @@ fn ensure_non_empty(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn puzzle_bad_request(
|
||||
request_context: &RequestContext,
|
||||
provider: &str,
|
||||
message: &str,
|
||||
) -> Response {
|
||||
fn puzzle_bad_request(request_context: &RequestContext, provider: &str, message: &str) -> Response {
|
||||
puzzle_error_response(
|
||||
request_context,
|
||||
provider,
|
||||
@@ -1279,7 +1323,11 @@ fn save_placeholder_puzzle_asset(
|
||||
fs::write(output_dir.join(&file_name), svg).map_err(io_error)?;
|
||||
|
||||
Ok(GeneratedPuzzleAssetResponse {
|
||||
image_src: format!("/{}/{}", relative_dir.to_string_lossy().replace('\\', "/"), file_name),
|
||||
image_src: format!(
|
||||
"/{}/{}",
|
||||
relative_dir.to_string_lossy().replace('\\', "/"),
|
||||
file_name
|
||||
),
|
||||
asset_id,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,35 +11,27 @@ use module_npc::{
|
||||
use module_runtime::RuntimeSnapshotRecord;
|
||||
use module_runtime_story_compat::{
|
||||
CONTINUE_ADVENTURE_FUNCTION_ID, CurrentEncounterNpcQuestContext, GeneratedStoryPayload,
|
||||
PendingQuestOfferContext, RuntimeStoryActionResponseParts,
|
||||
StoryResolution, add_player_currency, add_player_inventory_items,
|
||||
append_story_history, apply_equipment_loadout_to_state,
|
||||
battle_mode_text, build_battle_runtime_story_options, build_current_build_toast,
|
||||
build_status_patch,
|
||||
build_npc_gift_result_text,
|
||||
build_runtime_story_view_model,
|
||||
PendingQuestOfferContext, RuntimeStoryActionResponseParts, StoryResolution,
|
||||
add_player_currency, add_player_inventory_items, append_story_history,
|
||||
apply_equipment_loadout_to_state, battle_mode_text, build_battle_runtime_story_options,
|
||||
build_current_build_toast, build_disabled_runtime_story_option, build_npc_gift_result_text,
|
||||
build_runtime_story_option_from_story_option, build_runtime_story_view_model,
|
||||
build_static_runtime_story_option, build_status_patch, build_story_option_from_runtime_option,
|
||||
clear_encounter_only, clear_encounter_state, clone_inventory_item_with_quantity,
|
||||
current_encounter_id, current_encounter_name, current_world_type,
|
||||
ensure_inventory_action_available, ensure_json_object, equipment_slot_label,
|
||||
find_player_inventory_entry,
|
||||
format_now_rfc3339, grant_player_progression_experience, has_giftable_player_inventory,
|
||||
format_currency_text,
|
||||
increment_runtime_stat, normalize_equipped_item,
|
||||
normalize_equipment_slot_id, normalize_required_string, npc_buyback_price,
|
||||
npc_purchase_price, read_array_field, read_bool_field, read_field, read_i32_field,
|
||||
read_inventory_item_name, read_object_field, read_optional_string_field,
|
||||
find_player_inventory_entry, format_currency_text, format_now_rfc3339,
|
||||
grant_player_progression_experience, has_giftable_player_inventory, increment_runtime_stat,
|
||||
normalize_equipment_slot_id, normalize_equipped_item, normalize_required_string,
|
||||
npc_buyback_price, npc_purchase_price, read_array_field, read_bool_field, read_field,
|
||||
read_i32_field, read_inventory_item_name, read_object_field, read_optional_string_field,
|
||||
read_player_equipment_item, read_required_string_field, read_runtime_session_id,
|
||||
read_u32_field, recruit_companion_to_party, remove_player_inventory_item,
|
||||
restore_player_resource,
|
||||
resolve_action_text, resolve_battle_action, resolve_equipment_slot_for_item,
|
||||
resolve_forge_craft_action,
|
||||
resolve_forge_dismantle_action, resolve_forge_reforge_action,
|
||||
resolve_npc_gift_affinity_gain, simple_story_resolution, trade_quantity_suffix,
|
||||
resolve_current_encounter_npc_state,
|
||||
build_disabled_runtime_story_option, build_runtime_story_option_from_story_option,
|
||||
build_static_runtime_story_option, build_story_option_from_runtime_option,
|
||||
write_bool_field, write_i32_field, write_null_field, write_player_equipment_item,
|
||||
write_string_field, write_u32_field,
|
||||
read_u32_field, recruit_companion_to_party, remove_player_inventory_item, resolve_action_text,
|
||||
resolve_battle_action, resolve_current_encounter_npc_state, resolve_equipment_slot_for_item,
|
||||
resolve_forge_craft_action, resolve_forge_dismantle_action, resolve_forge_reforge_action,
|
||||
resolve_npc_gift_affinity_gain, restore_player_resource, simple_story_resolution,
|
||||
trade_quantity_suffix, write_bool_field, write_i32_field, write_null_field,
|
||||
write_player_equipment_item, write_string_field, write_u32_field,
|
||||
};
|
||||
use platform_llm::{LlmClient, LlmMessage, LlmTextRequest};
|
||||
use serde_json::{Map, Value, json};
|
||||
|
||||
Reference in New Issue
Block a user