@mcp-abap-adt/llm-agent-server 16.2.0 → 17.0.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/smart-agent/build-dag-coordinator-deps.d.ts +34 -0
- package/dist/smart-agent/build-dag-coordinator-deps.d.ts.map +1 -0
- package/dist/smart-agent/build-dag-coordinator-deps.js +107 -0
- package/dist/smart-agent/build-dag-coordinator-deps.js.map +1 -0
- package/dist/smart-agent/config.d.ts +89 -3
- package/dist/smart-agent/config.d.ts.map +1 -1
- package/dist/smart-agent/config.js +247 -16
- package/dist/smart-agent/config.js.map +1 -1
- package/dist/smart-agent/session-identity-resolver.d.ts +17 -0
- package/dist/smart-agent/session-identity-resolver.d.ts.map +1 -0
- package/dist/smart-agent/session-identity-resolver.js +37 -0
- package/dist/smart-agent/session-identity-resolver.js.map +1 -0
- package/dist/smart-agent/smart-server.d.ts +190 -3
- package/dist/smart-agent/smart-server.d.ts.map +1 -1
- package/dist/smart-agent/smart-server.js +788 -112
- package/dist/smart-agent/smart-server.js.map +1 -1
- package/package.json +26 -26
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const PACKAGE_VERSION = "
|
|
1
|
+
export declare const PACKAGE_VERSION = "17.0.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ILlm, ISubAgent } from '@mcp-abap-adt/llm-agent';
|
|
2
|
+
import type { DagCoordinatorHandlerDeps } from '@mcp-abap-adt/llm-agent-libs';
|
|
3
|
+
import { type NormalizedLlmMap } from './config.js';
|
|
4
|
+
import type { SmartServerLlmConfig } from './smart-server.js';
|
|
5
|
+
export interface BuildDagCoordinatorDepsInput {
|
|
6
|
+
coordCfg: Record<string, unknown> | undefined;
|
|
7
|
+
llmMap: NormalizedLlmMap | undefined;
|
|
8
|
+
/** Adapted from `pipeline.llm.main` (already shape-normalized to
|
|
9
|
+
* SmartServerLlmConfig: `{ provider, apiKey, url: baseURL, model,
|
|
10
|
+
* temperature }`). When set, used as the final fallback in the
|
|
11
|
+
* role-resolution chain map[name] → map.main → pipelineFallback. */
|
|
12
|
+
pipelineFallback: SmartServerLlmConfig | undefined;
|
|
13
|
+
mainLlm: ILlm;
|
|
14
|
+
helperLlm: ILlm | undefined;
|
|
15
|
+
mainTemp: number;
|
|
16
|
+
registry: ReadonlyMap<string, ISubAgent>;
|
|
17
|
+
makeLlm: (config: SmartServerLlmConfig) => Promise<ILlm>;
|
|
18
|
+
warn: (msg: string) => void;
|
|
19
|
+
}
|
|
20
|
+
export type BuiltDagCoordinatorDeps = Omit<DagCoordinatorHandlerDeps, 'workers'> & {
|
|
21
|
+
workers: Map<string, ISubAgent>;
|
|
22
|
+
oracleName?: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Assemble the full deps record for `withDagCoordinator`. Returns
|
|
26
|
+
* `undefined` when the YAML does not declare a DAG coordinator (no
|
|
27
|
+
* `planner` block), so the caller can branch.
|
|
28
|
+
*
|
|
29
|
+
* Roles resolve their LLM via the chain:
|
|
30
|
+
* resolveLlmConfig(llmMap, name, pipelineFallback)
|
|
31
|
+
* → top-level llm.<name> → llm.main → pipelineFallback (pipeline.llm.main)
|
|
32
|
+
*/
|
|
33
|
+
export declare function buildDagCoordinatorDeps(input: BuildDagCoordinatorDepsInput): Promise<BuiltDagCoordinatorDeps | undefined>;
|
|
34
|
+
//# sourceMappingURL=build-dag-coordinator-deps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-dag-coordinator-deps.d.ts","sourceRoot":"","sources":["../../src/smart-agent/build-dag-coordinator-deps.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,IAAI,EAEJ,SAAS,EACV,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;AAS9E,OAAO,EAEL,KAAK,gBAAgB,EAKtB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAE9D,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC9C,MAAM,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACrC;;;yEAGqE;IACrE,gBAAgB,EAAE,oBAAoB,GAAG,SAAS,CAAC;IACnD,OAAO,EAAE,IAAI,CAAC;IACd,SAAS,EAAE,IAAI,GAAG,SAAS,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACzC,OAAO,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED,MAAM,MAAM,uBAAuB,GAAG,IAAI,CACxC,yBAAyB,EACzB,SAAS,CACV,GAAG;IACF,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,uBAAuB,GAAG,SAAS,CAAC,CA0I9C"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { AbortErrorStrategy, DagPlanInterpreter, LlmDagPlanner, LlmReviewStrategy, ReplanErrorStrategy, SubAgentStateOracle, } from '@mcp-abap-adt/llm-agent-libs';
|
|
2
|
+
import { buildFinalizer, resolveCoordinatorActivation, resolveLlmConfig, resolveLlmConfigStrict, resolveReviewerLlmName, } from './config.js';
|
|
3
|
+
/**
|
|
4
|
+
* Assemble the full deps record for `withDagCoordinator`. Returns
|
|
5
|
+
* `undefined` when the YAML does not declare a DAG coordinator (no
|
|
6
|
+
* `planner` block), so the caller can branch.
|
|
7
|
+
*
|
|
8
|
+
* Roles resolve their LLM via the chain:
|
|
9
|
+
* resolveLlmConfig(llmMap, name, pipelineFallback)
|
|
10
|
+
* → top-level llm.<name> → llm.main → pipelineFallback (pipeline.llm.main)
|
|
11
|
+
*/
|
|
12
|
+
export async function buildDagCoordinatorDeps(input) {
|
|
13
|
+
const { coordCfg, llmMap, pipelineFallback, mainLlm, helperLlm, mainTemp, registry, makeLlm, warn, } = input;
|
|
14
|
+
if (!coordCfg || coordCfg.planner === undefined)
|
|
15
|
+
return undefined;
|
|
16
|
+
// ---- Role LLM resolver -----------------------------------------------
|
|
17
|
+
// Priority chain:
|
|
18
|
+
// 1. map[name] — explicit named entry in the top-level llm: block
|
|
19
|
+
// 2. 'helper' | 'planner' alias — route to the prebuilt helperLlm
|
|
20
|
+
// 3. pipelineFallback (pipeline.llm.main) — last resort
|
|
21
|
+
// 4. fallbackPrebuilt (mainLlm) — when nothing else resolves
|
|
22
|
+
const resolveRoleLlm = async (name, fallbackPrebuilt = mainLlm) => {
|
|
23
|
+
// 1. Explicit map[name] — use it (strict: no main fallback here).
|
|
24
|
+
const strict = resolveLlmConfigStrict(llmMap, name);
|
|
25
|
+
if (strict) {
|
|
26
|
+
return makeLlm({
|
|
27
|
+
...strict,
|
|
28
|
+
temperature: Number(strict.temperature ?? mainTemp),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
// 2. 'helper' | 'planner' alias → reuse prebuilt helperLlm.
|
|
32
|
+
if (name === 'helper' || name === 'planner') {
|
|
33
|
+
return helperLlm ?? fallbackPrebuilt;
|
|
34
|
+
}
|
|
35
|
+
// 3. Unknown name → final fallback chain via resolveLlmConfig (map.main → pipelineFallback).
|
|
36
|
+
if (name) {
|
|
37
|
+
const fb = resolveLlmConfig(llmMap, name, pipelineFallback);
|
|
38
|
+
if (fb) {
|
|
39
|
+
return makeLlm({
|
|
40
|
+
...fb,
|
|
41
|
+
temperature: Number(fb.temperature ?? mainTemp),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// 4. No name at all → fallbackPrebuilt (mainLlm by default).
|
|
46
|
+
return fallbackPrebuilt;
|
|
47
|
+
};
|
|
48
|
+
// ---- Planner ----------------------------------------------------------
|
|
49
|
+
const plannerBlock = coordCfg.planner;
|
|
50
|
+
const plannerLlm = await resolveRoleLlm(plannerBlock?.plannerLlm);
|
|
51
|
+
const planner = new LlmDagPlanner(plannerLlm);
|
|
52
|
+
// ---- Reviewer (optional) ---------------------------------------------
|
|
53
|
+
let reviewer;
|
|
54
|
+
if (coordCfg.reviewer !== undefined) {
|
|
55
|
+
const reviewerBlock = coordCfg.reviewer;
|
|
56
|
+
const reviewerName = resolveReviewerLlmName(reviewerBlock, warn);
|
|
57
|
+
const reviewerLlm = await resolveRoleLlm(reviewerName);
|
|
58
|
+
reviewer = new LlmReviewStrategy(reviewerLlm);
|
|
59
|
+
}
|
|
60
|
+
// ---- Interpreter, workers, oracle, activation, error strategy --------
|
|
61
|
+
const interpreter = new DagPlanInterpreter();
|
|
62
|
+
const oracleName = coordCfg.stateOracle;
|
|
63
|
+
let rawOracle;
|
|
64
|
+
if (oracleName) {
|
|
65
|
+
rawOracle = registry.get(oracleName);
|
|
66
|
+
if (!rawOracle) {
|
|
67
|
+
throw new Error(`coordinator.stateOracle '${oracleName}' is not a declared subagent`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const workers = new Map([...registry].filter(([name]) => name !== oracleName));
|
|
71
|
+
if (workers.size === 0) {
|
|
72
|
+
throw new Error('coordinator.planner is set (DAG mode) but no workers are configured. ' +
|
|
73
|
+
'Add at least one entry under the top-level `subagents:` block.');
|
|
74
|
+
}
|
|
75
|
+
const activation = resolveCoordinatorActivation((coordCfg.activation ?? 'explicit'));
|
|
76
|
+
let errorStrategy;
|
|
77
|
+
const esCfg = coordCfg.errorStrategy;
|
|
78
|
+
if (esCfg?.type === 'replan') {
|
|
79
|
+
errorStrategy = new ReplanErrorStrategy(planner, esCfg.maxReplans);
|
|
80
|
+
}
|
|
81
|
+
else if (esCfg?.type === 'abort') {
|
|
82
|
+
errorStrategy = new AbortErrorStrategy();
|
|
83
|
+
}
|
|
84
|
+
// ---- Finalizer --------------------------------------------------------
|
|
85
|
+
const finalizer = await buildFinalizer(coordCfg.finalizer, llmMap, pipelineFallback, async (lc) => makeLlm({
|
|
86
|
+
...lc,
|
|
87
|
+
temperature: Number(lc.temperature ?? mainTemp),
|
|
88
|
+
}));
|
|
89
|
+
// ---- Oracle wrap ------------------------------------------------------
|
|
90
|
+
const stateOracle = rawOracle
|
|
91
|
+
? new SubAgentStateOracle(rawOracle)
|
|
92
|
+
: undefined;
|
|
93
|
+
void mainLlm;
|
|
94
|
+
return {
|
|
95
|
+
planner,
|
|
96
|
+
interpreter,
|
|
97
|
+
workers,
|
|
98
|
+
activation,
|
|
99
|
+
reviewer,
|
|
100
|
+
errorStrategy,
|
|
101
|
+
stateOracle,
|
|
102
|
+
finalizer,
|
|
103
|
+
maxRoundTrips: coordCfg.maxRoundTrips,
|
|
104
|
+
oracleName,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=build-dag-coordinator-deps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-dag-coordinator-deps.js","sourceRoot":"","sources":["../../src/smart-agent/build-dag-coordinator-deps.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,45 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared config utilities for SmartServer.
|
|
3
3
|
*/
|
|
4
|
-
import type { ILlm, ISubAgentContextBuilder, IToolSelectionStrategy } from '@mcp-abap-adt/llm-agent';
|
|
4
|
+
import type { IFinalizer, ILlm, ISubAgentContextBuilder, IToolSelectionStrategy } from '@mcp-abap-adt/llm-agent';
|
|
5
5
|
import { AutoActivation, ExplicitActivation, HybridDispatch, OneShotPlanning, ReplanOnErrorPlanning, SelfDispatch, SkillStepsPlanning, SubAgentDispatch } from '@mcp-abap-adt/llm-agent-libs';
|
|
6
|
-
import type { SmartServerConfig } from './smart-server.js';
|
|
6
|
+
import type { SmartServerConfig, SmartServerLlmConfig } from './smart-server.js';
|
|
7
|
+
export type LlmConfigMap = Record<string, SmartServerLlmConfig>;
|
|
8
|
+
export type NormalizedLlmMap = {
|
|
9
|
+
main: SmartServerLlmConfig;
|
|
10
|
+
} & LlmConfigMap;
|
|
11
|
+
/**
|
|
12
|
+
* Normalize the optional top-level `llm:` block.
|
|
13
|
+
* - undefined → undefined (pipeline-only configs stay valid)
|
|
14
|
+
* - flat shape (has `provider` | `apiKey` | `model` | `url`) → { main: flat } (backward compat)
|
|
15
|
+
* - map shape → must include `main`; returned as NormalizedLlmMap
|
|
16
|
+
*/
|
|
17
|
+
export declare function normalizeLlmConfig(input?: SmartServerLlmConfig | LlmConfigMap): NormalizedLlmMap | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Strict lookup: returns map[name] if explicitly present, else undefined.
|
|
20
|
+
* Does NOT fall through to map.main. Use when the caller needs to
|
|
21
|
+
* detect explicit-presence (e.g. to decide between an alias and the
|
|
22
|
+
* named map entry).
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolveLlmConfigStrict(map: NormalizedLlmMap | undefined, name: string | undefined): SmartServerLlmConfig | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Resolve a per-role LLM config by name from a normalized map.
|
|
27
|
+
* Lookup chain: map[name] → map.main → pipelineFallback.
|
|
28
|
+
* When map is undefined, falls back to pipelineFallback (so pipeline-only
|
|
29
|
+
* configs keep working with no top-level llm: block).
|
|
30
|
+
*
|
|
31
|
+
* The caller decides whether `undefined` is an error.
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolveLlmConfig(map: NormalizedLlmMap | undefined, name?: string, pipelineFallback?: SmartServerLlmConfig): SmartServerLlmConfig | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Read the reviewer block's LLM-name selector, accepting both the
|
|
36
|
+
* preferred `reviewerLlm` field and the deprecated `plannerLlm` alias.
|
|
37
|
+
* When the alias is used, calls `warn(message)`.
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolveReviewerLlmName(block: {
|
|
40
|
+
reviewerLlm?: string;
|
|
41
|
+
plannerLlm?: string;
|
|
42
|
+
} | undefined, warn: (msg: string) => void): string | undefined;
|
|
7
43
|
export interface YamlCoordinator {
|
|
8
44
|
planning?: 'one-shot' | 'replan-on-error' | 'skill-steps';
|
|
9
45
|
dispatch?: 'subagent' | 'self' | 'hybrid';
|
|
@@ -13,8 +49,41 @@ export interface YamlCoordinator {
|
|
|
13
49
|
maxRetriesPerStep?: number;
|
|
14
50
|
failPolicy?: 'abort' | 'continue';
|
|
15
51
|
maxLayer?: number;
|
|
52
|
+
planner?: {
|
|
53
|
+
type?: string;
|
|
54
|
+
plannerLlm?: 'main' | 'planner' | 'helper';
|
|
55
|
+
} | Record<string, unknown>;
|
|
56
|
+
interpreter?: {
|
|
57
|
+
type?: string;
|
|
58
|
+
} | Record<string, unknown>;
|
|
59
|
+
reviewer?: {
|
|
60
|
+
type?: string;
|
|
61
|
+
reviewerLlm?: string;
|
|
62
|
+
plannerLlm?: 'main' | 'planner' | 'helper';
|
|
63
|
+
};
|
|
64
|
+
finalizer?: {
|
|
65
|
+
type?: 'passthrough' | 'llm' | 'template';
|
|
66
|
+
finalizerLlm?: string;
|
|
67
|
+
systemPrompt?: string;
|
|
68
|
+
};
|
|
69
|
+
errorStrategy?: {
|
|
70
|
+
type?: string;
|
|
71
|
+
maxReplans?: number;
|
|
72
|
+
};
|
|
73
|
+
stateOracle?: string;
|
|
74
|
+
maxRoundTrips?: number;
|
|
16
75
|
}
|
|
76
|
+
/** Fail-loud guard: a coordinator block is either DAG (has `planner`) or linear,
|
|
77
|
+
* never mixed. `activation` is shared and always allowed. */
|
|
78
|
+
export declare function assertCoordinatorConfigShape(coord: Record<string, unknown>): void;
|
|
17
79
|
export declare function resolveCoordinatorPlanning(name: string, plannerLlm: ILlm): OneShotPlanning | ReplanOnErrorPlanning | SkillStepsPlanning;
|
|
80
|
+
/**
|
|
81
|
+
* Default coordinator dispatch kind. Omitted → 'hybrid' for ALL planning kinds:
|
|
82
|
+
* agentless steps — the synthesized answer-directly step (#155) and skill steps
|
|
83
|
+
* without an explicit `agent:` — need a self-LLM fallback. Pin 'subagent'
|
|
84
|
+
* explicitly for strict subagent-only routing.
|
|
85
|
+
*/
|
|
86
|
+
export declare function resolveCoordinatorDispatchKind(explicit?: 'subagent' | 'self' | 'hybrid'): 'subagent' | 'self' | 'hybrid';
|
|
18
87
|
export declare function resolveCoordinatorDispatch(name: string, fallbackLlm?: ILlm, contextBuilder?: ISubAgentContextBuilder): SubAgentDispatch | SelfDispatch | HybridDispatch;
|
|
19
88
|
export declare function resolveCoordinatorActivation(name: string): AutoActivation | ExplicitActivation;
|
|
20
89
|
export declare function resolveToolSelectionStrategy(name: string, params?: {
|
|
@@ -48,10 +117,27 @@ export interface ResolveConfigArgs {
|
|
|
48
117
|
'plugin-dir'?: string;
|
|
49
118
|
mode?: string | boolean;
|
|
50
119
|
}
|
|
51
|
-
export declare const YAML_TEMPLATE = "port: 4004\nhost: 0.0.0.0\n\n# Request routing mode:\n# hard \u2014 Fully managed context. Ignores client history/system prompt. Uses RAG + internal MCP tools only.\n# pass \u2014 Transparent proxy. Logs everything but modifies nothing.\n# smart \u2014 Hybrid. Preserves client history but enriches it with RAG context and MCP tools based on analysis. (default)\nmode: smart\n\nllm:\n provider: deepseek # deepseek | openai | anthropic | sap-ai-sdk | ollama\n apiKey: ${DEEPSEEK_API_KEY} # not required for ollama / sap-ai-sdk\n model: deepseek-chat\n temperature: 0.7\n classifierTemperature: 0.1\n\nrag:\n type: in-memory # in-memory | qdrant | hana-vector | pg-vector\n embedder: ollama # Embedder to use: ollama | openai | sap-ai-core | <custom>\n url: http://localhost:11434\n model: bge-m3\n # resourceGroup: default # SAP AI Core resource group (sap-ai-core embedder)\n # scenario: orchestration # SAP AI Core scenario: orchestration (default) | foundation-models\n # collectionName: llm-agent # Collection/table name (qdrant | hana-vector | pg-vector)\n dedupThreshold: 0.92\n vectorWeight: 0.7 # Semantic similarity weight (0..1)\n keywordWeight: 0.3 # Lexical matching weight (0..1)\n\nmcp:\n # type: none | http | stdio\n # To disable MCP, set type to 'none'\n type: http\n url: http://localhost:3001/mcp/stream/http\n\nagent:\n externalToolsValidationMode: permissive # permissive | strict\n maxIterations: 10\n maxToolCalls: 30\n toolUnavailableTtlMs: 600000 # Temporary tool blacklist TTL (ms)\n ragQueryK: 10\n # contextBudgetTokens: 4000 # Max tokens for RAG context in system prompt (0 = no limit)\n # semanticHistoryEnabled: false # Enable semantic history via RAG\n # historyRecencyWindow: 4 # Last N messages from client history in LLM context\n # historyTurnSummaryPrompt: \"...\" # LLM prompt for turn summarization\n showReasoning: false # Explain strategy at start of response\n historyAutoSummarizeLimit: 10 # History length to trigger compression\n queryExpansionEnabled: false # Expand RAG queries with LLM-generated synonyms\n toolResultCacheTtlMs: 300000 # Tool result cache TTL (ms); 0 to disable\n sessionTokenBudget: 0 # Multi-turn token budget; 0 to disable\n # ragTranslateEnabled: true # Translate non-ASCII RAG queries to English (default: true)\n # classificationEnabled: false # Enable for custom pipelines with multi-store routing\n # toolReselectPerIteration: false # Re-select tools via RAG on each tool-loop iteration\n # llmCallStrategy: streaming # streaming | non-streaming | fallback\n # streamMode: full # full | final \u2014 streaming behavior for tool loops\n # heartbeatIntervalMs: 5000 # SSE heartbeat interval during tool execution (ms)\n # healthTimeoutMs: 5000 # Health check probe timeout (ms); increase for slow providers (SAP AI Core: 15000)\n # retry: # LLM retry config for 429/5xx errors\n # maxAttempts: 3\n # backoffMs: 1000\n # retryOn: [429, 500, 502, 503]\n # retryOnMidStream: ['SSE stream'] # Substrings triggering mid-stream retry\n\n# --- Advanced Multi-Model Pipeline (optional) -------------------------------\n# Use this section to assign different models for different internal tasks.\n# pipeline:\n# llm:\n# main:\n# provider: deepseek # deepseek | openai | anthropic | sap-ai-sdk\n# apiKey: ${DEEPSEEK_API_KEY}\n# model: deepseek-chat\n# temperature: 0.7\n# streaming: true # false to disable streaming for this provider\n# classifier: # optional; if absent, main config is reused\n# provider: deepseek\n# apiKey: ${DEEPSEEK_API_KEY}\n# model: deepseek-chat\n# temperature: 0.1\n# helper: # optional; if absent, main config is reused\n# provider: deepseek\n# apiKey: ${DEEPSEEK_API_KEY}\n# model: deepseek-chat\n# temperature: 0.1\n#\n# rag:\n# tools:\n# type: qdrant\n# url: http://qdrant:6333\n# embedder: openai # ollama | openai | <custom registered name>\n# model: text-embedding-3-small\n# apiKey: ${OPENAI_API_KEY}\n# history:\n# type: in-memory\n#\n# mcp:\n# - type: http\n# url: http://localhost:3001/mcp/stream/http\n\n# --- Structured Pipeline (optional) -------------------------------------------\n# Replaces the hardcoded orchestration flow with a YAML-defined stage tree.\n# When absent, the default flow runs unchanged (full backwards compatibility).\n#\n# pipeline:\n# version: \"1\"\n# stages:\n# - id: classify\n# type: classify\n# - id: summarize\n# type: summarize\n# - id: rag-retrieval\n# type: parallel\n# when: \"shouldRetrieve\"\n# stages:\n# - { id: translate, type: translate }\n# - { id: expand, type: expand }\n# after:\n# - id: rag-queries\n# type: parallel\n# stages:\n# - { id: tools, type: rag-query, config: { store: tools, k: 10 } }\n# - { id: history, type: rag-query, config: { store: history, k: 5 } }\n# - { id: rerank, type: rerank }\n# - { id: tool-select, type: tool-select }\n# - id: assemble\n# type: assemble\n# - id: tool-loop\n# type: tool-loop\n\n# prompts:\n# system: \"You are a helpful assistant specialized in SAP ABAP development.\"\n# classifier: |\n# You are an intent classifier... (see source for full default prompt)\n# reasoning: |\n# IMPORTANT: Always start your response with a brief <reasoning> block...\n# ragTranslate: |\n# Translate the user request to English for search purposes...\n# historySummary: |\n# Summarize the conversation so far...\n\nlog: smart-server.log # path to log file; omit for stdout\n# logDir: sessions # Directory for detailed session debug logs\n# pluginDir: ./my-plugins # Additional plugin directory (loaded after defaults)\n\n# subagents: # Optional: nested agents callable from pipeline\n# - name: code-reviewer # Used as stage config: { agent: code-reviewer }\n# description: | # Optional. Shown to the Coordinator planner LLM\n# Reviews code and returns # so it can pick this agent for the right step.\n# structured JSON.\n# config: ./agents/code-reviewer.yaml\n\n# coordinator: # Optional: enable autonomous plan-execute loop\n# planning: one-shot # one-shot | replan-on-error | skill-steps\n# dispatch: subagent # subagent | self | hybrid\n# activation: explicit # explicit (default) | auto\n# plannerLlm: main # main | planner | helper (unused by skill-steps)\n# maxSteps: 12\n# maxRetriesPerStep: 1\n# failPolicy: abort # abort | continue\n# maxLayer: 1 #
|
|
120
|
+
export declare const YAML_TEMPLATE = "port: 4004\nhost: 0.0.0.0\n\n# Request routing mode:\n# hard \u2014 Fully managed context. Ignores client history/system prompt. Uses RAG + internal MCP tools only.\n# pass \u2014 Transparent proxy. Logs everything but modifies nothing.\n# smart \u2014 Hybrid. Preserves client history but enriches it with RAG context and MCP tools based on analysis. (default)\nmode: smart\n\nllm:\n provider: deepseek # deepseek | openai | anthropic | sap-ai-sdk | ollama\n apiKey: ${DEEPSEEK_API_KEY} # not required for ollama / sap-ai-sdk\n model: deepseek-chat\n temperature: 0.7\n classifierTemperature: 0.1\n\nrag:\n type: in-memory # in-memory | qdrant | hana-vector | pg-vector\n embedder: ollama # Embedder to use: ollama | openai | sap-ai-core | <custom>\n url: http://localhost:11434\n model: bge-m3\n # resourceGroup: default # SAP AI Core resource group (sap-ai-core embedder)\n # scenario: orchestration # SAP AI Core scenario: orchestration (default) | foundation-models\n # collectionName: llm-agent # Collection/table name (qdrant | hana-vector | pg-vector)\n dedupThreshold: 0.92\n vectorWeight: 0.7 # Semantic similarity weight (0..1)\n keywordWeight: 0.3 # Lexical matching weight (0..1)\n\nmcp:\n # type: none | http | stdio\n # To disable MCP, set type to 'none'\n type: http\n url: http://localhost:3001/mcp/stream/http\n\nagent:\n externalToolsValidationMode: permissive # permissive | strict\n maxIterations: 10\n maxToolCalls: 30\n toolUnavailableTtlMs: 600000 # Temporary tool blacklist TTL (ms)\n ragQueryK: 10\n # contextBudgetTokens: 4000 # Max tokens for RAG context in system prompt (0 = no limit)\n # semanticHistoryEnabled: false # Enable semantic history via RAG\n # historyRecencyWindow: 4 # Last N messages from client history in LLM context\n # historyTurnSummaryPrompt: \"...\" # LLM prompt for turn summarization\n showReasoning: false # Explain strategy at start of response\n historyAutoSummarizeLimit: 10 # History length to trigger compression\n queryExpansionEnabled: false # Expand RAG queries with LLM-generated synonyms\n toolResultCacheTtlMs: 300000 # Tool result cache TTL (ms); 0 to disable\n sessionTokenBudget: 0 # Multi-turn token budget; 0 to disable\n # ragTranslateEnabled: true # Translate non-ASCII RAG queries to English (default: true)\n # classificationEnabled: false # Enable for custom pipelines with multi-store routing\n # toolReselectPerIteration: false # Re-select tools via RAG on each tool-loop iteration\n # llmCallStrategy: streaming # streaming | non-streaming | fallback\n # streamMode: full # full | final \u2014 streaming behavior for tool loops\n # heartbeatIntervalMs: 5000 # SSE heartbeat interval during tool execution (ms)\n # healthTimeoutMs: 5000 # Health check probe timeout (ms); increase for slow providers (SAP AI Core: 15000)\n # retry: # LLM retry config for 429/5xx errors\n # maxAttempts: 3\n # backoffMs: 1000\n # retryOn: [429, 500, 502, 503]\n # retryOnMidStream: ['SSE stream'] # Substrings triggering mid-stream retry\n\n# --- Advanced Multi-Model Pipeline (optional) -------------------------------\n# Use this section to assign different models for different internal tasks.\n# pipeline:\n# llm:\n# main:\n# provider: deepseek # deepseek | openai | anthropic | sap-ai-sdk\n# apiKey: ${DEEPSEEK_API_KEY}\n# model: deepseek-chat\n# temperature: 0.7\n# streaming: true # false to disable streaming for this provider\n# classifier: # optional; if absent, main config is reused\n# provider: deepseek\n# apiKey: ${DEEPSEEK_API_KEY}\n# model: deepseek-chat\n# temperature: 0.1\n# helper: # optional; if absent, main config is reused\n# provider: deepseek\n# apiKey: ${DEEPSEEK_API_KEY}\n# model: deepseek-chat\n# temperature: 0.1\n#\n# rag:\n# tools:\n# type: qdrant\n# url: http://qdrant:6333\n# embedder: openai # ollama | openai | <custom registered name>\n# model: text-embedding-3-small\n# apiKey: ${OPENAI_API_KEY}\n# history:\n# type: in-memory\n#\n# mcp:\n# - type: http\n# url: http://localhost:3001/mcp/stream/http\n\n# --- Structured Pipeline (optional) -------------------------------------------\n# Replaces the hardcoded orchestration flow with a YAML-defined stage tree.\n# When absent, the default flow runs unchanged (full backwards compatibility).\n#\n# pipeline:\n# version: \"1\"\n# stages:\n# - id: classify\n# type: classify\n# - id: summarize\n# type: summarize\n# - id: rag-retrieval\n# type: parallel\n# when: \"shouldRetrieve\"\n# stages:\n# - { id: translate, type: translate }\n# - { id: expand, type: expand }\n# after:\n# - id: rag-queries\n# type: parallel\n# stages:\n# - { id: tools, type: rag-query, config: { store: tools, k: 10 } }\n# - { id: history, type: rag-query, config: { store: history, k: 5 } }\n# - { id: rerank, type: rerank }\n# - { id: tool-select, type: tool-select }\n# - id: assemble\n# type: assemble\n# - id: tool-loop\n# type: tool-loop\n\n# prompts:\n# system: \"You are a helpful assistant specialized in SAP ABAP development.\"\n# classifier: |\n# You are an intent classifier... (see source for full default prompt)\n# reasoning: |\n# IMPORTANT: Always start your response with a brief <reasoning> block...\n# ragTranslate: |\n# Translate the user request to English for search purposes...\n# historySummary: |\n# Summarize the conversation so far...\n\nlog: smart-server.log # path to log file; omit for stdout\n# logDir: sessions # Directory for detailed session debug logs\n# pluginDir: ./my-plugins # Additional plugin directory (loaded after defaults)\n\n# subagents: # Optional: nested agents callable from pipeline\n# - name: code-reviewer # Used as stage config: { agent: code-reviewer }\n# description: | # Optional. Shown to the Coordinator planner LLM\n# Reviews code and returns # so it can pick this agent for the right step.\n# structured JSON.\n# config: ./agents/code-reviewer.yaml\n\n# coordinator: # Optional: enable autonomous plan-execute loop\n# planning: one-shot # one-shot | replan-on-error | skill-steps\n# dispatch: subagent # subagent | self | hybrid\n# activation: explicit # explicit (default) | auto\n# plannerLlm: main # main | planner | helper (unused by skill-steps)\n# maxSteps: 12\n# maxRetriesPerStep: 1\n# failPolicy: abort # abort | continue\n# maxLayer: 1 # DEPRECATED \u2014 accepted but ignored (nested\n# # dispatch removed; subagents are leaves)\n";
|
|
52
121
|
export declare function resolveEnvVars(value: unknown, env?: NodeJS.ProcessEnv): unknown;
|
|
53
122
|
export declare function loadYamlConfig(filePath: string, env?: NodeJS.ProcessEnv): YamlConfig;
|
|
54
123
|
export declare function generateConfigTemplate(outputPath: string): void;
|
|
124
|
+
export type FinalizerYaml = {
|
|
125
|
+
type?: 'passthrough' | 'llm' | 'template';
|
|
126
|
+
finalizerLlm?: string;
|
|
127
|
+
systemPrompt?: string;
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Build the IFinalizer impl from `coordinator.finalizer:` YAML.
|
|
131
|
+
*
|
|
132
|
+
* Lookup chain for `type: llm`:
|
|
133
|
+
* resolveLlmConfig(llmMap, cfg.finalizerLlm, pipelineFallback)
|
|
134
|
+
* → top-level llm.<name> → llm.main → pipelineFallback (pipeline.llm.main)
|
|
135
|
+
* → ConfigError if all three are missing.
|
|
136
|
+
*
|
|
137
|
+
* Absent block / `type: passthrough` → PassthroughFinalizer.
|
|
138
|
+
* `type: template` → TemplateFinalizer.
|
|
139
|
+
*/
|
|
140
|
+
export declare function buildFinalizer(cfg: FinalizerYaml | undefined, llmMap: NormalizedLlmMap | undefined, pipelineFallback: SmartServerLlmConfig | undefined, makeLlm: (config: SmartServerLlmConfig) => Promise<ILlm>): Promise<IFinalizer>;
|
|
55
141
|
export interface ResolveSmartServerConfigOptions {
|
|
56
142
|
/**
|
|
57
143
|
* Filesystem path of the YAML config that produced `yaml`. Required for
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/smart-agent/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EACV,IAAI,EACJ,uBAAuB,EACvB,sBAAsB,EACvB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,cAAc,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/smart-agent/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,8 +3,172 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import fs from 'node:fs';
|
|
5
5
|
import path from 'node:path';
|
|
6
|
-
import { AutoActivation, ExplicitActivation, HybridDispatch, OneShotPlanning, ReplanOnErrorPlanning, ScoreThresholdToolSelection, SelfDispatch, SkillStepsPlanning, SubAgentDispatch, TopKToolSelection, } from '@mcp-abap-adt/llm-agent-libs';
|
|
6
|
+
import { AutoActivation, ExplicitActivation, HybridDispatch, LlmFinalizer, OneShotPlanning, PassthroughFinalizer, ReplanOnErrorPlanning, ScoreThresholdToolSelection, SelfDispatch, SkillStepsPlanning, SubAgentDispatch, TemplateFinalizer, TopKToolSelection, } from '@mcp-abap-adt/llm-agent-libs';
|
|
7
7
|
import { parse as parseYaml } from 'yaml';
|
|
8
|
+
/**
|
|
9
|
+
* Detect whether an object is a flat SmartServerLlmConfig shape.
|
|
10
|
+
* Flat shape is identified by the presence of ANY of the known flat-shape
|
|
11
|
+
* fields: `provider`, `apiKey`, `model`, or `url`. This covers keyless
|
|
12
|
+
* providers (Ollama, SAP AI Core) that omit `apiKey` — using `apiKey`-only
|
|
13
|
+
* detection silently misclassified those configs as a "map" shape.
|
|
14
|
+
*/
|
|
15
|
+
function isFlatLlmConfig(input) {
|
|
16
|
+
const flat = input;
|
|
17
|
+
return (typeof flat.provider === 'string' ||
|
|
18
|
+
typeof flat.apiKey === 'string' ||
|
|
19
|
+
typeof flat.model === 'string' ||
|
|
20
|
+
typeof flat.url === 'string');
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Normalize the optional top-level `llm:` block.
|
|
24
|
+
* - undefined → undefined (pipeline-only configs stay valid)
|
|
25
|
+
* - flat shape (has `provider` | `apiKey` | `model` | `url`) → { main: flat } (backward compat)
|
|
26
|
+
* - map shape → must include `main`; returned as NormalizedLlmMap
|
|
27
|
+
*/
|
|
28
|
+
export function normalizeLlmConfig(input) {
|
|
29
|
+
if (input === undefined)
|
|
30
|
+
return undefined;
|
|
31
|
+
if (isFlatLlmConfig(input)) {
|
|
32
|
+
return { main: input };
|
|
33
|
+
}
|
|
34
|
+
const map = input;
|
|
35
|
+
if (!map.main) {
|
|
36
|
+
throw new Error("llm: map must include a 'main' key (default LLM for unspecified roles)");
|
|
37
|
+
}
|
|
38
|
+
return map;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Strict lookup: returns map[name] if explicitly present, else undefined.
|
|
42
|
+
* Does NOT fall through to map.main. Use when the caller needs to
|
|
43
|
+
* detect explicit-presence (e.g. to decide between an alias and the
|
|
44
|
+
* named map entry).
|
|
45
|
+
*/
|
|
46
|
+
export function resolveLlmConfigStrict(map, name) {
|
|
47
|
+
if (!map || !name)
|
|
48
|
+
return undefined;
|
|
49
|
+
return map[name];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Resolve a per-role LLM config by name from a normalized map.
|
|
53
|
+
* Lookup chain: map[name] → map.main → pipelineFallback.
|
|
54
|
+
* When map is undefined, falls back to pipelineFallback (so pipeline-only
|
|
55
|
+
* configs keep working with no top-level llm: block).
|
|
56
|
+
*
|
|
57
|
+
* The caller decides whether `undefined` is an error.
|
|
58
|
+
*/
|
|
59
|
+
export function resolveLlmConfig(map, name, pipelineFallback) {
|
|
60
|
+
if (!map)
|
|
61
|
+
return pipelineFallback;
|
|
62
|
+
if (!name || name === 'main')
|
|
63
|
+
return map.main;
|
|
64
|
+
return map[name] ?? map.main;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Read the reviewer block's LLM-name selector, accepting both the
|
|
68
|
+
* preferred `reviewerLlm` field and the deprecated `plannerLlm` alias.
|
|
69
|
+
* When the alias is used, calls `warn(message)`.
|
|
70
|
+
*/
|
|
71
|
+
export function resolveReviewerLlmName(block, warn) {
|
|
72
|
+
if (!block)
|
|
73
|
+
return undefined;
|
|
74
|
+
if (typeof block.reviewerLlm === 'string')
|
|
75
|
+
return block.reviewerLlm;
|
|
76
|
+
if (typeof block.plannerLlm === 'string') {
|
|
77
|
+
warn("coordinator.reviewer.plannerLlm is deprecated; rename to 'reviewerLlm'");
|
|
78
|
+
return block.plannerLlm;
|
|
79
|
+
}
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
const LINEAR_ONLY = [
|
|
83
|
+
'planning',
|
|
84
|
+
'dispatch',
|
|
85
|
+
'maxSteps',
|
|
86
|
+
'maxRetriesPerStep',
|
|
87
|
+
'failPolicy',
|
|
88
|
+
'maxLayer',
|
|
89
|
+
'plannerLlm',
|
|
90
|
+
];
|
|
91
|
+
const DAG_ONLY = [
|
|
92
|
+
'planner',
|
|
93
|
+
'interpreter',
|
|
94
|
+
'reviewer',
|
|
95
|
+
'errorStrategy',
|
|
96
|
+
'finalizer',
|
|
97
|
+
'stateOracle',
|
|
98
|
+
'maxRoundTrips',
|
|
99
|
+
];
|
|
100
|
+
/** Validate a coordinator role block shape. */
|
|
101
|
+
function assertLlmRoleShape(label, role) {
|
|
102
|
+
if (typeof role !== 'object' || role === null || Array.isArray(role)) {
|
|
103
|
+
throw new Error(`coordinator.${label} must be an object (e.g. { type: llm }), got: ${JSON.stringify(role)}`);
|
|
104
|
+
}
|
|
105
|
+
const kind = role.type;
|
|
106
|
+
if (kind !== undefined &&
|
|
107
|
+
kind !== 'llm' &&
|
|
108
|
+
!(label === 'finalizer' && (kind === 'passthrough' || kind === 'template'))) {
|
|
109
|
+
throw new Error(`coordinator.${label}: unknown type '${String(kind)}'`);
|
|
110
|
+
}
|
|
111
|
+
for (const field of ['plannerLlm', 'reviewerLlm', 'finalizerLlm']) {
|
|
112
|
+
const sel = role[field];
|
|
113
|
+
if (sel !== undefined && typeof sel !== 'string') {
|
|
114
|
+
throw new Error(`coordinator.${label}.${field} must be a string referencing an llm.* key, got: ${String(sel)}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const sp = role.systemPrompt;
|
|
118
|
+
if (sp !== undefined && typeof sp !== 'string') {
|
|
119
|
+
throw new Error(`coordinator.${label}.systemPrompt must be a string, got: ${String(sp)}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function assertErrorStrategyShape(es) {
|
|
123
|
+
if (typeof es !== 'object' || es === null || Array.isArray(es)) {
|
|
124
|
+
throw new Error(`coordinator.errorStrategy must be an object (e.g. { type: replan }), got: ${JSON.stringify(es)}`);
|
|
125
|
+
}
|
|
126
|
+
const type = es.type;
|
|
127
|
+
if (type !== undefined && type !== 'abort' && type !== 'replan') {
|
|
128
|
+
throw new Error(`coordinator.errorStrategy: unknown type '${String(type)}' (only 'abort' | 'replan')`);
|
|
129
|
+
}
|
|
130
|
+
const mr = es.maxReplans;
|
|
131
|
+
if (mr !== undefined && (typeof mr !== 'number' || mr < 0)) {
|
|
132
|
+
throw new Error(`coordinator.errorStrategy.maxReplans must be a non-negative number, got: ${String(mr)}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/** Fail-loud guard: a coordinator block is either DAG (has `planner`) or linear,
|
|
136
|
+
* never mixed. `activation` is shared and always allowed. */
|
|
137
|
+
export function assertCoordinatorConfigShape(coord) {
|
|
138
|
+
const isDag = coord.planner !== undefined;
|
|
139
|
+
if (isDag) {
|
|
140
|
+
assertLlmRoleShape('planner', coord.planner);
|
|
141
|
+
if (coord.reviewer !== undefined) {
|
|
142
|
+
assertLlmRoleShape('reviewer', coord.reviewer);
|
|
143
|
+
}
|
|
144
|
+
if (coord.finalizer !== undefined) {
|
|
145
|
+
assertLlmRoleShape('finalizer', coord.finalizer);
|
|
146
|
+
}
|
|
147
|
+
if (coord.errorStrategy !== undefined) {
|
|
148
|
+
assertErrorStrategyShape(coord.errorStrategy);
|
|
149
|
+
}
|
|
150
|
+
if (coord.stateOracle !== undefined &&
|
|
151
|
+
typeof coord.stateOracle !== 'string') {
|
|
152
|
+
throw new Error(`coordinator.stateOracle must be a string (a declared subagent name), got: ${JSON.stringify(coord.stateOracle)}`);
|
|
153
|
+
}
|
|
154
|
+
if (coord.maxRoundTrips !== undefined &&
|
|
155
|
+
(typeof coord.maxRoundTrips !== 'number' || coord.maxRoundTrips < 0)) {
|
|
156
|
+
throw new Error(`coordinator.maxRoundTrips must be a non-negative number, got: ${String(coord.maxRoundTrips)}`);
|
|
157
|
+
}
|
|
158
|
+
for (const f of LINEAR_ONLY) {
|
|
159
|
+
if (coord[f] !== undefined) {
|
|
160
|
+
throw new Error(`coordinator: '${f}' is a linear-only field and cannot be combined with 'planner' (DAG mode)`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
for (const f of DAG_ONLY) {
|
|
166
|
+
if (coord[f] !== undefined) {
|
|
167
|
+
throw new Error(`coordinator: '${f}' is a DAG-only field; a linear coordinator uses 'planning'/'dispatch'`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
8
172
|
export function resolveCoordinatorPlanning(name, plannerLlm) {
|
|
9
173
|
switch (name) {
|
|
10
174
|
case 'one-shot':
|
|
@@ -20,6 +184,15 @@ export function resolveCoordinatorPlanning(name, plannerLlm) {
|
|
|
20
184
|
throw new Error(`Unknown coordinator.planning strategy: '${name}'. Allowed: one-shot, replan-on-error, skill-steps.`);
|
|
21
185
|
}
|
|
22
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* Default coordinator dispatch kind. Omitted → 'hybrid' for ALL planning kinds:
|
|
189
|
+
* agentless steps — the synthesized answer-directly step (#155) and skill steps
|
|
190
|
+
* without an explicit `agent:` — need a self-LLM fallback. Pin 'subagent'
|
|
191
|
+
* explicitly for strict subagent-only routing.
|
|
192
|
+
*/
|
|
193
|
+
export function resolveCoordinatorDispatchKind(explicit) {
|
|
194
|
+
return explicit ?? 'hybrid';
|
|
195
|
+
}
|
|
23
196
|
export function resolveCoordinatorDispatch(name, fallbackLlm, contextBuilder) {
|
|
24
197
|
switch (name) {
|
|
25
198
|
case 'subagent':
|
|
@@ -241,7 +414,8 @@ log: smart-server.log # path to log file; omit for stdout
|
|
|
241
414
|
# maxSteps: 12
|
|
242
415
|
# maxRetriesPerStep: 1
|
|
243
416
|
# failPolicy: abort # abort | continue
|
|
244
|
-
# maxLayer: 1 #
|
|
417
|
+
# maxLayer: 1 # DEPRECATED — accepted but ignored (nested
|
|
418
|
+
# # dispatch removed; subagents are leaves)
|
|
245
419
|
`;
|
|
246
420
|
export function resolveEnvVars(value, env = process.env) {
|
|
247
421
|
if (typeof value === 'string')
|
|
@@ -262,6 +436,33 @@ export function loadYamlConfig(filePath, env = process.env) {
|
|
|
262
436
|
export function generateConfigTemplate(outputPath) {
|
|
263
437
|
fs.writeFileSync(outputPath, YAML_TEMPLATE, 'utf8');
|
|
264
438
|
}
|
|
439
|
+
/**
|
|
440
|
+
* Build the IFinalizer impl from `coordinator.finalizer:` YAML.
|
|
441
|
+
*
|
|
442
|
+
* Lookup chain for `type: llm`:
|
|
443
|
+
* resolveLlmConfig(llmMap, cfg.finalizerLlm, pipelineFallback)
|
|
444
|
+
* → top-level llm.<name> → llm.main → pipelineFallback (pipeline.llm.main)
|
|
445
|
+
* → ConfigError if all three are missing.
|
|
446
|
+
*
|
|
447
|
+
* Absent block / `type: passthrough` → PassthroughFinalizer.
|
|
448
|
+
* `type: template` → TemplateFinalizer.
|
|
449
|
+
*/
|
|
450
|
+
export async function buildFinalizer(cfg, llmMap, pipelineFallback, makeLlm) {
|
|
451
|
+
const kind = cfg?.type ?? 'passthrough';
|
|
452
|
+
if (kind === 'passthrough')
|
|
453
|
+
return new PassthroughFinalizer();
|
|
454
|
+
if (kind === 'template')
|
|
455
|
+
return new TemplateFinalizer();
|
|
456
|
+
// kind === 'llm'
|
|
457
|
+
const resolved = resolveLlmConfig(llmMap, cfg?.finalizerLlm, pipelineFallback);
|
|
458
|
+
if (!resolved) {
|
|
459
|
+
throw new Error('coordinator.finalizer (type: llm) requires an LLM config: provide top-level llm.<name>, llm.main, or pipeline.llm.main');
|
|
460
|
+
}
|
|
461
|
+
const llm = await makeLlm(resolved);
|
|
462
|
+
return new LlmFinalizer(llm, {
|
|
463
|
+
systemPrompt: cfg?.systemPrompt,
|
|
464
|
+
});
|
|
465
|
+
}
|
|
265
466
|
const get = (obj, ...keys) => keys.reduce((o, k) => {
|
|
266
467
|
if (o !== null && typeof o === 'object' && k in o) {
|
|
267
468
|
return o[k];
|
|
@@ -335,7 +536,10 @@ function checkRagStore(label, store, issues) {
|
|
|
335
536
|
issues.push(`${label}.model: required when an embedder is used (e.g. bge-m3 for ollama)`);
|
|
336
537
|
}
|
|
337
538
|
}
|
|
338
|
-
function
|
|
539
|
+
function validateLlmEntry(label, cfg, required, env, issues) {
|
|
540
|
+
checkLlmRole(label, cfg, required, env, issues);
|
|
541
|
+
}
|
|
542
|
+
function validateResolvedConfig(_resolved, yaml, env) {
|
|
339
543
|
const issues = [];
|
|
340
544
|
const usingPipeline = !!get(yaml, 'pipeline', 'llm', 'main');
|
|
341
545
|
if (usingPipeline) {
|
|
@@ -349,11 +553,34 @@ function validateResolvedConfig(resolved, yaml, env) {
|
|
|
349
553
|
}
|
|
350
554
|
}
|
|
351
555
|
else {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
556
|
+
// Read from the raw YAML so we can distinguish flat vs map shape.
|
|
557
|
+
// `resolved.llm` is always constructed as a flat object by resolveSmartServerConfig,
|
|
558
|
+
// so it cannot be used to detect the map shape.
|
|
559
|
+
const rawLlm = get(yaml, 'llm');
|
|
560
|
+
if (rawLlm === undefined) {
|
|
561
|
+
// No top-level llm: AND no pipeline.llm.main → would have been caught
|
|
562
|
+
// by `usingPipeline` branch. Defensive: surface a clear issue.
|
|
563
|
+
issues.push('llm: required when pipeline.llm.main is not configured');
|
|
564
|
+
}
|
|
565
|
+
else if (typeof rawLlm.provider === 'string') {
|
|
566
|
+
// Flat shape — existing behaviour.
|
|
567
|
+
validateLlmEntry('llm', rawLlm, true, env, issues);
|
|
568
|
+
}
|
|
569
|
+
else {
|
|
570
|
+
// Map shape — llm.main is required; every named entry is validated.
|
|
571
|
+
const map = rawLlm;
|
|
572
|
+
if (!map.main) {
|
|
573
|
+
issues.push("llm.main: required when 'llm' is a named map");
|
|
574
|
+
}
|
|
575
|
+
else {
|
|
576
|
+
validateLlmEntry('llm.main', map.main, true, env, issues);
|
|
577
|
+
}
|
|
578
|
+
for (const [name, entry] of Object.entries(map)) {
|
|
579
|
+
if (name === 'main')
|
|
580
|
+
continue;
|
|
581
|
+
validateLlmEntry(`llm.${name}`, entry, true, env, issues);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
357
584
|
}
|
|
358
585
|
if (get(yaml, 'mcp')) {
|
|
359
586
|
const mcpType = get(yaml, 'mcp', 'type');
|
|
@@ -478,14 +705,18 @@ export function resolveSmartServerConfig(args = {}, yaml = {}, env = process.env
|
|
|
478
705
|
const resolved = {
|
|
479
706
|
port: Number(args.port ?? get(yaml, 'port') ?? env.PORT ?? 4004),
|
|
480
707
|
host: args.host ?? get(yaml, 'host') ?? '0.0.0.0',
|
|
481
|
-
llm:
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
708
|
+
llm: get(yaml, 'llm')
|
|
709
|
+
? typeof get(yaml, 'llm', 'provider') === 'string'
|
|
710
|
+
? {
|
|
711
|
+
provider: get(yaml, 'llm', 'provider'),
|
|
712
|
+
apiKey,
|
|
713
|
+
url: get(yaml, 'llm', 'url'),
|
|
714
|
+
model: get(yaml, 'llm', 'model'),
|
|
715
|
+
temperature: Number(get(yaml, 'llm', 'temperature') ?? 0.7),
|
|
716
|
+
classifierTemperature: Number(get(yaml, 'llm', 'classifierTemperature') ?? 0.1),
|
|
717
|
+
}
|
|
718
|
+
: get(yaml, 'llm')
|
|
719
|
+
: undefined,
|
|
489
720
|
rag: get(yaml, 'rag')
|
|
490
721
|
? {
|
|
491
722
|
type: get(yaml, 'rag', 'type'),
|