@gt-fe/eap-runtime 0.0.29 → 0.0.30
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.
|
@@ -36,7 +36,7 @@ import { pickContextFields } from '../../token/context-window.js';
|
|
|
36
36
|
import { isHitlDecision, lastAssistantHasUnansweredToolCalls } from '../hitl/hitl-handler.js';
|
|
37
37
|
import { classifySkillRead } from './skill-read.js';
|
|
38
38
|
import { skillEntryInvocationFromTool } from './skill-entry-span.js';
|
|
39
|
-
import { withGraphSpan } from '../trace-span.js';
|
|
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
42
|
/**
|
|
@@ -788,17 +788,30 @@ export class LangGraphAdapter {
|
|
|
788
788
|
case 'tool':
|
|
789
789
|
return this.createToolNodeFunction(node);
|
|
790
790
|
case 'condition':
|
|
791
|
-
return async (state) =>
|
|
791
|
+
return async (state) => {
|
|
792
792
|
const conditionExpr = node.config?.['conditionExpr']
|
|
793
793
|
?? node.config?.['conditionFn'];
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
794
|
+
const branchValues = node.config?.['branchValues']
|
|
795
|
+
?? this.extractBranchValuesFromDsl(node.id);
|
|
796
|
+
const fallback = node.config?.['fallback'] ?? '__default__';
|
|
797
|
+
return withGraphSpan(state, {
|
|
798
|
+
name: node.id,
|
|
799
|
+
type: 'condition',
|
|
800
|
+
extra: {
|
|
801
|
+
input: {
|
|
802
|
+
conditionExpr: conditionExpr ?? null,
|
|
803
|
+
branchValues,
|
|
804
|
+
fallback,
|
|
805
|
+
},
|
|
806
|
+
},
|
|
807
|
+
}, async () => {
|
|
808
|
+
if (conditionExpr) {
|
|
809
|
+
const result = await this.conditionEvaluator.evaluate(conditionExpr, state, branchValues, fallback);
|
|
810
|
+
return { currentNode: node.id, __conditionResult: result };
|
|
811
|
+
}
|
|
812
|
+
return { currentNode: node.id };
|
|
813
|
+
}, { outputFrom: (result) => result['__conditionResult'] });
|
|
814
|
+
};
|
|
802
815
|
case 'subgraph':
|
|
803
816
|
return this.createSubGraphNodeFunction(node);
|
|
804
817
|
case 'human':
|
|
@@ -1019,136 +1032,155 @@ export class LangGraphAdapter {
|
|
|
1019
1032
|
lastAssistantHasUnansweredToolCalls(rawMessages)) {
|
|
1020
1033
|
return { currentNode: node.id };
|
|
1021
1034
|
}
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
:
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
},
|
|
1049
|
-
});
|
|
1050
|
-
if (loadedTools && loadedTools !== loadedToolsBase) {
|
|
1051
|
-
state['loadedTools'] = loadedTools;
|
|
1052
|
-
}
|
|
1053
|
-
// [DEBUG] 打印 loadedTools 实际状态
|
|
1054
|
-
if (process.env.EAP_LLM_DEBUG === '1') {
|
|
1055
|
-
const toolNames = loadedTools ? Object.keys(loadedTools) : [];
|
|
1056
|
-
console.log(`[LLM-DEBUG] node=${node.id} loadedTools=${toolNames.length}:`, toolNames);
|
|
1057
|
-
}
|
|
1058
|
-
const catalog = includeSkillCatalog ? this.buildSkillCatalog(skillDefs) : undefined;
|
|
1059
|
-
const toolCatalog = disableTools ? undefined : buildToolCatalog(loadedTools);
|
|
1060
|
-
const jail = resolveJailFromState({
|
|
1061
|
-
agentDir: state['agentDir'],
|
|
1062
|
-
userId,
|
|
1063
|
-
executionContext: execCtx,
|
|
1064
|
-
loadedTools,
|
|
1065
|
-
skillDefinitions: skillDefs,
|
|
1066
|
-
});
|
|
1067
|
-
if (this.overlayExtraRoots.length > 0) {
|
|
1068
|
-
jail.extraRoots = [...(jail.extraRoots ?? []), ...this.overlayExtraRoots];
|
|
1069
|
-
}
|
|
1070
|
-
const workingDirectory = jail.workdir;
|
|
1071
|
-
const jailRoots = resolveJailRoots(jail);
|
|
1072
|
-
const skillDirs = Object.values(skillDefs ?? {})
|
|
1073
|
-
.filter((skill) => Boolean(skill.bundlePath))
|
|
1074
|
-
.map((skill) => ({
|
|
1075
|
-
skillId: skill.skillId,
|
|
1076
|
-
bundlePath: skill.bundlePath,
|
|
1077
|
-
entryFile: skill.entryFile,
|
|
1078
|
-
}));
|
|
1079
|
-
const promptInputs = resolveLlmPromptInputs({
|
|
1080
|
-
silent,
|
|
1081
|
-
agentSystemPrompt: state['agentSystemPrompt'],
|
|
1082
|
-
nodePrompt: resolvedPrompt,
|
|
1035
|
+
const promptRoots = Array.isArray(state['promptRoots']) ? state['promptRoots'] : [];
|
|
1036
|
+
const resolvedPrompt = resolveGraphPrompt(baseConfig.systemPrompt, promptRoots);
|
|
1037
|
+
const loadedToolsBase = state['loadedTools'];
|
|
1038
|
+
const agentCode = typeof state['agentCode'] === 'string' ? state['agentCode'] : undefined;
|
|
1039
|
+
const skillDefs = state['skillDefinitions'];
|
|
1040
|
+
const execCtx = state['executionContext'];
|
|
1041
|
+
const userId = (typeof execCtx?.userId === 'string' && execCtx.userId) || state['userId'];
|
|
1042
|
+
const loadedTools = disableTools
|
|
1043
|
+
? loadedToolsBase
|
|
1044
|
+
: await hydrateMcpTools(loadedToolsBase, this.toolExecutor, {
|
|
1045
|
+
authContext: {
|
|
1046
|
+
userId: String(userId ?? ''),
|
|
1047
|
+
delegationToken: String(execCtx?.delegationToken ?? state['delegationToken'] ?? ''),
|
|
1048
|
+
agentCode: String(agentCode ?? ''),
|
|
1049
|
+
},
|
|
1050
|
+
correlationId: typeof state['correlationId'] === 'string' ? state['correlationId'] : undefined,
|
|
1051
|
+
taskId: typeof state['taskId'] === 'string' ? state['taskId'] : undefined,
|
|
1052
|
+
onListFailure: ({ toolset, error }) => {
|
|
1053
|
+
this.emitToolExecutionEvents({
|
|
1054
|
+
toolCallId: `mcp-list:${toolset}`,
|
|
1055
|
+
toolName: MCP_LIST_TOOL_NAME,
|
|
1056
|
+
toolset,
|
|
1057
|
+
status: 'error',
|
|
1058
|
+
error,
|
|
1059
|
+
});
|
|
1060
|
+
},
|
|
1083
1061
|
});
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1062
|
+
if (loadedTools && loadedTools !== loadedToolsBase) {
|
|
1063
|
+
state['loadedTools'] = loadedTools;
|
|
1064
|
+
}
|
|
1065
|
+
// [DEBUG] 打印 loadedTools 实际状态
|
|
1066
|
+
if (process.env.EAP_LLM_DEBUG === '1') {
|
|
1067
|
+
const toolNames = loadedTools ? Object.keys(loadedTools) : [];
|
|
1068
|
+
console.log(`[LLM-DEBUG] node=${node.id} loadedTools=${toolNames.length}:`, toolNames);
|
|
1069
|
+
}
|
|
1070
|
+
const catalog = includeSkillCatalog ? this.buildSkillCatalog(skillDefs) : undefined;
|
|
1071
|
+
const toolCatalog = disableTools ? undefined : buildToolCatalog(loadedTools);
|
|
1072
|
+
const jail = resolveJailFromState({
|
|
1073
|
+
agentDir: state['agentDir'],
|
|
1074
|
+
userId,
|
|
1075
|
+
executionContext: execCtx,
|
|
1076
|
+
loadedTools,
|
|
1077
|
+
skillDefinitions: skillDefs,
|
|
1078
|
+
});
|
|
1079
|
+
if (this.overlayExtraRoots.length > 0) {
|
|
1080
|
+
jail.extraRoots = [...(jail.extraRoots ?? []), ...this.overlayExtraRoots];
|
|
1081
|
+
}
|
|
1082
|
+
const workingDirectory = jail.workdir;
|
|
1083
|
+
const jailRoots = resolveJailRoots(jail);
|
|
1084
|
+
const skillDirs = Object.values(skillDefs ?? {})
|
|
1085
|
+
.filter((skill) => Boolean(skill.bundlePath))
|
|
1086
|
+
.map((skill) => ({
|
|
1087
|
+
skillId: skill.skillId,
|
|
1088
|
+
bundlePath: skill.bundlePath,
|
|
1089
|
+
entryFile: skill.entryFile,
|
|
1090
|
+
}));
|
|
1091
|
+
const promptInputs = resolveLlmPromptInputs({
|
|
1092
|
+
silent,
|
|
1093
|
+
agentSystemPrompt: state['agentSystemPrompt'],
|
|
1094
|
+
nodePrompt: resolvedPrompt,
|
|
1095
|
+
});
|
|
1096
|
+
const assembled = this.systemPromptBuilder.build({
|
|
1097
|
+
agentPrompt: promptInputs.agentPrompt,
|
|
1098
|
+
nodePrompt: promptInputs.nodePrompt,
|
|
1099
|
+
skillCatalog: catalog,
|
|
1100
|
+
toolCatalog,
|
|
1101
|
+
agentCode,
|
|
1102
|
+
userId,
|
|
1103
|
+
workingDirectory,
|
|
1104
|
+
os: { platform: os.platform(), release: os.release() },
|
|
1105
|
+
jailRoots,
|
|
1106
|
+
skillDirs,
|
|
1107
|
+
});
|
|
1108
|
+
const apiTools = disableTools ? [] : loadedToolsToApiTools(loadedTools);
|
|
1109
|
+
const finalConfig = {
|
|
1110
|
+
...baseConfig,
|
|
1111
|
+
systemPrompt: assembled.text,
|
|
1112
|
+
systemSections: assembled.sections,
|
|
1113
|
+
turn: {
|
|
1114
|
+
sessionId: typeof state['sessionId'] === 'string' ? state['sessionId'] : undefined,
|
|
1115
|
+
taskId: typeof state['taskId'] === 'string' ? state['taskId'] : undefined,
|
|
1116
|
+
nodeId: node.id,
|
|
1089
1117
|
agentCode,
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
// 流式旁路:有本次 stream 袋时启用 LLM token 推送;silent 节点不推给用户
|
|
1112
|
-
const stream = getExecutionStream();
|
|
1113
|
-
const enableStream = Boolean(stream) && !silent;
|
|
1114
|
-
if (stream) {
|
|
1115
|
-
stream.reasoningStartedThisTurn = false;
|
|
1116
|
-
stream.streamedTextThisTurn = false;
|
|
1117
|
-
}
|
|
1118
|
-
const llmNode = new LLMNode(node.id, finalConfig, this.llmProvider, enableStream, enableStream
|
|
1119
|
-
? (text) => {
|
|
1120
|
-
const live = getExecutionStream();
|
|
1121
|
-
if (!live || !text)
|
|
1122
|
-
return;
|
|
1123
|
-
const timestamp = new Date().toISOString();
|
|
1124
|
-
if (!live.reasoningStartedThisTurn) {
|
|
1125
|
-
live.reasoningStartedThisTurn = true;
|
|
1126
|
-
live.iteration += 1;
|
|
1127
|
-
live.sink({
|
|
1128
|
-
type: 'reasoning.started',
|
|
1129
|
-
taskId: undefined,
|
|
1130
|
-
correlationId: undefined,
|
|
1131
|
-
timestamp,
|
|
1132
|
-
payload: {
|
|
1133
|
-
iterationCount: live.iteration,
|
|
1134
|
-
model: finalConfig.model,
|
|
1135
|
-
},
|
|
1136
|
-
});
|
|
1137
|
-
}
|
|
1138
|
-
live.streamedTextThisTurn = true;
|
|
1118
|
+
correlationId: typeof state['correlationId'] === 'string' ? state['correlationId'] : undefined,
|
|
1119
|
+
},
|
|
1120
|
+
tools: disableTools ? undefined : (apiTools.length > 0 ? apiTools : baseConfig.tools),
|
|
1121
|
+
toolChoice: disableTools ? 'none' : baseConfig.toolChoice,
|
|
1122
|
+
};
|
|
1123
|
+
// 流式旁路:有本次 stream 袋时启用 LLM token 推送;silent 节点不推给用户
|
|
1124
|
+
const stream = getExecutionStream();
|
|
1125
|
+
const enableStream = Boolean(stream) && !silent;
|
|
1126
|
+
if (stream) {
|
|
1127
|
+
stream.reasoningStartedThisTurn = false;
|
|
1128
|
+
stream.streamedTextThisTurn = false;
|
|
1129
|
+
}
|
|
1130
|
+
const llmNode = new LLMNode(node.id, finalConfig, this.llmProvider, enableStream, enableStream
|
|
1131
|
+
? (text) => {
|
|
1132
|
+
const live = getExecutionStream();
|
|
1133
|
+
if (!live || !text)
|
|
1134
|
+
return;
|
|
1135
|
+
const timestamp = new Date().toISOString();
|
|
1136
|
+
if (!live.reasoningStartedThisTurn) {
|
|
1137
|
+
live.reasoningStartedThisTurn = true;
|
|
1138
|
+
live.iteration += 1;
|
|
1139
1139
|
live.sink({
|
|
1140
|
-
type: '
|
|
1140
|
+
type: 'reasoning.started',
|
|
1141
1141
|
taskId: undefined,
|
|
1142
1142
|
correlationId: undefined,
|
|
1143
1143
|
timestamp,
|
|
1144
1144
|
payload: {
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
node: node.id,
|
|
1145
|
+
iterationCount: live.iteration,
|
|
1146
|
+
model: finalConfig.model,
|
|
1148
1147
|
},
|
|
1149
1148
|
});
|
|
1150
1149
|
}
|
|
1151
|
-
|
|
1150
|
+
live.streamedTextThisTurn = true;
|
|
1151
|
+
live.sink({
|
|
1152
|
+
type: 'response.chunk',
|
|
1153
|
+
taskId: undefined,
|
|
1154
|
+
correlationId: undefined,
|
|
1155
|
+
timestamp,
|
|
1156
|
+
payload: {
|
|
1157
|
+
content: text,
|
|
1158
|
+
index: live.chunkIndex++,
|
|
1159
|
+
node: node.id,
|
|
1160
|
+
},
|
|
1161
|
+
});
|
|
1162
|
+
}
|
|
1163
|
+
: undefined, this.interceptors);
|
|
1164
|
+
const toolsForSpan = disableTools || !loadedTools
|
|
1165
|
+
? undefined
|
|
1166
|
+
: Object.values(loadedTools).map((t) => ({
|
|
1167
|
+
name: t.name,
|
|
1168
|
+
...(t.manifest.description?.trim()
|
|
1169
|
+
? { description: t.manifest.description.trim() }
|
|
1170
|
+
: {}),
|
|
1171
|
+
}));
|
|
1172
|
+
// 组装后再 createNode:input 必须是发给模型的 system/tools/messages;completeNode 不能补 input。
|
|
1173
|
+
return withGraphSpan(state, {
|
|
1174
|
+
name: node.id,
|
|
1175
|
+
type: 'llm',
|
|
1176
|
+
extra: {
|
|
1177
|
+
input: llmSpanInput({
|
|
1178
|
+
system: assembled.text,
|
|
1179
|
+
tools: toolsForSpan,
|
|
1180
|
+
messages: state['messages'],
|
|
1181
|
+
}),
|
|
1182
|
+
},
|
|
1183
|
+
}, async () => {
|
|
1152
1184
|
const next = await llmNode.execute(state);
|
|
1153
1185
|
const hydratedUpdate = loadedTools && loadedTools !== loadedToolsBase ? { loadedTools } : {};
|
|
1154
1186
|
if (!silent)
|
|
@@ -1163,7 +1195,7 @@ export class LangGraphAdapter {
|
|
|
1163
1195
|
silentOutput: raw,
|
|
1164
1196
|
...hydratedUpdate,
|
|
1165
1197
|
};
|
|
1166
|
-
});
|
|
1198
|
+
}, { outputFrom: llmSpanOutputFromResult });
|
|
1167
1199
|
};
|
|
1168
1200
|
}
|
|
1169
1201
|
/**
|
|
@@ -289,6 +289,12 @@ export class HitlHandler {
|
|
|
289
289
|
name: extras.toolName ?? 'hitl',
|
|
290
290
|
type: 'hitl',
|
|
291
291
|
parentSpanId: extras.toolSpanId ?? skillSpanId,
|
|
292
|
+
extra: {
|
|
293
|
+
input: {
|
|
294
|
+
question: config.question,
|
|
295
|
+
options: config.options,
|
|
296
|
+
},
|
|
297
|
+
},
|
|
292
298
|
});
|
|
293
299
|
const persisted = {
|
|
294
300
|
taskId,
|
|
@@ -12,4 +12,18 @@ export declare function completeGraphSpan(state: Record<string, unknown>, spanId
|
|
|
12
12
|
export declare function withGraphSpan(state: Record<string, unknown>, spec: {
|
|
13
13
|
name: string;
|
|
14
14
|
type: ExecutionNodeEvent['type'];
|
|
15
|
-
|
|
15
|
+
extra?: Partial<Omit<ExecutionNodeEvent, 'traceId' | 'name' | 'type' | 'parentNodeId'>>;
|
|
16
|
+
}, run: () => Promise<Record<string, unknown>>, complete?: {
|
|
17
|
+
outputFrom?: (result: Record<string, unknown>) => unknown;
|
|
18
|
+
}): Promise<Record<string, unknown>>;
|
|
19
|
+
/** llm Span create 时的 input:组装后的 system、工具短目录、本回合 messages。 */
|
|
20
|
+
export declare function llmSpanInput(input: {
|
|
21
|
+
system?: string;
|
|
22
|
+
tools?: Array<{
|
|
23
|
+
name: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
}>;
|
|
26
|
+
messages: unknown;
|
|
27
|
+
}): Record<string, unknown>;
|
|
28
|
+
/** llm Span complete 的 output:本回合新增 assistant(含 toolCalls)或 silentOutput。 */
|
|
29
|
+
export declare function llmSpanOutputFromResult(result: Record<string, unknown>): unknown;
|
package/dist/graph/trace-span.js
CHANGED
|
@@ -43,15 +43,17 @@ export async function completeGraphSpan(state, spanId, params, collectorOverride
|
|
|
43
43
|
warnTraceSpanFailure('completeNode', { spanId, traceId: resolveExecutionTrace(state).traceId }, err);
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
|
-
export async function withGraphSpan(state, spec, run) {
|
|
46
|
+
export async function withGraphSpan(state, spec, run, complete) {
|
|
47
47
|
const spanId = await startGraphSpan(state, spec);
|
|
48
48
|
try {
|
|
49
49
|
const result = await run();
|
|
50
50
|
const usage = Array.isArray(result['__llmTokenUsage'])
|
|
51
51
|
? result['__llmTokenUsage'].at(-1)
|
|
52
52
|
: undefined;
|
|
53
|
+
const output = complete?.outputFrom?.(result);
|
|
53
54
|
await completeGraphSpan(state, spanId, {
|
|
54
55
|
status: 'completed',
|
|
56
|
+
...(output !== undefined ? { output } : {}),
|
|
55
57
|
inputTokens: usage?.inputTokens,
|
|
56
58
|
outputTokens: usage?.outputTokens,
|
|
57
59
|
totalTokens: usage?.inputTokens != null || usage?.outputTokens != null
|
|
@@ -69,3 +71,69 @@ export async function withGraphSpan(state, spec, run) {
|
|
|
69
71
|
throw err;
|
|
70
72
|
}
|
|
71
73
|
}
|
|
74
|
+
function spanGraphMessages(raw) {
|
|
75
|
+
if (!Array.isArray(raw))
|
|
76
|
+
return [];
|
|
77
|
+
return raw.map((msg) => {
|
|
78
|
+
if (typeof msg !== 'object' || msg === null) {
|
|
79
|
+
return { role: 'user', content: String(msg) };
|
|
80
|
+
}
|
|
81
|
+
const m = msg;
|
|
82
|
+
const out = {
|
|
83
|
+
role: typeof m['role'] === 'string' ? m['role'] : 'user',
|
|
84
|
+
content: typeof m['content'] === 'string' ? m['content'] : '',
|
|
85
|
+
};
|
|
86
|
+
const toolCalls = m['toolCalls'] ?? m['tool_calls'];
|
|
87
|
+
if (toolCalls !== undefined)
|
|
88
|
+
out.toolCalls = toolCalls;
|
|
89
|
+
const toolCallId = typeof m['toolCallId'] === 'string'
|
|
90
|
+
? m['toolCallId']
|
|
91
|
+
: typeof m['tool_call_id'] === 'string'
|
|
92
|
+
? m['tool_call_id']
|
|
93
|
+
: undefined;
|
|
94
|
+
if (toolCallId)
|
|
95
|
+
out.toolCallId = toolCallId;
|
|
96
|
+
if (typeof m['toolName'] === 'string')
|
|
97
|
+
out.toolName = m['toolName'];
|
|
98
|
+
if (typeof m['success'] === 'boolean')
|
|
99
|
+
out.success = m['success'];
|
|
100
|
+
const reasoning = typeof m['reasoningContent'] === 'string'
|
|
101
|
+
? m['reasoningContent']
|
|
102
|
+
: typeof m['reasoning_content'] === 'string'
|
|
103
|
+
? m['reasoning_content']
|
|
104
|
+
: undefined;
|
|
105
|
+
if (reasoning)
|
|
106
|
+
out.reasoningContent = reasoning;
|
|
107
|
+
return out;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
/** llm Span create 时的 input:组装后的 system、工具短目录、本回合 messages。 */
|
|
111
|
+
export function llmSpanInput(input) {
|
|
112
|
+
const tools = input.tools?.filter((t) => t.name);
|
|
113
|
+
return {
|
|
114
|
+
...(input.system ? { system: input.system } : {}),
|
|
115
|
+
...(tools && tools.length > 0 ? { tools } : {}),
|
|
116
|
+
messages: spanGraphMessages(input.messages),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/** llm Span complete 的 output:本回合新增 assistant(含 toolCalls)或 silentOutput。 */
|
|
120
|
+
export function llmSpanOutputFromResult(result) {
|
|
121
|
+
if (typeof result['silentOutput'] === 'string') {
|
|
122
|
+
return { content: result['silentOutput'] };
|
|
123
|
+
}
|
|
124
|
+
const messages = Array.isArray(result['messages']) ? result['messages'] : [];
|
|
125
|
+
const added = messages[0];
|
|
126
|
+
if (!added || typeof added !== 'object')
|
|
127
|
+
return undefined;
|
|
128
|
+
const m = added;
|
|
129
|
+
const out = {
|
|
130
|
+
content: typeof m['content'] === 'string' ? m['content'] : '',
|
|
131
|
+
};
|
|
132
|
+
const toolCalls = m['toolCalls'] ?? m['tool_calls'];
|
|
133
|
+
if (Array.isArray(toolCalls) && toolCalls.length > 0)
|
|
134
|
+
out.toolCalls = toolCalls;
|
|
135
|
+
if (typeof m['reasoningContent'] === 'string' && m['reasoningContent']) {
|
|
136
|
+
out.reasoningContent = m['reasoningContent'];
|
|
137
|
+
}
|
|
138
|
+
return out;
|
|
139
|
+
}
|