@mcp-abap-adt/llm-agent-server-libs 30.0.0 → 30.1.0
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/generated/version.d.ts +1 -1
- package/dist/generated/version.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/smart-agent/config-reload-watcher.d.ts +7 -0
- package/dist/smart-agent/config-reload-watcher.d.ts.map +1 -1
- package/dist/smart-agent/config-reload-watcher.js +23 -6
- package/dist/smart-agent/config-reload-watcher.js.map +1 -1
- package/dist/smart-agent/config-validator.d.ts.map +1 -1
- package/dist/smart-agent/config-validator.js +170 -5
- package/dist/smart-agent/config-validator.js.map +1 -1
- package/dist/smart-agent/config.d.ts.map +1 -1
- package/dist/smart-agent/config.js +9 -1
- package/dist/smart-agent/config.js.map +1 -1
- package/dist/smart-agent/decision-config.d.ts +32 -0
- package/dist/smart-agent/decision-config.d.ts.map +1 -0
- package/dist/smart-agent/decision-config.js +18 -0
- package/dist/smart-agent/decision-config.js.map +1 -0
- package/dist/smart-agent/http/adapter-route-handler.d.ts +1 -1
- package/dist/smart-agent/http/adapter-route-handler.d.ts.map +1 -1
- package/dist/smart-agent/http/adapter-route-handler.js +34 -3
- package/dist/smart-agent/http/adapter-route-handler.js.map +1 -1
- package/dist/smart-agent/http/chat-route-handler.d.ts.map +1 -1
- package/dist/smart-agent/http/chat-route-handler.js +35 -4
- package/dist/smart-agent/http/chat-route-handler.js.map +1 -1
- package/dist/smart-agent/http/config-route-handler.d.ts +6 -3
- package/dist/smart-agent/http/config-route-handler.d.ts.map +1 -1
- package/dist/smart-agent/http/config-route-handler.js +7 -5
- package/dist/smart-agent/http/config-route-handler.js.map +1 -1
- package/dist/smart-agent/http/request-abort.d.ts +11 -0
- package/dist/smart-agent/http/request-abort.d.ts.map +1 -0
- package/dist/smart-agent/http/request-abort.js +19 -0
- package/dist/smart-agent/http/request-abort.js.map +1 -0
- package/dist/smart-agent/llm/llm-circuit-breakers.d.ts +21 -0
- package/dist/smart-agent/llm/llm-circuit-breakers.d.ts.map +1 -0
- package/dist/smart-agent/llm/llm-circuit-breakers.js +43 -0
- package/dist/smart-agent/llm/llm-circuit-breakers.js.map +1 -0
- package/dist/smart-agent/llm/role-llm-resolver.d.ts +3 -0
- package/dist/smart-agent/llm/role-llm-resolver.d.ts.map +1 -1
- package/dist/smart-agent/llm/role-llm-resolver.js +4 -1
- package/dist/smart-agent/llm/role-llm-resolver.js.map +1 -1
- package/dist/smart-agent/rag-config.d.ts +3 -0
- package/dist/smart-agent/rag-config.d.ts.map +1 -1
- package/dist/smart-agent/rag-config.js.map +1 -1
- package/dist/smart-agent/resolve-agent-embedder.d.ts +6 -1
- package/dist/smart-agent/resolve-agent-embedder.d.ts.map +1 -1
- package/dist/smart-agent/resolve-agent-embedder.js +8 -3
- package/dist/smart-agent/resolve-agent-embedder.js.map +1 -1
- package/dist/smart-agent/resolve-config-sections.d.ts +7 -0
- package/dist/smart-agent/resolve-config-sections.d.ts.map +1 -1
- package/dist/smart-agent/resolve-config-sections.js +58 -0
- package/dist/smart-agent/resolve-config-sections.js.map +1 -1
- package/dist/smart-agent/resolve-reranker.d.ts +10 -0
- package/dist/smart-agent/resolve-reranker.d.ts.map +1 -0
- package/dist/smart-agent/resolve-reranker.js +8 -0
- package/dist/smart-agent/resolve-reranker.js.map +1 -0
- package/dist/smart-agent/resolve-retrieval.d.ts +26 -0
- package/dist/smart-agent/resolve-retrieval.d.ts.map +1 -0
- package/dist/smart-agent/resolve-retrieval.js +96 -0
- package/dist/smart-agent/resolve-retrieval.js.map +1 -0
- package/dist/smart-agent/smart-server.d.ts +70 -6
- package/dist/smart-agent/smart-server.d.ts.map +1 -1
- package/dist/smart-agent/smart-server.js +221 -77
- package/dist/smart-agent/smart-server.js.map +1 -1
- package/dist/smart-agent/tools-rag-handle.d.ts.map +1 -1
- package/dist/smart-agent/tools-rag-handle.js +4 -2
- package/dist/smart-agent/tools-rag-handle.js.map +1 -1
- package/package.json +7 -7
|
@@ -6,8 +6,8 @@ import http from 'node:http';
|
|
|
6
6
|
import { createRequire } from 'node:module';
|
|
7
7
|
import { resolve as pathResolve } from 'node:path';
|
|
8
8
|
import { pathToFileURL } from 'node:url';
|
|
9
|
-
import { asymmetricEmbedder, buildNamespacedTools, defaultToolNamespace, isReadinessReporter, SimpleRagProviderRegistry, symmetricEmbedder, } from '@mcp-abap-adt/llm-agent';
|
|
10
|
-
import { ClaudeSkillManager, CodexSkillManager, FileSystemPluginLoader, FileSystemSkillManager, getDefaultPluginDirs, HealthChecker, InMemoryKnowledgeBackend, KnowledgeRag, mergePluginExports, SessionRequestLogger, SmartAgentBuilder, SmartAgentSubAgent, WindowContextStrategy, } from '@mcp-abap-adt/llm-agent-libs';
|
|
9
|
+
import { asymmetricEmbedder, buildNamespacedTools, CircuitBreaker, defaultToolNamespace, isReadinessReporter, SimpleRagProviderRegistry, symmetricEmbedder, withCircuitBreaker, } from '@mcp-abap-adt/llm-agent';
|
|
10
|
+
import { applyRetrievalStrategy, ClaudeSkillManager, CodexSkillManager, FileSystemPluginLoader, FileSystemSkillManager, getDefaultPluginDirs, HealthChecker, InMemoryKnowledgeBackend, KnowledgeRag, mergePluginExports, SessionRequestLogger, SmartAgentBuilder, SmartAgentSubAgent, WindowContextStrategy, } from '@mcp-abap-adt/llm-agent-libs';
|
|
11
11
|
import { DefaultMcpFailureClassifier, MCPClientWrapper, McpClientAdapter, } from '@mcp-abap-adt/llm-agent-mcp';
|
|
12
12
|
import { prefetchEmbedderFactories } from '@mcp-abap-adt/llm-agent-rag';
|
|
13
13
|
import { PACKAGE_VERSION } from '../generated/version.js';
|
|
@@ -21,9 +21,12 @@ import { CORS_HEADERS, jsonError, writeNotReady, } from './http/response-helpers
|
|
|
21
21
|
import { HttpRouteTable } from './http/route-table.js';
|
|
22
22
|
import { handleSessionDelete, handleSessionResume, handleSessionsList, } from './http/sessions-route-handler.js';
|
|
23
23
|
import { handleUsageRoute } from './http/usage-route-handler.js';
|
|
24
|
+
import { LlmCircuitBreakers } from './llm/llm-circuit-breakers.js';
|
|
24
25
|
import { RoleLlmResolver, } from './llm/role-llm-resolver.js';
|
|
25
26
|
import { assertRagConfigShape, embedderSectionFor, toMakeRagInput, } from './rag-config.js';
|
|
26
27
|
import { resolveRetrievalEmbedder } from './resolve-agent-embedder.js';
|
|
28
|
+
import { resolveReranker } from './resolve-reranker.js';
|
|
29
|
+
import { resolveRetrievalStrategies, unknownRetrievalKeyWarnings, } from './resolve-retrieval.js';
|
|
27
30
|
import { makeToolsRagHandle } from './tools-rag-handle.js';
|
|
28
31
|
import { assertWorkerLlmConfig, parseWorkerLlm } from './worker-llm.js';
|
|
29
32
|
export { writeNotReady } from './http/response-helpers.js';
|
|
@@ -236,6 +239,39 @@ export class SmartServer {
|
|
|
236
239
|
_mainLlm;
|
|
237
240
|
_classifierLlm;
|
|
238
241
|
_helperLlm;
|
|
242
|
+
/**
|
|
243
|
+
* `circuitBreaker:` — one LLM breaker per `llm:` key, wrapped where each LLM
|
|
244
|
+
* is created (held main/classifier/helper and every resolver entry), so every
|
|
245
|
+
* session and role — controller and stepper included — shares it (§14.2).
|
|
246
|
+
*/
|
|
247
|
+
_llmBreakers;
|
|
248
|
+
/** `circuitBreaker:` — the one embedder breaker, fed by the retrieval embedder. */
|
|
249
|
+
_embedderBreaker;
|
|
250
|
+
/** The one reranker of this server, resolved once in `_buildInfra()` (§7.4). */
|
|
251
|
+
_reranker;
|
|
252
|
+
/**
|
|
253
|
+
* `rag.retrieval` resolved once in `_buildInfra` — one strategy per listed
|
|
254
|
+
* store key, server-wide (§13.4). Applied at creation of the server-built
|
|
255
|
+
* stores (`tools` / `history`, workers' too) and, through
|
|
256
|
+
* `withRetrievalStrategy`, in every builder's `ragStores` projection.
|
|
257
|
+
*/
|
|
258
|
+
_retrievalStrategies = new Map();
|
|
259
|
+
/** `agent.toolSelection`, resolved once; applied to every builder (§13.4). */
|
|
260
|
+
_toolSelectionStrategy;
|
|
261
|
+
/** Plugin output validator, resolved once; applied to every builder (§14.1). */
|
|
262
|
+
_outputValidator;
|
|
263
|
+
/** Plugin query expander, resolved once; applied to every builder (§14.1). */
|
|
264
|
+
_queryExpander;
|
|
265
|
+
/** DI > plugin > YAML `skills:`, resolved once; vectorized at startup only. */
|
|
266
|
+
_skillManager;
|
|
267
|
+
/**
|
|
268
|
+
* `agent.llmCallStrategy`, resolved once as a FACTORY: a strategy may be
|
|
269
|
+
* stateful (`fallback` disables streaming stickily), so each builder gets its
|
|
270
|
+
* own instance (§14.1).
|
|
271
|
+
*/
|
|
272
|
+
_llmCallStrategyFactory;
|
|
273
|
+
/** DI > plugin > default `ClineClientAdapter`, resolved once (§14.1). */
|
|
274
|
+
_clientAdapters = [];
|
|
239
275
|
_fileLogger;
|
|
240
276
|
_mergedEmbedderFactories;
|
|
241
277
|
/**
|
|
@@ -276,6 +312,12 @@ export class SmartServer {
|
|
|
276
312
|
* (the stepper catalog handle), which falls back to catalog order regardless.
|
|
277
313
|
*/
|
|
278
314
|
_toolsRag;
|
|
315
|
+
/**
|
|
316
|
+
* The strategy-wrapped history store, shared by the startup agent and every
|
|
317
|
+
* session agent (one store; reads are scoped per session by metadata).
|
|
318
|
+
* Unset when `rag:` is not configured.
|
|
319
|
+
*/
|
|
320
|
+
_historyRag;
|
|
279
321
|
/**
|
|
280
322
|
* The providers every build of this server creates through, and the catalogs
|
|
281
323
|
* a session registry is hydrated from. One object, handed to every build:
|
|
@@ -453,6 +495,9 @@ export class SmartServer {
|
|
|
453
495
|
...(deps.connectMcpWithDescriptors
|
|
454
496
|
? { connectMcpWithDescriptors: deps.connectMcpWithDescriptors }
|
|
455
497
|
: {}),
|
|
498
|
+
...(deps.makeDecisionModel
|
|
499
|
+
? { makeDecisionModel: deps.makeDecisionModel }
|
|
500
|
+
: {}),
|
|
456
501
|
};
|
|
457
502
|
}
|
|
458
503
|
async start() {
|
|
@@ -526,17 +571,25 @@ export class SmartServer {
|
|
|
526
571
|
temperature: optionalNumber(helperCfg.temperature),
|
|
527
572
|
})
|
|
528
573
|
: undefined;
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
this.
|
|
574
|
+
// `circuitBreaker:` → the shared breakers (§14.2): created once, before any
|
|
575
|
+
// LLM is held, and wrapped where each LLM is created — never in a builder.
|
|
576
|
+
if (this.cfg.circuitBreaker) {
|
|
577
|
+
this._llmBreakers = new LlmCircuitBreakers(this.cfg.circuitBreaker);
|
|
578
|
+
this._embedderBreaker = new CircuitBreaker(this.cfg.circuitBreaker);
|
|
579
|
+
}
|
|
580
|
+
this._mainLlm = this.guardLlm(mainLlm, 'main');
|
|
581
|
+
this._classifierLlm = this.guardLlm(classifierLlm, 'classifier');
|
|
582
|
+
this._helperLlm = helperLlm && this.guardLlm(helperLlm, 'helper');
|
|
532
583
|
this._llmMap = llmMap;
|
|
533
584
|
this._mainTemp = mainTemp;
|
|
585
|
+
const breakers = this._llmBreakers;
|
|
534
586
|
this._roleLlm = new RoleLlmResolver({
|
|
535
587
|
getMain: () => this._mainLlm,
|
|
536
588
|
getHelper: () => this._helperLlm,
|
|
537
589
|
getClassifier: () => this._classifierLlm,
|
|
538
590
|
getLlmMap: () => this._llmMap,
|
|
539
591
|
build: (entry) => this._deps.makeLlm(entry),
|
|
592
|
+
...(breakers ? { wrap: (llm, key) => breakers.wrap(llm, key) } : {}),
|
|
540
593
|
});
|
|
541
594
|
// The programmatic subAgentConfigs path never passed the YAML check, so a
|
|
542
595
|
// worker's named keys are checked here too (§4.6.7): startup fails loudly.
|
|
@@ -588,6 +641,54 @@ export class SmartServer {
|
|
|
588
641
|
const registered = mergePluginExports(plugins, mod, spec);
|
|
589
642
|
log({ event: 'plugin_specifier_loaded', spec, registered });
|
|
590
643
|
}
|
|
644
|
+
// ---- Reranker (§7.4) -------------------------------------------------
|
|
645
|
+
// Resolved ONCE here — the infra build shared by start() and the embeddable
|
|
646
|
+
// buildAgent() — and applied by buildBaseBuilder outside the
|
|
647
|
+
// applyServerExtras gate, so per-session agents get it too.
|
|
648
|
+
this._reranker = await resolveReranker({
|
|
649
|
+
pluginReranker: plugins.reranker,
|
|
650
|
+
});
|
|
651
|
+
// ---- Validator, expander, skills, LLM-call strategy, adapters (§14.1) --
|
|
652
|
+
// Resolved ONCE here and applied by buildBaseBuilder to every builder
|
|
653
|
+
// (per-session agents serve the requests).
|
|
654
|
+
this._outputValidator = plugins.outputValidator;
|
|
655
|
+
this._queryExpander = plugins.queryExpander;
|
|
656
|
+
this._skillManager =
|
|
657
|
+
this.cfg.skillManager ??
|
|
658
|
+
plugins.skillManager ??
|
|
659
|
+
resolveSkillManager(this.cfg.skills);
|
|
660
|
+
const strategyName = this.cfg.agent?.llmCallStrategy;
|
|
661
|
+
if (strategyName) {
|
|
662
|
+
const { StreamingLlmCallStrategy, NonStreamingLlmCallStrategy, FallbackLlmCallStrategy, } = await import('@mcp-abap-adt/llm-agent');
|
|
663
|
+
const strategies = {
|
|
664
|
+
streaming: () => new StreamingLlmCallStrategy(),
|
|
665
|
+
'non-streaming': () => new NonStreamingLlmCallStrategy(),
|
|
666
|
+
fallback: () => new FallbackLlmCallStrategy(this._fileLogger),
|
|
667
|
+
};
|
|
668
|
+
this._llmCallStrategyFactory = strategies[strategyName];
|
|
669
|
+
}
|
|
670
|
+
const { ClineClientAdapter } = await import('@mcp-abap-adt/llm-agent');
|
|
671
|
+
this._clientAdapters = [
|
|
672
|
+
...(this.cfg.clientAdapters ?? []),
|
|
673
|
+
...plugins.clientAdapters,
|
|
674
|
+
new ClineClientAdapter(),
|
|
675
|
+
];
|
|
676
|
+
// ---- Per-store retrieval strategies (§13.4) ---------------------------
|
|
677
|
+
// Resolved ONCE, server-wide. An explicit per-store strategy (embedding
|
|
678
|
+
// included) wins over the plugin reranker above; an unlisted store keeps
|
|
679
|
+
// today's behaviour. Decision-backed rerankers share one decision model.
|
|
680
|
+
this._retrievalStrategies = await resolveRetrievalStrategies({
|
|
681
|
+
retrieval: this.cfg.rag?.retrieval,
|
|
682
|
+
decisionCfg: this.cfg.decision,
|
|
683
|
+
makeDecisionModel: this._deps.makeDecisionModel,
|
|
684
|
+
resolveLlm: (key) => this.roleLlm().resolveNamed(key),
|
|
685
|
+
});
|
|
686
|
+
const toolSelectionCfg = this.cfg.agent?.toolSelection;
|
|
687
|
+
this._toolSelectionStrategy = toolSelectionCfg?.strategy
|
|
688
|
+
? resolveToolSelectionStrategy(toolSelectionCfg.strategy, {
|
|
689
|
+
minScore: toolSelectionCfg.minScore,
|
|
690
|
+
})
|
|
691
|
+
: undefined;
|
|
591
692
|
// ---- Pipeline-plugin registry: factories (§4.6.7) -------------------
|
|
592
693
|
// Built-ins are server code — parse, validate, construct with typed settings.
|
|
593
694
|
// A dynamic instance export is registered as a factory that ignores its
|
|
@@ -656,7 +757,10 @@ export class SmartServer {
|
|
|
656
757
|
// ONE retrieval embedder: stores write with its `embedDocument`, every
|
|
657
758
|
// search path embeds with its `embedQuery` (an asymmetric model is two
|
|
658
759
|
// halves behind it — see resolveRetrievalEmbedder).
|
|
659
|
-
const resolvedEmbedder = await resolveRetrievalEmbedder(this.cfg.rag, this._deps.embedder ?? this.cfg.embedder, this._deps.resolveEmbedder, mergedEmbedderFactories, this._fileLogger
|
|
760
|
+
const resolvedEmbedder = await resolveRetrievalEmbedder(this.cfg.rag, this._deps.embedder ?? this.cfg.embedder, this._deps.resolveEmbedder, mergedEmbedderFactories, this._fileLogger,
|
|
761
|
+
// The embedder breaker sees the real embedding calls (§14.2): each half
|
|
762
|
+
// is wrapped below the document/query role. Worker embedders keep none.
|
|
763
|
+
this.embedderBreakerWrap());
|
|
660
764
|
// Hold the resolved embedder so buildServerCtx can thread it onto every
|
|
661
765
|
// pipeline context (the controller pipeline needs it for target-state).
|
|
662
766
|
this._resolvedEmbedder = resolvedEmbedder;
|
|
@@ -745,12 +849,13 @@ export class SmartServer {
|
|
|
745
849
|
// through its own. Two calls, two stores — the history store never shared
|
|
746
850
|
// the tools store's instance.
|
|
747
851
|
const input = toMakeRagInput(this.cfg.rag.store, resolvedEmbedder, 'rag');
|
|
748
|
-
toolsRag = await this._deps.makeRag(input);
|
|
749
|
-
historyRag = await this._deps.makeRag(input);
|
|
852
|
+
toolsRag = this.withStrategy('tools', await this._deps.makeRag(input));
|
|
853
|
+
historyRag = this.withStrategy('history', await this._deps.makeRag(input));
|
|
750
854
|
}
|
|
751
855
|
// Capture the tools store for the flat/smart pipeline's ToolSelectHandler
|
|
752
856
|
// (and white-box vectorization assertions). See field doc.
|
|
753
857
|
this._toolsRag = toolsRag;
|
|
858
|
+
this._historyRag = historyRag;
|
|
754
859
|
// NOTE: the legacy per-pipeline named-RAG multistore (`pipeline.rag.{name}`)
|
|
755
860
|
// is GONE with the `pipeline: {name,config}` migration. The top-level `rag:`
|
|
756
861
|
// block above is the single source of truth for the tools/history stores.
|
|
@@ -869,9 +974,10 @@ export class SmartServer {
|
|
|
869
974
|
// Assemble everything EXCEPT the coordinator via the shared base-builder
|
|
870
975
|
// factory; the coordinator gate below wires the chosen variant.
|
|
871
976
|
const builder = await this.buildBaseBuilder({
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
977
|
+
// The held (breaker-guarded) instances: the builder never wraps an LLM.
|
|
978
|
+
mainLlm: this._mainLlm,
|
|
979
|
+
classifierLlm: this._classifierLlm,
|
|
980
|
+
helperLlm: this._helperLlm,
|
|
875
981
|
fileLogger,
|
|
876
982
|
toolsRag,
|
|
877
983
|
historyRag,
|
|
@@ -898,6 +1004,9 @@ export class SmartServer {
|
|
|
898
1004
|
const { agent: smartAgent, chat, streamChat, close: closeAgent, circuitBreakers, ragStores, modelProvider, } = agentHandle;
|
|
899
1005
|
const { ragRegistry: globalRagRegistry, mcpClients: globalMcpClients } = agentHandle;
|
|
900
1006
|
this._globalRagRegistry = globalRagRegistry;
|
|
1007
|
+
for (const w of unknownRetrievalKeyWarnings(this.cfg.rag?.retrieval, globalRagRegistry.list().map((c) => c.name))) {
|
|
1008
|
+
this.warn(w);
|
|
1009
|
+
}
|
|
901
1010
|
// Two limits of this server, stated rather than fixed (§6.4): it registers
|
|
902
1011
|
// no RAG providers, so no session registry has a catalog to hydrate from —
|
|
903
1012
|
// each holds only the deployment's globals; and its sessions carry a
|
|
@@ -1041,7 +1150,8 @@ export class SmartServer {
|
|
|
1041
1150
|
agent: smartAgent,
|
|
1042
1151
|
startTime,
|
|
1043
1152
|
version: this.cfg.version ?? PACKAGE_VERSION,
|
|
1044
|
-
|
|
1153
|
+
// Re-read per check: a PUT /v1/config swap replaces a key's breaker.
|
|
1154
|
+
circuitBreakers: this.breakerList() ?? circuitBreakers,
|
|
1045
1155
|
});
|
|
1046
1156
|
// Startup health check removed — use `npm run models:check` for diagnostics.
|
|
1047
1157
|
// Running health check at startup wastes rate-limit budget when combined
|
|
@@ -1261,11 +1371,13 @@ export class SmartServer {
|
|
|
1261
1371
|
cache: this._workers.cache,
|
|
1262
1372
|
// Worker-OWN tools RAG (from subCfg.rag, if declared). Built once;
|
|
1263
1373
|
// re-wired per-session by reference — never re-vectorized.
|
|
1374
|
+
// rag.retrieval is server-wide: the worker's stores take the MAIN
|
|
1375
|
+
// config's strategy for their key (§13.4).
|
|
1264
1376
|
makeToolsRag: subCfg.rag
|
|
1265
|
-
? async () => this._deps.makeRag(await this._workerRagInput(name, subCfg.rag, subCfg.embedder, embedderFactories))
|
|
1377
|
+
? async () => this.withStrategy('tools', await this._deps.makeRag(await this._workerRagInput(name, subCfg.rag, subCfg.embedder, embedderFactories)))
|
|
1266
1378
|
: undefined,
|
|
1267
1379
|
makeHistoryRag: subCfg.rag
|
|
1268
|
-
? async () => this._deps.makeRag(await this._workerRagInput(name, subCfg.rag, subCfg.embedder, embedderFactories))
|
|
1380
|
+
? async () => this.withStrategy('history', await this._deps.makeRag(await this._workerRagInput(name, subCfg.rag, subCfg.embedder, embedderFactories)))
|
|
1269
1381
|
: undefined,
|
|
1270
1382
|
// Worker-OWN MCP clients. DI list (subCfg.mcpClients) wins; otherwise
|
|
1271
1383
|
// SmartAgentBuilder's own MCP-connect path handles `subCfg.mcp` — we
|
|
@@ -1327,6 +1439,12 @@ export class SmartServer {
|
|
|
1327
1439
|
else if (injected?.mcpClients && injected.mcpClients.length > 0) {
|
|
1328
1440
|
subBuilder = subBuilder.withMcpClients(injected.mcpClients);
|
|
1329
1441
|
}
|
|
1442
|
+
// rag.retrieval is server-wide: a worker's projection (named collections
|
|
1443
|
+
// of the shared registry included) gets the same per-store strategies.
|
|
1444
|
+
// Its own tools/history are already wrapped; the brand keeps them as is.
|
|
1445
|
+
for (const [key, strategy] of this._retrievalStrategies) {
|
|
1446
|
+
subBuilder = subBuilder.withRetrievalStrategy(key, strategy);
|
|
1447
|
+
}
|
|
1330
1448
|
const handle = await subBuilder.build();
|
|
1331
1449
|
// Backfill the per-worker cache from the BUILT handle (review HIGH #7).
|
|
1332
1450
|
// Only runs on the primary build path (no `injected`) so per-session
|
|
@@ -1341,6 +1459,16 @@ export class SmartServer {
|
|
|
1341
1459
|
}
|
|
1342
1460
|
// -- Pipeline-context dep sources (promoted from the inline coordinator-gate
|
|
1343
1461
|
// closures; consumed by buildServerCtx, which later tasks call) ----------
|
|
1462
|
+
/**
|
|
1463
|
+
* A server-built store (`tools` / `history`, main or worker) wrapped in the
|
|
1464
|
+
* strategy `rag.retrieval` configures for its key; unlisted → unchanged.
|
|
1465
|
+
* Idempotent (`applyRetrievalStrategy`), so the builder's projection of the
|
|
1466
|
+
* same store never wraps it twice — one rerank per query.
|
|
1467
|
+
*/
|
|
1468
|
+
withStrategy(key, store) {
|
|
1469
|
+
const strategy = this._retrievalStrategies.get(key);
|
|
1470
|
+
return strategy ? applyRetrievalStrategy(store, strategy) : store;
|
|
1471
|
+
}
|
|
1344
1472
|
/** A worker's own store input: its embedder through the seam, then paired. */
|
|
1345
1473
|
async _workerRagInput(name, rag, diEmbedder, extraFactories) {
|
|
1346
1474
|
const embedder = await resolveRetrievalEmbedder(rag, diEmbedder, this._deps.resolveEmbedder, extraFactories, this._fileLogger);
|
|
@@ -1361,6 +1489,23 @@ export class SmartServer {
|
|
|
1361
1489
|
? this.roleLlm().resolveNamed(key)
|
|
1362
1490
|
: this.roleLlm().resolve(role);
|
|
1363
1491
|
}
|
|
1492
|
+
/** `llm` behind its key's circuit breaker when `circuitBreaker:` is set. */
|
|
1493
|
+
guardLlm(llm, key) {
|
|
1494
|
+
return this._llmBreakers ? this._llmBreakers.wrap(llm, key) : llm;
|
|
1495
|
+
}
|
|
1496
|
+
/** The `wrap` argument of the main `resolveRetrievalEmbedder` call, if any. */
|
|
1497
|
+
embedderBreakerWrap() {
|
|
1498
|
+
const breaker = this._embedderBreaker;
|
|
1499
|
+
return breaker ? (e) => withCircuitBreaker(e, breaker) : undefined;
|
|
1500
|
+
}
|
|
1501
|
+
/** `/health`'s breakers: every key's current LLM breaker, then the embedder's. */
|
|
1502
|
+
breakerList() {
|
|
1503
|
+
const llm = this._llmBreakers;
|
|
1504
|
+
const embedder = this._embedderBreaker;
|
|
1505
|
+
if (!llm || !embedder)
|
|
1506
|
+
return undefined;
|
|
1507
|
+
return () => [...llm.list(), embedder];
|
|
1508
|
+
}
|
|
1364
1509
|
roleLlm() {
|
|
1365
1510
|
if (!this._roleLlm) {
|
|
1366
1511
|
throw new Error('role LLM lookup invoked before _buildInfra built the resolver');
|
|
@@ -1773,11 +1918,14 @@ export class SmartServer {
|
|
|
1773
1918
|
* supplies its own scope's values (startup = global; session = session-scoped);
|
|
1774
1919
|
* every `.withXxx` is applied conditionally on its `parts` field so both work.
|
|
1775
1920
|
*
|
|
1776
|
-
* `applyServerExtras`
|
|
1777
|
-
* (
|
|
1778
|
-
*
|
|
1779
|
-
*
|
|
1780
|
-
*
|
|
1921
|
+
* `applyServerExtras` now guards only the YAML `mcp:` auto-connect fallback,
|
|
1922
|
+
* which stays startup-only (one connection). It also decides whether the skill
|
|
1923
|
+
* manager vectorizes its skills into the tools store (startup build only).
|
|
1924
|
+
* Not gated, because per-session agents serve the requests: the reranker
|
|
1925
|
+
* (§7.4), the per-store retrieval strategies and `agent.toolSelection`
|
|
1926
|
+
* (§13.4), the shared embedder circuit breaker (§14.2), and the output
|
|
1927
|
+
* validator, query expander, skill manager, LLM-call strategy and client
|
|
1928
|
+
* adapters (§14.1).
|
|
1781
1929
|
*/
|
|
1782
1930
|
async buildBaseBuilder(parts) {
|
|
1783
1931
|
let builder = new SmartAgentBuilder({
|
|
@@ -1820,64 +1968,55 @@ export class SmartServer {
|
|
|
1820
1968
|
if (parts.requestLogger) {
|
|
1821
1969
|
builder = builder.withRequestLogger(parts.requestLogger);
|
|
1822
1970
|
}
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1971
|
+
// Not gated: requests are served by per-session agents, built with
|
|
1972
|
+
// applyServerExtras=false; the startup agent is infrastructure only.
|
|
1973
|
+
if (this._reranker) {
|
|
1974
|
+
builder = builder.withReranker(this._reranker);
|
|
1975
|
+
}
|
|
1976
|
+
// Not gated, same reason: the per-store strategies reach every store the
|
|
1977
|
+
// pipeline reads through the builder's `ragStores` projection (named
|
|
1978
|
+
// collections included). Server-built stores are already wrapped; the
|
|
1979
|
+
// projection sees the brand and leaves them as they are.
|
|
1980
|
+
for (const [key, strategy] of this._retrievalStrategies) {
|
|
1981
|
+
builder = builder.withRetrievalStrategy(key, strategy);
|
|
1982
|
+
}
|
|
1983
|
+
// Not gated, same reason: `agent.toolSelection` must reach the per-session
|
|
1984
|
+
// agents that serve requests (§13.4). With a reranked `tools` store,
|
|
1985
|
+
// `minScore` compares reranker probabilities in [0, 1], not cosine.
|
|
1986
|
+
if (this._toolSelectionStrategy) {
|
|
1987
|
+
builder = builder.withToolSelectionStrategy(this._toolSelectionStrategy);
|
|
1988
|
+
}
|
|
1989
|
+
// Not gated, same reason: the ONE embedder breaker guards the stores of
|
|
1990
|
+
// every agent (§14.2). The LLMs given above are already breaker-guarded.
|
|
1991
|
+
if (this._embedderBreaker) {
|
|
1992
|
+
builder = builder.withCircuitBreakers({
|
|
1993
|
+
embedder: this._embedderBreaker,
|
|
1994
|
+
});
|
|
1995
|
+
}
|
|
1996
|
+
// Not gated, same reason: the output validator, query expander, skill
|
|
1997
|
+
// manager, LLM-call strategy and client adapters reach the per-session
|
|
1998
|
+
// agents that serve requests (§14.1). Skills are vectorized into the tools
|
|
1999
|
+
// store by the startup build only.
|
|
2000
|
+
if (this._queryExpander) {
|
|
2001
|
+
builder = builder.withQueryExpander(this._queryExpander);
|
|
2002
|
+
}
|
|
2003
|
+
if (this._outputValidator) {
|
|
2004
|
+
builder = builder.withOutputValidator(this._outputValidator);
|
|
2005
|
+
}
|
|
2006
|
+
if (this._skillManager) {
|
|
2007
|
+
builder = builder.withSkillManager(this._skillManager, {
|
|
2008
|
+
vectorize: parts.applyServerExtras,
|
|
2009
|
+
});
|
|
2010
|
+
}
|
|
2011
|
+
if (this._llmCallStrategyFactory) {
|
|
2012
|
+
builder = builder.withLlmCallStrategy(this._llmCallStrategyFactory());
|
|
2013
|
+
}
|
|
2014
|
+
for (const adapter of this._clientAdapters) {
|
|
2015
|
+
builder = builder.withClientAdapter(adapter);
|
|
1865
2016
|
}
|
|
1866
2017
|
if (parts.mcpClients) {
|
|
1867
2018
|
builder = builder.withMcpClients(parts.mcpClients);
|
|
1868
2019
|
}
|
|
1869
|
-
if (parts.applyServerExtras) {
|
|
1870
|
-
// Client adapters (DI > plugin; ClineClientAdapter is the default).
|
|
1871
|
-
const { ClineClientAdapter } = await import('@mcp-abap-adt/llm-agent');
|
|
1872
|
-
const adapterSources = [
|
|
1873
|
-
...(this.cfg.clientAdapters ?? []),
|
|
1874
|
-
...(parts.plugins?.clientAdapters ?? []),
|
|
1875
|
-
new ClineClientAdapter(),
|
|
1876
|
-
];
|
|
1877
|
-
for (const adapter of adapterSources) {
|
|
1878
|
-
builder = builder.withClientAdapter(adapter);
|
|
1879
|
-
}
|
|
1880
|
-
}
|
|
1881
2020
|
if (parts.workerRegistry.size > 0) {
|
|
1882
2021
|
builder = builder.withSubAgents(parts.workerRegistry);
|
|
1883
2022
|
}
|
|
@@ -1932,6 +2071,7 @@ export class SmartServer {
|
|
|
1932
2071
|
const inst = await this.buildPipelineInstance({
|
|
1933
2072
|
sessionId: parts.sessionId,
|
|
1934
2073
|
parts,
|
|
2074
|
+
historyRag: this._historyRag,
|
|
1935
2075
|
});
|
|
1936
2076
|
// Register the pipeline's disposal hook keyed by sessionId. A prior
|
|
1937
2077
|
// instance for the same sessionId (e.g. invalidateAll rebuild) is closed
|
|
@@ -2125,7 +2265,7 @@ export class SmartServer {
|
|
|
2125
2265
|
return;
|
|
2126
2266
|
}
|
|
2127
2267
|
await rc.server._withSession(rc.req, rc.res, async (graph, sessionId, traceId) => {
|
|
2128
|
-
await handleAdapterRequest(rc.req, rc.res, graph.agent ?? rc.smartAgent, anthropicAdapter, { sessionId, traceId, graph }, this.cfg.agent?.heartbeatIntervalMs);
|
|
2268
|
+
await handleAdapterRequest(rc.req, rc.res, graph.agent ?? rc.smartAgent, anthropicAdapter, { sessionId, traceId, graph }, this.cfg.agent?.heartbeatIntervalMs, rc.log);
|
|
2129
2269
|
});
|
|
2130
2270
|
},
|
|
2131
2271
|
});
|
|
@@ -2156,14 +2296,18 @@ export class SmartServer {
|
|
|
2156
2296
|
return {
|
|
2157
2297
|
modelResolver: this.cfg.modelResolver,
|
|
2158
2298
|
skipModelValidation: this.cfg.skipModelValidation === true,
|
|
2299
|
+
// A swapped-in LLM gets a fresh breaker for its key (§14.2).
|
|
2159
2300
|
setMainLlm: (llm) => {
|
|
2160
|
-
this._mainLlm = llm;
|
|
2301
|
+
this._mainLlm = this.guardLlm(llm, 'main');
|
|
2302
|
+
return this._mainLlm;
|
|
2161
2303
|
},
|
|
2162
2304
|
setClassifierLlm: (llm) => {
|
|
2163
|
-
this._classifierLlm = llm;
|
|
2305
|
+
this._classifierLlm = this.guardLlm(llm, 'classifier');
|
|
2306
|
+
return this._classifierLlm;
|
|
2164
2307
|
},
|
|
2165
2308
|
setHelperLlm: (llm) => {
|
|
2166
|
-
this._helperLlm = llm;
|
|
2309
|
+
this._helperLlm = this.guardLlm(llm, 'helper');
|
|
2310
|
+
return this._helperLlm;
|
|
2167
2311
|
},
|
|
2168
2312
|
mirrorAgentCfg: (patch) => {
|
|
2169
2313
|
const merged = {
|