@librechat/agents 3.6.16 → 3.7.1
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/cjs/_virtual/_rolldown/runtime.cjs +10 -0
- package/dist/cjs/agents/AgentContext.cjs +11 -2
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +12 -9
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/lazyRequire.cjs +33 -0
- package/dist/cjs/lazyRequire.cjs.map +1 -0
- package/dist/cjs/llm/anthropic/index.cjs +13 -0
- package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/index.cjs +10 -1
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/toolCache.cjs +3 -2
- package/dist/cjs/llm/bedrock/toolCache.cjs.map +1 -1
- package/dist/cjs/llm/google/index.cjs +10 -1
- package/dist/cjs/llm/google/index.cjs.map +1 -1
- package/dist/cjs/llm/init.cjs +6 -4
- package/dist/cjs/llm/init.cjs.map +1 -1
- package/dist/cjs/llm/invoke.cjs +56 -9
- package/dist/cjs/llm/invoke.cjs.map +1 -1
- package/dist/cjs/llm/mistral/index.cjs +9 -0
- package/dist/cjs/llm/mistral/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +42 -2
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openrouter/index.cjs +12 -3
- package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
- package/dist/cjs/llm/openrouter/toolCache.cjs +3 -2
- package/dist/cjs/llm/openrouter/toolCache.cjs.map +1 -1
- package/dist/cjs/llm/providerRegistry.cjs +15 -3
- package/dist/cjs/llm/providerRegistry.cjs.map +1 -1
- package/dist/cjs/llm/providers.cjs +18 -22
- package/dist/cjs/llm/providers.cjs.map +1 -1
- package/dist/cjs/llm/providers.eager.cjs +21 -0
- package/dist/cjs/llm/providers.eager.cjs.map +1 -0
- package/dist/cjs/llm/vertexai/index.cjs +17 -0
- package/dist/cjs/llm/vertexai/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +19 -11
- package/dist/cjs/messages/content.cjs +8 -5
- package/dist/cjs/messages/content.cjs.map +1 -1
- package/dist/cjs/messages/core.cjs +9 -4
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +15 -5
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/messages/index.cjs +2 -1
- package/dist/cjs/messages/projectionInvariant.cjs +74 -0
- package/dist/cjs/messages/projectionInvariant.cjs.map +1 -0
- package/dist/cjs/messages/provenance.cjs +30 -8
- package/dist/cjs/messages/provenance.cjs.map +1 -1
- package/dist/cjs/messages/prune.cjs +42 -8
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/messages/recency.cjs +202 -7
- package/dist/cjs/messages/recency.cjs.map +1 -1
- package/dist/cjs/messages/toolResultTypes.cjs +1 -0
- package/dist/cjs/run.cjs +7 -2
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/AgentSession.cjs +4 -23
- package/dist/cjs/session/AgentSession.cjs.map +1 -1
- package/dist/cjs/session/deriveMessages.cjs +25 -0
- package/dist/cjs/session/deriveMessages.cjs.map +1 -0
- package/dist/cjs/session/index.cjs +1 -0
- package/dist/cjs/summarization/node.cjs +13 -6
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/Calculator.cjs +7 -4
- package/dist/cjs/tools/Calculator.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +1 -1
- package/dist/cjs/tools/subagent/InMemorySubagentTaskStore.cjs +65 -8
- package/dist/cjs/tools/subagent/InMemorySubagentTaskStore.cjs.map +1 -1
- package/dist/cjs/utils/llm.cjs +16 -0
- package/dist/cjs/utils/llm.cjs.map +1 -1
- package/dist/esm/_virtual/_rolldown/runtime.mjs +14 -0
- package/dist/esm/agents/AgentContext.mjs +11 -2
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +12 -9
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/lazyRequire.mjs +30 -0
- package/dist/esm/lazyRequire.mjs.map +1 -0
- package/dist/esm/llm/anthropic/index.mjs +6 -1
- package/dist/esm/llm/anthropic/index.mjs.map +1 -1
- package/dist/esm/llm/bedrock/index.mjs +4 -2
- package/dist/esm/llm/bedrock/index.mjs.map +1 -1
- package/dist/esm/llm/bedrock/toolCache.mjs +3 -2
- package/dist/esm/llm/bedrock/toolCache.mjs.map +1 -1
- package/dist/esm/llm/google/index.mjs +4 -2
- package/dist/esm/llm/google/index.mjs.map +1 -1
- package/dist/esm/llm/init.mjs +7 -5
- package/dist/esm/llm/init.mjs.map +1 -1
- package/dist/esm/llm/invoke.mjs +56 -9
- package/dist/esm/llm/invoke.mjs.map +1 -1
- package/dist/esm/llm/mistral/index.mjs +3 -1
- package/dist/esm/llm/mistral/index.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +32 -10
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openrouter/index.mjs +3 -1
- package/dist/esm/llm/openrouter/index.mjs.map +1 -1
- package/dist/esm/llm/openrouter/toolCache.mjs +3 -2
- package/dist/esm/llm/openrouter/toolCache.mjs.map +1 -1
- package/dist/esm/llm/providerRegistry.mjs +15 -3
- package/dist/esm/llm/providerRegistry.mjs.map +1 -1
- package/dist/esm/llm/providers.eager.mjs +22 -0
- package/dist/esm/llm/providers.eager.mjs.map +1 -0
- package/dist/esm/llm/providers.mjs +19 -23
- package/dist/esm/llm/providers.mjs.map +1 -1
- package/dist/esm/llm/vertexai/index.mjs +8 -1
- package/dist/esm/llm/vertexai/index.mjs.map +1 -1
- package/dist/esm/main.mjs +10 -11
- package/dist/esm/messages/content.mjs +8 -6
- package/dist/esm/messages/content.mjs.map +1 -1
- package/dist/esm/messages/core.mjs +9 -5
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +16 -6
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/messages/index.mjs +2 -1
- package/dist/esm/messages/projectionInvariant.mjs +72 -0
- package/dist/esm/messages/projectionInvariant.mjs.map +1 -0
- package/dist/esm/messages/provenance.mjs +30 -9
- package/dist/esm/messages/provenance.mjs.map +1 -1
- package/dist/esm/messages/prune.mjs +42 -9
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/messages/recency.mjs +201 -8
- package/dist/esm/messages/recency.mjs.map +1 -1
- package/dist/esm/messages/toolResultTypes.mjs +1 -1
- package/dist/esm/run.mjs +8 -3
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/AgentSession.mjs +4 -23
- package/dist/esm/session/AgentSession.mjs.map +1 -1
- package/dist/esm/session/deriveMessages.mjs +25 -0
- package/dist/esm/session/deriveMessages.mjs.map +1 -0
- package/dist/esm/session/index.mjs +1 -0
- package/dist/esm/summarization/node.mjs +14 -7
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/Calculator.mjs +7 -2
- package/dist/esm/tools/Calculator.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +1 -1
- package/dist/esm/tools/subagent/InMemorySubagentTaskStore.mjs +65 -8
- package/dist/esm/tools/subagent/InMemorySubagentTaskStore.mjs.map +1 -1
- package/dist/esm/utils/llm.mjs +15 -1
- package/dist/esm/utils/llm.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +6 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/lazyRequire.d.ts +8 -0
- package/dist/types/llm/providerRegistry.d.ts +11 -0
- package/dist/types/llm/providers.eager.d.ts +1 -0
- package/dist/types/messages/content.d.ts +4 -1
- package/dist/types/messages/core.d.ts +2 -0
- package/dist/types/messages/format.d.ts +6 -0
- package/dist/types/messages/index.d.ts +1 -0
- package/dist/types/messages/projectionInvariant.d.ts +25 -0
- package/dist/types/messages/provenance.d.ts +10 -0
- package/dist/types/messages/prune.d.ts +5 -4
- package/dist/types/messages/recency.d.ts +30 -18
- package/dist/types/session/deriveMessages.d.ts +11 -0
- package/dist/types/session/index.d.ts +2 -0
- package/dist/types/tools/subagent/InMemorySubagentTaskStore.d.ts +13 -1
- package/dist/types/types/graph.d.ts +1 -1
- package/dist/types/types/subagentTasks.d.ts +22 -0
- package/dist/types/types/summarize.d.ts +15 -13
- package/dist/types/utils/llm.d.ts +10 -0
- package/package.json +60 -1
- package/src/agents/AgentContext.ts +19 -5
- package/src/graphs/Graph.ts +11 -9
- package/src/index.ts +3 -3
- package/src/lazyRequire.ts +67 -0
- package/src/llm/bedrock/toolCache.ts +6 -2
- package/src/llm/init.ts +15 -8
- package/src/llm/invoke.ts +102 -23
- package/src/llm/openrouter/toolCache.ts +8 -2
- package/src/llm/providerRegistry.ts +41 -1
- package/src/llm/providers.eager.ts +24 -0
- package/src/llm/providers.ts +63 -49
- package/src/messages/content.ts +20 -10
- package/src/messages/core.ts +16 -11
- package/src/messages/format.ts +29 -5
- package/src/messages/index.ts +1 -0
- package/src/messages/projectionInvariant.ts +134 -0
- package/src/messages/provenance.ts +60 -18
- package/src/messages/prune.ts +86 -11
- package/src/messages/recency.ts +429 -27
- package/src/run.ts +14 -6
- package/src/session/AgentSession.ts +4 -30
- package/src/session/deriveMessages.ts +37 -0
- package/src/session/index.ts +2 -0
- package/src/summarization/node.ts +37 -15
- package/src/tools/Calculator.ts +9 -2
- package/src/tools/subagent/InMemorySubagentTaskStore.ts +139 -6
- package/src/types/graph.ts +1 -0
- package/src/types/subagentTasks.ts +28 -0
- package/src/types/summarize.ts +15 -13
- package/src/utils/llm.ts +39 -0
|
@@ -5,7 +5,13 @@ import {
|
|
|
5
5
|
buildAnthropicCacheControl,
|
|
6
6
|
type PromptCacheTtl,
|
|
7
7
|
} from '@/messages/cache';
|
|
8
|
-
import {
|
|
8
|
+
import { requireInternalModule } from '@/lazyRequire';
|
|
9
|
+
|
|
10
|
+
/** Loads with the first OpenRouter request rather than alongside every graph. */
|
|
11
|
+
const convertToOpenAITool = (
|
|
12
|
+
tool: BindToolsInput
|
|
13
|
+
): OpenAIClient.ChatCompletionTool =>
|
|
14
|
+
requireInternalModule<typeof import('@/llm/openai')>('llm/openai/index')._convertToOpenAITool(tool);
|
|
9
15
|
|
|
10
16
|
type OpenRouterCacheControl = { type: 'ephemeral'; ttl?: '1h' };
|
|
11
17
|
|
|
@@ -38,7 +44,7 @@ function hasDeferredMarker(tool: unknown): boolean {
|
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
function toOpenRouterTool(tool: unknown): OpenRouterToolWithCacheControl {
|
|
41
|
-
const converted =
|
|
47
|
+
const converted = convertToOpenAITool(
|
|
42
48
|
tool as BindToolsInput
|
|
43
49
|
) as OpenRouterToolWithCacheControl;
|
|
44
50
|
|
|
@@ -5,8 +5,13 @@ import type {
|
|
|
5
5
|
} from '../provider-registration';
|
|
6
6
|
import type { ProviderModelConstructor, ProviderName } from '@/types';
|
|
7
7
|
|
|
8
|
+
type ProviderModelClass = new (config: never) => BaseChatModel;
|
|
9
|
+
|
|
8
10
|
interface StoredProviderRegistration {
|
|
9
|
-
model
|
|
11
|
+
model?: ProviderModelClass;
|
|
12
|
+
/** Built-ins defer their provider SDK import until the first model request;
|
|
13
|
+
* the resolved class is validated once and memoized into `model`. */
|
|
14
|
+
loadModel?: () => ProviderModelClass;
|
|
10
15
|
family: ProviderFamily;
|
|
11
16
|
manualToolStream: boolean;
|
|
12
17
|
strictAlternation: boolean;
|
|
@@ -107,6 +112,31 @@ export function registerBuiltInProvider<
|
|
|
107
112
|
builtInProviders.set(provider, createRegistration(provider, options));
|
|
108
113
|
}
|
|
109
114
|
|
|
115
|
+
interface BuiltInProviderLoaderOptions {
|
|
116
|
+
provider: ProviderName;
|
|
117
|
+
loadModel: () => ProviderModelClass;
|
|
118
|
+
family?: ProviderFamily;
|
|
119
|
+
manualToolStream?: boolean;
|
|
120
|
+
strictAlternation?: boolean;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Registers a built-in whose provider SDK loads on first use, not at import time. */
|
|
124
|
+
export function registerBuiltInProviderLoader(
|
|
125
|
+
options: BuiltInProviderLoaderOptions
|
|
126
|
+
): void {
|
|
127
|
+
const provider = normalizeProvider(options.provider);
|
|
128
|
+
if (builtInProviders.has(provider)) {
|
|
129
|
+
throw new Error(`LLM provider already registered: ${provider}`);
|
|
130
|
+
}
|
|
131
|
+
builtInProviders.set(provider, {
|
|
132
|
+
loadModel: options.loadModel,
|
|
133
|
+
family: options.family ?? 'generic',
|
|
134
|
+
manualToolStream: options.manualToolStream ?? false,
|
|
135
|
+
strictAlternation: options.strictAlternation ?? false,
|
|
136
|
+
owner: Symbol(provider),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
110
140
|
export function getRegisteredChatModelClass<P extends ProviderName>(
|
|
111
141
|
provider: P
|
|
112
142
|
): ProviderModelConstructor<P> {
|
|
@@ -114,6 +144,16 @@ export function getRegisteredChatModelClass<P extends ProviderName>(
|
|
|
114
144
|
if (!registration) {
|
|
115
145
|
throw new Error(`Unsupported LLM provider: ${provider}`);
|
|
116
146
|
}
|
|
147
|
+
if (registration.model == null && registration.loadModel != null) {
|
|
148
|
+
const loaded = registration.loadModel();
|
|
149
|
+
if (typeof loaded !== 'function' || !isConstructible(loaded)) {
|
|
150
|
+
throw new TypeError(`LLM provider constructor is invalid: ${provider}`);
|
|
151
|
+
}
|
|
152
|
+
registration.model = loaded;
|
|
153
|
+
}
|
|
154
|
+
if (registration.model == null) {
|
|
155
|
+
throw new Error(`Unsupported LLM provider: ${provider}`);
|
|
156
|
+
}
|
|
117
157
|
return registration.model as ProviderModelConstructor<P>;
|
|
118
158
|
}
|
|
119
159
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as openai from '@/llm/openai';
|
|
2
|
+
import * as google from '@/llm/google';
|
|
3
|
+
import * as bedrock from '@/llm/bedrock';
|
|
4
|
+
import * as mistral from '@/llm/mistral';
|
|
5
|
+
import * as vertexai from '@/llm/vertexai';
|
|
6
|
+
import * as anthropic from '@/llm/anthropic';
|
|
7
|
+
import * as openrouter from '@/llm/openrouter';
|
|
8
|
+
import { registerSourceModeModules } from '@/lazyRequire';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Source-mode counterpart of the lazy provider loading the built package uses: commands
|
|
12
|
+
* that run the TypeScript directly import this module first, so every lazily loadable
|
|
13
|
+
* provider module is loaded through the active ESM loader — one module graph, one set of
|
|
14
|
+
* class identities — and registered with the lazy-require seam.
|
|
15
|
+
*/
|
|
16
|
+
registerSourceModeModules({
|
|
17
|
+
'llm/openai/index': openai,
|
|
18
|
+
'llm/google/index': google,
|
|
19
|
+
'llm/bedrock/index': bedrock,
|
|
20
|
+
'llm/mistral/index': mistral,
|
|
21
|
+
'llm/vertexai/index': vertexai,
|
|
22
|
+
'llm/anthropic/index': anthropic,
|
|
23
|
+
'llm/openrouter/index': openrouter,
|
|
24
|
+
});
|
package/src/llm/providers.ts
CHANGED
|
@@ -1,81 +1,95 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
ProviderModelConstructor,
|
|
4
|
-
ProviderName,
|
|
5
|
-
} from '@/types';
|
|
1
|
+
import type { ProviderModelConstructor, ProviderName } from '@/types';
|
|
2
|
+
import { requireInternalModule } from '@/lazyRequire';
|
|
6
3
|
import type { ProviderFamily } from '../provider-registration';
|
|
7
|
-
import {
|
|
8
|
-
AzureChatOpenAI,
|
|
9
|
-
ChatDeepSeek,
|
|
10
|
-
ChatMoonshot,
|
|
11
|
-
ChatOpenAI,
|
|
12
|
-
ChatXAI,
|
|
13
|
-
} from '@/llm/openai';
|
|
4
|
+
import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
14
5
|
import {
|
|
15
6
|
getRegisteredChatModelClass,
|
|
16
|
-
|
|
7
|
+
registerBuiltInProviderLoader,
|
|
17
8
|
} from '@/llm/providerRegistry';
|
|
18
|
-
import { CustomChatGoogleGenerativeAI } from '@/llm/google';
|
|
19
|
-
import { CustomChatBedrockConverse } from '@/llm/bedrock';
|
|
20
|
-
import { CustomChatMistralAI } from '@/llm/mistral';
|
|
21
|
-
import { CustomAnthropic } from '@/llm/anthropic';
|
|
22
|
-
import { ChatOpenRouter } from '@/llm/openrouter';
|
|
23
|
-
import { ChatVertexAI } from '@/llm/vertexai';
|
|
24
9
|
import { Providers } from '@/common';
|
|
25
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Built-in provider SDKs load on first model request, not at import time: eagerly
|
|
13
|
+
* importing every provider cost hundreds of milliseconds of boot in hosts that
|
|
14
|
+
* configure one or two of them. `createRequire` keeps resolution synchronous and
|
|
15
|
+
* correct from both the CJS and ESM builds.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
type ProviderModelClass = new (config: never) => BaseChatModel;
|
|
19
|
+
|
|
26
20
|
type BuiltInProviderTraits = {
|
|
27
21
|
family: ProviderFamily;
|
|
28
22
|
manualToolStream?: boolean;
|
|
29
23
|
strictAlternation?: boolean;
|
|
30
24
|
};
|
|
31
25
|
|
|
32
|
-
function initializeBuiltInProvider
|
|
33
|
-
provider:
|
|
34
|
-
|
|
26
|
+
function initializeBuiltInProvider(
|
|
27
|
+
provider: Providers,
|
|
28
|
+
loadModel: () => ProviderModelClass,
|
|
35
29
|
traits: BuiltInProviderTraits
|
|
36
30
|
): void {
|
|
37
|
-
|
|
31
|
+
registerBuiltInProviderLoader({
|
|
38
32
|
provider,
|
|
39
|
-
|
|
33
|
+
loadModel,
|
|
40
34
|
...traits,
|
|
41
35
|
});
|
|
42
36
|
}
|
|
43
37
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
const fromOpenAI =
|
|
39
|
+
(name: string) => (): ProviderModelClass =>
|
|
40
|
+
(requireInternalModule<Record<string, ProviderModelClass>>('llm/openai/index'))[
|
|
41
|
+
name
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
initializeBuiltInProvider(Providers.XAI, fromOpenAI('ChatXAI'), { family: 'openai' });
|
|
45
|
+
initializeBuiltInProvider(Providers.OPENAI, fromOpenAI('ChatOpenAI'), { family: 'openai' });
|
|
46
|
+
initializeBuiltInProvider(Providers.AZURE, fromOpenAI('AzureChatOpenAI'), {
|
|
47
47
|
family: 'openai',
|
|
48
48
|
});
|
|
49
|
-
initializeBuiltInProvider(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
initializeBuiltInProvider(
|
|
50
|
+
Providers.VERTEXAI,
|
|
51
|
+
() => (requireInternalModule<typeof import('@/llm/vertexai')>('llm/vertexai/index')).ChatVertexAI,
|
|
52
|
+
{ family: 'google' }
|
|
53
|
+
);
|
|
54
|
+
initializeBuiltInProvider(Providers.DEEPSEEK, fromOpenAI('ChatDeepSeek'), {
|
|
53
55
|
family: 'openai',
|
|
54
56
|
});
|
|
55
|
-
|
|
57
|
+
const loadMistral = (): ProviderModelClass =>
|
|
58
|
+
(requireInternalModule<typeof import('@/llm/mistral')>('llm/mistral/index')).CustomChatMistralAI;
|
|
59
|
+
initializeBuiltInProvider(Providers.MISTRALAI, loadMistral, {
|
|
56
60
|
family: 'mistral',
|
|
57
61
|
strictAlternation: true,
|
|
58
62
|
});
|
|
59
|
-
initializeBuiltInProvider(Providers.MISTRAL,
|
|
63
|
+
initializeBuiltInProvider(Providers.MISTRAL, loadMistral, {
|
|
60
64
|
family: 'mistral',
|
|
61
65
|
strictAlternation: true,
|
|
62
66
|
});
|
|
63
|
-
initializeBuiltInProvider(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
67
|
+
initializeBuiltInProvider(
|
|
68
|
+
Providers.ANTHROPIC,
|
|
69
|
+
() => (requireInternalModule<typeof import('@/llm/anthropic')>('llm/anthropic/index')).CustomAnthropic,
|
|
70
|
+
{ family: 'anthropic', manualToolStream: true }
|
|
71
|
+
);
|
|
72
|
+
initializeBuiltInProvider(
|
|
73
|
+
Providers.OPENROUTER,
|
|
74
|
+
() =>
|
|
75
|
+
(requireInternalModule<typeof import('@/llm/openrouter')>('llm/openrouter/index')).ChatOpenRouter,
|
|
76
|
+
{ family: 'openai' }
|
|
77
|
+
);
|
|
78
|
+
initializeBuiltInProvider(
|
|
79
|
+
Providers.BEDROCK,
|
|
80
|
+
() =>
|
|
81
|
+
(requireInternalModule<typeof import('@/llm/bedrock')>('llm/bedrock/index'))
|
|
82
|
+
.CustomChatBedrockConverse,
|
|
83
|
+
{ family: 'bedrock', manualToolStream: true, strictAlternation: true }
|
|
84
|
+
);
|
|
85
|
+
initializeBuiltInProvider(
|
|
86
|
+
Providers.GOOGLE,
|
|
87
|
+
() =>
|
|
88
|
+
(requireInternalModule<typeof import('@/llm/google')>('llm/google/index'))
|
|
89
|
+
.CustomChatGoogleGenerativeAI,
|
|
90
|
+
{ family: 'google' }
|
|
91
|
+
);
|
|
92
|
+
initializeBuiltInProvider(Providers.MOONSHOT, fromOpenAI('ChatMoonshot'), {
|
|
79
93
|
family: 'generic',
|
|
80
94
|
});
|
|
81
95
|
|
package/src/messages/content.ts
CHANGED
|
@@ -22,6 +22,20 @@ export const isLegacyConvertible = (message: BaseMessage): boolean => {
|
|
|
22
22
|
return message.content.every((block) => block.type === ContentTypes.TEXT);
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
+
/** Joins the text of {@link isLegacyConvertible} content blocks into the exact
|
|
26
|
+
* string {@link formatContentStrings} has always produced for them. */
|
|
27
|
+
export const flattenLegacyContent = (
|
|
28
|
+
blocks: MessageContentComplex[]
|
|
29
|
+
): string => {
|
|
30
|
+
const content = blocks.reduce((acc, curr) => {
|
|
31
|
+
if (curr.type === ContentTypes.TEXT) {
|
|
32
|
+
return `${acc}${curr[ContentTypes.TEXT] ?? ''}\n`;
|
|
33
|
+
}
|
|
34
|
+
return acc;
|
|
35
|
+
}, '');
|
|
36
|
+
return content.trim();
|
|
37
|
+
};
|
|
38
|
+
|
|
25
39
|
/**
|
|
26
40
|
* Formats an array of messages for LangChain, making sure all content fields are strings
|
|
27
41
|
* @param {Array<HumanMessage | AIMessage | SystemMessage | ToolMessage>} payload - The array of messages to format.
|
|
@@ -39,16 +53,12 @@ export const formatContentStrings = (
|
|
|
39
53
|
continue;
|
|
40
54
|
}
|
|
41
55
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return acc;
|
|
49
|
-
}, '');
|
|
50
|
-
|
|
51
|
-
result.push(cloneMessage(message, content.trim()));
|
|
56
|
+
result.push(
|
|
57
|
+
cloneMessage(
|
|
58
|
+
message,
|
|
59
|
+
flattenLegacyContent(message.content as MessageContentComplex[])
|
|
60
|
+
)
|
|
61
|
+
);
|
|
52
62
|
}
|
|
53
63
|
|
|
54
64
|
return result;
|
package/src/messages/core.ts
CHANGED
|
@@ -980,6 +980,19 @@ function isCompleteToolStreamContentBlock(block: unknown): boolean {
|
|
|
980
980
|
}
|
|
981
981
|
}
|
|
982
982
|
|
|
983
|
+
/** Drops incomplete streamed text blocks before provider-facing tool shaping. */
|
|
984
|
+
export function dropIncompleteToolStreamContent(
|
|
985
|
+
content: AIMessage['content']
|
|
986
|
+
): AIMessage['content'] {
|
|
987
|
+
if (!Array.isArray(content)) {
|
|
988
|
+
return content;
|
|
989
|
+
}
|
|
990
|
+
const completeContent = content.filter(isCompleteToolStreamContentBlock);
|
|
991
|
+
return completeContent.length === content.length
|
|
992
|
+
? content
|
|
993
|
+
: toLangChainContent(completeContent);
|
|
994
|
+
}
|
|
995
|
+
|
|
983
996
|
function projectPreemptedResponsesV1Content(
|
|
984
997
|
content: AIMessage['content'],
|
|
985
998
|
reasoning: unknown,
|
|
@@ -2026,20 +2039,12 @@ export function projectToolStreamContentForProvider(
|
|
|
2026
2039
|
}
|
|
2027
2040
|
continue;
|
|
2028
2041
|
}
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
}
|
|
2032
|
-
const content = assistantMessage.content.filter(
|
|
2033
|
-
isCompleteToolStreamContentBlock
|
|
2034
|
-
);
|
|
2035
|
-
if (content.length === assistantMessage.content.length) {
|
|
2042
|
+
const content = dropIncompleteToolStreamContent(assistantMessage.content);
|
|
2043
|
+
if (content === assistantMessage.content) {
|
|
2036
2044
|
continue;
|
|
2037
2045
|
}
|
|
2038
2046
|
projected ??= [...messages];
|
|
2039
|
-
projected[i] = cloneAIMessageWithContent(
|
|
2040
|
-
assistantMessage,
|
|
2041
|
-
toLangChainContent(content)
|
|
2042
|
-
);
|
|
2047
|
+
projected[i] = cloneAIMessageWithContent(assistantMessage, content);
|
|
2043
2048
|
}
|
|
2044
2049
|
return projected ?? messages;
|
|
2045
2050
|
}
|
package/src/messages/format.ts
CHANGED
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
hasBijectiveProviderContentPartMapping,
|
|
47
47
|
inspectProviderMessageProvenance,
|
|
48
48
|
inspectProviderSourceMessageIds,
|
|
49
|
+
setFreshProviderMessageProvenance,
|
|
49
50
|
setInvalidProviderMessageProvenance,
|
|
50
51
|
setProviderMessageProvenance,
|
|
51
52
|
} from './provenance';
|
|
@@ -57,6 +58,7 @@ import {
|
|
|
57
58
|
} from '@/utils/toolContent';
|
|
58
59
|
import { normalizeAnthropicToolCallId } from '@/llm/anthropic/utils/message_inputs';
|
|
59
60
|
import { toLangChainContent, toLangChainMessageFields } from './langchain';
|
|
61
|
+
import { flattenLegacyContent, isLegacyConvertible } from './content';
|
|
60
62
|
import { HARD_MAX_TOOL_RESULT_CHARS } from '@/utils/truncation';
|
|
61
63
|
import { Providers, ContentTypes, Constants } from '@/common';
|
|
62
64
|
import { emitAgentLog } from '@/utils/events';
|
|
@@ -378,6 +380,12 @@ type SourceContentPartIndices = number | readonly number[];
|
|
|
378
380
|
|
|
379
381
|
interface FormatAgentMessagesOptions {
|
|
380
382
|
provider?: ProviderName;
|
|
383
|
+
/** Emit flattenable text content as the joined string the legacy-content
|
|
384
|
+
* projection would produce, so the per-request `formatContentStrings` pass
|
|
385
|
+
* finds nothing to convert and every history message keeps its identity —
|
|
386
|
+
* which is what lets exact-count reuse skip re-tokenizing it. Set this if
|
|
387
|
+
* and only if the run's provider uses legacy string content. */
|
|
388
|
+
legacyContent?: boolean;
|
|
381
389
|
/** Reconstruct hidden `reasoning_content` from `THINK` parts onto prior
|
|
382
390
|
* tool-call messages. Explicit opt-in for OpenAI-compatible endpoints that
|
|
383
391
|
* replay reasoning across turns; defaults to on for DeepSeek thinking-mode. */
|
|
@@ -1380,7 +1388,7 @@ function stampSourceMessageIdentity(
|
|
|
1380
1388
|
},
|
|
1381
1389
|
];
|
|
1382
1390
|
}
|
|
1383
|
-
|
|
1391
|
+
setFreshProviderMessageProvenance(message, partsToStamp);
|
|
1384
1392
|
if (sourceMessageId == null || derivedIndex !== 0) {
|
|
1385
1393
|
return;
|
|
1386
1394
|
}
|
|
@@ -1996,6 +2004,22 @@ export const formatAgentMessages = (
|
|
|
1996
2004
|
* assistant turn. Held rather than emitted so an entry that produces
|
|
1997
2005
|
* nothing cannot leave the anchor stranded as the final turn.
|
|
1998
2006
|
*/
|
|
2007
|
+
const legacyContentEnabled = options?.legacyContent === true;
|
|
2008
|
+
/** Emission choke point: every formatted message enters the result here, so
|
|
2009
|
+
* the legacy flatten happens once per message with no closing rescan. The
|
|
2010
|
+
* summary boundary slices payload entries before formatting, and nothing
|
|
2011
|
+
* mutates an emitted message's content afterwards, so flattening at
|
|
2012
|
+
* emission and flattening at return are equivalent. */
|
|
2013
|
+
const emitFormattedMessage = (message: (typeof messages)[number]): void => {
|
|
2014
|
+
if (legacyContentEnabled && isLegacyConvertible(message)) {
|
|
2015
|
+
const flattened = flattenLegacyContent(
|
|
2016
|
+
message.content as MessageContentComplex[]
|
|
2017
|
+
);
|
|
2018
|
+
message.content = flattened;
|
|
2019
|
+
message.lc_kwargs.content = flattened;
|
|
2020
|
+
}
|
|
2021
|
+
messages.push(message);
|
|
2022
|
+
};
|
|
1999
2023
|
let pendingSteerAnchor = false;
|
|
2000
2024
|
/**
|
|
2001
2025
|
* Emits the deferred anchor ahead of `next` — the message about to be
|
|
@@ -2018,7 +2042,7 @@ export const formatAgentMessages = (
|
|
|
2018
2042
|
'assistant'
|
|
2019
2043
|
);
|
|
2020
2044
|
stampSourceMessageIdentity(anchor, undefined, 0, 'synthetic');
|
|
2021
|
-
|
|
2045
|
+
emitFormattedMessage(anchor);
|
|
2022
2046
|
};
|
|
2023
2047
|
// If indexTokenCountMap is provided, create a new map to track the updated indices
|
|
2024
2048
|
const updatedIndexTokenCountMap: Record<number, number> = {};
|
|
@@ -2098,7 +2122,7 @@ export const formatAgentMessages = (
|
|
|
2098
2122
|
provenanceParts
|
|
2099
2123
|
);
|
|
2100
2124
|
flushSteerAnchor(formattedMessage);
|
|
2101
|
-
|
|
2125
|
+
emitFormattedMessage(formattedMessage);
|
|
2102
2126
|
|
|
2103
2127
|
// Update the index mapping for this message
|
|
2104
2128
|
indexMapping[i] = [messages.length - 1];
|
|
@@ -2359,7 +2383,7 @@ export const formatAgentMessages = (
|
|
|
2359
2383
|
flushSteerAnchor(formattedMessages[0]);
|
|
2360
2384
|
}
|
|
2361
2385
|
for (const formattedMessage of formattedMessages) {
|
|
2362
|
-
|
|
2386
|
+
emitFormattedMessage(formattedMessage);
|
|
2363
2387
|
}
|
|
2364
2388
|
if (endsWithSteerMessage(formattedMessages)) {
|
|
2365
2389
|
pendingSteerAnchor = true;
|
|
@@ -2390,7 +2414,7 @@ export const formatAgentMessages = (
|
|
|
2390
2414
|
'user'
|
|
2391
2415
|
);
|
|
2392
2416
|
stampSourceMessageIdentity(skillMessage, undefined, 0, 'synthetic');
|
|
2393
|
-
|
|
2417
|
+
emitFormattedMessage(skillMessage);
|
|
2394
2418
|
}
|
|
2395
2419
|
}
|
|
2396
2420
|
}
|
package/src/messages/index.ts
CHANGED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { BaseMessage } from '@langchain/core/messages';
|
|
2
|
+
import { inspectProviderMessageProvenance } from './provenance';
|
|
3
|
+
|
|
4
|
+
export type ProviderMessageProjectionInvariantMode =
|
|
5
|
+
| 'off'
|
|
6
|
+
| 'observe'
|
|
7
|
+
| 'assert';
|
|
8
|
+
|
|
9
|
+
export type ProviderMessageProjectionIssueCode =
|
|
10
|
+
| 'absent_provenance'
|
|
11
|
+
| 'invalid_provenance'
|
|
12
|
+
| 'unsourced_non_synthetic_part';
|
|
13
|
+
|
|
14
|
+
export interface ProviderMessageProjectionInvariantIssue {
|
|
15
|
+
readonly code: ProviderMessageProjectionIssueCode;
|
|
16
|
+
readonly messageIndex: number;
|
|
17
|
+
readonly messageType: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ProviderMessageProjectionInvariantReport {
|
|
21
|
+
readonly valid: boolean;
|
|
22
|
+
readonly messageCount: number;
|
|
23
|
+
readonly sourceBackedMessageCount: number;
|
|
24
|
+
readonly syntheticMessageCount: number;
|
|
25
|
+
readonly gapMessageCount: number;
|
|
26
|
+
readonly issues: readonly ProviderMessageProjectionInvariantIssue[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const MAX_REPORTED_PROJECTION_ISSUES = 64;
|
|
30
|
+
|
|
31
|
+
function getMessageType(message: BaseMessage): string {
|
|
32
|
+
try {
|
|
33
|
+
return typeof message.type === 'string' ? message.type : 'unknown';
|
|
34
|
+
} catch {
|
|
35
|
+
return 'unknown';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function appendIssue(
|
|
40
|
+
issues: ProviderMessageProjectionInvariantIssue[],
|
|
41
|
+
code: ProviderMessageProjectionIssueCode,
|
|
42
|
+
messageIndex: number,
|
|
43
|
+
messageType: string
|
|
44
|
+
): void {
|
|
45
|
+
if (issues.length >= MAX_REPORTED_PROJECTION_ISSUES) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
issues.push(Object.freeze({ code, messageIndex, messageType }));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Resolves the opt-in provider projection invariant without enabling it for
|
|
52
|
+
* unrecognized values. */
|
|
53
|
+
export function resolveProviderMessageProjectionInvariantMode(
|
|
54
|
+
value = process.env.AGENT_MESSAGE_PROJECTION_INVARIANT
|
|
55
|
+
): ProviderMessageProjectionInvariantMode {
|
|
56
|
+
if (value === 'observe' || value === 'assert') {
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
return 'off';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Inspects provider-bound lineage without reading message content or source ids. */
|
|
63
|
+
export function inspectProviderMessageProjection(
|
|
64
|
+
messages: readonly BaseMessage[]
|
|
65
|
+
): ProviderMessageProjectionInvariantReport {
|
|
66
|
+
const issues: ProviderMessageProjectionInvariantIssue[] = [];
|
|
67
|
+
let sourceBackedMessageCount = 0;
|
|
68
|
+
let syntheticMessageCount = 0;
|
|
69
|
+
let gapMessageCount = 0;
|
|
70
|
+
|
|
71
|
+
for (let messageIndex = 0; messageIndex < messages.length; messageIndex++) {
|
|
72
|
+
const message = messages[messageIndex];
|
|
73
|
+
const messageType = getMessageType(message);
|
|
74
|
+
const state = inspectProviderMessageProvenance(message);
|
|
75
|
+
if (state.status !== 'valid') {
|
|
76
|
+
gapMessageCount++;
|
|
77
|
+
appendIssue(
|
|
78
|
+
issues,
|
|
79
|
+
state.status === 'absent'
|
|
80
|
+
? 'absent_provenance'
|
|
81
|
+
: 'invalid_provenance',
|
|
82
|
+
messageIndex,
|
|
83
|
+
messageType
|
|
84
|
+
);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let hasSourceBackedPart = false;
|
|
89
|
+
let hasGap = false;
|
|
90
|
+
for (const part of state.provenance.parts) {
|
|
91
|
+
if (part.sourceMessageId != null) {
|
|
92
|
+
hasSourceBackedPart = true;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (part.attribution === 'synthetic') {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
hasGap = true;
|
|
99
|
+
appendIssue(
|
|
100
|
+
issues,
|
|
101
|
+
'unsourced_non_synthetic_part',
|
|
102
|
+
messageIndex,
|
|
103
|
+
messageType
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (hasGap) {
|
|
108
|
+
gapMessageCount++;
|
|
109
|
+
} else if (hasSourceBackedPart) {
|
|
110
|
+
sourceBackedMessageCount++;
|
|
111
|
+
} else {
|
|
112
|
+
syntheticMessageCount++;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return Object.freeze({
|
|
117
|
+
valid: gapMessageCount === 0,
|
|
118
|
+
messageCount: messages.length,
|
|
119
|
+
sourceBackedMessageCount,
|
|
120
|
+
syntheticMessageCount,
|
|
121
|
+
gapMessageCount,
|
|
122
|
+
issues: Object.freeze(issues),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export class ProviderMessageProjectionInvariantError extends Error {
|
|
127
|
+
readonly report: ProviderMessageProjectionInvariantReport;
|
|
128
|
+
|
|
129
|
+
constructor(report: ProviderMessageProjectionInvariantReport) {
|
|
130
|
+
super('Provider message projection invariant failed');
|
|
131
|
+
this.name = 'ProviderMessageProjectionInvariantError';
|
|
132
|
+
this.report = report;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -766,11 +766,14 @@ function publishProviderMessageProvenance(
|
|
|
766
766
|
}
|
|
767
767
|
}
|
|
768
768
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
769
|
+
interface ResolvedProvenancePublication {
|
|
770
|
+
provenance: ProviderMessageProvenance;
|
|
771
|
+
sourceMessageIds?: readonly string[];
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
function resolveProvenancePublication(
|
|
772
775
|
parts: readonly ProviderMessageProvenancePart[]
|
|
773
|
-
):
|
|
776
|
+
): ResolvedProvenancePublication {
|
|
774
777
|
const normalizedParts = normalizeProvenanceParts(parts);
|
|
775
778
|
const provenance: ProviderMessageProvenance = Object.freeze({
|
|
776
779
|
version: PROVIDER_MESSAGE_PROVENANCE_VERSION,
|
|
@@ -782,21 +785,60 @@ export function setProviderMessageProvenance(
|
|
|
782
785
|
for (const part of normalizedParts) {
|
|
783
786
|
appendSourceMessageId(sourceMessageIds, seen, part.sourceMessageId);
|
|
784
787
|
}
|
|
785
|
-
if (sourceMessageIds.length
|
|
786
|
-
|
|
787
|
-
immutableProviderSourceMessageIds.add(immutableSourceMessageIds);
|
|
788
|
-
immutableProviderMessageSourceIds.set(
|
|
789
|
-
provenance,
|
|
790
|
-
immutableSourceMessageIds
|
|
791
|
-
);
|
|
792
|
-
publishProviderMessageProvenance(
|
|
793
|
-
message,
|
|
794
|
-
provenance,
|
|
795
|
-
immutableSourceMessageIds
|
|
796
|
-
);
|
|
797
|
-
return;
|
|
788
|
+
if (sourceMessageIds.length === 0) {
|
|
789
|
+
return { provenance };
|
|
798
790
|
}
|
|
799
|
-
|
|
791
|
+
const immutableSourceMessageIds = Object.freeze(sourceMessageIds);
|
|
792
|
+
immutableProviderSourceMessageIds.add(immutableSourceMessageIds);
|
|
793
|
+
immutableProviderMessageSourceIds.set(provenance, immutableSourceMessageIds);
|
|
794
|
+
return { provenance, sourceMessageIds: immutableSourceMessageIds };
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/** Replaces typed provenance and synchronizes its stable plural source ids. */
|
|
798
|
+
export function setProviderMessageProvenance(
|
|
799
|
+
message: BaseMessage,
|
|
800
|
+
parts: readonly ProviderMessageProvenancePart[]
|
|
801
|
+
): void {
|
|
802
|
+
const resolved = resolveProvenancePublication(parts);
|
|
803
|
+
publishProviderMessageProvenance(
|
|
804
|
+
message,
|
|
805
|
+
resolved.provenance,
|
|
806
|
+
resolved.sourceMessageIds
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* {@link setProviderMessageProvenance} for a message the caller itself just
|
|
812
|
+
* constructed from locally built plain objects. Such a message cannot carry
|
|
813
|
+
* proxies, accessors, or foreign aliases in `additional_kwargs`/`lc_kwargs`,
|
|
814
|
+
* so the publication skips the hardened descriptor walks while producing the
|
|
815
|
+
* same end state: fresh replacement objects on both slots, with the
|
|
816
|
+
* serialization mirror aliasing the live kwargs. Never call this with a
|
|
817
|
+
* message received across a seam — that is what the hardened variant is for.
|
|
818
|
+
*/
|
|
819
|
+
export function setFreshProviderMessageProvenance(
|
|
820
|
+
message: BaseMessage,
|
|
821
|
+
parts: readonly ProviderMessageProvenancePart[]
|
|
822
|
+
): void {
|
|
823
|
+
const resolved = resolveProvenancePublication(parts);
|
|
824
|
+
const replacement: UntrustedProviderMessageAdditionalKwargs = {
|
|
825
|
+
...message.additional_kwargs,
|
|
826
|
+
};
|
|
827
|
+
replacement.provenance = resolved.provenance;
|
|
828
|
+
const sourceMessageIds = resolved.sourceMessageIds;
|
|
829
|
+
if (sourceMessageIds != null && sourceMessageIds.length > 0) {
|
|
830
|
+
replacement.sourceMessageIds = sourceMessageIds;
|
|
831
|
+
replacement.sourceMessageId = sourceMessageIds[sourceMessageIds.length - 1];
|
|
832
|
+
} else {
|
|
833
|
+
delete replacement.sourceMessageIds;
|
|
834
|
+
delete replacement.sourceMessageId;
|
|
835
|
+
}
|
|
836
|
+
const serializedReplacement: BaseMessage['lc_kwargs'] = {
|
|
837
|
+
...message.lc_kwargs,
|
|
838
|
+
};
|
|
839
|
+
serializedReplacement.additional_kwargs = replacement;
|
|
840
|
+
message.additional_kwargs = replacement;
|
|
841
|
+
message.lc_kwargs = serializedReplacement;
|
|
800
842
|
}
|
|
801
843
|
|
|
802
844
|
/** Publishes the canonical fail-closed marker for malformed provenance. */
|