Update AI planner integration, docs, and config

This commit is contained in:
周瑞哲
2026-07-30 16:33:39 +08:00
parent 33c14eec86
commit ac4179086c
13 changed files with 192 additions and 29 deletions
+3
View File
@@ -2,10 +2,13 @@ export { isRemoteTravelAssistantEnabled } from './remote'
export { adjustPlan, createPlan } from './service'
export {
loadPlan,
loadPlanningRequest,
loadPreferences,
PLAN_STORAGE_KEY,
PLANNING_REQUEST_STORAGE_KEY,
PREFERENCES_STORAGE_KEY,
savePlan,
savePlanningRequest,
savePreferences,
} from './storage'
+6 -3
View File
@@ -5,7 +5,6 @@ import {
cloneTravelPreferences,
createLocalPlan,
createRecommendedPlan,
normalizePlanResponse,
normalizePlanningRequest,
normalizeTravelPreferences,
type PlanningOrigin,
@@ -15,7 +14,7 @@ import {
TravelValidationError,
} from '@/domain/travel'
import { createRemoteRecommendations, isRemoteTravelAssistantEnabled } from './remote'
import { loadPlan, loadPreferences, savePreferences } from './storage'
import { loadPlan, loadPlanningRequest, loadPreferences, savePlanningRequest, savePreferences } from './storage'
interface LocalSession {
plan: PlanResponse
@@ -55,7 +54,8 @@ function restoreLocalSession(conversationId: string): LocalSession | null {
const preferences = loadPreferences()
if (!plan || !preferences || plan.conversationId !== conversationId)
return null
rememberLocalSession(plan, {
const storedRequest = loadPlanningRequest()
rememberLocalSession(plan, storedRequest ?? {
mode: plan.itinerary.planningMode,
preferences,
durationMinutes: plan.itinerary.requestedMinutes,
@@ -118,11 +118,13 @@ export async function createPlan(
remote.generationMode === 'real' ? 'remote_ai' : 'remote_mock',
)
rememberLocalSession(plan, request)
savePlanningRequest(request)
return clonePlanResponse(plan, repository)
}
const plan = createLocalPlan(request, repository)
rememberLocalSession(plan, request)
savePlanningRequest(request)
return clonePlanResponse(plan, repository)
}
@@ -153,6 +155,7 @@ export async function adjustPlan(conversationId: string, message: string): Promi
adjusted.plan.source = session.plan.source
}
rememberLocalSession(adjusted.plan, adjusted.request)
savePlanningRequest(adjusted.request)
savePreferences(adjusted.preferences)
return clonePlanResponse(adjusted.plan, repository)
}
+67
View File
@@ -4,12 +4,14 @@ import {
cloneTravelPreferences,
normalizePlanResponse,
normalizeTravelPreferences,
type PlanningRequest,
type PlanResponse,
type TravelPreferences,
} from '@/domain/travel'
export const PLAN_STORAGE_KEY = 'guangming:last-plan'
export const PREFERENCES_STORAGE_KEY = 'guangming:last-preferences'
export const PLANNING_REQUEST_STORAGE_KEY = 'guangming:last-planning-request'
const STORAGE_SCHEMA_VERSION = 1
@@ -99,3 +101,68 @@ export function loadPreferences(): TravelPreferences | null {
return null
}
}
export function savePlanningRequest(request: PlanningRequest): void {
const repository = getPoiRepository()
const sanitized: PlanningRequest = {
mode: request.mode,
preferences: normalizeTravelPreferences(request.preferences),
durationMinutes: request.durationMinutes,
selectedPoiIds: [...request.selectedPoiIds],
}
if (sanitized.mode !== 'quick' && sanitized.mode !== 'custom')
throw new Error('无法保存规划条件:规划模式无效')
if (!Number.isInteger(sanitized.durationMinutes)
|| sanitized.durationMinutes < 120
|| sanitized.durationMinutes > 600
|| sanitized.durationMinutes % 30 !== 0) {
throw new Error('无法保存规划条件:规划时长无效')
}
if (sanitized.mode === 'quick' && sanitized.selectedPoiIds.length > 0)
throw new Error('无法保存规划条件:快速规划不能预选点位')
if (sanitized.mode === 'custom'
&& (sanitized.selectedPoiIds.length < 2 || sanitized.selectedPoiIds.length > 8)) {
throw new Error('无法保存规划条件:自选点位数量无效')
}
if (new Set(sanitized.selectedPoiIds).size !== sanitized.selectedPoiIds.length
|| sanitized.selectedPoiIds.some(poiId => !repository.getPoiById(poiId))) {
throw new Error('无法保存规划条件:自选点位无效')
}
writeStorage(PLANNING_REQUEST_STORAGE_KEY, createEnvelope(sanitized))
}
export function loadPlanningRequest(): PlanningRequest | null {
const repository = getPoiRepository()
const envelope = readEnvelope(readStorage(PLANNING_REQUEST_STORAGE_KEY))
if (!envelope || envelope.datasetVersion !== repository.getDatasetMeta().datasetVersion || !isRecord(envelope.payload))
return null
try {
const payload = envelope.payload
const request: PlanningRequest = {
mode: payload.mode as PlanningRequest['mode'],
preferences: normalizeTravelPreferences(payload.preferences),
durationMinutes: Number(payload.durationMinutes),
selectedPoiIds: Array.isArray(payload.selectedPoiIds) ? payload.selectedPoiIds.map(String) : [],
}
if (request.mode !== 'quick' && request.mode !== 'custom')
return null
if (!Number.isInteger(request.durationMinutes)
|| request.durationMinutes < 120
|| request.durationMinutes > 600
|| request.durationMinutes % 30 !== 0) {
return null
}
if (request.mode === 'quick' && request.selectedPoiIds.length > 0)
return null
if (request.mode === 'custom' && (request.selectedPoiIds.length < 2 || request.selectedPoiIds.length > 8))
return null
if (new Set(request.selectedPoiIds).size !== request.selectedPoiIds.length
|| request.selectedPoiIds.some(poiId => !repository.getPoiById(poiId))) {
return null
}
return request
}
catch {
return null
}
}