Compare commits

...

2 Commits

Author SHA1 Message Date
YuTengjing d47848c7a1 feat(runtime): refine runtime request types 2026-05-15 11:37:25 +08:00
YuTengjing 8af46e7511 feat(runtime): define request trigger metadata 2026-05-15 11:37:24 +08:00
14 changed files with 85 additions and 39 deletions
+30
View File
@@ -1,23 +1,53 @@
export enum RequestTrigger {
AgentExecution = 'agent_execution',
AgentSignal = 'agent_signal',
AiGeneration = 'ai_generation',
Api = 'api',
Bot = 'bot',
Chat = 'chat',
Cli = 'cli',
ContextCompression = 'context_compression',
Cron = 'cron',
Eval = 'eval',
FileEmbedding = 'file_embedding',
FollowUp = 'follow_up',
Image = 'image',
Memory = 'memory',
Notify = 'notify',
Onboarding = 'onboarding',
Openapi = 'openapi',
ProviderCheck = 'provider_check',
SemanticSearch = 'semantic_search',
StructuredOutput = 'structured_output',
TaskBrief = 'task_brief',
TaskBriefJudge = 'task_brief_judge',
TaskHandoff = 'task_handoff',
TaskReview = 'task_review',
TaskRun = 'task',
Topic = 'topic',
Video = 'video',
VisualAnalysis = 'visual_analysis',
}
export enum RuntimeRequestType {
/** Scheduled or workflow-driven runtime execution without a live client request. */
BackgroundJob = 'background_job',
/** First-party web browser request. */
Browser = 'browser',
/** Third-party or API-style entrypoint, such as OpenAPI, bot, or CLI. */
ExternalApi = 'external_api',
/** Server-owned runtime work that is not part of a direct user request chain. */
InternalTask = 'internal_task',
/** First-party mobile app request. */
Mobile = 'mobile',
/** Server-side application request chain, distinct from Next.js Server Actions. */
ServerSide = 'server_side',
}
export interface RuntimeCallMetadata extends Record<string, unknown> {
trigger: RequestTrigger;
}
// ******* Runtime Biz Error ******* //
export const AgentRuntimeErrorType = {
AgentRuntimeError: 'AgentRuntimeError', // Agent Runtime module runtime error
@@ -30,7 +30,8 @@ import {
import { parse } from '@lobechat/conversation-flow';
import { consumeStreamUntilDone } from '@lobechat/model-runtime';
import { chainCompressContext } from '@lobechat/prompts';
import { type ChatToolPayload, type MessageToolCall, type UIChatMessage } from '@lobechat/types';
import type { ChatToolPayload, MessageToolCall, UIChatMessage } from '@lobechat/types';
import { RequestTrigger } from '@lobechat/types';
import { sanitizeToolCallArguments, serializePartsForStorage } from '@lobechat/utils';
import debug from 'debug';
@@ -910,7 +911,7 @@ export const createRuntimeExecutors = (
metadata: {
operationId,
topicId: state.metadata?.topicId,
trigger: state.metadata?.trigger,
trigger: state.metadata?.trigger ?? RequestTrigger.AgentExecution,
},
user: ctx.userId,
});
@@ -1327,6 +1328,7 @@ export const createRuntimeExecutors = (
summaryContent += text;
},
},
metadata: { trigger: RequestTrigger.ContextCompression },
user: ctx.userId,
},
);
@@ -1,5 +1,5 @@
// @vitest-environment node
import { ThreadType } from '@lobechat/types';
import { RequestTrigger, ThreadType } from '@lobechat/types';
import { describe, expect, it, vi } from 'vitest';
import { AgentModel } from '@/database/models/agent';
@@ -828,7 +828,7 @@ describe('aiChatRouter', () => {
schema: input.schema,
tools: undefined,
},
{ metadata: { trigger: 'chat' } },
{ metadata: { trigger: RequestTrigger.StructuredOutput } },
);
expect(result).toEqual(mockResult);
});
@@ -872,7 +872,7 @@ describe('aiChatRouter', () => {
schema: undefined,
tools: mockTools,
},
{ metadata: { trigger: 'chat' } },
{ metadata: { trigger: RequestTrigger.StructuredOutput } },
);
});
});
+2 -2
View File
@@ -640,7 +640,7 @@ export const aiAgentRouter = router({
resume: !!parentMessageId,
resumeApproval,
slug,
trigger: trigger ?? RequestTrigger.Chat,
trigger: trigger ?? RequestTrigger.AgentExecution,
userInterventionConfig,
});
} catch (error: any) {
@@ -703,7 +703,7 @@ export const aiAgentRouter = router({
// When parentMessageId is provided, this is a regeneration/continue — skip user message creation
resume: !!parentMessageId,
slug,
trigger: trigger ?? RequestTrigger.Chat,
trigger: trigger ?? RequestTrigger.AgentExecution,
});
return {
+1 -1
View File
@@ -49,7 +49,7 @@ export const aiChatRouter = router({
schema: input.schema,
tools: input.tools,
},
{ metadata: { trigger: RequestTrigger.Chat } },
{ metadata: { trigger: RequestTrigger.StructuredOutput } },
);
log('generateObject completed, result: %O', result);
+10 -6
View File
@@ -1,3 +1,4 @@
import { RequestTrigger } from '@lobechat/types';
import { TRPCError } from '@trpc/server';
import { z } from 'zod';
@@ -60,12 +61,15 @@ export const aiProviderRouter = router({
try {
const modelRuntime = await initModelRuntimeFromDB(ctx.serverDB, ctx.userId, input.id);
const response = await modelRuntime.chat({
messages: [{ content: 'Hi', role: 'user' }],
model,
stream: false,
temperature: 0,
});
const response = await modelRuntime.chat(
{
messages: [{ content: 'Hi', role: 'user' }],
model,
stream: false,
temperature: 0,
},
{ metadata: { trigger: RequestTrigger.ProviderCheck } },
);
// If we get a response without error, connectivity is ok
if (response.ok) {
@@ -1,4 +1,5 @@
// @vitest-environment node
import { RequestTrigger } from '@lobechat/types';
import { describe, expect, it, vi } from 'vitest';
import { AgentSignalProcedureInspector, createProcedurePolicyOptions } from '../../../procedure';
@@ -583,7 +584,7 @@ describe('feedbackActionPlanner', () => {
skillRoute: 'direct_decision',
sourceId: 'source_skill_server',
target: 'skill',
trigger: 'chat',
trigger: RequestTrigger.Chat,
});
const result = await handler.handle(signal, context);
+6 -5
View File
@@ -331,6 +331,7 @@ export class AiAgentService {
resume,
resumeApproval,
} = params;
const operationTrigger = trigger ?? RequestTrigger.AgentExecution;
// Validate that either agentId or slug is provided
if (!agentId && !slug) {
@@ -619,13 +620,13 @@ export class AiAgentService {
metadata,
title:
title !== undefined ? title : prompt.slice(0, 50) + (prompt.length > 50 ? '...' : ''),
trigger,
trigger: operationTrigger,
});
topicId = newTopic.id;
log(
'execAgent: created new topic %s with trigger %s, cronJobId %s',
topicId,
trigger || 'default',
operationTrigger,
cronJobId || 'none',
);
} else {
@@ -1680,7 +1681,7 @@ export class AiAgentService {
message: prompt,
threadId: appContext?.threadId ?? undefined,
topicId,
trigger,
trigger: operationTrigger,
messageId: userMessageRecord.id,
},
sourceId: userMessageRecord.id,
@@ -1931,7 +1932,7 @@ export class AiAgentService {
taskId: operationTaskId,
threadId: appContext?.threadId,
topicId,
trigger,
trigger: operationTrigger,
},
autoStart,
botContext,
@@ -2091,7 +2092,7 @@ export class AiAgentService {
appContext: { groupId, topicId },
autoStart: true,
prompt: message,
trigger: RequestTrigger.Chat,
trigger: RequestTrigger.AgentExecution,
});
log(
+12 -8
View File
@@ -1,5 +1,6 @@
import { DEFAULT_SYSTEM_AGENT_CONFIG } from '@lobechat/const';
import type { FollowUpChip, FollowUpExtractInput, FollowUpExtractResult } from '@lobechat/types';
import { RequestTrigger } from '@lobechat/types';
import debug from 'debug';
import { type LobeChatDatabase } from '@/database/type';
@@ -48,14 +49,17 @@ export class FollowUpActionService {
let raw: unknown;
try {
const modelRuntime = await initModelRuntimeFromDB(this.db, this.userId, provider);
raw = await modelRuntime.generateObject({
messages: [
{ content: system, role: 'system' as const },
{ content: user, role: 'user' as const },
],
model,
schema: SUGGESTION_RESPONSE_JSON_SCHEMA,
});
raw = await modelRuntime.generateObject(
{
messages: [
{ content: system, role: 'system' as const },
{ content: user, role: 'user' as const },
],
model,
schema: SUGGESTION_RESPONSE_JSON_SCHEMA,
},
{ metadata: { trigger: RequestTrigger.FollowUp } },
);
} catch (error) {
log('LLM call failed: %O', error);
return EMPTY_RESULT(row.id);
@@ -1,3 +1,4 @@
import { RequestTrigger } from '@lobechat/types';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import type { UserMemoryEmbeddingRuntime } from '../embedding';
@@ -64,7 +65,7 @@ describe('embedUserMemoryTexts', () => {
input: ['two three four', 'short text'],
model: 'text-embedding-3-large',
},
{ metadata: { trigger: 'memory' }, user: 'user-test' },
{ metadata: { trigger: RequestTrigger.Memory }, user: 'user-test' },
);
expect(result).toEqual([[1, 2, 3], undefined, undefined, [4, 5, 6]]);
expect(console.warn).toHaveBeenCalledWith('[user-memory] trimmed embedding input', {
@@ -98,7 +99,7 @@ describe('embedUserMemoryTexts', () => {
input: ['one two three four'],
model: 'text-embedding-3-large',
},
{ metadata: { trigger: 'memory' }, user: 'user-test' },
{ metadata: { trigger: RequestTrigger.Memory }, user: 'user-test' },
);
expect(result).toEqual([[1, 2, 3]]);
});
+2 -1
View File
@@ -17,6 +17,7 @@ import {
MAX_ONBOARDING_STEPS,
MIN_DISCOVERY_USER_MESSAGES,
RECOMMENDED_DISCOVERY_USER_MESSAGES,
RequestTrigger,
SAVE_USER_QUESTION_FIELDS,
} from '@lobechat/types';
import { merge } from '@lobechat/utils';
@@ -297,7 +298,7 @@ export class OnboardingService {
const topic = await this.topicModel.create({
agentId,
title: 'Onboarding',
trigger: 'chat',
trigger: RequestTrigger.Chat,
});
return { created: true, topicId: topic.id };
+4 -4
View File
@@ -13,7 +13,7 @@ import type {
TaskSchedulerContext,
TaskTopicHandoff,
} from '@lobechat/types';
import { DEFAULT_BRIEF_ACTIONS } from '@lobechat/types';
import { DEFAULT_BRIEF_ACTIONS, RequestTrigger } from '@lobechat/types';
import debug from 'debug';
import { BriefModel } from '@/database/models/brief';
@@ -305,7 +305,7 @@ export class TaskLifecycleService {
model,
schema: { name: 'task_topic_handoff', schema: TASK_TOPIC_HANDOFF_SCHEMA },
},
{ metadata: { trigger: 'task-handoff' } },
{ metadata: { trigger: RequestTrigger.TaskHandoff } },
);
const handoff = result as {
@@ -405,7 +405,7 @@ export class TaskLifecycleService {
model,
schema: { name: 'task_topic_brief_judge', schema: JUDGE_BRIEF_EMIT_SCHEMA },
},
{ metadata: { trigger: 'task-brief-judge' } },
{ metadata: { trigger: RequestTrigger.TaskBriefJudge } },
)) as { emit?: boolean; reason?: string };
decision = {
@@ -458,7 +458,7 @@ export class TaskLifecycleService {
model,
schema: { name: 'task_topic_brief', schema: GENERATE_BRIEF_SCHEMA },
},
{ metadata: { trigger: 'task-brief' } },
{ metadata: { trigger: RequestTrigger.TaskBrief } },
);
const generated = result as { summary?: string; title?: string };
+4 -2
View File
@@ -1,6 +1,8 @@
import { DEFAULT_SYSTEM_AGENT_CONFIG } from '@lobechat/const';
import { evaluate, type EvaluateResult, type RubricResult } from '@lobechat/eval-rubric';
import type { EvaluateResult, RubricResult } from '@lobechat/eval-rubric';
import { evaluate } from '@lobechat/eval-rubric';
import type { EvalBenchmarkRubric } from '@lobechat/types';
import { RequestTrigger } from '@lobechat/types';
import debug from 'debug';
import { UserModel } from '@/database/models/user';
@@ -80,7 +82,7 @@ export class TaskReviewService {
model: payload.model || model,
schema: { name: 'judge_score', schema: payload.schema },
},
{ metadata: { trigger: 'task-review' } },
{ metadata: { trigger: RequestTrigger.TaskReview } },
);
},
judgeModel: model,
+2 -2
View File
@@ -2,10 +2,10 @@ import { TaskIdentifier as TaskSkillIdentifier } from '@lobechat/builtin-skills'
import { BriefIdentifier } from '@lobechat/builtin-tool-brief';
import { INBOX_SESSION_ID } from '@lobechat/const';
import type { ExecAgentResult, TaskItem } from '@lobechat/types';
import { RequestTrigger } from '@lobechat/types';
import { TRPCError } from '@trpc/server';
import debug from 'debug';
import { TopicTrigger } from '@/const/topic';
import { AgentModel } from '@/database/models/agent';
import { BriefModel } from '@/database/models/brief';
import { TaskModel } from '@/database/models/task';
@@ -203,7 +203,7 @@ export class TaskRunnerService {
prompt,
taskId: task.id,
title: extraPrompt ? extraPrompt.slice(0, 100) : task.name || task.identifier,
trigger: TopicTrigger.RunTask,
trigger: RequestTrigger.TaskRun,
userInterventionConfig: { approvalMode: 'headless' },
...(continueTopicId && { appContext: { topicId: continueTopicId } }),
});