kimi-code / packages /agent-core-v2 /src /agent /interruptionReminder /interruptionReminderService.ts
SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
68d7816 verified
Raw History Blame Contribute Delete
2.66 kB
import { Disposable } from '#/_base/di/lifecycle';
import { LifecycleScope } from '#/app/scopes';
import { ScopeActivation, registerScopedService } from '#/_base/di/scope';
import { IAgentContextMemoryService } from '#/agent/contextMemory/contextMemory';
import type { ContextMessage } from '#/agent/contextMemory/types';
import { isVacuousContentPart } from '#/agent/contextMemory/vacuousContent';
import { TurnEnded } from '#/agent/loop/turnOps';
import { IAgentReminderService } from '#/features/reminder/reminderService';
import { IAgentStateService } from '#/agent/state/agentState';
import { IEventBus } from '#/app/event/eventBus';
import { IAgentInterruptionReminderService } from './interruptionReminder';
import { INTERRUPTION_REMINDER_VARIANT, interruptionReminderKey } from './interruptionReminderOps';
const INTERRUPTION_REMINDER = [
'The previous turn was interrupted by the user before completion;',
'any partial output shown above is incomplete.',
"The user's next message continues the conversation.",
].join(' ');
export class AgentInterruptionReminderService
extends Disposable
implements IAgentInterruptionReminderService
{
declare readonly _serviceBrand: undefined;
constructor(
@IEventBus eventBus: IEventBus,
@IAgentContextMemoryService private readonly context: IAgentContextMemoryService,
@IAgentReminderService private readonly reminder: IAgentReminderService,
@IAgentStateService agentState: IAgentStateService,
) {
super();
agentState.contributeState(interruptionReminderKey);
this._register(
eventBus.subscribe(TurnEnded, (event) => {
if (event.reason !== 'cancelled' || event.interruptReason !== 'user_cancelled') return;
const origin = lastComparableMessage(this.context.get())?.origin;
if (origin?.kind === 'injection' && origin.variant === INTERRUPTION_REMINDER_VARIANT) return;
this.reminder.notify(INTERRUPTION_REMINDER, {
variant: INTERRUPTION_REMINDER_VARIANT,
});
}),
);
}
}
function lastComparableMessage(messages: readonly ContextMessage[]): ContextMessage | undefined {
for (let index = messages.length - 1; index >= 0; index--) {
const message = messages[index]!;
if (
message.role === 'assistant' &&
message.partial === true &&
message.toolCalls.length === 0 &&
message.content.every(isVacuousContentPart)
) {
continue;
}
return message;
}
return undefined;
}
registerScopedService(
LifecycleScope.Agent,
IAgentInterruptionReminderService,
AgentInterruptionReminderService,
ScopeActivation.OnScopeCreated,
'interruptionReminder',
);