@falai/agent 1.1.3 → 1.2.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/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 +4 -1
- 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 -126
- 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 -2
- package/dist/cjs/core/ResponseEngine.d.ts.map +1 -1
- package/dist/cjs/core/ResponseEngine.js +8 -8
- 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 +120 -70
- package/dist/cjs/core/ResponseModal.js.map +1 -1
- package/dist/cjs/core/ResponsePipeline.d.ts +2 -1
- package/dist/cjs/core/ResponsePipeline.d.ts.map +1 -1
- package/dist/cjs/core/ResponsePipeline.js +17 -19
- package/dist/cjs/core/ResponsePipeline.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 +5 -4
- 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 +7 -0
- package/dist/cjs/providers/AnthropicProvider.d.ts.map +1 -1
- package/dist/cjs/providers/AnthropicProvider.js +109 -19
- package/dist/cjs/providers/AnthropicProvider.js.map +1 -1
- package/dist/cjs/providers/GeminiProvider.d.ts +32 -0
- package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/cjs/providers/GeminiProvider.js +160 -53
- package/dist/cjs/providers/GeminiProvider.js.map +1 -1
- package/dist/cjs/providers/OpenAIProvider.d.ts +5 -0
- package/dist/cjs/providers/OpenAIProvider.d.ts.map +1 -1
- package/dist/cjs/providers/OpenAIProvider.js +65 -18
- package/dist/cjs/providers/OpenAIProvider.js.map +1 -1
- package/dist/cjs/providers/OpenRouterProvider.d.ts +5 -0
- package/dist/cjs/providers/OpenRouterProvider.d.ts.map +1 -1
- package/dist/cjs/providers/OpenRouterProvider.js +57 -18
- 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/ai.d.ts +2 -2
- package/dist/cjs/types/ai.d.ts.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 +4 -1
- 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 -126
- 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 -2
- package/dist/core/ResponseEngine.d.ts.map +1 -1
- package/dist/core/ResponseEngine.js +8 -8
- package/dist/core/ResponseEngine.js.map +1 -1
- package/dist/core/ResponseModal.d.ts.map +1 -1
- package/dist/core/ResponseModal.js +121 -71
- package/dist/core/ResponseModal.js.map +1 -1
- package/dist/core/ResponsePipeline.d.ts +2 -1
- package/dist/core/ResponsePipeline.d.ts.map +1 -1
- package/dist/core/ResponsePipeline.js +18 -20
- package/dist/core/ResponsePipeline.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 +6 -5
- 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 +7 -0
- package/dist/providers/AnthropicProvider.d.ts.map +1 -1
- package/dist/providers/AnthropicProvider.js +109 -19
- package/dist/providers/AnthropicProvider.js.map +1 -1
- package/dist/providers/GeminiProvider.d.ts +32 -0
- package/dist/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/providers/GeminiProvider.js +160 -53
- package/dist/providers/GeminiProvider.js.map +1 -1
- package/dist/providers/OpenAIProvider.d.ts +5 -0
- package/dist/providers/OpenAIProvider.d.ts.map +1 -1
- package/dist/providers/OpenAIProvider.js +65 -18
- package/dist/providers/OpenAIProvider.js.map +1 -1
- package/dist/providers/OpenRouterProvider.d.ts +5 -0
- package/dist/providers/OpenRouterProvider.d.ts.map +1 -1
- package/dist/providers/OpenRouterProvider.js +57 -18
- 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/ai.d.ts +2 -2
- package/dist/types/ai.d.ts.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 +4 -1
- package/src/core/CompactionEngine.ts +318 -0
- package/src/core/PromptComposer.ts +259 -156
- package/src/core/PromptSectionCache.ts +136 -0
- package/src/core/ResponseEngine.ts +7 -11
- package/src/core/ResponseModal.ts +133 -83
- package/src/core/ResponsePipeline.ts +22 -22
- package/src/core/RoutingEngine.ts +16 -5
- 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 +121 -24
- package/src/providers/GeminiProvider.ts +174 -54
- package/src/providers/OpenAIProvider.ts +77 -25
- package/src/providers/OpenRouterProvider.ts +68 -25
- package/src/types/agent.ts +45 -0
- package/src/types/ai.ts +2 -2
- package/src/types/compaction.ts +52 -0
- package/src/types/index.ts +35 -14
- package/src/types/tool.ts +108 -0
|
@@ -3,58 +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
|
-
`You are "${agent.name}". Always refer to yourself by this name.`
|
|
22
|
-
);
|
|
23
|
-
if (agent.identity) {
|
|
24
|
-
lines.push(await render(agent.identity, this.renderContext));
|
|
25
|
-
}
|
|
26
|
-
if (agent.personality) {
|
|
27
|
-
lines.push(
|
|
28
|
-
`Communicate in the following style: ${await render(
|
|
29
|
-
agent.personality,
|
|
30
|
-
this.renderContext
|
|
31
|
-
)}`
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
if (agent.goal) {
|
|
35
|
-
lines.push(`Your primary goal: ${agent.goal}`);
|
|
36
|
-
}
|
|
37
|
-
if (agent.description) {
|
|
38
|
-
lines.push(`About you: ${agent.description}`);
|
|
39
|
-
}
|
|
40
|
-
if (agent.rules?.length) {
|
|
41
|
-
const renderedRules = await renderMany(agent.rules, this.renderContext);
|
|
42
|
-
lines.push(
|
|
43
|
-
`You MUST always follow these rules:\n- ${renderedRules.join("\n- ")}`
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
if (agent.prohibitions?.length) {
|
|
47
|
-
const renderedProhibitions = await renderMany(
|
|
48
|
-
agent.prohibitions,
|
|
49
|
-
this.renderContext
|
|
50
|
-
);
|
|
25
|
+
const compute = async (): Promise<string | null> => {
|
|
26
|
+
const lines: string[] = [];
|
|
27
|
+
lines.push("## Agent Identity");
|
|
51
28
|
lines.push(
|
|
52
|
-
`You
|
|
53
|
-
"\n- "
|
|
54
|
-
)}`
|
|
29
|
+
`You are "${agent.name}". Always refer to yourself by this name.`
|
|
55
30
|
);
|
|
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,
|
|
57
|
+
this.renderContext
|
|
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);
|
|
56
73
|
}
|
|
57
|
-
this.parts.push(lines.join("\n"));
|
|
58
74
|
return this;
|
|
59
75
|
}
|
|
60
76
|
|
|
@@ -63,58 +79,99 @@ export class PromptComposer<TContext = unknown, TData = unknown> {
|
|
|
63
79
|
}
|
|
64
80
|
|
|
65
81
|
async addScoringRules(): Promise<this> {
|
|
66
|
-
|
|
67
|
-
`## Scoring Rules\n\n${[
|
|
82
|
+
const compute = (): string | null => {
|
|
83
|
+
return `## Scoring Rules\n\n${[
|
|
68
84
|
"- 90-100: explicit keywords + clear intent",
|
|
69
85
|
"- 70-89: strong contextual evidence + relevant keywords",
|
|
70
86
|
"- 50-69: moderate relevance",
|
|
71
87
|
"- 30-49: weak connection or ambiguous",
|
|
72
88
|
"- 0-29: minimal/none",
|
|
73
89
|
"Return ONLY JSON matching the provided schema. Include scores for ALL routes.",
|
|
74
|
-
].join("\n")}
|
|
75
|
-
|
|
90
|
+
].join("\n")}`;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
if (this.cache) {
|
|
94
|
+
this.cache.register("scoringRules", "static", compute);
|
|
95
|
+
} else {
|
|
96
|
+
this.parts.push(compute()!);
|
|
97
|
+
}
|
|
76
98
|
return Promise.resolve(this);
|
|
77
99
|
}
|
|
78
100
|
|
|
79
101
|
async addInstruction(text: string): Promise<this> {
|
|
80
|
-
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
|
+
}
|
|
81
112
|
return Promise.resolve(this);
|
|
82
113
|
}
|
|
83
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
|
+
*/
|
|
84
119
|
async addInteractionHistory(history: Event[], note?: string): Promise<this> {
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
`## Interaction History\n\n${header}Recent conversation events:\n\n${recent}
|
|
92
|
-
|
|
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
|
+
}
|
|
93
134
|
return Promise.resolve(this);
|
|
94
135
|
}
|
|
95
136
|
|
|
96
137
|
async addLastMessage(message: string): Promise<this> {
|
|
97
|
-
|
|
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
|
+
}
|
|
98
147
|
return Promise.resolve(this);
|
|
99
148
|
}
|
|
100
149
|
|
|
101
150
|
async addGlossary(terms: Term<TContext>[]): Promise<this> {
|
|
102
151
|
if (!terms.length) return this;
|
|
103
152
|
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
+
}
|
|
118
175
|
return this;
|
|
119
176
|
}
|
|
120
177
|
|
|
@@ -122,56 +179,64 @@ export class PromptComposer<TContext = unknown, TData = unknown> {
|
|
|
122
179
|
const enabled = guidelines.filter((g) => g.enabled !== false);
|
|
123
180
|
if (!enabled.length) return this;
|
|
124
181
|
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
182
|
+
const compute = async (): Promise<string | null> => {
|
|
183
|
+
const evaluator = new ConditionEvaluator(this.renderContext);
|
|
184
|
+
const activeGuidelines: Guideline<TContext, TData>[] = [];
|
|
185
|
+
const allAIContextStrings: string[] = [];
|
|
128
186
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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');
|
|
133
191
|
|
|
134
|
-
|
|
135
|
-
|
|
192
|
+
// Collect AI context strings for prompt
|
|
193
|
+
allAIContextStrings.push(...evaluation.aiContextStrings);
|
|
136
194
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
|
141
203
|
activeGuidelines.push(guideline);
|
|
142
204
|
}
|
|
143
|
-
} else {
|
|
144
|
-
// No condition means always active
|
|
145
|
-
activeGuidelines.push(guideline);
|
|
146
205
|
}
|
|
147
|
-
}
|
|
148
206
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
+
}
|
|
160
219
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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);
|
|
171
236
|
} else {
|
|
172
|
-
|
|
237
|
+
const result = await compute();
|
|
238
|
+
if (result) this.parts.push(result);
|
|
173
239
|
}
|
|
174
|
-
|
|
175
240
|
return this;
|
|
176
241
|
}
|
|
177
242
|
|
|
@@ -179,65 +244,88 @@ export class PromptComposer<TContext = unknown, TData = unknown> {
|
|
|
179
244
|
agentKnowledgeBase?: Record<string, unknown>,
|
|
180
245
|
routeKnowledgeBase?: Record<string, unknown>
|
|
181
246
|
): Promise<this> {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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;
|
|
186
259
|
};
|
|
187
260
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
261
|
+
if (this.cache) {
|
|
262
|
+
this.cache.register("knowledgeBase", "static", compute);
|
|
263
|
+
} else {
|
|
264
|
+
const result = compute();
|
|
265
|
+
if (result) this.parts.push(result);
|
|
192
266
|
}
|
|
193
|
-
|
|
194
267
|
return Promise.resolve(this);
|
|
195
268
|
}
|
|
196
269
|
|
|
197
270
|
async addActiveRoutes(routes: Route<TContext, TData>[]): Promise<this> {
|
|
198
271
|
if (!routes.length) return this;
|
|
199
272
|
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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}`
|
|
206
283
|
: "";
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
);
|
|
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
|
+
};
|
|
229
305
|
|
|
230
|
-
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
|
+
}
|
|
231
312
|
return this;
|
|
232
313
|
}
|
|
233
314
|
|
|
234
315
|
async addDirectives(directives?: string[]): Promise<this> {
|
|
235
316
|
if (!directives?.length) return this;
|
|
236
|
-
|
|
237
|
-
|
|
317
|
+
|
|
318
|
+
const compute = (): string | null => {
|
|
319
|
+
return `## Directives\n\nAddress concisely:\n\n${directives
|
|
238
320
|
.map((d) => `- ${d}`)
|
|
239
|
-
.join("\n")}
|
|
240
|
-
|
|
321
|
+
.join("\n")}`;
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
if (this.cache) {
|
|
325
|
+
this.cache.register("directives", "dynamic", compute);
|
|
326
|
+
} else {
|
|
327
|
+
this.parts.push(compute()!);
|
|
328
|
+
}
|
|
241
329
|
return Promise.resolve(this);
|
|
242
330
|
}
|
|
243
331
|
|
|
@@ -251,19 +339,34 @@ export class PromptComposer<TContext = unknown, TData = unknown> {
|
|
|
251
339
|
): Promise<this> {
|
|
252
340
|
if (!tools?.length) return this;
|
|
253
341
|
|
|
254
|
-
const
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
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
|
+
};
|
|
261
352
|
|
|
262
|
-
this.
|
|
353
|
+
if (this.cache) {
|
|
354
|
+
this.cache.register("availableTools", "dynamic", compute);
|
|
355
|
+
} else {
|
|
356
|
+
this.parts.push(compute()!);
|
|
357
|
+
}
|
|
263
358
|
return Promise.resolve(this);
|
|
264
359
|
}
|
|
265
360
|
|
|
266
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
|
+
}
|
|
267
370
|
const prompt = this.parts.filter(Boolean).join("\n\n").trim();
|
|
268
371
|
return Promise.resolve(prompt);
|
|
269
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
|
+
}
|