@gt-fe/eap-runtime 0.0.32 → 0.0.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/handlers/invoke.handler.d.ts +0 -1
- package/dist/api/handlers/invoke.handler.js +25 -6
- package/dist/api/handlers/stream.handler.d.ts +0 -1
- package/dist/api/handlers/stream.handler.js +22 -18
- package/dist/common/types.d.ts +4 -3
- package/dist/graph/adapters/langgraph.adapter.d.ts +0 -1
- package/dist/graph/adapters/langgraph.adapter.js +117 -82
- package/dist/graph/adapters/skill-entry-span.js +4 -1
- package/dist/graph/adapters/subgraph-compiler.js +11 -9
- package/dist/graph/contracts/executable-graph.d.ts +2 -0
- package/dist/graph/execution-trace-context.d.ts +3 -0
- package/dist/graph/nodes/llm-node.d.ts +7 -2
- package/dist/graph/nodes/llm-node.js +8 -3
- package/dist/graph/nodes/skill-bind-node.d.ts +1 -1
- package/dist/graph/nodes/skill-bind-node.js +6 -1
- package/dist/graph/nodes/tool-node.js +1 -0
- package/dist/providers/http-llm-provider.js +27 -10
- package/dist/registry/graph-loader.d.ts +11 -0
- package/dist/registry/graph-loader.js +28 -4
- package/dist/runtime/agent-runtime.d.ts +7 -0
- package/dist/runtime/agent-runtime.js +69 -38
- package/dist/runtime/types.d.ts +1 -1
- package/dist/session/persist-turn-messages.d.ts +10 -2
- package/dist/session/persist-turn-messages.js +35 -10
- package/dist/sse/events/index.d.ts +6 -4
- package/dist/sse/events/index.js +3 -1
- package/dist/sse/events/reasoning-events.d.ts +24 -2
- package/dist/sse/events/reasoning-events.js +3 -1
- package/dist/sse/events/skill-events.d.ts +2 -0
- package/dist/task/contracts/task-manager.contract.d.ts +2 -2
- package/dist/task/contracts/task-manager.contract.js +1 -1
- package/dist/task/index.d.ts +2 -0
- package/dist/task/index.js +1 -0
- package/dist/task/task-abort.d.ts +14 -0
- package/dist/task/task-abort.js +33 -0
- package/dist/task/task-timeout.d.ts +11 -0
- package/dist/task/task-timeout.js +40 -4
- package/dist/task/task.service.js +11 -5
- package/dist/tool/index.d.ts +1 -1
- package/dist/tool/index.js +1 -1
- package/dist/tool/mcp-list.d.ts +3 -1
- package/dist/tool/mcp-list.js +4 -1
- package/dist/tool/system-prompt-builder.d.ts +10 -8
- package/dist/tool/system-prompt-builder.js +36 -26
- package/package.json +1 -1
|
@@ -81,7 +81,6 @@ export declare class InvokeOrchestrator {
|
|
|
81
81
|
execute(req: InvokeRequest, userId: string): Promise<InvokeResponse>;
|
|
82
82
|
private buildResponse;
|
|
83
83
|
private buildErrorResponse;
|
|
84
|
-
private isTimeoutError;
|
|
85
84
|
private persistInvokeUsages;
|
|
86
85
|
}
|
|
87
86
|
/** @deprecated 请直接从 `graph/hitl/hitl-handler` 导入;此处保留 re-export 兼容旧测试引用 */
|
|
@@ -26,8 +26,9 @@ import { AppError, ErrorCodes } from '@gt-fe/eap-contracts';
|
|
|
26
26
|
import { classifyRuntimeError } from '../../utils/http-error.js';
|
|
27
27
|
import { DEFAULT_GRAPH_TIMEOUT_MS } from '../../common/types.js';
|
|
28
28
|
import { resolveTaskTimeoutMs } from '../../task/resolve-task-timeout.js';
|
|
29
|
+
import { taskAbortKindOf } from '../../task/task-abort.js';
|
|
29
30
|
import { runWithExecutionTrace } from '../../graph/execution-trace-context.js';
|
|
30
|
-
import { resolveAgentSystemPromptFromLoader } from '../../registry/graph-loader.js';
|
|
31
|
+
import { resolveAgentDisplayNameFromLoader, resolveAgentSystemPromptFromLoader, } from '../../registry/graph-loader.js';
|
|
31
32
|
import { toLoadedToolMap } from '../../tool/loaded-tool-definition.js';
|
|
32
33
|
import { toLoadedSkillMap } from '../../tool/system-prompt-builder.js';
|
|
33
34
|
import { assembleLoadedTools } from '../../tool/harness-primitives.js';
|
|
@@ -204,6 +205,7 @@ export class InvokeOrchestrator {
|
|
|
204
205
|
? (await this.graphLoader.resolveAgentDir(agentCode, req.agentVersion)) ?? undefined
|
|
205
206
|
: undefined;
|
|
206
207
|
const agentSystemPrompt = await resolveAgentSystemPromptFromLoader(this.graphLoader, agentCode, req.agentVersion);
|
|
208
|
+
const agentName = await resolveAgentDisplayNameFromLoader(this.graphLoader, agentCode, req.agentVersion);
|
|
207
209
|
// 模型上下文:Checkpoint 已有 messages 则压它;压成功用 __set 写回最新 Checkpoint。Message 库不改。
|
|
208
210
|
const facing = await loadModelFacingHistory({
|
|
209
211
|
sessionHistory: messageRecordsToGraphMessages(sessionContext.messages),
|
|
@@ -261,6 +263,7 @@ export class InvokeOrchestrator {
|
|
|
261
263
|
promptRoots,
|
|
262
264
|
agentDir,
|
|
263
265
|
agentSystemPrompt,
|
|
266
|
+
agentName,
|
|
264
267
|
traceId: resolveTraceId(req),
|
|
265
268
|
traceCollector: this.traceCollector,
|
|
266
269
|
abortSignal,
|
|
@@ -311,8 +314,8 @@ export class InvokeOrchestrator {
|
|
|
311
314
|
status: 'token_exhausted',
|
|
312
315
|
});
|
|
313
316
|
}
|
|
314
|
-
|
|
315
|
-
if (
|
|
317
|
+
const abortKind = taskAbortKindOf(error, abortSignal);
|
|
318
|
+
if (abortKind === 'timeout') {
|
|
316
319
|
await this.taskService.tryUpdateStatus(task.id, 'running', 'timeout');
|
|
317
320
|
return this.buildResponse(traceId, {
|
|
318
321
|
taskId: task.id,
|
|
@@ -323,6 +326,25 @@ export class InvokeOrchestrator {
|
|
|
323
326
|
status: 'timeout',
|
|
324
327
|
});
|
|
325
328
|
}
|
|
329
|
+
if (abortKind === 'cancelled') {
|
|
330
|
+
const moved = await this.taskService.tryUpdateStatus(task.id, 'running', 'cancelled');
|
|
331
|
+
if (!moved) {
|
|
332
|
+
await this.taskService.tryUpdateStatus(task.id, 'waiting_hitl', 'cancelled');
|
|
333
|
+
}
|
|
334
|
+
// @gt-fe/eap-contracts 的 InvokeStatus 还没有 cancelled。这次不改包,响应体仍写 cancelled。
|
|
335
|
+
return {
|
|
336
|
+
code: 'runtime.task.cancelled',
|
|
337
|
+
data: {
|
|
338
|
+
taskId: task.id,
|
|
339
|
+
sessionId,
|
|
340
|
+
output: 'Task cancelled',
|
|
341
|
+
tokenUsage,
|
|
342
|
+
duration: Date.now() - startTime,
|
|
343
|
+
status: 'cancelled',
|
|
344
|
+
},
|
|
345
|
+
traceId,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
326
348
|
// ─── 错误处理:通用错误 ───
|
|
327
349
|
const classified = classifyRuntimeError(error);
|
|
328
350
|
const fuse = classified.code === ErrorCodes.GRAPH_MAX_ITERATIONS ? classified : undefined;
|
|
@@ -430,9 +452,6 @@ export class InvokeOrchestrator {
|
|
|
430
452
|
}
|
|
431
453
|
return { code: 'runtime.graph.execution_error', data, traceId };
|
|
432
454
|
}
|
|
433
|
-
isTimeoutError(error) {
|
|
434
|
-
return error instanceof Error && /timed out/i.test(error.message);
|
|
435
|
-
}
|
|
436
455
|
async persistInvokeUsages(taskId, sessionId) {
|
|
437
456
|
if (!this.checkpointSaver)
|
|
438
457
|
return [];
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* @see design.md §1.5 /stream 内部执行逻辑
|
|
20
20
|
* @see requirements.md Requirement 11 AC2
|
|
21
21
|
*/
|
|
22
|
-
import { buildHitlWaitingTaskOutput, buildTurnTranscriptFromStreamEvents, loadModelFacingHistory, turnGraphMessagesAfterCompression,
|
|
22
|
+
import { buildHitlWaitingTaskOutput, buildTurnTranscriptFromStreamEvents, loadModelFacingHistory, turnGraphMessagesAfterCompression, applyStreamReasoningEvent, applyStreamTextDelta, createStreamAssistantBuckets, createStreamReasoningState, resolvedStreamReasoning, visibleStreamAssistantText, streamAssistantTexts, messageRecordsToGraphMessages, persistHitlWaitingPlaceholder, persistTurnTranscript, persistUserTurnMessage, appendTerminalToolResult, } from '../../session/persist-turn-messages.js';
|
|
23
23
|
import { collectPublicArtifactLinks, collectPublicArtifactLinksFromEvent, injectPublicArtifactLinks, mergeArtifactLinks, } from '../../tool/inject-public-artifact-links.js';
|
|
24
24
|
import { SessionBudgetExhaustedError } from '../../token/session-budget-checker.js';
|
|
25
25
|
import { TokenBudgetExhaustedError } from '../../token/token-budget-guard.js';
|
|
@@ -31,7 +31,7 @@ import { SSEEmitter } from '../../sse/sse-emitter.js';
|
|
|
31
31
|
import { resolveExecutionContext } from '../../governance/governance-client.js';
|
|
32
32
|
import { resolveSkillInputFromInvokeInput } from '../../registry/skill-io-guard.js';
|
|
33
33
|
import { ErrorCodes } from '@gt-fe/eap-contracts';
|
|
34
|
-
import { resolveAgentSystemPromptFromLoader } from '../../registry/graph-loader.js';
|
|
34
|
+
import { resolveAgentDisplayNameFromLoader, resolveAgentSystemPromptFromLoader, } from '../../registry/graph-loader.js';
|
|
35
35
|
import { classifyRuntimeError } from '../../utils/http-error.js';
|
|
36
36
|
import { toLoadedToolMap } from '../../tool/loaded-tool-definition.js';
|
|
37
37
|
import { toLoadedSkillMap } from '../../tool/system-prompt-builder.js';
|
|
@@ -42,6 +42,7 @@ import { buildHitlWaitedPayload } from '../../graph/hitl/hitl-handler.js';
|
|
|
42
42
|
import { resolveTraceId } from '../../common/trace-id.js';
|
|
43
43
|
import { DEFAULT_GRAPH_TIMEOUT_MS } from '../../common/types.js';
|
|
44
44
|
import { resolveTaskTimeoutMs } from '../../task/resolve-task-timeout.js';
|
|
45
|
+
import { taskAbortKindOf } from '../../task/task-abort.js';
|
|
45
46
|
import { runWithExecutionTrace } from '../../graph/execution-trace-context.js';
|
|
46
47
|
// ─────────────────────────────────────────────
|
|
47
48
|
// Placeholder Graph DSL (Agent Registry 接入前)
|
|
@@ -222,6 +223,7 @@ export class StreamOrchestrator {
|
|
|
222
223
|
? (await this.graphLoader.resolveAgentDir(agentCode, req.agentVersion)) ?? undefined
|
|
223
224
|
: undefined;
|
|
224
225
|
const agentSystemPrompt = await resolveAgentSystemPromptFromLoader(this.graphLoader, agentCode, req.agentVersion);
|
|
226
|
+
const agentName = await resolveAgentDisplayNameFromLoader(this.graphLoader, agentCode, req.agentVersion);
|
|
225
227
|
// 模型上下文:Checkpoint 已有 messages 则压它;压成功用 __set 写回最新 Checkpoint。Message 库不改。
|
|
226
228
|
const facing = await loadModelFacingHistory({
|
|
227
229
|
sessionHistory: messageRecordsToGraphMessages(sessionContext.messages),
|
|
@@ -265,8 +267,8 @@ export class StreamOrchestrator {
|
|
|
265
267
|
});
|
|
266
268
|
const skillInput = resolveSkillInputFromInvokeInput(req.input);
|
|
267
269
|
const assistantBuckets = createStreamAssistantBuckets();
|
|
270
|
+
const reasoningState = createStreamReasoningState();
|
|
268
271
|
let toolCallReasoningContent;
|
|
269
|
-
let assistantReasoningContent;
|
|
270
272
|
const pendingToolCalls = [];
|
|
271
273
|
const toolResults = [];
|
|
272
274
|
const artifactLinks = [];
|
|
@@ -293,6 +295,7 @@ export class StreamOrchestrator {
|
|
|
293
295
|
promptRoots,
|
|
294
296
|
agentDir,
|
|
295
297
|
agentSystemPrompt,
|
|
298
|
+
agentName,
|
|
296
299
|
traceId: resolveTraceId(req),
|
|
297
300
|
traceCollector: this.traceCollector,
|
|
298
301
|
abortSignal,
|
|
@@ -323,15 +326,11 @@ export class StreamOrchestrator {
|
|
|
323
326
|
arguments: p.arguments ?? {},
|
|
324
327
|
});
|
|
325
328
|
}
|
|
326
|
-
|
|
327
|
-
toolCallReasoningContent = p.reasoningContent;
|
|
328
|
-
}
|
|
329
|
+
toolCallReasoningContent = resolvedStreamReasoning(reasoningState);
|
|
329
330
|
}
|
|
330
331
|
appendTerminalToolResult(runtimeEvent.type, runtimeEvent.payload, toolResults);
|
|
331
332
|
collectPublicArtifactLinksFromEvent(runtimeEvent.type, runtimeEvent.payload, artifactLinks);
|
|
332
|
-
|
|
333
|
-
if (streamedReasoning)
|
|
334
|
-
assistantReasoningContent = streamedReasoning;
|
|
333
|
+
applyStreamReasoningEvent(reasoningState, runtimeEvent.type, runtimeEvent.payload);
|
|
335
334
|
applyStreamTextDelta(assistantBuckets, runtimeEvent.type, runtimeEvent.payload);
|
|
336
335
|
if (shouldEmitTokenSse(tokenDetail, runtimeEvent.type)) {
|
|
337
336
|
emitter.emit(this.runtimeEventToSSE(runtimeEvent, correlationId, taskId));
|
|
@@ -367,13 +366,21 @@ export class StreamOrchestrator {
|
|
|
367
366
|
this.emitErrorAndClose(emitter, correlationId, taskId, error);
|
|
368
367
|
return;
|
|
369
368
|
}
|
|
370
|
-
|
|
371
|
-
if (
|
|
369
|
+
const abortKind = taskAbortKindOf(error, abortSignal);
|
|
370
|
+
if (abortKind === 'timeout' || abortKind === 'cancelled') {
|
|
372
371
|
await recordTaskTokenUsages(this.taskService, taskId, this.tokenStatsService.list(taskId));
|
|
373
|
-
|
|
372
|
+
if (abortKind === 'timeout') {
|
|
373
|
+
await this.taskService.tryUpdateStatus(taskId, 'running', 'timeout');
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
const moved = await this.taskService.tryUpdateStatus(taskId, 'running', 'cancelled');
|
|
377
|
+
if (!moved) {
|
|
378
|
+
await this.taskService.tryUpdateStatus(taskId, 'waiting_hitl', 'cancelled');
|
|
379
|
+
}
|
|
380
|
+
}
|
|
374
381
|
emitter.emit(this.buildSSEEvent('task.error', correlationId, taskId, {
|
|
375
|
-
code: ErrorCodes.TASK_TIMEOUT,
|
|
376
|
-
message: 'Task execution timed out',
|
|
382
|
+
code: abortKind === 'timeout' ? ErrorCodes.TASK_TIMEOUT : 'runtime.task.cancelled',
|
|
383
|
+
message: abortKind === 'timeout' ? 'Task execution timed out' : 'Task cancelled',
|
|
377
384
|
recoverable: false,
|
|
378
385
|
}));
|
|
379
386
|
emitter.close();
|
|
@@ -439,7 +446,7 @@ export class StreamOrchestrator {
|
|
|
439
446
|
assistantText: injected,
|
|
440
447
|
finishPoints: graph.finishPoints,
|
|
441
448
|
toolCallReasoningContent,
|
|
442
|
-
assistantReasoningContent,
|
|
449
|
+
assistantReasoningContent: resolvedStreamReasoning(reasoningState),
|
|
443
450
|
}),
|
|
444
451
|
assistantTokenCount: tokenStats.total.output ?? 0,
|
|
445
452
|
assistantDurationMs: duration,
|
|
@@ -487,7 +494,4 @@ export class StreamOrchestrator {
|
|
|
487
494
|
}));
|
|
488
495
|
emitter.close();
|
|
489
496
|
}
|
|
490
|
-
isTimeoutError(error) {
|
|
491
|
-
return error instanceof Error && /timed out/i.test(error.message);
|
|
492
|
-
}
|
|
493
497
|
}
|
package/dist/common/types.d.ts
CHANGED
|
@@ -58,11 +58,12 @@ export interface RuntimeRunContext {
|
|
|
58
58
|
/**
|
|
59
59
|
* Task 执行状态(Runtime 内部使用)
|
|
60
60
|
*
|
|
61
|
-
* 状态流转:pending → running → success | failed | timeout | token_exhausted | waiting_hitl
|
|
61
|
+
* 状态流转:pending → running → success | failed | timeout | token_exhausted | cancelled | waiting_hitl
|
|
62
62
|
*
|
|
63
|
-
* waiting_hitl:等待人工介入确认,可通过 HITL API 恢复为 running
|
|
63
|
+
* waiting_hitl:等待人工介入确认,可通过 HITL API 恢复为 running。
|
|
64
|
+
* cancelled:调用方停止这一轮(Hosting `POST /tasks/:id/cancel`),不是超时。
|
|
64
65
|
*/
|
|
65
|
-
export type TaskStatus = 'pending' | 'running' | 'waiting_hitl' | 'success' | 'failed' | 'timeout' | 'token_exhausted';
|
|
66
|
+
export type TaskStatus = 'pending' | 'running' | 'waiting_hitl' | 'success' | 'failed' | 'timeout' | 'token_exhausted' | 'cancelled';
|
|
66
67
|
/**
|
|
67
68
|
* Runtime 执行上下文绑定状态(非平台 Session 生命周期)
|
|
68
69
|
*
|
|
@@ -127,7 +127,6 @@ export declare class LangGraphAdapter implements IStateGraphEngine {
|
|
|
127
127
|
private createNodeFunction;
|
|
128
128
|
private createNodeFunctionBody;
|
|
129
129
|
private createSkillBindNodeFunction;
|
|
130
|
-
private buildSkillCatalog;
|
|
131
130
|
private createToolNodeFunction;
|
|
132
131
|
private createLLMNodeFunction;
|
|
133
132
|
/**
|
|
@@ -23,7 +23,7 @@ import { ToolResultInjector } from '../../tool/tool-result-injector.js';
|
|
|
23
23
|
import { fileArtifactFromToolOutput } from '../../tool/file-artifact.js';
|
|
24
24
|
import { EchoLLMProvider } from '../../providers/echo-llm-provider.js';
|
|
25
25
|
import { ConditionEvaluator } from '../condition-evaluator.js';
|
|
26
|
-
import { SystemPromptBuilder,
|
|
26
|
+
import { SystemPromptBuilder, buildSkillCatalog, loadedToolsToApiTools, resolveLlmPromptInputs, spokenName } from '../../tool/system-prompt-builder.js';
|
|
27
27
|
import { resolveJailFromState, resolveJailRoots } from '../../tool/harness-primitives.js';
|
|
28
28
|
import os from 'node:os';
|
|
29
29
|
import { bindSelectedSkill } from '../nodes/skill-bind-node.js';
|
|
@@ -39,6 +39,81 @@ import { recordSkillEntrySpan, skillEntryInvocationFromTool } from './skill-entr
|
|
|
39
39
|
import { llmSpanInput, llmSpanOutputFromResult, withGraphSpan } from '../trace-span.js';
|
|
40
40
|
import { executionTraceFromState, getExecutionStream, getExecutionTrace, runWithExecutionTrace, } from '../execution-trace-context.js';
|
|
41
41
|
import { isRetryableToolFailure, resolveToolRetryConfig, sleepToolRetry, toolRetryDelayMs, } from '../../tool/tool-retry.js';
|
|
42
|
+
function emitReasoningStartedIfNeeded(live, model, timestamp) {
|
|
43
|
+
if (live.reasoningStartedThisTurn)
|
|
44
|
+
return;
|
|
45
|
+
live.reasoningStartedThisTurn = true;
|
|
46
|
+
live.iteration += 1;
|
|
47
|
+
live.sink({
|
|
48
|
+
type: 'reasoning.started',
|
|
49
|
+
taskId: undefined,
|
|
50
|
+
correlationId: undefined,
|
|
51
|
+
timestamp,
|
|
52
|
+
payload: {
|
|
53
|
+
iterationCount: live.iteration,
|
|
54
|
+
model,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
function nodeGraphExtra(live, nodeId) {
|
|
59
|
+
return {
|
|
60
|
+
node: nodeId,
|
|
61
|
+
...(live.graphId ? { graphId: live.graphId } : {}),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function emitLlmStreamDelta(live, nodeId, model, delta) {
|
|
65
|
+
const timestamp = new Date().toISOString();
|
|
66
|
+
const extra = nodeGraphExtra(live, nodeId);
|
|
67
|
+
if (delta.reasoningContent) {
|
|
68
|
+
emitReasoningStartedIfNeeded(live, model, timestamp);
|
|
69
|
+
live.sink({
|
|
70
|
+
type: 'reasoning.chunk',
|
|
71
|
+
taskId: undefined,
|
|
72
|
+
correlationId: undefined,
|
|
73
|
+
timestamp,
|
|
74
|
+
payload: {
|
|
75
|
+
content: delta.reasoningContent,
|
|
76
|
+
index: live.reasoningChunkIndex++,
|
|
77
|
+
...extra,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if (delta.content) {
|
|
82
|
+
emitReasoningStartedIfNeeded(live, model, timestamp);
|
|
83
|
+
live.streamedTextThisTurn = true;
|
|
84
|
+
live.sink({
|
|
85
|
+
type: 'response.chunk',
|
|
86
|
+
taskId: undefined,
|
|
87
|
+
correlationId: undefined,
|
|
88
|
+
timestamp,
|
|
89
|
+
payload: {
|
|
90
|
+
content: delta.content,
|
|
91
|
+
index: live.chunkIndex++,
|
|
92
|
+
...extra,
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function emitReasoningCompletedIfAny(live, nodeId, next) {
|
|
98
|
+
if (!live)
|
|
99
|
+
return;
|
|
100
|
+
const added = Array.isArray(next.messages) ? next.messages[0] : undefined;
|
|
101
|
+
const reasoning = added && typeof added === 'object' && typeof added.reasoningContent === 'string'
|
|
102
|
+
? added.reasoningContent
|
|
103
|
+
: '';
|
|
104
|
+
if (!reasoning)
|
|
105
|
+
return;
|
|
106
|
+
live.sink({
|
|
107
|
+
type: 'reasoning.completed',
|
|
108
|
+
taskId: undefined,
|
|
109
|
+
correlationId: undefined,
|
|
110
|
+
timestamp: new Date().toISOString(),
|
|
111
|
+
payload: {
|
|
112
|
+
content: reasoning,
|
|
113
|
+
...nodeGraphExtra(live, nodeId),
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
}
|
|
42
117
|
/**
|
|
43
118
|
* 传给 LangGraph 的 `recursionLimit`:一轮调度(superstep)+1。
|
|
44
119
|
* 同一步里并行的多个节点只 +1。ReAct 的 llm 再 tool 大约 +2。
|
|
@@ -306,6 +381,7 @@ export class LangGraphAdapter {
|
|
|
306
381
|
alreadyInSkillSpan: progress.inSkillSubgraph,
|
|
307
382
|
});
|
|
308
383
|
if (catalogSkill) {
|
|
384
|
+
const skillName = spokenName(progress.skillName, catalogSkill.skillId) ?? catalogSkill.skillId;
|
|
309
385
|
stream.sink({
|
|
310
386
|
type: 'skill.started',
|
|
311
387
|
taskId,
|
|
@@ -313,7 +389,7 @@ export class LangGraphAdapter {
|
|
|
313
389
|
timestamp,
|
|
314
390
|
payload: {
|
|
315
391
|
skillId: catalogSkill.skillId,
|
|
316
|
-
skillName
|
|
392
|
+
skillName,
|
|
317
393
|
path: catalogSkill.path,
|
|
318
394
|
...(progress.skillSpace ? { skillSpace: progress.skillSpace } : {}),
|
|
319
395
|
...(progress.skillVersion ? { skillVersion: progress.skillVersion } : {}),
|
|
@@ -326,7 +402,7 @@ export class LangGraphAdapter {
|
|
|
326
402
|
timestamp,
|
|
327
403
|
payload: {
|
|
328
404
|
skillId: catalogSkill.skillId,
|
|
329
|
-
skillName
|
|
405
|
+
skillName,
|
|
330
406
|
},
|
|
331
407
|
});
|
|
332
408
|
}
|
|
@@ -460,6 +536,7 @@ export class LangGraphAdapter {
|
|
|
460
536
|
// agentId 已废弃:channel 保留仅为旧 Checkpoint 反序列化兼容,新代码只写 agentCode
|
|
461
537
|
agentId: ephemeral(''),
|
|
462
538
|
agentCode: ephemeral(''),
|
|
539
|
+
agentName: ephemeral(undefined),
|
|
463
540
|
agentVersion: ephemeral(undefined),
|
|
464
541
|
delegationToken: ephemeral(''),
|
|
465
542
|
correlationId: ephemeral(''),
|
|
@@ -594,15 +671,25 @@ export class LangGraphAdapter {
|
|
|
594
671
|
sink: enqueue,
|
|
595
672
|
eventContext: { taskId: state.taskId, correlationId: state.correlationId },
|
|
596
673
|
chunkIndex: 0,
|
|
674
|
+
reasoningChunkIndex: 0,
|
|
597
675
|
iteration: 0,
|
|
598
676
|
reasoningStartedThisTurn: false,
|
|
599
677
|
streamedTextThisTurn: false,
|
|
678
|
+
graphId: graph.id,
|
|
600
679
|
};
|
|
601
680
|
const traceCtx = executionTraceFromState(state);
|
|
602
681
|
const graphSignal = graphAbortSignal(state);
|
|
682
|
+
let graphIterator;
|
|
683
|
+
const stopGraphIterator = () => {
|
|
684
|
+
const iterator = graphIterator;
|
|
685
|
+
if (!iterator?.return)
|
|
686
|
+
return;
|
|
687
|
+
void iterator.return().catch(() => undefined);
|
|
688
|
+
};
|
|
603
689
|
const onGraphAbort = () => {
|
|
604
690
|
failure = graphSignal ? abortReason(graphSignal) : new Error('aborted');
|
|
605
691
|
finished = true;
|
|
692
|
+
stopGraphIterator();
|
|
606
693
|
signal();
|
|
607
694
|
};
|
|
608
695
|
graphSignal?.addEventListener('abort', onGraphAbort, { once: true });
|
|
@@ -617,9 +704,19 @@ export class LangGraphAdapter {
|
|
|
617
704
|
const streamIterator = await withGraphAbort(Promise.resolve(ref.compiled.stream(input, langGraphRunOptions({
|
|
618
705
|
configurable: this.checkpointConfigurable(state, graph),
|
|
619
706
|
streamMode: 'updates',
|
|
707
|
+
...(graphSignal ? { signal: graphSignal } : {}),
|
|
620
708
|
}))), graphSignal);
|
|
621
|
-
|
|
622
|
-
|
|
709
|
+
const iterator = streamIterator[Symbol.asyncIterator]();
|
|
710
|
+
graphIterator = iterator;
|
|
711
|
+
while (true) {
|
|
712
|
+
if (graphSignal?.aborted) {
|
|
713
|
+
failure = failure ?? abortReason(graphSignal);
|
|
714
|
+
break;
|
|
715
|
+
}
|
|
716
|
+
const next = await iterator.next();
|
|
717
|
+
if (next.done)
|
|
718
|
+
break;
|
|
719
|
+
for (const event of this.convertUpdatesToEvents(next.value, graph.id, () => streamBag.chunkIndex++, ref.silentNodeIds, ref.subgraphNodeIds)) {
|
|
623
720
|
enqueue(event);
|
|
624
721
|
}
|
|
625
722
|
}
|
|
@@ -665,6 +762,7 @@ export class LangGraphAdapter {
|
|
|
665
762
|
abortDeadlineMs,
|
|
666
763
|
}, () => ref.compiled.invoke(input, langGraphRunOptions({
|
|
667
764
|
configurable: this.checkpointConfigurable(state, graph),
|
|
765
|
+
...(abortSignal ? { signal: abortSignal } : {}),
|
|
668
766
|
}))), abortSignal);
|
|
669
767
|
}
|
|
670
768
|
catch (error) {
|
|
@@ -856,7 +954,7 @@ export class LangGraphAdapter {
|
|
|
856
954
|
correlationId: undefined,
|
|
857
955
|
timestamp: new Date().toISOString(),
|
|
858
956
|
payload: {
|
|
859
|
-
skillName: patch.skillName
|
|
957
|
+
skillName: spokenName(patch.skillName, patch.activatedSkill) ?? patch.activatedSkill,
|
|
860
958
|
skillId: patch.activatedSkill,
|
|
861
959
|
confidence: 1,
|
|
862
960
|
},
|
|
@@ -898,20 +996,6 @@ export class LangGraphAdapter {
|
|
|
898
996
|
};
|
|
899
997
|
};
|
|
900
998
|
}
|
|
901
|
-
buildSkillCatalog(defs) {
|
|
902
|
-
if (!defs)
|
|
903
|
-
return undefined;
|
|
904
|
-
const rows = Object.values(defs).map((skill) => {
|
|
905
|
-
const desc = (skill.description ?? '').replace(/\s+/g, ' ').trim();
|
|
906
|
-
const entry = skill.entryFile ?? (skill.bundlePath ? `${skill.bundlePath}/SKILL.md` : '');
|
|
907
|
-
const title = skill.name && skill.name !== skill.skillId ? `${skill.skillId}(${skill.name})` : skill.skillId;
|
|
908
|
-
const pathLine = entry ? `\n SKILL.md: ${entry}` : '';
|
|
909
|
-
return `- **${title}**${desc ? `:${desc}` : ''}${pathLine}`;
|
|
910
|
-
});
|
|
911
|
-
if (rows.length === 0)
|
|
912
|
-
return undefined;
|
|
913
|
-
return `# 可用技能\n要用某个技能时,调用 read_file 读取其 SKILL.md。脚本和参考文档默认在同一目录。\n${rows.join('\n')}`;
|
|
914
|
-
}
|
|
915
999
|
createToolNodeFunction(node) {
|
|
916
1000
|
const toolName = node.config?.['tool'] ?? node.id;
|
|
917
1001
|
const defaultArgs = node.config?.['arguments'] ?? {};
|
|
@@ -1061,6 +1145,7 @@ export class LangGraphAdapter {
|
|
|
1061
1145
|
const resolvedPrompt = resolveGraphPrompt(baseConfig.systemPrompt, promptRoots);
|
|
1062
1146
|
const loadedToolsBase = state['loadedTools'];
|
|
1063
1147
|
const agentCode = typeof state['agentCode'] === 'string' ? state['agentCode'] : undefined;
|
|
1148
|
+
const agentName = typeof state['agentName'] === 'string' ? state['agentName'] : undefined;
|
|
1064
1149
|
const skillDefs = state['skillDefinitions'];
|
|
1065
1150
|
const execCtx = state['executionContext'];
|
|
1066
1151
|
const userId = (typeof execCtx?.userId === 'string' && execCtx.userId) || state['userId'];
|
|
@@ -1092,8 +1177,7 @@ export class LangGraphAdapter {
|
|
|
1092
1177
|
const toolNames = loadedTools ? Object.keys(loadedTools) : [];
|
|
1093
1178
|
console.log(`[LLM-DEBUG] node=${node.id} loadedTools=${toolNames.length}:`, toolNames);
|
|
1094
1179
|
}
|
|
1095
|
-
const catalog = includeSkillCatalog ?
|
|
1096
|
-
const toolCatalog = disableTools ? undefined : buildToolCatalog(loadedTools);
|
|
1180
|
+
const catalog = includeSkillCatalog ? buildSkillCatalog(skillDefs) : undefined;
|
|
1097
1181
|
const jail = resolveJailFromState({
|
|
1098
1182
|
agentDir: state['agentDir'],
|
|
1099
1183
|
userId,
|
|
@@ -1122,7 +1206,7 @@ export class LangGraphAdapter {
|
|
|
1122
1206
|
agentPrompt: promptInputs.agentPrompt,
|
|
1123
1207
|
nodePrompt: promptInputs.nodePrompt,
|
|
1124
1208
|
skillCatalog: catalog,
|
|
1125
|
-
|
|
1209
|
+
agentName,
|
|
1126
1210
|
agentCode,
|
|
1127
1211
|
userId,
|
|
1128
1212
|
workingDirectory,
|
|
@@ -1151,39 +1235,14 @@ export class LangGraphAdapter {
|
|
|
1151
1235
|
if (stream) {
|
|
1152
1236
|
stream.reasoningStartedThisTurn = false;
|
|
1153
1237
|
stream.streamedTextThisTurn = false;
|
|
1238
|
+
stream.reasoningChunkIndex = 0;
|
|
1154
1239
|
}
|
|
1155
1240
|
const llmNode = new LLMNode(node.id, finalConfig, this.llmProvider, enableStream, enableStream
|
|
1156
|
-
? (
|
|
1241
|
+
? (delta) => {
|
|
1157
1242
|
const live = getExecutionStream();
|
|
1158
|
-
if (!live
|
|
1243
|
+
if (!live)
|
|
1159
1244
|
return;
|
|
1160
|
-
|
|
1161
|
-
if (!live.reasoningStartedThisTurn) {
|
|
1162
|
-
live.reasoningStartedThisTurn = true;
|
|
1163
|
-
live.iteration += 1;
|
|
1164
|
-
live.sink({
|
|
1165
|
-
type: 'reasoning.started',
|
|
1166
|
-
taskId: undefined,
|
|
1167
|
-
correlationId: undefined,
|
|
1168
|
-
timestamp,
|
|
1169
|
-
payload: {
|
|
1170
|
-
iterationCount: live.iteration,
|
|
1171
|
-
model: finalConfig.model,
|
|
1172
|
-
},
|
|
1173
|
-
});
|
|
1174
|
-
}
|
|
1175
|
-
live.streamedTextThisTurn = true;
|
|
1176
|
-
live.sink({
|
|
1177
|
-
type: 'response.chunk',
|
|
1178
|
-
taskId: undefined,
|
|
1179
|
-
correlationId: undefined,
|
|
1180
|
-
timestamp,
|
|
1181
|
-
payload: {
|
|
1182
|
-
content: text,
|
|
1183
|
-
index: live.chunkIndex++,
|
|
1184
|
-
node: node.id,
|
|
1185
|
-
},
|
|
1186
|
-
});
|
|
1245
|
+
emitLlmStreamDelta(live, node.id, finalConfig.model, delta);
|
|
1187
1246
|
}
|
|
1188
1247
|
: undefined, this.interceptors);
|
|
1189
1248
|
const toolsForSpan = disableTools || !loadedTools
|
|
@@ -1207,6 +1266,9 @@ export class LangGraphAdapter {
|
|
|
1207
1266
|
},
|
|
1208
1267
|
}, async () => {
|
|
1209
1268
|
const next = await llmNode.execute(state);
|
|
1269
|
+
if (enableStream) {
|
|
1270
|
+
emitReasoningCompletedIfAny(getExecutionStream(), node.id, next);
|
|
1271
|
+
}
|
|
1210
1272
|
const hydratedUpdate = loadedTools && loadedTools !== loadedToolsBase ? { loadedTools } : {};
|
|
1211
1273
|
if (!silent)
|
|
1212
1274
|
return { ...next, ...hydratedUpdate };
|
|
@@ -1552,23 +1614,17 @@ export class LangGraphAdapter {
|
|
|
1552
1614
|
continue;
|
|
1553
1615
|
}
|
|
1554
1616
|
const hasText = typeof msg.content === 'string' && msg.content.trim().length > 0;
|
|
1555
|
-
if (
|
|
1556
|
-
(!streamedTextThisTurn || msg.reasoningContent)) {
|
|
1557
|
-
if (streamedTextThisTurn && !msg.reasoningContent)
|
|
1558
|
-
continue;
|
|
1617
|
+
if (hasText && !streamedTextThisTurn) {
|
|
1559
1618
|
yield {
|
|
1560
1619
|
type: 'response.chunk',
|
|
1561
1620
|
taskId: undefined,
|
|
1562
1621
|
correlationId: undefined,
|
|
1563
1622
|
timestamp,
|
|
1564
1623
|
payload: {
|
|
1565
|
-
content:
|
|
1624
|
+
content: msg.content ?? '',
|
|
1566
1625
|
index: nextChunkIndex(),
|
|
1567
1626
|
node: nodeId,
|
|
1568
1627
|
graphId,
|
|
1569
|
-
...(msg.reasoningContent
|
|
1570
|
-
? { reasoningContent: msg.reasoningContent }
|
|
1571
|
-
: {}),
|
|
1572
1628
|
},
|
|
1573
1629
|
};
|
|
1574
1630
|
}
|
|
@@ -1579,26 +1635,8 @@ export class LangGraphAdapter {
|
|
|
1579
1635
|
continue;
|
|
1580
1636
|
if (subgraphNodeIds.has(nodeId) && streamedTextThisTurn)
|
|
1581
1637
|
continue;
|
|
1582
|
-
|
|
1583
|
-
if (streamedTextThisTurn) {
|
|
1584
|
-
// token 已流过:仍补发一条带 reasoning 的标记 chunk(空 content),供落库回传
|
|
1585
|
-
if (msg.reasoningContent) {
|
|
1586
|
-
yield {
|
|
1587
|
-
type: 'response.chunk',
|
|
1588
|
-
taskId: undefined,
|
|
1589
|
-
correlationId: undefined,
|
|
1590
|
-
timestamp,
|
|
1591
|
-
payload: {
|
|
1592
|
-
content: '',
|
|
1593
|
-
index: nextChunkIndex(),
|
|
1594
|
-
node: nodeId,
|
|
1595
|
-
graphId,
|
|
1596
|
-
reasoningContent: msg.reasoningContent,
|
|
1597
|
-
},
|
|
1598
|
-
};
|
|
1599
|
-
}
|
|
1638
|
+
if (streamedTextThisTurn)
|
|
1600
1639
|
continue;
|
|
1601
|
-
}
|
|
1602
1640
|
yield {
|
|
1603
1641
|
type: 'response.chunk',
|
|
1604
1642
|
taskId: undefined,
|
|
@@ -1609,9 +1647,6 @@ export class LangGraphAdapter {
|
|
|
1609
1647
|
index: nextChunkIndex(),
|
|
1610
1648
|
node: nodeId,
|
|
1611
1649
|
graphId,
|
|
1612
|
-
...(msg.reasoningContent
|
|
1613
|
-
? { reasoningContent: msg.reasoningContent }
|
|
1614
|
-
: {}),
|
|
1615
1650
|
},
|
|
1616
1651
|
};
|
|
1617
1652
|
continue;
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { classifySkillFile, classifySkillSpace, isReadTool, } from './skill-read.js';
|
|
12
12
|
import { completeGraphSpan, startGraphSpan } from '../trace-span.js';
|
|
13
|
+
import { spokenName } from '../../tool/system-prompt-builder.js';
|
|
13
14
|
function declaredSkillVersion(skillId, version) {
|
|
14
15
|
const raw = version?.trim();
|
|
15
16
|
if (!raw || raw === skillId)
|
|
@@ -27,9 +28,10 @@ export function skillTraceIdentity(state, skillId, fallbackPath) {
|
|
|
27
28
|
const bundle = info?.bundlePath || fallbackPath;
|
|
28
29
|
const skillSpace = bundle ? classifySkillSpace(bundle, { agentDir, userId }) : undefined;
|
|
29
30
|
const skillVersion = declaredSkillVersion(skillId, info?.version);
|
|
31
|
+
const skillName = spokenName(info?.name, undefined);
|
|
30
32
|
return {
|
|
31
33
|
skillId,
|
|
32
|
-
...(
|
|
34
|
+
...(skillName ? { skillName } : {}),
|
|
33
35
|
...(skillVersion ? { skillVersion } : {}),
|
|
34
36
|
...(skillSpace ? { skillSpace } : {}),
|
|
35
37
|
};
|
|
@@ -39,6 +41,7 @@ export function skillSpanFields(identity, extra) {
|
|
|
39
41
|
...(identity.skillVersion ? { version: identity.skillVersion } : {}),
|
|
40
42
|
input: {
|
|
41
43
|
skillId: identity.skillId,
|
|
44
|
+
...(identity.skillName ? { skillName: identity.skillName } : {}),
|
|
42
45
|
...(identity.skillSpace ? { skillSpace: identity.skillSpace } : {}),
|
|
43
46
|
...(extra?.path ? { path: extra.path } : {}),
|
|
44
47
|
...(extra?.source ? { source: extra.source } : {}),
|