@inkeep/agents-run-api 0.0.0-dev-20260105192809 → 0.0.0-dev-20260105194521

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.
@@ -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
- ${existingSummaryContext}
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
- ${formattedMessages}
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
- * Extract model ID from model settings for llm-info lookup
8
- */
9
- function extractModelIdForLlmInfo(modelSettings) {
10
- if (!modelSettings?.model) return null;
11
- const modelString = modelSettings.model.trim();
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
- return parts[parts.length - 1];
15
- }
16
- return modelString;
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
- } else logger.debug({
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
- else if (contextWindow < 5e5) return {
98
+ if (contextWindow < 5e5) return {
90
99
  threshold: .9,
91
100
  bufferPct: .07
92
101
  };
93
- else return {
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 || "120000");
105
- const envSafetyBuffer = parseInt(process.env.AGENTS_COMPRESSION_SAFETY_BUFFER || "20000");
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
- const params = getCompressionParams(modelContextInfo.contextWindow);
109
- const hardLimit = Math.floor(modelContextInfo.contextWindow * params.threshold);
110
- const safetyBuffer = Math.floor(modelContextInfo.contextWindow * params.bufferPct);
111
- const triggerPoint = hardLimit - safetyBuffer;
112
- const triggerPercentage = (triggerPoint / modelContextInfo.contextWindow * 100).toFixed(1);
113
- logger.info({
114
- modelId: modelContextInfo.modelId,
115
- contextWindow: modelContextInfo.contextWindow,
116
- hardLimit,
117
- safetyBuffer,
118
- triggerPoint,
119
- triggerPercentage: `${triggerPercentage}%`,
120
- threshold: params.threshold,
121
- bufferPct: params.bufferPct
122
- }, "Using model-size aware compression configuration");
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-20260105192809",
3
+ "version": "0.0.0-dev-20260105194521",
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-20260105192809"
44
+ "@inkeep/agents-core": "^0.0.0-dev-20260105194521"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "@hono/zod-openapi": "^1.1.5",