66 lines
3.6 KiB
JavaScript
66 lines
3.6 KiB
JavaScript
// Offline YCode state samples only; these do not establish original-app parity.
|
|
const now = Date.now()
|
|
const history = Array.from({ length: 14 }, (_, index) => {
|
|
const createdAt = now + index * 2
|
|
const text = Array.from({ length: 4 }, (__, paragraph) =>
|
|
`历史消息 ${index + 1},段落 ${paragraph + 1}。用于让会话时间线形成可滚动的真实内容。`.repeat(3)
|
|
).join('\n\n')
|
|
return [
|
|
{ id: `history-user-${index}`, role: 'user', content: `历史请求 ${index + 1}`, status: 'done', createdAt },
|
|
{ id: `history-assistant-${index}`, role: 'assistant', content: text, status: 'done', createdAt: createdAt + 1 }
|
|
]
|
|
}).flat()
|
|
|
|
export const conversationStatesFixture = {
|
|
sessions: [{ id: 'conversation-states', title: '对话状态回归', createdAt: now, updatedAt: now }],
|
|
messages: { 'conversation-states': history }
|
|
}
|
|
|
|
export const conversationStatesBridge = `<script>
|
|
(()=>{
|
|
const listeners=new Set(),runs=Object.create(null);let sequence=0;
|
|
const state={lastRunId:null,runs,responses:[],responsePlans:[],deferredResponses:[],earlyBatches:[],emit(event){
|
|
const runId=event.runId||state.lastRunId,run=runs[runId];
|
|
if(!run)throw new Error('No fixture run for event '+event.type);
|
|
const payload={...event,runId,sessionId:run.sessionId};
|
|
run.events.push(payload);listeners.forEach(listener=>listener(payload));
|
|
if(payload.type==='interaction')run.interactions[payload.interaction.id]=payload.interaction;
|
|
if(['done','cancelled','error'].includes(payload.type))run.finished=true;
|
|
return payload;
|
|
},queueResponse(mode){state.responsePlans.push(mode)},queueEarlyBatch(events){state.earlyBatches.push(events)},releaseDeferredResponse(){const release=state.deferredResponses.shift();if(!release)return false;release();return true}};
|
|
const completeResponse=(runId,id,response)=>{
|
|
const run=runs[runId],interaction=run?.interactions[id];
|
|
if(!interaction)return{accepted:false};
|
|
const option=interaction.options.find(item=>item.optionId===response.optionId);
|
|
if(!option)return{accepted:false};
|
|
const status=option.kind.startsWith('reject')?'rejected':'answered';
|
|
state.emit({runId,type:'interaction',interaction:{...interaction,selectedOptionId:option.optionId,status}});
|
|
return{accepted:true};
|
|
};
|
|
window.__YCODE_CONVERSATION_STATE_TEST__=state;
|
|
window.ycode.chat.onEvent=listener=>{listeners.add(listener);return()=>listeners.delete(listener)};
|
|
window.ycode.chat.send=async payload=>{
|
|
const runId='state-run-'+(++sequence),assistantMessageId=runId+'-assistant';
|
|
const run={runId,sessionId:payload.sessionId,assistantMessageId,events:[],interactions:Object.create(null),finished:false};
|
|
runs[runId]=run;state.lastRunId=runId;
|
|
const userMessage={id:runId+'-user',requestId:runId,role:'user',content:payload.text,attachments:payload.attachments,createdAt:Date.now(),status:'done'};
|
|
const earlyEvents=state.earlyBatches.shift()||[];
|
|
earlyEvents.forEach(event=>state.emit({...event,runId,...(event.type==='interaction'?{interaction:{...event.interaction,runId}}:{})}));
|
|
return{...run,userMessage};
|
|
};
|
|
window.ycode.chat.cancel=async runId=>{
|
|
const run=runs[runId];
|
|
if(!run||run.finished)return{cancelled:false};
|
|
state.emit({runId,type:'cancelled'});
|
|
return{cancelled:true};
|
|
};
|
|
window.ycode.chat.respond=async(runId,id,response)=>{
|
|
state.responses.push({runId,interactionId:id,response});
|
|
const mode=state.responsePlans.shift()||'success';
|
|
if(mode==='failure')throw new Error('fixture respond failure');
|
|
if(mode==='defer')return await new Promise(resolve=>state.deferredResponses.push(()=>resolve(completeResponse(runId,id,response))));
|
|
return completeResponse(runId,id,response);
|
|
};
|
|
})();
|
|
</script>`
|