mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-05-15 22:30:38 +08:00
fix: restore luker llm priority order
This commit is contained in:
17
llm/llm.js
17
llm/llm.js
@@ -1965,17 +1965,7 @@ async function callDedicatedOpenAICompatible(
|
|||||||
profileName: config.requestedLlmPresetName || "",
|
profileName: config.requestedLlmPresetName || "",
|
||||||
});
|
});
|
||||||
const settings = extension_settings[MODULE_NAME] || {};
|
const settings = extension_settings[MODULE_NAME] || {};
|
||||||
const shouldPreferLukerHostRoute =
|
const hasDedicatedConfig = hasDedicatedLLMConfig(config);
|
||||||
hostRouting.hostProfile === "luker" &&
|
|
||||||
(
|
|
||||||
config.llmConfigSource === "global" ||
|
|
||||||
(
|
|
||||||
String(config.llmConfigSource || "").startsWith("global-fallback-") &&
|
|
||||||
hostRouting.routeApplied === true
|
|
||||||
)
|
|
||||||
);
|
|
||||||
const hasDedicatedConfig =
|
|
||||||
hasDedicatedLLMConfig(config) && !shouldPreferLukerHostRoute;
|
|
||||||
if (taskType && config.llmPresetFallbackReason) {
|
if (taskType && config.llmPresetFallbackReason) {
|
||||||
debugWarn(
|
debugWarn(
|
||||||
`[ST-BME] 任务 ${taskType} 指定的 API 模板不可用,已回退当前 API: ` +
|
`[ST-BME] 任务 ${taskType} 指定的 API 模板不可用,已回退当前 API: ` +
|
||||||
@@ -2052,7 +2042,10 @@ async function callDedicatedOpenAICompatible(
|
|||||||
hostRequestApi: hostRouting.requestApi,
|
hostRequestApi: hostRouting.requestApi,
|
||||||
hostRouteApplied: hostRouting.routeApplied,
|
hostRouteApplied: hostRouting.routeApplied,
|
||||||
hostRouteReason: hostRouting.routeReason,
|
hostRouteReason: hostRouting.routeReason,
|
||||||
preferHostRoute: shouldPreferLukerHostRoute,
|
preferHostRoute:
|
||||||
|
!hasDedicatedConfig &&
|
||||||
|
hostRouting.hostProfile === "luker" &&
|
||||||
|
hostRouting.routeApplied === true,
|
||||||
apiSettingsOverride: hostRouting.apiSettingsOverride,
|
apiSettingsOverride: hostRouting.apiSettingsOverride,
|
||||||
maxCompletionTokens,
|
maxCompletionTokens,
|
||||||
...buildStreamDebugSnapshot(streamState),
|
...buildStreamDebugSnapshot(streamState),
|
||||||
|
|||||||
@@ -1152,7 +1152,7 @@ export async function extractMemories({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 调用 LLM
|
// 调用 LLM
|
||||||
const result = await callLLMForJSON({
|
const llmResult = await callLLMForJSON({
|
||||||
systemPrompt: llmSystemPrompt,
|
systemPrompt: llmSystemPrompt,
|
||||||
userPrompt: promptPayload.userPrompt,
|
userPrompt: promptPayload.userPrompt,
|
||||||
maxRetries: 2,
|
maxRetries: 2,
|
||||||
@@ -1166,8 +1166,18 @@ export async function extractMemories({
|
|||||||
promptMessages: promptPayload.promptMessages,
|
promptMessages: promptPayload.promptMessages,
|
||||||
additionalMessages: promptPayloadAdditionalMessages,
|
additionalMessages: promptPayloadAdditionalMessages,
|
||||||
onStreamProgress,
|
onStreamProgress,
|
||||||
|
returnFailureDetails: true,
|
||||||
});
|
});
|
||||||
throwIfAborted(signal);
|
throwIfAborted(signal);
|
||||||
|
const llmFailure =
|
||||||
|
llmResult && typeof llmResult === "object" && "ok" in llmResult
|
||||||
|
? llmResult
|
||||||
|
: null;
|
||||||
|
const result = llmFailure
|
||||||
|
? llmFailure.ok
|
||||||
|
? llmFailure.data
|
||||||
|
: null
|
||||||
|
: llmResult;
|
||||||
const normalizedResult = normalizeExtractionResultPayload(result, schema);
|
const normalizedResult = normalizeExtractionResultPayload(result, schema);
|
||||||
const ownershipWarnings = [];
|
const ownershipWarnings = [];
|
||||||
const extractionOwnerContext = deriveExtractionOwnerContext(
|
const extractionOwnerContext = deriveExtractionOwnerContext(
|
||||||
@@ -1199,11 +1209,23 @@ export async function extractMemories({
|
|||||||
`[ST-BME] 提取 LLM 未返回有效操作 ` +
|
`[ST-BME] 提取 LLM 未返回有效操作 ` +
|
||||||
`[type=${diagType}]` +
|
`[type=${diagType}]` +
|
||||||
(diagKeys ? ` [keys=${diagKeys}]` : "") +
|
(diagKeys ? ` [keys=${diagKeys}]` : "") +
|
||||||
(diagPreview ? ` [preview=${diagPreview}]` : ""),
|
(diagPreview ? ` [preview=${diagPreview}]` : "") +
|
||||||
|
(llmFailure?.ok === false && llmFailure?.errorType
|
||||||
|
? ` [failureType=${String(llmFailure.errorType)}]`
|
||||||
|
: "") +
|
||||||
|
(llmFailure?.ok === false && llmFailure?.failureReason
|
||||||
|
? ` [failureReason=${String(llmFailure.failureReason).slice(0, 200)}]`
|
||||||
|
: ""),
|
||||||
);
|
);
|
||||||
|
const failureReason =
|
||||||
|
llmFailure?.ok === false
|
||||||
|
? String(llmFailure.failureReason || "").trim()
|
||||||
|
: "";
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: "提取 LLM 未返回有效操作",
|
error: failureReason
|
||||||
|
? `提取 LLM 未返回有效操作: ${failureReason}`
|
||||||
|
: "提取 LLM 未返回有效操作",
|
||||||
newNodes: 0,
|
newNodes: 0,
|
||||||
updatedNodes: 0,
|
updatedNodes: 0,
|
||||||
newEdges: 0,
|
newEdges: 0,
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ const originalRequire = globalThis.require;
|
|||||||
const originalExtensionSettings = globalThis.__lukerLlmRoutingExtensionSettings;
|
const originalExtensionSettings = globalThis.__lukerLlmRoutingExtensionSettings;
|
||||||
const originalSendOpenAIRequest = globalThis.__lukerLlmRoutingSendOpenAIRequest;
|
const originalSendOpenAIRequest = globalThis.__lukerLlmRoutingSendOpenAIRequest;
|
||||||
const originalLuker = globalThis.Luker;
|
const originalLuker = globalThis.Luker;
|
||||||
|
const originalFetch = globalThis.fetch;
|
||||||
|
|
||||||
globalThis.__lukerLlmRoutingExtensionSettings = {
|
globalThis.__lukerLlmRoutingExtensionSettings = {
|
||||||
st_bme: {},
|
st_bme: {},
|
||||||
@@ -80,6 +81,8 @@ if (originalExtensionSettings === undefined) {
|
|||||||
|
|
||||||
let capturedOptions = null;
|
let capturedOptions = null;
|
||||||
let capturedMessages = null;
|
let capturedMessages = null;
|
||||||
|
let sendOpenAIRequestCalls = 0;
|
||||||
|
let capturedFetchBody = null;
|
||||||
|
|
||||||
globalThis.Luker = {
|
globalThis.Luker = {
|
||||||
getContext() {
|
getContext() {
|
||||||
@@ -112,6 +115,7 @@ globalThis.__lukerLlmRoutingSendOpenAIRequest = async (
|
|||||||
signal,
|
signal,
|
||||||
options = {},
|
options = {},
|
||||||
) => {
|
) => {
|
||||||
|
sendOpenAIRequestCalls += 1;
|
||||||
capturedOptions = { ...(options || {}) };
|
capturedOptions = { ...(options || {}) };
|
||||||
capturedMessages = Array.isArray(messages) ? [...messages] : messages;
|
capturedMessages = Array.isArray(messages) ? [...messages] : messages;
|
||||||
return {
|
return {
|
||||||
@@ -125,6 +129,28 @@ globalThis.__lukerLlmRoutingSendOpenAIRequest = async (
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
globalThis.fetch = async (_url, options = {}) => {
|
||||||
|
capturedFetchBody = JSON.parse(String(options.body || "{}"));
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
message: {
|
||||||
|
content: '{"operations":[]}',
|
||||||
|
},
|
||||||
|
finish_reason: "stop",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 200,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
extensionsApi.extension_settings.st_bme = {
|
extensionsApi.extension_settings.st_bme = {
|
||||||
taskProfiles: createDefaultTaskProfiles(),
|
taskProfiles: createDefaultTaskProfiles(),
|
||||||
};
|
};
|
||||||
@@ -151,6 +177,8 @@ try {
|
|||||||
|
|
||||||
capturedOptions = null;
|
capturedOptions = null;
|
||||||
capturedMessages = null;
|
capturedMessages = null;
|
||||||
|
capturedFetchBody = null;
|
||||||
|
sendOpenAIRequestCalls = 0;
|
||||||
extensionsApi.extension_settings.st_bme = {
|
extensionsApi.extension_settings.st_bme = {
|
||||||
llmApiUrl: "https://stale-generic-config.invalid/v1",
|
llmApiUrl: "https://stale-generic-config.invalid/v1",
|
||||||
llmApiKey: "sk-stale-generic",
|
llmApiKey: "sk-stale-generic",
|
||||||
@@ -167,15 +195,20 @@ try {
|
|||||||
});
|
});
|
||||||
|
|
||||||
assert.deepEqual(routedResult, { operations: [] });
|
assert.deepEqual(routedResult, { operations: [] });
|
||||||
assert.deepEqual(capturedOptions?.apiSettingsOverride, {
|
assert.equal(
|
||||||
chat_completion_source: "openai",
|
sendOpenAIRequestCalls,
|
||||||
reverse_proxy: "https://example-luker-route.test/v1",
|
0,
|
||||||
proxy_password: "sk-luker-route",
|
"存在可用的 BME 全局专用 LLM 配置时,不应退回宿主当前聊天路由",
|
||||||
secret_id: "luker-secret-1",
|
);
|
||||||
});
|
assert.equal(
|
||||||
|
capturedFetchBody?.custom_url,
|
||||||
|
"https://stale-generic-config.invalid/v1",
|
||||||
|
);
|
||||||
|
|
||||||
capturedOptions = null;
|
capturedOptions = null;
|
||||||
capturedMessages = null;
|
capturedMessages = null;
|
||||||
|
capturedFetchBody = null;
|
||||||
|
sendOpenAIRequestCalls = 0;
|
||||||
const taskProfiles = createDefaultTaskProfiles();
|
const taskProfiles = createDefaultTaskProfiles();
|
||||||
taskProfiles.extract.profiles[0].generation.llm_preset = "luker-profile-alpha";
|
taskProfiles.extract.profiles[0].generation.llm_preset = "luker-profile-alpha";
|
||||||
extensionsApi.extension_settings.st_bme = {
|
extensionsApi.extension_settings.st_bme = {
|
||||||
@@ -218,11 +251,15 @@ try {
|
|||||||
});
|
});
|
||||||
|
|
||||||
assert.deepEqual(profileRoutedResult, { operations: [] });
|
assert.deepEqual(profileRoutedResult, { operations: [] });
|
||||||
assert.deepEqual(capturedOptions?.apiSettingsOverride, {
|
assert.equal(
|
||||||
chat_completion_source: "openai",
|
sendOpenAIRequestCalls,
|
||||||
reverse_proxy: "https://example-luker-profile.test/v1",
|
0,
|
||||||
proxy_password: "sk-luker-profile",
|
"存在可用的 BME 全局专用 LLM 配置时,不应因为 Luker profile 名而劫持到当前聊天路由",
|
||||||
});
|
);
|
||||||
|
assert.equal(
|
||||||
|
capturedFetchBody?.custom_url,
|
||||||
|
"https://stale-generic-config.invalid/v1",
|
||||||
|
);
|
||||||
} finally {
|
} finally {
|
||||||
if (originalSendOpenAIRequest === undefined) {
|
if (originalSendOpenAIRequest === undefined) {
|
||||||
delete globalThis.__lukerLlmRoutingSendOpenAIRequest;
|
delete globalThis.__lukerLlmRoutingSendOpenAIRequest;
|
||||||
@@ -230,6 +267,12 @@ try {
|
|||||||
globalThis.__lukerLlmRoutingSendOpenAIRequest = originalSendOpenAIRequest;
|
globalThis.__lukerLlmRoutingSendOpenAIRequest = originalSendOpenAIRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (originalFetch === undefined) {
|
||||||
|
delete globalThis.fetch;
|
||||||
|
} else {
|
||||||
|
globalThis.fetch = originalFetch;
|
||||||
|
}
|
||||||
|
|
||||||
if (originalLuker === undefined) {
|
if (originalLuker === undefined) {
|
||||||
delete globalThis.Luker;
|
delete globalThis.Luker;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -2565,6 +2565,38 @@ async function testExtractorNormalizesArrayPayloadAndPreservesScopeField() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function testExtractorPropagatesLlmFailureReason() {
|
||||||
|
const graph = createEmptyGraph();
|
||||||
|
const restoreOverrides = pushTestOverrides({
|
||||||
|
llm: {
|
||||||
|
async callLLMForJSON() {
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
errorType: "provider-error",
|
||||||
|
failureReason: "Invalid character name",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await extractMemories({
|
||||||
|
graph,
|
||||||
|
messages: [{ seq: 9, role: "assistant", content: "测试 LLM 失败原因" }],
|
||||||
|
startSeq: 9,
|
||||||
|
endSeq: 9,
|
||||||
|
schema,
|
||||||
|
embeddingConfig: null,
|
||||||
|
settings: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(result.success, false);
|
||||||
|
assert.match(result.error, /Invalid character name/);
|
||||||
|
} finally {
|
||||||
|
restoreOverrides();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function testConsolidatorMergeUpdatesSeqRange() {
|
async function testConsolidatorMergeUpdatesSeqRange() {
|
||||||
const graph = createEmptyGraph();
|
const graph = createEmptyGraph();
|
||||||
const target = createNode({
|
const target = createNode({
|
||||||
@@ -7005,6 +7037,7 @@ await testCompressTypeAcceptsTopLevelFieldsResult();
|
|||||||
await testExtractorFailsOnUnknownOperation();
|
await testExtractorFailsOnUnknownOperation();
|
||||||
await testExtractorNormalizesFlatCreateOperation();
|
await testExtractorNormalizesFlatCreateOperation();
|
||||||
await testExtractorNormalizesArrayPayloadAndPreservesScopeField();
|
await testExtractorNormalizesArrayPayloadAndPreservesScopeField();
|
||||||
|
await testExtractorPropagatesLlmFailureReason();
|
||||||
await testConsolidatorMergeUpdatesSeqRange();
|
await testConsolidatorMergeUpdatesSeqRange();
|
||||||
await testConsolidatorMergeFallbackKeepsNodeWhenTargetMissing();
|
await testConsolidatorMergeFallbackKeepsNodeWhenTargetMissing();
|
||||||
await testBatchJournalVectorDeltaCapturesRecoveryFields();
|
await testBatchJournalVectorDeltaCapturesRecoveryFields();
|
||||||
|
|||||||
Reference in New Issue
Block a user