@falai/agent 1.1.2 → 1.2.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/README.md +9 -0
- package/dist/cjs/core/Agent.d.ts +17 -1
- package/dist/cjs/core/Agent.d.ts.map +1 -1
- package/dist/cjs/core/Agent.js +47 -0
- package/dist/cjs/core/Agent.js.map +1 -1
- package/dist/cjs/core/BatchPromptBuilder.d.ts +3 -0
- package/dist/cjs/core/BatchPromptBuilder.d.ts.map +1 -1
- package/dist/cjs/core/BatchPromptBuilder.js +14 -11
- package/dist/cjs/core/BatchPromptBuilder.js.map +1 -1
- package/dist/cjs/core/CompactionEngine.d.ts +65 -0
- package/dist/cjs/core/CompactionEngine.d.ts.map +1 -0
- package/dist/cjs/core/CompactionEngine.js +251 -0
- package/dist/cjs/core/CompactionEngine.js.map +1 -0
- package/dist/cjs/core/PromptComposer.d.ts +8 -1
- package/dist/cjs/core/PromptComposer.d.ts.map +1 -1
- package/dist/cjs/core/PromptComposer.js +238 -118
- package/dist/cjs/core/PromptComposer.js.map +1 -1
- package/dist/cjs/core/PromptSectionCache.d.ts +57 -0
- package/dist/cjs/core/PromptSectionCache.d.ts.map +1 -0
- package/dist/cjs/core/PromptSectionCache.js +108 -0
- package/dist/cjs/core/PromptSectionCache.js.map +1 -0
- package/dist/cjs/core/ResponseEngine.d.ts +3 -0
- package/dist/cjs/core/ResponseEngine.d.ts.map +1 -1
- package/dist/cjs/core/ResponseEngine.js +10 -6
- package/dist/cjs/core/ResponseEngine.js.map +1 -1
- package/dist/cjs/core/ResponseModal.d.ts.map +1 -1
- package/dist/cjs/core/ResponseModal.js +79 -20
- package/dist/cjs/core/ResponseModal.js.map +1 -1
- package/dist/cjs/core/RoutingEngine.d.ts +10 -0
- package/dist/cjs/core/RoutingEngine.d.ts.map +1 -1
- package/dist/cjs/core/RoutingEngine.js +3 -2
- package/dist/cjs/core/RoutingEngine.js.map +1 -1
- package/dist/cjs/core/SessionManager.d.ts.map +1 -1
- package/dist/cjs/core/SessionManager.js +20 -0
- package/dist/cjs/core/SessionManager.js.map +1 -1
- package/dist/cjs/core/StreamingToolExecutor.d.ts +142 -0
- package/dist/cjs/core/StreamingToolExecutor.d.ts.map +1 -0
- package/dist/cjs/core/StreamingToolExecutor.js +455 -0
- package/dist/cjs/core/StreamingToolExecutor.js.map +1 -0
- package/dist/cjs/core/ToolManager.d.ts +18 -1
- package/dist/cjs/core/ToolManager.d.ts.map +1 -1
- package/dist/cjs/core/ToolManager.js +91 -0
- package/dist/cjs/core/ToolManager.js.map +1 -1
- package/dist/cjs/index.d.ts +5 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +8 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/providers/AnthropicProvider.d.ts.map +1 -1
- package/dist/cjs/providers/AnthropicProvider.js +8 -7
- package/dist/cjs/providers/AnthropicProvider.js.map +1 -1
- package/dist/cjs/providers/GeminiProvider.d.ts +25 -0
- package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/cjs/providers/GeminiProvider.js +79 -51
- package/dist/cjs/providers/GeminiProvider.js.map +1 -1
- package/dist/cjs/providers/OpenAIProvider.d.ts.map +1 -1
- package/dist/cjs/providers/OpenAIProvider.js +14 -6
- package/dist/cjs/providers/OpenAIProvider.js.map +1 -1
- package/dist/cjs/providers/OpenRouterProvider.d.ts.map +1 -1
- package/dist/cjs/providers/OpenRouterProvider.js +7 -6
- package/dist/cjs/providers/OpenRouterProvider.js.map +1 -1
- package/dist/cjs/types/agent.d.ts +44 -0
- package/dist/cjs/types/agent.d.ts.map +1 -1
- package/dist/cjs/types/agent.js.map +1 -1
- package/dist/cjs/types/compaction.d.ts +50 -0
- package/dist/cjs/types/compaction.d.ts.map +1 -0
- package/dist/cjs/types/compaction.js +6 -0
- package/dist/cjs/types/compaction.js.map +1 -0
- package/dist/cjs/types/index.d.ts +4 -2
- package/dist/cjs/types/index.d.ts.map +1 -1
- package/dist/cjs/types/index.js.map +1 -1
- package/dist/cjs/types/tool.d.ts +84 -0
- package/dist/cjs/types/tool.d.ts.map +1 -1
- package/dist/core/Agent.d.ts +17 -1
- package/dist/core/Agent.d.ts.map +1 -1
- package/dist/core/Agent.js +47 -0
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/BatchPromptBuilder.d.ts +3 -0
- package/dist/core/BatchPromptBuilder.d.ts.map +1 -1
- package/dist/core/BatchPromptBuilder.js +14 -11
- package/dist/core/BatchPromptBuilder.js.map +1 -1
- package/dist/core/CompactionEngine.d.ts +65 -0
- package/dist/core/CompactionEngine.d.ts.map +1 -0
- package/dist/core/CompactionEngine.js +244 -0
- package/dist/core/CompactionEngine.js.map +1 -0
- package/dist/core/PromptComposer.d.ts +8 -1
- package/dist/core/PromptComposer.d.ts.map +1 -1
- package/dist/core/PromptComposer.js +238 -118
- package/dist/core/PromptComposer.js.map +1 -1
- package/dist/core/PromptSectionCache.d.ts +57 -0
- package/dist/core/PromptSectionCache.d.ts.map +1 -0
- package/dist/core/PromptSectionCache.js +104 -0
- package/dist/core/PromptSectionCache.js.map +1 -0
- package/dist/core/ResponseEngine.d.ts +3 -0
- package/dist/core/ResponseEngine.d.ts.map +1 -1
- package/dist/core/ResponseEngine.js +10 -6
- package/dist/core/ResponseEngine.js.map +1 -1
- package/dist/core/ResponseModal.d.ts.map +1 -1
- package/dist/core/ResponseModal.js +79 -20
- package/dist/core/ResponseModal.js.map +1 -1
- package/dist/core/RoutingEngine.d.ts +10 -0
- package/dist/core/RoutingEngine.d.ts.map +1 -1
- package/dist/core/RoutingEngine.js +3 -2
- package/dist/core/RoutingEngine.js.map +1 -1
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +17 -0
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/StreamingToolExecutor.d.ts +142 -0
- package/dist/core/StreamingToolExecutor.d.ts.map +1 -0
- package/dist/core/StreamingToolExecutor.js +448 -0
- package/dist/core/StreamingToolExecutor.js.map +1 -0
- package/dist/core/ToolManager.d.ts +18 -1
- package/dist/core/ToolManager.d.ts.map +1 -1
- package/dist/core/ToolManager.js +91 -0
- package/dist/core/ToolManager.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/providers/AnthropicProvider.d.ts.map +1 -1
- package/dist/providers/AnthropicProvider.js +8 -7
- package/dist/providers/AnthropicProvider.js.map +1 -1
- package/dist/providers/GeminiProvider.d.ts +25 -0
- package/dist/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/providers/GeminiProvider.js +79 -51
- package/dist/providers/GeminiProvider.js.map +1 -1
- package/dist/providers/OpenAIProvider.d.ts.map +1 -1
- package/dist/providers/OpenAIProvider.js +14 -6
- package/dist/providers/OpenAIProvider.js.map +1 -1
- package/dist/providers/OpenRouterProvider.d.ts.map +1 -1
- package/dist/providers/OpenRouterProvider.js +7 -6
- package/dist/providers/OpenRouterProvider.js.map +1 -1
- package/dist/types/agent.d.ts +44 -0
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/agent.js.map +1 -1
- package/dist/types/compaction.d.ts +50 -0
- package/dist/types/compaction.d.ts.map +1 -0
- package/dist/types/compaction.js +5 -0
- package/dist/types/compaction.js.map +1 -0
- package/dist/types/index.d.ts +4 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/tool.d.ts +84 -0
- package/dist/types/tool.d.ts.map +1 -1
- package/docs/api/overview.md +140 -0
- package/docs/core/tools/enhanced-tool.md +186 -0
- package/docs/core/tools/streaming-execution.md +161 -0
- package/docs/guides/context-compaction.md +96 -0
- package/docs/guides/prompt-optimization.md +164 -0
- package/examples/advanced-patterns/context-compaction.ts +223 -0
- package/examples/advanced-patterns/streaming-responses.ts +85 -7
- package/examples/tools/enhanced-tool-metadata.ts +268 -0
- package/examples/tools/streaming-tool-execution.ts +283 -0
- package/package.json +1 -1
- package/src/core/Agent.ts +58 -2
- package/src/core/BatchPromptBuilder.ts +14 -11
- package/src/core/CompactionEngine.ts +318 -0
- package/src/core/PromptComposer.ts +261 -141
- package/src/core/PromptSectionCache.ts +136 -0
- package/src/core/ResponseEngine.ts +9 -6
- package/src/core/ResponseModal.ts +81 -20
- package/src/core/RoutingEngine.ts +13 -2
- package/src/core/SessionManager.ts +19 -0
- package/src/core/StreamingToolExecutor.ts +572 -0
- package/src/core/ToolManager.ts +151 -41
- package/src/index.ts +14 -0
- package/src/providers/AnthropicProvider.ts +11 -12
- package/src/providers/GeminiProvider.ts +83 -52
- package/src/providers/OpenAIProvider.ts +21 -13
- package/src/providers/OpenRouterProvider.ts +13 -13
- package/src/types/agent.ts +45 -0
- package/src/types/compaction.ts +52 -0
- package/src/types/index.ts +35 -14
- package/src/types/tool.ts +108 -0
|
@@ -3,41 +3,74 @@ import type { Route } from "./Route";
|
|
|
3
3
|
import { render, renderMany, formatKnowledgeBase, createTemplateContext } from "../utils/template";
|
|
4
4
|
import { TemplateContext } from "../types/template";
|
|
5
5
|
import { extractAIContextStrings, ConditionEvaluator } from "../utils/condition";
|
|
6
|
+
import { PromptSectionCache } from "./PromptSectionCache";
|
|
6
7
|
|
|
7
8
|
export class PromptComposer<TContext = unknown, TData = unknown> {
|
|
8
9
|
private parts: string[] = [];
|
|
9
10
|
private renderContext: TemplateContext<TContext, TData>;
|
|
11
|
+
private cache: PromptSectionCache | null;
|
|
12
|
+
private instructionCounter = 0;
|
|
10
13
|
|
|
11
|
-
constructor(
|
|
14
|
+
constructor(
|
|
15
|
+
context: TemplateContext<TContext, TData> = createTemplateContext({}),
|
|
16
|
+
cache?: PromptSectionCache
|
|
17
|
+
) {
|
|
12
18
|
this.renderContext = context;
|
|
19
|
+
this.cache = cache ?? null;
|
|
13
20
|
}
|
|
14
21
|
|
|
15
22
|
// Specific, typed sections tailored to the framework
|
|
16
23
|
|
|
17
24
|
async addAgentMeta(agent: AgentOptions<TContext, TData>): Promise<this> {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (agent.goal) {
|
|
22
|
-
lines.push(`**Goal:** ${agent.goal}`);
|
|
23
|
-
}
|
|
24
|
-
if (agent.description) {
|
|
25
|
-
lines.push(`**Description:** ${agent.description}`);
|
|
26
|
-
}
|
|
27
|
-
if (agent.identity) {
|
|
25
|
+
const compute = async (): Promise<string | null> => {
|
|
26
|
+
const lines: string[] = [];
|
|
27
|
+
lines.push("## Agent Identity");
|
|
28
28
|
lines.push(
|
|
29
|
-
|
|
29
|
+
`You are "${agent.name}". Always refer to yourself by this name.`
|
|
30
30
|
);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
if (agent.identity) {
|
|
32
|
+
lines.push(await render(agent.identity, this.renderContext));
|
|
33
|
+
}
|
|
34
|
+
if (agent.personality) {
|
|
35
|
+
lines.push(
|
|
36
|
+
`Communicate in the following style: ${await render(
|
|
37
|
+
agent.personality,
|
|
38
|
+
this.renderContext
|
|
39
|
+
)}`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
if (agent.goal) {
|
|
43
|
+
lines.push(`Your primary goal: ${agent.goal}`);
|
|
44
|
+
}
|
|
45
|
+
if (agent.description) {
|
|
46
|
+
lines.push(`About you: ${agent.description}`);
|
|
47
|
+
}
|
|
48
|
+
if (agent.rules?.length) {
|
|
49
|
+
const renderedRules = await renderMany(agent.rules, this.renderContext);
|
|
50
|
+
lines.push(
|
|
51
|
+
`You MUST always follow these rules:\n- ${renderedRules.join("\n- ")}`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
if (agent.prohibitions?.length) {
|
|
55
|
+
const renderedProhibitions = await renderMany(
|
|
56
|
+
agent.prohibitions,
|
|
36
57
|
this.renderContext
|
|
37
|
-
)
|
|
38
|
-
|
|
58
|
+
);
|
|
59
|
+
lines.push(
|
|
60
|
+
`You MUST NEVER do the following:\n- ${renderedProhibitions.join(
|
|
61
|
+
"\n- "
|
|
62
|
+
)}`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
return lines.join("\n");
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
if (this.cache) {
|
|
69
|
+
this.cache.register("agentMeta", "static", compute);
|
|
70
|
+
} else {
|
|
71
|
+
const result = await compute();
|
|
72
|
+
if (result) this.parts.push(result);
|
|
39
73
|
}
|
|
40
|
-
this.parts.push(lines.join("\n"));
|
|
41
74
|
return this;
|
|
42
75
|
}
|
|
43
76
|
|
|
@@ -46,58 +79,99 @@ export class PromptComposer<TContext = unknown, TData = unknown> {
|
|
|
46
79
|
}
|
|
47
80
|
|
|
48
81
|
async addScoringRules(): Promise<this> {
|
|
49
|
-
|
|
50
|
-
`## Scoring Rules\n\n${[
|
|
82
|
+
const compute = (): string | null => {
|
|
83
|
+
return `## Scoring Rules\n\n${[
|
|
51
84
|
"- 90-100: explicit keywords + clear intent",
|
|
52
85
|
"- 70-89: strong contextual evidence + relevant keywords",
|
|
53
86
|
"- 50-69: moderate relevance",
|
|
54
87
|
"- 30-49: weak connection or ambiguous",
|
|
55
88
|
"- 0-29: minimal/none",
|
|
56
89
|
"Return ONLY JSON matching the provided schema. Include scores for ALL routes.",
|
|
57
|
-
].join("\n")}
|
|
58
|
-
|
|
90
|
+
].join("\n")}`;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
if (this.cache) {
|
|
94
|
+
this.cache.register("scoringRules", "static", compute);
|
|
95
|
+
} else {
|
|
96
|
+
this.parts.push(compute()!);
|
|
97
|
+
}
|
|
59
98
|
return Promise.resolve(this);
|
|
60
99
|
}
|
|
61
100
|
|
|
62
101
|
async addInstruction(text: string): Promise<this> {
|
|
63
|
-
if (text)
|
|
102
|
+
if (!text) return Promise.resolve(this);
|
|
103
|
+
|
|
104
|
+
const content = `## Instruction\n\n${text}`;
|
|
105
|
+
|
|
106
|
+
if (this.cache) {
|
|
107
|
+
const key = `instruction-${this.instructionCounter++}`;
|
|
108
|
+
this.cache.register(key, "dynamic", () => content);
|
|
109
|
+
} else {
|
|
110
|
+
this.parts.push(content);
|
|
111
|
+
}
|
|
64
112
|
return Promise.resolve(this);
|
|
65
113
|
}
|
|
66
114
|
|
|
115
|
+
/**
|
|
116
|
+
* @deprecated History should flow through `GenerateMessageInput.history` natively.
|
|
117
|
+
* This method is kept for backward compatibility but will be removed in a future version.
|
|
118
|
+
*/
|
|
67
119
|
async addInteractionHistory(history: Event[], note?: string): Promise<this> {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
`## Interaction History\n\n${header}Recent conversation events:\n\n${recent}
|
|
75
|
-
|
|
120
|
+
const compute = (): string | null => {
|
|
121
|
+
const recent = history
|
|
122
|
+
.slice(-10)
|
|
123
|
+
.map((e) => `- ${JSON.stringify(e)}`)
|
|
124
|
+
.join("\n");
|
|
125
|
+
const header = note ? `${note}\n\n` : "";
|
|
126
|
+
return `## Interaction History\n\n${header}Recent conversation events:\n\n${recent}`;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
if (this.cache) {
|
|
130
|
+
this.cache.register("interactionHistory", "dynamic", compute);
|
|
131
|
+
} else {
|
|
132
|
+
this.parts.push(compute()!);
|
|
133
|
+
}
|
|
76
134
|
return Promise.resolve(this);
|
|
77
135
|
}
|
|
78
136
|
|
|
79
137
|
async addLastMessage(message: string): Promise<this> {
|
|
80
|
-
|
|
138
|
+
const compute = (): string | null => {
|
|
139
|
+
return `## Last Message\n\n${message}`;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
if (this.cache) {
|
|
143
|
+
this.cache.register("lastMessage", "dynamic", compute);
|
|
144
|
+
} else {
|
|
145
|
+
this.parts.push(compute()!);
|
|
146
|
+
}
|
|
81
147
|
return Promise.resolve(this);
|
|
82
148
|
}
|
|
83
149
|
|
|
84
150
|
async addGlossary(terms: Term<TContext>[]): Promise<this> {
|
|
85
151
|
if (!terms.length) return this;
|
|
86
152
|
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
153
|
+
const compute = async (): Promise<string | null> => {
|
|
154
|
+
const renderedTerms = await Promise.all(
|
|
155
|
+
terms.map(async (t) => {
|
|
156
|
+
const name = await render(t.name, this.renderContext);
|
|
157
|
+
const description = await render(t.description, this.renderContext);
|
|
158
|
+
const synonyms = t.synonyms
|
|
159
|
+
? await renderMany(t.synonyms, this.renderContext)
|
|
160
|
+
: [];
|
|
161
|
+
const synonymText =
|
|
162
|
+
synonyms.length > 0 ? ` (synonyms: ${synonyms.join(", ")})` : "";
|
|
163
|
+
return `- **${name}**${synonymText}: ${description}`;
|
|
164
|
+
})
|
|
165
|
+
);
|
|
166
|
+
return `## Glossary\n\n${renderedTerms.join("\n")}`;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
if (this.cache) {
|
|
170
|
+
this.cache.register("glossary", "static", compute);
|
|
171
|
+
} else {
|
|
172
|
+
const result = await compute();
|
|
173
|
+
if (result) this.parts.push(result);
|
|
174
|
+
}
|
|
101
175
|
return this;
|
|
102
176
|
}
|
|
103
177
|
|
|
@@ -105,56 +179,64 @@ export class PromptComposer<TContext = unknown, TData = unknown> {
|
|
|
105
179
|
const enabled = guidelines.filter((g) => g.enabled !== false);
|
|
106
180
|
if (!enabled.length) return this;
|
|
107
181
|
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
182
|
+
const compute = async (): Promise<string | null> => {
|
|
183
|
+
const evaluator = new ConditionEvaluator(this.renderContext);
|
|
184
|
+
const activeGuidelines: Guideline<TContext, TData>[] = [];
|
|
185
|
+
const allAIContextStrings: string[] = [];
|
|
186
|
+
|
|
187
|
+
// Evaluate guideline conditions to determine which are active
|
|
188
|
+
for (const guideline of enabled) {
|
|
189
|
+
if (guideline.condition) {
|
|
190
|
+
const evaluation = await evaluator.evaluateCondition(guideline.condition, 'AND');
|
|
191
|
+
|
|
192
|
+
// Collect AI context strings for prompt
|
|
193
|
+
allAIContextStrings.push(...evaluation.aiContextStrings);
|
|
194
|
+
|
|
195
|
+
// Include guideline if:
|
|
196
|
+
// 1. No programmatic conditions (only strings) - always active
|
|
197
|
+
// 2. Programmatic conditions evaluate to true
|
|
198
|
+
if (!evaluation.hasProgrammaticConditions || evaluation.programmaticResult) {
|
|
199
|
+
activeGuidelines.push(guideline);
|
|
200
|
+
}
|
|
201
|
+
} else {
|
|
202
|
+
// No condition means always active
|
|
124
203
|
activeGuidelines.push(guideline);
|
|
125
204
|
}
|
|
126
|
-
} else {
|
|
127
|
-
// No condition means always active
|
|
128
|
-
activeGuidelines.push(guideline);
|
|
129
205
|
}
|
|
130
|
-
}
|
|
131
206
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
207
|
+
if (!activeGuidelines.length && !allAIContextStrings.length) return null;
|
|
208
|
+
|
|
209
|
+
const renderedGuidelines = await Promise.all(
|
|
210
|
+
activeGuidelines.map(async (g, i) => {
|
|
211
|
+
const action = await render(g.action, this.renderContext);
|
|
212
|
+
if (g.condition) {
|
|
213
|
+
// Use AI context strings if available, otherwise render the condition
|
|
214
|
+
const conditionStrings = extractAIContextStrings(g.condition);
|
|
215
|
+
if (conditionStrings.length > 0) {
|
|
216
|
+
const conditionText = conditionStrings.join(" AND ");
|
|
217
|
+
return `- Guideline #${i + 1}: When ${conditionText}, then ${action}`;
|
|
218
|
+
}
|
|
143
219
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
220
|
+
return `- Guideline #${i + 1}: ${action}`;
|
|
221
|
+
})
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
// Add any additional AI context from inactive guidelines
|
|
225
|
+
if (allAIContextStrings.length > 0) {
|
|
226
|
+
const uniqueContextStrings = Array.from(new Set(allAIContextStrings));
|
|
227
|
+
const contextSection = `\n\n**Additional Context:** ${uniqueContextStrings.join(", ")}`;
|
|
228
|
+
return `## Guidelines\n\n${renderedGuidelines.join("\n")}${contextSection}`;
|
|
229
|
+
} else {
|
|
230
|
+
return `## Guidelines\n\n${renderedGuidelines.join("\n")}`;
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
if (this.cache) {
|
|
235
|
+
this.cache.register("guidelines", "dynamic", compute);
|
|
154
236
|
} else {
|
|
155
|
-
|
|
237
|
+
const result = await compute();
|
|
238
|
+
if (result) this.parts.push(result);
|
|
156
239
|
}
|
|
157
|
-
|
|
158
240
|
return this;
|
|
159
241
|
}
|
|
160
242
|
|
|
@@ -162,65 +244,88 @@ export class PromptComposer<TContext = unknown, TData = unknown> {
|
|
|
162
244
|
agentKnowledgeBase?: Record<string, unknown>,
|
|
163
245
|
routeKnowledgeBase?: Record<string, unknown>
|
|
164
246
|
): Promise<this> {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
247
|
+
const compute = (): string | null => {
|
|
248
|
+
// Merge agent and route knowledge bases (route takes precedence for conflicts)
|
|
249
|
+
const mergedKnowledge = {
|
|
250
|
+
...(agentKnowledgeBase || {}),
|
|
251
|
+
...(routeKnowledgeBase || {}),
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
// Only add section if there's knowledge data
|
|
255
|
+
if (Object.keys(mergedKnowledge).length > 0) {
|
|
256
|
+
return formatKnowledgeBase(mergedKnowledge, "Knowledge Base");
|
|
257
|
+
}
|
|
258
|
+
return null;
|
|
169
259
|
};
|
|
170
260
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
261
|
+
if (this.cache) {
|
|
262
|
+
this.cache.register("knowledgeBase", "static", compute);
|
|
263
|
+
} else {
|
|
264
|
+
const result = compute();
|
|
265
|
+
if (result) this.parts.push(result);
|
|
175
266
|
}
|
|
176
|
-
|
|
177
267
|
return Promise.resolve(this);
|
|
178
268
|
}
|
|
179
269
|
|
|
180
270
|
async addActiveRoutes(routes: Route<TContext, TData>[]): Promise<this> {
|
|
181
271
|
if (!routes.length) return this;
|
|
182
272
|
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
273
|
+
const compute = async (): Promise<string | null> => {
|
|
274
|
+
const renderedRoutes = await Promise.all(
|
|
275
|
+
routes.map(async (r, i) => {
|
|
276
|
+
const whenContextStrings = r.when ? extractAIContextStrings(r.when) : [];
|
|
277
|
+
const conditions =
|
|
278
|
+
whenContextStrings.length > 0
|
|
279
|
+
? `\n\n **Triggered when:** ${whenContextStrings.join(" OR ")}`
|
|
280
|
+
: "";
|
|
281
|
+
const desc = r.description
|
|
282
|
+
? `\n\n **Description:** ${r.description}`
|
|
189
283
|
: "";
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
);
|
|
284
|
+
const rules = await renderMany(r.getRules(), this.renderContext);
|
|
285
|
+
const prohibitions = await renderMany(
|
|
286
|
+
r.getProhibitions(),
|
|
287
|
+
this.renderContext
|
|
288
|
+
);
|
|
289
|
+
const rulesInfo =
|
|
290
|
+
rules.length > 0
|
|
291
|
+
? `\n\n **Rules:**\n ${rules.map((x) => ` - ${x}`).join("\n ")}`
|
|
292
|
+
: "";
|
|
293
|
+
const prohibitionsInfo =
|
|
294
|
+
prohibitions.length > 0
|
|
295
|
+
? `\n\n **Prohibitions:**\n ${prohibitions
|
|
296
|
+
.map((x) => ` - ${x}`)
|
|
297
|
+
.join("\n ")}`
|
|
298
|
+
: "";
|
|
299
|
+
return `### Route ${i + 1}: ${r.title
|
|
300
|
+
}${desc}${conditions}${rulesInfo}${prohibitionsInfo}`;
|
|
301
|
+
})
|
|
302
|
+
);
|
|
303
|
+
return `## Available Routes\n\n${renderedRoutes.join("\n\n")}`;
|
|
304
|
+
};
|
|
212
305
|
|
|
213
|
-
this.
|
|
306
|
+
if (this.cache) {
|
|
307
|
+
this.cache.register("activeRoutes", "static", compute);
|
|
308
|
+
} else {
|
|
309
|
+
const result = await compute();
|
|
310
|
+
if (result) this.parts.push(result);
|
|
311
|
+
}
|
|
214
312
|
return this;
|
|
215
313
|
}
|
|
216
314
|
|
|
217
315
|
async addDirectives(directives?: string[]): Promise<this> {
|
|
218
316
|
if (!directives?.length) return this;
|
|
219
|
-
|
|
220
|
-
|
|
317
|
+
|
|
318
|
+
const compute = (): string | null => {
|
|
319
|
+
return `## Directives\n\nAddress concisely:\n\n${directives
|
|
221
320
|
.map((d) => `- ${d}`)
|
|
222
|
-
.join("\n")}
|
|
223
|
-
|
|
321
|
+
.join("\n")}`;
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
if (this.cache) {
|
|
325
|
+
this.cache.register("directives", "dynamic", compute);
|
|
326
|
+
} else {
|
|
327
|
+
this.parts.push(compute()!);
|
|
328
|
+
}
|
|
224
329
|
return Promise.resolve(this);
|
|
225
330
|
}
|
|
226
331
|
|
|
@@ -234,19 +339,34 @@ export class PromptComposer<TContext = unknown, TData = unknown> {
|
|
|
234
339
|
): Promise<this> {
|
|
235
340
|
if (!tools?.length) return this;
|
|
236
341
|
|
|
237
|
-
const
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
342
|
+
const compute = (): string | null => {
|
|
343
|
+
const renderedTools = tools.map((tool, i) => {
|
|
344
|
+
const toolName = tool.name || tool.id;
|
|
345
|
+
const desc = tool.description
|
|
346
|
+
? `\n Description: ${tool.description}`
|
|
347
|
+
: "";
|
|
348
|
+
return `### Tool ${i + 1}: ${toolName}${desc}`;
|
|
349
|
+
});
|
|
350
|
+
return `## Available Tools\n\n${renderedTools.join("\n\n")}`;
|
|
351
|
+
};
|
|
244
352
|
|
|
245
|
-
this.
|
|
353
|
+
if (this.cache) {
|
|
354
|
+
this.cache.register("availableTools", "dynamic", compute);
|
|
355
|
+
} else {
|
|
356
|
+
this.parts.push(compute()!);
|
|
357
|
+
}
|
|
246
358
|
return Promise.resolve(this);
|
|
247
359
|
}
|
|
248
360
|
|
|
249
361
|
async build(): Promise<string> {
|
|
362
|
+
if (this.cache) {
|
|
363
|
+
const sections = await this.cache.resolveAll();
|
|
364
|
+
const prompt = sections
|
|
365
|
+
.filter((s): s is string => s != null && s !== "")
|
|
366
|
+
.join("\n\n")
|
|
367
|
+
.trim();
|
|
368
|
+
return prompt;
|
|
369
|
+
}
|
|
250
370
|
const prompt = this.parts.filter(Boolean).join("\n\n").trim();
|
|
251
371
|
return Promise.resolve(prompt);
|
|
252
372
|
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PromptSectionCache - Memoizes static prompt sections across turns,
|
|
3
|
+
* recomputing only dynamic sections per-turn.
|
|
4
|
+
*
|
|
5
|
+
* Static sections (agent identity, glossary, knowledge base, route descriptions)
|
|
6
|
+
* are cached after first computation. Dynamic sections (current step context,
|
|
7
|
+
* directives, available tools) are recomputed on every resolveAll() call.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** Section type: static sections are cached, dynamic sections recompute every turn */
|
|
11
|
+
export type PromptSectionType = "static" | "dynamic";
|
|
12
|
+
|
|
13
|
+
/** Configuration for prompt section caching behavior */
|
|
14
|
+
export interface PromptCacheConfig {
|
|
15
|
+
/** Whether to enable section memoization (default: true) */
|
|
16
|
+
enabled?: boolean;
|
|
17
|
+
/** Keys of sections that should always recompute every turn, even if registered as static */
|
|
18
|
+
volatileKeys?: string[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Compute function that produces a section's content */
|
|
22
|
+
export type SectionCompute = () => string | null | Promise<string | null>;
|
|
23
|
+
|
|
24
|
+
/** Internal entry tracking a registered section */
|
|
25
|
+
interface PromptSectionEntry {
|
|
26
|
+
key: string;
|
|
27
|
+
type: PromptSectionType;
|
|
28
|
+
compute: SectionCompute;
|
|
29
|
+
/** undefined = not yet computed; null = computed to null; string = cached value */
|
|
30
|
+
cachedValue?: string | null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class PromptSectionCache {
|
|
34
|
+
private sections: Map<string, PromptSectionEntry> = new Map();
|
|
35
|
+
private insertionOrder: string[] = [];
|
|
36
|
+
private config: Required<PromptCacheConfig>;
|
|
37
|
+
|
|
38
|
+
constructor(config?: PromptCacheConfig) {
|
|
39
|
+
this.config = {
|
|
40
|
+
enabled: config?.enabled ?? true,
|
|
41
|
+
volatileKeys: config?.volatileKeys ?? [],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Register a section with its compute function and type.
|
|
47
|
+
* Sections are resolved in registration order during resolveAll().
|
|
48
|
+
*/
|
|
49
|
+
register(key: string, type: PromptSectionType, compute: SectionCompute): void {
|
|
50
|
+
const existing = this.sections.has(key);
|
|
51
|
+
this.sections.set(key, { key, type, compute, cachedValue: undefined });
|
|
52
|
+
if (!existing) {
|
|
53
|
+
this.insertionOrder.push(key);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get a section's value. Static sections return cached value if available;
|
|
59
|
+
* dynamic sections always recompute.
|
|
60
|
+
*/
|
|
61
|
+
async get(key: string): Promise<string | null> {
|
|
62
|
+
const entry = this.sections.get(key);
|
|
63
|
+
if (!entry) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
if (this.shouldRecompute(entry)) {
|
|
67
|
+
const value = await entry.compute();
|
|
68
|
+
entry.cachedValue = value;
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
// cachedValue is defined (string or null) — return it
|
|
72
|
+
return entry.cachedValue!;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Resolve all registered sections in registration order.
|
|
77
|
+
* Static sections use cache when available; dynamic sections always recompute.
|
|
78
|
+
*/
|
|
79
|
+
async resolveAll(): Promise<(string | null)[]> {
|
|
80
|
+
const results: (string | null)[] = [];
|
|
81
|
+
for (const key of this.insertionOrder) {
|
|
82
|
+
results.push(await this.get(key));
|
|
83
|
+
}
|
|
84
|
+
return results;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Invalidate a specific section's cache, forcing recomputation on next resolve.
|
|
89
|
+
*/
|
|
90
|
+
invalidate(key: string): void {
|
|
91
|
+
const entry = this.sections.get(key);
|
|
92
|
+
if (entry) {
|
|
93
|
+
entry.cachedValue = undefined;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Invalidate all cached sections (e.g., on session change or /clear).
|
|
99
|
+
*/
|
|
100
|
+
invalidateAll(): void {
|
|
101
|
+
for (const entry of this.sections.values()) {
|
|
102
|
+
entry.cachedValue = undefined;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Check whether a section has a cached value.
|
|
108
|
+
*/
|
|
109
|
+
has(key: string): boolean {
|
|
110
|
+
return this.sections.has(key);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Determine if a section needs recomputation.
|
|
115
|
+
*/
|
|
116
|
+
private shouldRecompute(entry: PromptSectionEntry): boolean {
|
|
117
|
+
// Caching disabled — always recompute
|
|
118
|
+
if (!this.config.enabled) {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
// Dynamic sections always recompute
|
|
122
|
+
if (entry.type === "dynamic") {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
// Volatile keys always recompute
|
|
126
|
+
if (this.config.volatileKeys.includes(entry.key)) {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
// Static section not yet computed
|
|
130
|
+
if (entry.cachedValue === undefined) {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
// Static section with cached value — use cache
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|