@inkeep/agents-run-api 0.0.0-dev-20260105192809 → 0.0.0-dev-20260105201718
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/a2a/handlers.js +2 -1
- package/dist/agents/Agent.d.ts +47 -1
- package/dist/agents/Agent.js +558 -569
- package/dist/agents/generateTaskHandler.js +12 -3
- package/dist/constants/execution-limits/defaults.d.ts +4 -0
- package/dist/constants/execution-limits/defaults.js +5 -1
- package/dist/constants/execution-limits/index.d.ts +2 -2
- package/dist/constants/execution-limits/index.js +8 -3
- package/dist/data/conversations.d.ts +43 -3
- package/dist/data/conversations.js +317 -10
- package/dist/handlers/executionHandler.js +1 -1
- package/dist/services/AgentSession.d.ts +1 -1
- package/dist/services/AgentSession.js +1 -1
- package/dist/services/ArtifactService.js +2 -1
- package/dist/services/BaseCompressor.d.ts +183 -0
- package/dist/services/BaseCompressor.js +504 -0
- package/dist/services/ConversationCompressor.d.ts +32 -0
- package/dist/services/ConversationCompressor.js +91 -0
- package/dist/services/MidGenerationCompressor.d.ts +35 -63
- package/dist/services/MidGenerationCompressor.js +40 -311
- package/dist/tools/distill-conversation-history-tool.d.ts +62 -0
- package/dist/tools/distill-conversation-history-tool.js +206 -0
- package/dist/tools/distill-conversation-tool.js +20 -35
- package/dist/utils/model-context-utils.d.ts +10 -2
- package/dist/utils/model-context-utils.js +79 -47
- package/package.json +2 -2
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { ModelSettings } from "@inkeep/agents-core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
//#region src/tools/distill-conversation-history-tool.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Conversation History Summary Schema - structured object for replacing entire conversation histories
|
|
8
|
+
*/
|
|
9
|
+
declare const ConversationHistorySummarySchema: z.ZodObject<{
|
|
10
|
+
type: z.ZodLiteral<"conversation_history_summary_v1">;
|
|
11
|
+
session_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
12
|
+
conversation_overview: z.ZodString;
|
|
13
|
+
user_goals: z.ZodObject<{
|
|
14
|
+
primary: z.ZodString;
|
|
15
|
+
secondary: z.ZodArray<z.ZodString>;
|
|
16
|
+
}, z.core.$strip>;
|
|
17
|
+
key_outcomes: z.ZodObject<{
|
|
18
|
+
completed: z.ZodArray<z.ZodString>;
|
|
19
|
+
partial: z.ZodArray<z.ZodString>;
|
|
20
|
+
discoveries: z.ZodArray<z.ZodString>;
|
|
21
|
+
}, z.core.$strip>;
|
|
22
|
+
technical_context: z.ZodObject<{
|
|
23
|
+
technologies: z.ZodArray<z.ZodString>;
|
|
24
|
+
configurations: z.ZodArray<z.ZodString>;
|
|
25
|
+
issues_encountered: z.ZodArray<z.ZodString>;
|
|
26
|
+
solutions_applied: z.ZodArray<z.ZodString>;
|
|
27
|
+
}, z.core.$strip>;
|
|
28
|
+
conversation_artifacts: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
29
|
+
id: z.ZodString;
|
|
30
|
+
name: z.ZodString;
|
|
31
|
+
tool_name: z.ZodString;
|
|
32
|
+
tool_call_id: z.ZodString;
|
|
33
|
+
content_summary: z.ZodString;
|
|
34
|
+
relevance: z.ZodEnum<{
|
|
35
|
+
high: "high";
|
|
36
|
+
medium: "medium";
|
|
37
|
+
low: "low";
|
|
38
|
+
}>;
|
|
39
|
+
}, z.core.$strip>>>;
|
|
40
|
+
conversation_flow: z.ZodObject<{
|
|
41
|
+
major_phases: z.ZodArray<z.ZodString>;
|
|
42
|
+
decision_points: z.ZodArray<z.ZodString>;
|
|
43
|
+
topic_shifts: z.ZodArray<z.ZodString>;
|
|
44
|
+
}, z.core.$strip>;
|
|
45
|
+
context_for_continuation: z.ZodObject<{
|
|
46
|
+
current_state: z.ZodString;
|
|
47
|
+
next_logical_steps: z.ZodArray<z.ZodString>;
|
|
48
|
+
important_context: z.ZodArray<z.ZodString>;
|
|
49
|
+
}, z.core.$strip>;
|
|
50
|
+
}, z.core.$strip>;
|
|
51
|
+
type ConversationHistorySummary = z.infer<typeof ConversationHistorySummarySchema>;
|
|
52
|
+
/**
|
|
53
|
+
* Distill entire conversation history into a comprehensive summary that can replace the full message history
|
|
54
|
+
*/
|
|
55
|
+
declare function distillConversationHistory(params: {
|
|
56
|
+
messages: any[];
|
|
57
|
+
conversationId: string;
|
|
58
|
+
summarizerModel: ModelSettings;
|
|
59
|
+
toolCallToArtifactMap?: Record<string, string>;
|
|
60
|
+
}): Promise<ConversationHistorySummary>;
|
|
61
|
+
//#endregion
|
|
62
|
+
export { ConversationHistorySummary, ConversationHistorySummarySchema, distillConversationHistory };
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { getLogger } from "../logger.js";
|
|
2
|
+
import { ModelFactory } from "@inkeep/agents-core";
|
|
3
|
+
import { generateObject } from "ai";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
|
|
6
|
+
//#region src/tools/distill-conversation-history-tool.ts
|
|
7
|
+
const logger = getLogger("distill-conversation-history-tool");
|
|
8
|
+
/**
|
|
9
|
+
* Conversation History Summary Schema - structured object for replacing entire conversation histories
|
|
10
|
+
*/
|
|
11
|
+
const ConversationHistorySummarySchema = z.object({
|
|
12
|
+
type: z.literal("conversation_history_summary_v1"),
|
|
13
|
+
session_id: z.string().nullable().optional(),
|
|
14
|
+
conversation_overview: z.string().describe("2-4 sentences capturing the full conversation context and what was accomplished"),
|
|
15
|
+
user_goals: z.object({
|
|
16
|
+
primary: z.string().describe("Main objective the user was trying to achieve"),
|
|
17
|
+
secondary: z.array(z.string()).describe("Additional goals or tasks that emerged during conversation")
|
|
18
|
+
}),
|
|
19
|
+
key_outcomes: z.object({
|
|
20
|
+
completed: z.array(z.string()).describe("Tasks, questions, or problems that were fully resolved"),
|
|
21
|
+
partial: z.array(z.string()).describe("Work that was started but not completed"),
|
|
22
|
+
discoveries: z.array(z.string()).describe("Important information, insights, or findings uncovered")
|
|
23
|
+
}),
|
|
24
|
+
technical_context: z.object({
|
|
25
|
+
technologies: z.array(z.string()).describe("Relevant technologies, frameworks, tools mentioned"),
|
|
26
|
+
configurations: z.array(z.string()).describe("Settings, parameters, or configurations discussed"),
|
|
27
|
+
issues_encountered: z.array(z.string()).describe("Problems, errors, or challenges that came up"),
|
|
28
|
+
solutions_applied: z.array(z.string()).describe("Fixes, workarounds, or solutions that were implemented")
|
|
29
|
+
}),
|
|
30
|
+
conversation_artifacts: z.array(z.object({
|
|
31
|
+
id: z.string().describe("Artifact ID"),
|
|
32
|
+
name: z.string().describe("Human-readable name describing the content"),
|
|
33
|
+
tool_name: z.string().describe("Tool that generated this artifact"),
|
|
34
|
+
tool_call_id: z.string().describe("Specific tool call ID for precise referencing"),
|
|
35
|
+
content_summary: z.string().describe("Brief summary of what this artifact contains"),
|
|
36
|
+
relevance: z.enum([
|
|
37
|
+
"high",
|
|
38
|
+
"medium",
|
|
39
|
+
"low"
|
|
40
|
+
]).describe("Importance of this artifact to the overall conversation")
|
|
41
|
+
})).optional().describe("All artifacts referenced in this conversation with their significance"),
|
|
42
|
+
conversation_flow: z.object({
|
|
43
|
+
major_phases: z.array(z.string()).describe("Main phases or stages the conversation went through"),
|
|
44
|
+
decision_points: z.array(z.string()).describe("Key decisions made during the conversation"),
|
|
45
|
+
topic_shifts: z.array(z.string()).describe("When and why the conversation changed direction")
|
|
46
|
+
}),
|
|
47
|
+
context_for_continuation: z.object({
|
|
48
|
+
current_state: z.string().describe("Where things stand now - current status or position"),
|
|
49
|
+
next_logical_steps: z.array(z.string()).describe("What should naturally happen next based on conversation"),
|
|
50
|
+
important_context: z.array(z.string()).describe("Critical background info needed for future interactions")
|
|
51
|
+
})
|
|
52
|
+
});
|
|
53
|
+
/**
|
|
54
|
+
* Distill entire conversation history into a comprehensive summary that can replace the full message history
|
|
55
|
+
*/
|
|
56
|
+
async function distillConversationHistory(params) {
|
|
57
|
+
const { messages, conversationId, summarizerModel, toolCallToArtifactMap } = params;
|
|
58
|
+
try {
|
|
59
|
+
if (!summarizerModel?.model?.trim()) throw new Error("Summarizer model is required");
|
|
60
|
+
const { object: summary } = await generateObject({
|
|
61
|
+
model: ModelFactory.createModel(summarizerModel),
|
|
62
|
+
prompt: `You are a conversation history summarization assistant. Your job is to create a comprehensive summary that can COMPLETELY REPLACE the original conversation history while preserving all essential context.
|
|
63
|
+
|
|
64
|
+
**Complete Conversation to Summarize:**
|
|
65
|
+
|
|
66
|
+
\`\`\`text
|
|
67
|
+
${messages.map((msg) => {
|
|
68
|
+
const parts = [];
|
|
69
|
+
if (typeof msg.content === "string") parts.push(msg.content);
|
|
70
|
+
else if (Array.isArray(msg.content)) {
|
|
71
|
+
for (const block of msg.content) if (block.type === "text") parts.push(block.text);
|
|
72
|
+
else if (block.type === "tool-call") parts.push(`[TOOL CALL] ${block.toolName}(${JSON.stringify(block.input)}) [ID: ${block.toolCallId}]`);
|
|
73
|
+
else if (block.type === "tool-result") {
|
|
74
|
+
const artifactId = toolCallToArtifactMap?.[block.toolCallId];
|
|
75
|
+
const artifactInfo = artifactId ? `\n[ARTIFACT CREATED: ${artifactId}]` : "";
|
|
76
|
+
parts.push(`[TOOL RESULT] ${block.toolName} [ID: ${block.toolCallId}]${artifactInfo}\nResult: ${JSON.stringify(block.result)}`);
|
|
77
|
+
}
|
|
78
|
+
} else if (msg.content?.text) parts.push(msg.content.text);
|
|
79
|
+
return parts.length > 0 ? `${msg.role || "system"}: ${parts.join("\n")}` : "";
|
|
80
|
+
}).filter((line) => line.trim().length > 0).join("\n\n")}
|
|
81
|
+
\`\`\`
|
|
82
|
+
|
|
83
|
+
Create a comprehensive summary using this exact JSON schema:
|
|
84
|
+
|
|
85
|
+
\`\`\`json
|
|
86
|
+
{
|
|
87
|
+
"type": "conversation_history_summary_v1",
|
|
88
|
+
"session_id": "<conversationId>",
|
|
89
|
+
"conversation_overview": "<2-4 sentences capturing full context and accomplishments>",
|
|
90
|
+
"user_goals": {
|
|
91
|
+
"primary": "<main objective user was trying to achieve>",
|
|
92
|
+
"secondary": ["<additional goals that emerged>"]
|
|
93
|
+
},
|
|
94
|
+
"key_outcomes": {
|
|
95
|
+
"completed": ["<tasks/questions fully resolved>"],
|
|
96
|
+
"partial": ["<work started but not completed>"],
|
|
97
|
+
"discoveries": ["<important findings uncovered>"]
|
|
98
|
+
},
|
|
99
|
+
"technical_context": {
|
|
100
|
+
"technologies": ["<relevant tech/frameworks/tools>"],
|
|
101
|
+
"configurations": ["<settings/parameters discussed>"],
|
|
102
|
+
"issues_encountered": ["<problems/errors that came up>"],
|
|
103
|
+
"solutions_applied": ["<fixes/workarounds implemented>"]
|
|
104
|
+
},
|
|
105
|
+
"conversation_artifacts": [
|
|
106
|
+
{
|
|
107
|
+
"id": "<artifact_id>",
|
|
108
|
+
"name": "<descriptive name>",
|
|
109
|
+
"tool_name": "<tool_name>",
|
|
110
|
+
"tool_call_id": "<tool_call_id>",
|
|
111
|
+
"content_summary": "<what this artifact contains>",
|
|
112
|
+
"relevance": "<high|medium|low>"
|
|
113
|
+
}
|
|
114
|
+
],
|
|
115
|
+
"conversation_flow": {
|
|
116
|
+
"major_phases": ["<main stages conversation went through>"],
|
|
117
|
+
"decision_points": ["<key decisions made>"],
|
|
118
|
+
"topic_shifts": ["<when/why conversation changed direction>"]
|
|
119
|
+
},
|
|
120
|
+
"context_for_continuation": {
|
|
121
|
+
"current_state": "<where things stand now>",
|
|
122
|
+
"next_logical_steps": ["<what should happen next>"],
|
|
123
|
+
"important_context": ["<critical background for future interactions>"]
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
\`\`\`
|
|
127
|
+
|
|
128
|
+
**CRITICAL RULES - COMPREHENSIVE HISTORICAL PRESERVATION:**
|
|
129
|
+
|
|
130
|
+
🎯 **COMPLETE CONTEXT CAPTURE**: This summary must contain ALL information needed to continue the conversation as if the full history was available
|
|
131
|
+
|
|
132
|
+
🎯 **PRESERVE OUTCOMES**: Document everything that was accomplished, learned, discovered, or decided
|
|
133
|
+
|
|
134
|
+
🎯 **TECHNICAL PRECISION**: Include specific technologies, configurations, error messages, solutions - technical details matter
|
|
135
|
+
|
|
136
|
+
🎯 **ARTIFACT COMPLETENESS**: Reference ALL artifacts with clear descriptions of their contents and importance
|
|
137
|
+
|
|
138
|
+
🎯 **CONVERSATION NARRATIVE**: Capture the logical flow - how did we get from start to current state?
|
|
139
|
+
|
|
140
|
+
🎯 **CONTINUATION CONTEXT**: Provide everything needed for smooth conversation continuation
|
|
141
|
+
|
|
142
|
+
🎯 **NO OPERATIONAL DETAILS**: Focus on WHAT was accomplished, not HOW (tools used, compression, etc.)
|
|
143
|
+
|
|
144
|
+
🎯 **HANDLE TRANSFERS SIMPLY**: Agent transfers/delegations are just routing - don't look for reasons or justifications. Simply note "conversation transferred to [specialist]" if relevant to context.
|
|
145
|
+
|
|
146
|
+
**Examples of GOOD content:**
|
|
147
|
+
✅ "User was implementing OAuth2 authentication in React app using Auth0, encountered CORS errors on localhost:3000, resolved by adding domain to Auth0 dashboard allowed origins"
|
|
148
|
+
✅ "Discovered that the API supports both REST and GraphQL endpoints, with GraphQL providing better performance for complex queries"
|
|
149
|
+
✅ "Configured webpack dev server with proxy settings to handle API calls during development"
|
|
150
|
+
✅ "Conversation transferred to QA specialist to handle testing questions"
|
|
151
|
+
|
|
152
|
+
**Examples of BAD content:**
|
|
153
|
+
❌ "Assistant used search tool to find information"
|
|
154
|
+
❌ "Multiple tool calls were made"
|
|
155
|
+
❌ "Artifacts were created for reference"
|
|
156
|
+
❌ "Assistant needed to transfer because user required specialized help" (don't invent reasons for transfers)
|
|
157
|
+
|
|
158
|
+
**REMEMBER**: This summary is REPLACING the entire conversation history. Include everything essential for context continuation.
|
|
159
|
+
|
|
160
|
+
Return **only** valid JSON.`,
|
|
161
|
+
schema: ConversationHistorySummarySchema
|
|
162
|
+
});
|
|
163
|
+
summary.session_id = conversationId;
|
|
164
|
+
return summary;
|
|
165
|
+
} catch (error) {
|
|
166
|
+
logger.error({
|
|
167
|
+
conversationId,
|
|
168
|
+
messageCount: messages.length,
|
|
169
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
170
|
+
}, "Failed to distill conversation history");
|
|
171
|
+
return {
|
|
172
|
+
type: "conversation_history_summary_v1",
|
|
173
|
+
session_id: conversationId,
|
|
174
|
+
conversation_overview: "Conversation session with technical discussion and problem-solving",
|
|
175
|
+
user_goals: {
|
|
176
|
+
primary: "Technical assistance and problem-solving",
|
|
177
|
+
secondary: []
|
|
178
|
+
},
|
|
179
|
+
key_outcomes: {
|
|
180
|
+
completed: [],
|
|
181
|
+
partial: ["Ongoing technical work"],
|
|
182
|
+
discoveries: []
|
|
183
|
+
},
|
|
184
|
+
technical_context: {
|
|
185
|
+
technologies: [],
|
|
186
|
+
configurations: [],
|
|
187
|
+
issues_encountered: [],
|
|
188
|
+
solutions_applied: []
|
|
189
|
+
},
|
|
190
|
+
conversation_artifacts: [],
|
|
191
|
+
conversation_flow: {
|
|
192
|
+
major_phases: ["Initial discussion", "Technical exploration"],
|
|
193
|
+
decision_points: [],
|
|
194
|
+
topic_shifts: []
|
|
195
|
+
},
|
|
196
|
+
context_for_continuation: {
|
|
197
|
+
current_state: "In progress - technical work ongoing",
|
|
198
|
+
next_logical_steps: ["Continue with current technical objectives"],
|
|
199
|
+
important_context: ["Review previous discussion for technical context"]
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
//#endregion
|
|
206
|
+
export { ConversationHistorySummarySchema, distillConversationHistory };
|
|
@@ -36,42 +36,29 @@ async function distillConversation(params) {
|
|
|
36
36
|
try {
|
|
37
37
|
const modelToUse = summarizerModel;
|
|
38
38
|
if (!modelToUse?.model?.trim()) throw new Error("Summarizer model is required");
|
|
39
|
-
const model = ModelFactory.createModel(modelToUse);
|
|
40
|
-
const existingSummaryContext = currentSummary ? `**Current summary:**\n\n\`\`\`json\n${JSON.stringify(currentSummary, null, 2)}\n\`\`\`` : "**Current summary:** None (first distillation)";
|
|
41
|
-
const formattedMessages = messages.map((msg) => {
|
|
42
|
-
const parts = [];
|
|
43
|
-
if (typeof msg.content === "string") parts.push(msg.content);
|
|
44
|
-
else if (Array.isArray(msg.content)) {
|
|
45
|
-
for (const block of msg.content) if (block.type === "text") parts.push(block.text);
|
|
46
|
-
else if (block.type === "tool-call") parts.push(`[TOOL CALL] ${block.toolName}(${JSON.stringify(block.input)}) [ID: ${block.toolCallId}]`);
|
|
47
|
-
else if (block.type === "tool-result") {
|
|
48
|
-
const artifactId = toolCallToArtifactMap?.[block.toolCallId];
|
|
49
|
-
const artifactInfo = artifactId ? `\n[ARTIFACT CREATED: ${artifactId}]` : "";
|
|
50
|
-
parts.push(`[TOOL RESULT] ${block.toolName} [ID: ${block.toolCallId}]${artifactInfo}\nResult: ${JSON.stringify(block.result)}`);
|
|
51
|
-
}
|
|
52
|
-
} else if (msg.content?.text) parts.push(msg.content.text);
|
|
53
|
-
return parts.length > 0 ? `${msg.role || "system"}: ${parts.join("\n")}` : "";
|
|
54
|
-
}).filter((line) => line.trim().length > 0).join("\n\n");
|
|
55
|
-
logger.debug({
|
|
56
|
-
conversationId,
|
|
57
|
-
messageCount: messages.length,
|
|
58
|
-
formattedLength: formattedMessages.length,
|
|
59
|
-
sampleMessages: messages.slice(0, 2).map((m) => ({
|
|
60
|
-
role: m.role,
|
|
61
|
-
contentType: typeof m.content,
|
|
62
|
-
hasContent: !!m.content
|
|
63
|
-
}))
|
|
64
|
-
}, "Formatting messages for distillation");
|
|
65
39
|
const { object: summary } = await generateObject({
|
|
66
|
-
model,
|
|
40
|
+
model: ModelFactory.createModel(modelToUse),
|
|
67
41
|
prompt: `You are a conversation summarization assistant. Your job is to create or update a compact, structured summary that captures VALUABLE CONTENT and FINDINGS, not just operational details.
|
|
68
42
|
|
|
69
|
-
${
|
|
43
|
+
${currentSummary ? `**Current summary:**\n\n\`\`\`json\n${JSON.stringify(currentSummary, null, 2)}\n\`\`\`` : "**Current summary:** None (first distillation)"}
|
|
70
44
|
|
|
71
45
|
**Messages to summarize:**
|
|
72
46
|
|
|
73
47
|
\`\`\`text
|
|
74
|
-
${
|
|
48
|
+
${messages.map((msg) => {
|
|
49
|
+
const parts = [];
|
|
50
|
+
if (typeof msg.content === "string") parts.push(msg.content);
|
|
51
|
+
else if (Array.isArray(msg.content)) {
|
|
52
|
+
for (const block of msg.content) if (block.type === "text") parts.push(block.text);
|
|
53
|
+
else if (block.type === "tool-call") parts.push(`[TOOL CALL] ${block.toolName}(${JSON.stringify(block.input)}) [ID: ${block.toolCallId}]`);
|
|
54
|
+
else if (block.type === "tool-result") {
|
|
55
|
+
const artifactId = toolCallToArtifactMap?.[block.toolCallId];
|
|
56
|
+
const artifactInfo = artifactId ? `\n[ARTIFACT CREATED: ${artifactId}]` : "";
|
|
57
|
+
parts.push(`[TOOL RESULT] ${block.toolName} [ID: ${block.toolCallId}]${artifactInfo}\nResult: ${JSON.stringify(block.output)}`);
|
|
58
|
+
}
|
|
59
|
+
} else if (msg.content?.text) parts.push(msg.content.text);
|
|
60
|
+
return parts.length > 0 ? `${msg.role || "system"}: ${parts.join("\n")}` : "";
|
|
61
|
+
}).filter((line) => line.trim().length > 0).join("\n\n")}
|
|
75
62
|
\`\`\`
|
|
76
63
|
|
|
77
64
|
Create/update a summary using this exact JSON schema:
|
|
@@ -109,6 +96,7 @@ Create/update a summary using this exact JSON schema:
|
|
|
109
96
|
🎯 **BE CONCRETE**: Use specific details from tool results, not generic descriptions
|
|
110
97
|
🎯 **BE CONCISE**: Keep ALL fields brief - you are compressing to save context, not writing a report
|
|
111
98
|
🎯 **LIMIT NEXT STEPS**: Agent has already done substantial work - suggest minimal follow-up actions only
|
|
99
|
+
🎯 **HANDLE TRANSFERS SIMPLY**: Agent transfers/delegations are just routing - don't look for reasons or justifications. Simply note "conversation transferred to [specialist]" if relevant.
|
|
112
100
|
|
|
113
101
|
**Examples:**
|
|
114
102
|
❌ BAD: "Assistant used search tool and created artifacts"
|
|
@@ -117,18 +105,15 @@ Create/update a summary using this exact JSON schema:
|
|
|
117
105
|
❌ BAD: "Tool calls were made to gather information"
|
|
118
106
|
✅ GOOD: "Platform includes 10 feature categories: chat widgets, knowledge base, analytics, integrations, theming options"
|
|
119
107
|
|
|
108
|
+
❌ BAD: "Assistant needed to transfer to QA because user required specialized testing help"
|
|
109
|
+
✅ GOOD: "Conversation transferred to QA specialist"
|
|
110
|
+
|
|
120
111
|
**Focus on WHAT WAS LEARNED, not HOW IT WAS LEARNED**
|
|
121
112
|
|
|
122
113
|
Return **only** valid JSON.`,
|
|
123
114
|
schema: ConversationSummarySchema
|
|
124
115
|
});
|
|
125
116
|
summary.session_id = conversationId;
|
|
126
|
-
logger.info({
|
|
127
|
-
conversationId,
|
|
128
|
-
messageCount: messages.length,
|
|
129
|
-
artifactsCount: summary.related_artifacts?.length || 0,
|
|
130
|
-
decisionsCount: summary.decisions.length
|
|
131
|
-
}, "Successfully distilled conversation");
|
|
132
117
|
return summary;
|
|
133
118
|
} catch (error) {
|
|
134
119
|
logger.error({
|
|
@@ -11,6 +11,12 @@ interface ModelContextInfo {
|
|
|
11
11
|
modelId: string;
|
|
12
12
|
source: 'llm-info' | 'fallback';
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Extract model ID from model string for llm-info lookup
|
|
16
|
+
* Includes smart mapping to find dated versions for models that don't have exact matches
|
|
17
|
+
*/
|
|
18
|
+
declare function extractModelIdForLlmInfo(modelInput: string): string;
|
|
19
|
+
declare function extractModelIdForLlmInfo(modelSettings?: ModelSettings): string | null;
|
|
14
20
|
/**
|
|
15
21
|
* Get context window size for a model using llm-info
|
|
16
22
|
* Falls back to default values if model not found
|
|
@@ -19,8 +25,10 @@ declare function getModelContextWindow(modelSettings?: ModelSettings): ModelCont
|
|
|
19
25
|
/**
|
|
20
26
|
* Get compression configuration based on model context window
|
|
21
27
|
* Uses actual model context window when available, otherwise falls back to environment variables
|
|
28
|
+
* @param modelSettings - Model settings to get context window for
|
|
29
|
+
* @param targetPercentage - Target percentage of context window to use (e.g., 0.5 for conversation, undefined for model-aware defaults)
|
|
22
30
|
*/
|
|
23
|
-
declare function getCompressionConfigForModel(modelSettings?: ModelSettings): {
|
|
31
|
+
declare function getCompressionConfigForModel(modelSettings?: ModelSettings, targetPercentage?: number): {
|
|
24
32
|
hardLimit: number;
|
|
25
33
|
safetyBuffer: number;
|
|
26
34
|
enabled: boolean;
|
|
@@ -28,4 +36,4 @@ declare function getCompressionConfigForModel(modelSettings?: ModelSettings): {
|
|
|
28
36
|
modelContextInfo: ModelContextInfo;
|
|
29
37
|
};
|
|
30
38
|
//#endregion
|
|
31
|
-
export { ModelContextInfo, getCompressionConfigForModel, getModelContextWindow };
|
|
39
|
+
export { ModelContextInfo, extractModelIdForLlmInfo, getCompressionConfigForModel, getModelContextWindow };
|
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
import { getLogger } from "../logger.js";
|
|
2
|
+
import { COMPRESSION_ENABLED, COMPRESSION_HARD_LIMIT, COMPRESSION_SAFETY_BUFFER } from "../constants/execution-limits/index.js";
|
|
2
3
|
import { ModelInfoMap } from "llm-info";
|
|
3
4
|
|
|
4
5
|
//#region src/utils/model-context-utils.ts
|
|
5
6
|
const logger = getLogger("ModelContextUtils");
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
function extractModelIdForLlmInfo(modelInput) {
|
|
8
|
+
let modelString;
|
|
9
|
+
if (typeof modelInput === "string") {
|
|
10
|
+
modelString = modelInput.trim();
|
|
11
|
+
if (!modelString) return null;
|
|
12
|
+
} else if (modelInput?.model) {
|
|
13
|
+
modelString = modelInput.model.trim();
|
|
14
|
+
if (!modelString) return null;
|
|
15
|
+
} else return null;
|
|
16
|
+
let modelId;
|
|
12
17
|
if (modelString.includes("/")) {
|
|
13
18
|
const parts = modelString.split("/");
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
return
|
|
19
|
+
modelId = parts[parts.length - 1];
|
|
20
|
+
} else modelId = modelString;
|
|
21
|
+
if (modelId in ModelInfoMap) return modelId;
|
|
22
|
+
const matchingModels = Object.keys(ModelInfoMap).filter((key) => key.startsWith(modelId));
|
|
23
|
+
if (matchingModels.length > 0) return matchingModels.sort().reverse()[0];
|
|
24
|
+
return modelId;
|
|
17
25
|
}
|
|
18
26
|
/**
|
|
19
27
|
* Get context window size for a model using llm-info
|
|
@@ -54,7 +62,8 @@ function getModelContextWindow(modelSettings) {
|
|
|
54
62
|
modelId,
|
|
55
63
|
source: "llm-info"
|
|
56
64
|
};
|
|
57
|
-
}
|
|
65
|
+
}
|
|
66
|
+
logger.debug({
|
|
58
67
|
modelId,
|
|
59
68
|
modelDetails,
|
|
60
69
|
originalModel: modelSettings.model
|
|
@@ -86,11 +95,11 @@ function getCompressionParams(contextWindow) {
|
|
|
86
95
|
threshold: .85,
|
|
87
96
|
bufferPct: .1
|
|
88
97
|
};
|
|
89
|
-
|
|
98
|
+
if (contextWindow < 5e5) return {
|
|
90
99
|
threshold: .9,
|
|
91
100
|
bufferPct: .07
|
|
92
101
|
};
|
|
93
|
-
|
|
102
|
+
return {
|
|
94
103
|
threshold: .95,
|
|
95
104
|
bufferPct: .04
|
|
96
105
|
};
|
|
@@ -98,28 +107,52 @@ function getCompressionParams(contextWindow) {
|
|
|
98
107
|
/**
|
|
99
108
|
* Get compression configuration based on model context window
|
|
100
109
|
* Uses actual model context window when available, otherwise falls back to environment variables
|
|
110
|
+
* @param modelSettings - Model settings to get context window for
|
|
111
|
+
* @param targetPercentage - Target percentage of context window to use (e.g., 0.5 for conversation, undefined for model-aware defaults)
|
|
101
112
|
*/
|
|
102
|
-
function getCompressionConfigForModel(modelSettings) {
|
|
113
|
+
function getCompressionConfigForModel(modelSettings, targetPercentage) {
|
|
103
114
|
const modelContextInfo = getModelContextWindow(modelSettings);
|
|
104
|
-
const envHardLimit = parseInt(process.env.AGENTS_COMPRESSION_HARD_LIMIT ||
|
|
105
|
-
const envSafetyBuffer = parseInt(process.env.AGENTS_COMPRESSION_SAFETY_BUFFER ||
|
|
106
|
-
const enabled = process.env.AGENTS_COMPRESSION_ENABLED !== "false";
|
|
115
|
+
const envHardLimit = parseInt(process.env.AGENTS_COMPRESSION_HARD_LIMIT || COMPRESSION_HARD_LIMIT.toString());
|
|
116
|
+
const envSafetyBuffer = parseInt(process.env.AGENTS_COMPRESSION_SAFETY_BUFFER || COMPRESSION_SAFETY_BUFFER.toString());
|
|
117
|
+
const enabled = process.env.AGENTS_COMPRESSION_ENABLED !== "false" && COMPRESSION_ENABLED;
|
|
107
118
|
if (modelContextInfo.hasValidContextWindow && modelContextInfo.contextWindow) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
let hardLimit;
|
|
120
|
+
let safetyBuffer;
|
|
121
|
+
let logContext;
|
|
122
|
+
if (targetPercentage !== void 0) {
|
|
123
|
+
hardLimit = Math.floor(modelContextInfo.contextWindow * targetPercentage);
|
|
124
|
+
safetyBuffer = Math.floor(modelContextInfo.contextWindow * .05);
|
|
125
|
+
const triggerPoint = hardLimit - safetyBuffer;
|
|
126
|
+
const triggerPercentage = (triggerPoint / modelContextInfo.contextWindow * 100).toFixed(1);
|
|
127
|
+
logContext = {
|
|
128
|
+
modelId: modelContextInfo.modelId,
|
|
129
|
+
contextWindow: modelContextInfo.contextWindow,
|
|
130
|
+
hardLimit,
|
|
131
|
+
safetyBuffer,
|
|
132
|
+
triggerPoint,
|
|
133
|
+
triggerPercentage: `${triggerPercentage}%`,
|
|
134
|
+
targetPercentage: `${(targetPercentage * 100).toFixed(1)}%`,
|
|
135
|
+
compressionType: targetPercentage <= .6 ? "conversation" : "mid-generation"
|
|
136
|
+
};
|
|
137
|
+
logger.info(logContext, "Using percentage-based compression configuration");
|
|
138
|
+
} else {
|
|
139
|
+
const params = getCompressionParams(modelContextInfo.contextWindow);
|
|
140
|
+
hardLimit = Math.floor(modelContextInfo.contextWindow * params.threshold);
|
|
141
|
+
safetyBuffer = Math.floor(modelContextInfo.contextWindow * params.bufferPct);
|
|
142
|
+
const triggerPoint = hardLimit - safetyBuffer;
|
|
143
|
+
const triggerPercentage = (triggerPoint / modelContextInfo.contextWindow * 100).toFixed(1);
|
|
144
|
+
logContext = {
|
|
145
|
+
modelId: modelContextInfo.modelId,
|
|
146
|
+
contextWindow: modelContextInfo.contextWindow,
|
|
147
|
+
hardLimit,
|
|
148
|
+
safetyBuffer,
|
|
149
|
+
triggerPoint,
|
|
150
|
+
triggerPercentage: `${triggerPercentage}%`,
|
|
151
|
+
threshold: params.threshold,
|
|
152
|
+
bufferPct: params.bufferPct
|
|
153
|
+
};
|
|
154
|
+
logger.info(logContext, "Using model-size aware compression configuration");
|
|
155
|
+
}
|
|
123
156
|
return {
|
|
124
157
|
hardLimit,
|
|
125
158
|
safetyBuffer,
|
|
@@ -127,23 +160,22 @@ function getCompressionConfigForModel(modelSettings) {
|
|
|
127
160
|
source: "model-specific",
|
|
128
161
|
modelContextInfo
|
|
129
162
|
};
|
|
130
|
-
} else {
|
|
131
|
-
const source = process.env.AGENTS_COMPRESSION_HARD_LIMIT ? "environment" : "default";
|
|
132
|
-
logger.debug({
|
|
133
|
-
modelId: modelContextInfo.modelId,
|
|
134
|
-
hardLimit: envHardLimit,
|
|
135
|
-
safetyBuffer: envSafetyBuffer,
|
|
136
|
-
source
|
|
137
|
-
}, "Using fallback compression configuration");
|
|
138
|
-
return {
|
|
139
|
-
hardLimit: envHardLimit,
|
|
140
|
-
safetyBuffer: envSafetyBuffer,
|
|
141
|
-
enabled,
|
|
142
|
-
source,
|
|
143
|
-
modelContextInfo
|
|
144
|
-
};
|
|
145
163
|
}
|
|
164
|
+
const source = process.env.AGENTS_COMPRESSION_HARD_LIMIT ? "environment" : "default";
|
|
165
|
+
logger.debug({
|
|
166
|
+
modelId: modelContextInfo.modelId,
|
|
167
|
+
hardLimit: envHardLimit,
|
|
168
|
+
safetyBuffer: envSafetyBuffer,
|
|
169
|
+
source
|
|
170
|
+
}, "Using fallback compression configuration");
|
|
171
|
+
return {
|
|
172
|
+
hardLimit: envHardLimit,
|
|
173
|
+
safetyBuffer: envSafetyBuffer,
|
|
174
|
+
enabled,
|
|
175
|
+
source,
|
|
176
|
+
modelContextInfo
|
|
177
|
+
};
|
|
146
178
|
}
|
|
147
179
|
|
|
148
180
|
//#endregion
|
|
149
|
-
export { getCompressionConfigForModel, getModelContextWindow };
|
|
181
|
+
export { extractModelIdForLlmInfo, getCompressionConfigForModel, getModelContextWindow };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inkeep/agents-run-api",
|
|
3
|
-
"version": "0.0.0-dev-
|
|
3
|
+
"version": "0.0.0-dev-20260105201718",
|
|
4
4
|
"description": "Agents Run API for Inkeep Agent Framework - handles chat, agent execution, and streaming",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"hono": "^4.10.4",
|
|
42
42
|
"jmespath": "^0.16.0",
|
|
43
43
|
"llm-info": "^1.0.69",
|
|
44
|
-
"@inkeep/agents-core": "^0.0.0-dev-
|
|
44
|
+
"@inkeep/agents-core": "^0.0.0-dev-20260105201718"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"@hono/zod-openapi": "^1.1.5",
|