@google/adk 1.4.0 → 1.5.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/dist/cjs/a2a/agent_to_a2a.js +18 -2
- package/dist/cjs/a2a/event_converter_utils.js +8 -4
- package/dist/cjs/a2a/metadata_converter_utils.js +1 -1
- package/dist/cjs/agents/base_agent.js +39 -0
- package/dist/cjs/agents/functions.js +6 -21
- package/dist/cjs/agents/llm_agent.js +2 -1
- package/dist/cjs/agents/loop_agent.js +15 -3
- package/dist/cjs/agents/processors/content_processor_utils.js +2 -1
- package/dist/cjs/agents/routed_agent.js +31 -0
- package/dist/cjs/agents/run_config.js +3 -2
- package/dist/cjs/artifacts/in_memory_artifact_service.js +7 -0
- package/dist/cjs/common.js +29 -0
- package/dist/cjs/context/anchored_context_compactor.js +127 -0
- package/dist/cjs/context/compaction_utils.js +52 -0
- package/dist/cjs/context/token_based_context_compactor.js +11 -24
- package/dist/cjs/events/compacted_event.js +9 -3
- package/dist/cjs/events/event.js +28 -0
- package/dist/cjs/index.js +4 -0
- package/dist/cjs/integrations/agent_registry/agent_registry_mcp_toolset.js +9 -1
- package/dist/cjs/plugins/global_instruction_plugin.js +97 -0
- package/dist/cjs/runner/runner.js +51 -12
- package/dist/cjs/tools/enterprise_web_search_tool.js +81 -0
- package/dist/cjs/tools/example_tool.js +68 -0
- package/dist/cjs/tools/load_web_page.js +250 -0
- package/dist/cjs/tools/mcp/load_mcp_resource_tool.js +140 -0
- package/dist/cjs/tools/mcp/mcp_toolset.js +63 -0
- package/dist/cjs/tools/openapi_tool/openapi_spec_parser/tool_auth_handler.js +13 -10
- package/dist/cjs/tools/skill/run_skill_inline_script_tool.js +54 -4
- package/dist/cjs/tools/skill/skill_toolset.js +4 -2
- package/dist/cjs/utils/branch_trie.js +41 -0
- package/dist/cjs/utils/streaming_utils.js +5 -5
- package/dist/cjs/version.js +1 -1
- package/dist/esm/a2a/agent_to_a2a.js +18 -2
- package/dist/esm/a2a/event_converter_utils.js +8 -4
- package/dist/esm/a2a/metadata_converter_utils.js +1 -1
- package/dist/esm/agents/base_agent.js +39 -0
- package/dist/esm/agents/functions.js +7 -17
- package/dist/esm/agents/llm_agent.js +4 -3
- package/dist/esm/agents/loop_agent.js +15 -3
- package/dist/esm/agents/processors/content_processor_utils.js +2 -1
- package/dist/esm/agents/routed_agent.js +31 -0
- package/dist/esm/agents/run_config.js +3 -2
- package/dist/esm/artifacts/in_memory_artifact_service.js +7 -0
- package/dist/esm/common.js +25 -1
- package/dist/esm/context/anchored_context_compactor.js +99 -0
- package/dist/esm/context/compaction_utils.js +27 -0
- package/dist/esm/context/token_based_context_compactor.js +11 -24
- package/dist/esm/events/compacted_event.js +7 -2
- package/dist/esm/events/event.js +24 -0
- package/dist/esm/index.js +6 -1
- package/dist/esm/integrations/agent_registry/agent_registry_mcp_toolset.js +9 -1
- package/dist/esm/plugins/global_instruction_plugin.js +69 -0
- package/dist/esm/runner/runner.js +51 -12
- package/dist/esm/tools/enterprise_web_search_tool.js +55 -0
- package/dist/esm/tools/example_tool.js +42 -0
- package/dist/esm/tools/load_web_page.js +221 -0
- package/dist/esm/tools/mcp/load_mcp_resource_tool.js +114 -0
- package/dist/esm/tools/mcp/mcp_toolset.js +63 -0
- package/dist/esm/tools/openapi_tool/openapi_spec_parser/tool_auth_handler.js +13 -10
- package/dist/esm/tools/skill/run_skill_inline_script_tool.js +53 -4
- package/dist/esm/tools/skill/skill_toolset.js +4 -2
- package/dist/esm/utils/branch_trie.js +13 -0
- package/dist/esm/utils/streaming_utils.js +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/types/a2a/a2a_remote_agent.d.ts +5 -1
- package/dist/types/a2a/agent_to_a2a.d.ts +38 -0
- package/dist/types/agents/base_agent.d.ts +29 -1
- package/dist/types/agents/functions.d.ts +1 -9
- package/dist/types/agents/llm_agent.d.ts +5 -1
- package/dist/types/agents/loop_agent.d.ts +2 -2
- package/dist/types/agents/routed_agent.d.ts +24 -1
- package/dist/types/agents/run_config.d.ts +1 -1
- package/dist/types/common.d.ts +9 -2
- package/dist/types/context/anchored_context_compactor.d.ts +35 -0
- package/dist/types/context/compaction_utils.d.ts +15 -0
- package/dist/types/events/compacted_event.d.ts +10 -0
- package/dist/types/events/event.d.ts +13 -0
- package/dist/types/index.d.ts +3 -2
- package/dist/types/plugins/global_instruction_plugin.d.ts +38 -0
- package/dist/types/runner/runner.d.ts +1 -1
- package/dist/types/tools/enterprise_web_search_tool.d.ts +26 -0
- package/dist/types/tools/example_tool.d.ts +21 -0
- package/dist/types/tools/load_web_page.d.ts +33 -0
- package/dist/types/tools/mcp/load_mcp_resource_tool.d.ts +26 -0
- package/dist/types/tools/mcp/mcp_toolset.d.ts +27 -0
- package/dist/types/tools/skill/run_skill_inline_script_tool.d.ts +25 -0
- package/dist/types/tools/skill/skill_toolset.d.ts +9 -0
- package/dist/types/utils/branch_trie.d.ts +14 -0
- package/dist/types/version.d.ts +1 -1
- package/dist/web/a2a/agent_to_a2a.js +18 -2
- package/dist/web/a2a/event_converter_utils.js +8 -4
- package/dist/web/a2a/metadata_converter_utils.js +1 -1
- package/dist/web/agents/base_agent.js +39 -0
- package/dist/web/agents/functions.js +7 -17
- package/dist/web/agents/llm_agent.js +4 -3
- package/dist/web/agents/loop_agent.js +27 -3
- package/dist/web/agents/processors/content_processor_utils.js +2 -1
- package/dist/web/agents/routed_agent.js +31 -0
- package/dist/web/agents/run_config.js +8 -3
- package/dist/web/artifacts/in_memory_artifact_service.js +7 -0
- package/dist/web/common.js +25 -1
- package/dist/web/context/anchored_context_compactor.js +117 -0
- package/dist/web/context/compaction_utils.js +27 -0
- package/dist/web/context/token_based_context_compactor.js +11 -24
- package/dist/web/events/compacted_event.js +7 -2
- package/dist/web/events/event.js +24 -0
- package/dist/web/index.js +6 -1
- package/dist/web/integrations/agent_registry/agent_registry_mcp_toolset.js +9 -1
- package/dist/web/plugins/global_instruction_plugin.js +67 -0
- package/dist/web/runner/runner.js +48 -11
- package/dist/web/tools/enterprise_web_search_tool.js +55 -0
- package/dist/web/tools/example_tool.js +42 -0
- package/dist/web/tools/load_web_page.js +221 -0
- package/dist/web/tools/mcp/load_mcp_resource_tool.js +107 -0
- package/dist/web/tools/mcp/mcp_toolset.js +63 -0
- package/dist/web/tools/openapi_tool/openapi_spec_parser/tool_auth_handler.js +13 -10
- package/dist/web/tools/skill/run_skill_inline_script_tool.js +51 -4
- package/dist/web/tools/skill/skill_toolset.js +4 -2
- package/dist/web/utils/branch_trie.js +13 -0
- package/dist/web/utils/streaming_utils.js +1 -1
- package/dist/web/version.js +1 -1
- package/package.json +1 -1
|
@@ -7,6 +7,7 @@ const require = topLevelCreateRequire(import.meta.url);
|
|
|
7
7
|
*/
|
|
8
8
|
import { getContents } from "../agents/processors/content_processor_utils.js";
|
|
9
9
|
import { isCompactedEvent } from "../events/compacted_event.js";
|
|
10
|
+
import { calculateRetainStartIndex } from "./compaction_utils.js";
|
|
10
11
|
const CHARS_PER_TOKEN = 4;
|
|
11
12
|
class TokenBasedContextCompactor {
|
|
12
13
|
constructor(options) {
|
|
@@ -39,6 +40,13 @@ class TokenBasedContextCompactor {
|
|
|
39
40
|
if (rawEvents.length <= this.eventRetentionSize) {
|
|
40
41
|
return false;
|
|
41
42
|
}
|
|
43
|
+
const retainStartIndex = calculateRetainStartIndex(
|
|
44
|
+
rawEvents,
|
|
45
|
+
this.eventRetentionSize
|
|
46
|
+
);
|
|
47
|
+
if (retainStartIndex === 0) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
42
50
|
const promptTokenCount = latestPromptTokenCount(
|
|
43
51
|
activeEvents,
|
|
44
52
|
invocationContext
|
|
@@ -55,19 +63,10 @@ class TokenBasedContextCompactor {
|
|
|
55
63
|
if (rawEvents.length <= this.eventRetentionSize) {
|
|
56
64
|
return;
|
|
57
65
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
66
|
+
const retainStartIndex = calculateRetainStartIndex(
|
|
67
|
+
rawEvents,
|
|
68
|
+
this.eventRetentionSize
|
|
61
69
|
);
|
|
62
|
-
while (retainStartIndex > 0) {
|
|
63
|
-
const eventToRetain = rawEvents[retainStartIndex];
|
|
64
|
-
const previousEvent = rawEvents[retainStartIndex - 1];
|
|
65
|
-
if (hasFunctionResponse(eventToRetain) && hasFunctionCall(previousEvent)) {
|
|
66
|
-
retainStartIndex--;
|
|
67
|
-
} else {
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
70
|
if (retainStartIndex === 0) {
|
|
72
71
|
return;
|
|
73
72
|
}
|
|
@@ -122,18 +121,6 @@ function estimatePromptTokenCount(activeEvents, invocationContext) {
|
|
|
122
121
|
}
|
|
123
122
|
return Math.ceil(totalChars / CHARS_PER_TOKEN);
|
|
124
123
|
}
|
|
125
|
-
function hasFunctionCall(event) {
|
|
126
|
-
var _a, _b;
|
|
127
|
-
return !!((_b = (_a = event.content) == null ? void 0 : _a.parts) == null ? void 0 : _b.some(
|
|
128
|
-
(part) => part.functionCall !== void 0
|
|
129
|
-
));
|
|
130
|
-
}
|
|
131
|
-
function hasFunctionResponse(event) {
|
|
132
|
-
var _a, _b;
|
|
133
|
-
return !!((_b = (_a = event.content) == null ? void 0 : _a.parts) == null ? void 0 : _b.some(
|
|
134
|
-
(part) => part.functionResponse !== void 0
|
|
135
|
-
));
|
|
136
|
-
}
|
|
137
124
|
export {
|
|
138
125
|
TokenBasedContextCompactor
|
|
139
126
|
};
|
|
@@ -9,16 +9,21 @@ import { createEvent } from "./event.js";
|
|
|
9
9
|
function isCompactedEvent(event) {
|
|
10
10
|
return "isCompacted" in event && event.isCompacted === true;
|
|
11
11
|
}
|
|
12
|
+
function isScratchpadEvent(event) {
|
|
13
|
+
return isCompactedEvent(event) && event.isScratchpad === true;
|
|
14
|
+
}
|
|
12
15
|
function createCompactedEvent(params = {}) {
|
|
13
16
|
return {
|
|
14
17
|
...createEvent(params),
|
|
15
18
|
isCompacted: params.isCompacted || true,
|
|
16
19
|
startTime: params.startTime,
|
|
17
20
|
endTime: params.endTime,
|
|
18
|
-
compactedContent: params.compactedContent
|
|
21
|
+
compactedContent: params.compactedContent,
|
|
22
|
+
isScratchpad: params.isScratchpad
|
|
19
23
|
};
|
|
20
24
|
}
|
|
21
25
|
export {
|
|
22
26
|
createCompactedEvent,
|
|
23
|
-
isCompactedEvent
|
|
27
|
+
isCompactedEvent,
|
|
28
|
+
isScratchpadEvent
|
|
24
29
|
};
|
package/dist/esm/events/event.js
CHANGED
|
@@ -5,6 +5,7 @@ const require = topLevelCreateRequire(import.meta.url);
|
|
|
5
5
|
* Copyright 2026 Google LLC
|
|
6
6
|
* SPDX-License-Identifier: Apache-2.0
|
|
7
7
|
*/
|
|
8
|
+
import { randomUUID } from "../utils/env_aware_utils.js";
|
|
8
9
|
import { toCamelCase, toSnakeCase } from "../utils/object_notation_utils.js";
|
|
9
10
|
import { createEventActions } from "./event_actions.js";
|
|
10
11
|
function createEvent(params = {}) {
|
|
@@ -36,6 +37,17 @@ function getFunctionCalls(event) {
|
|
|
36
37
|
}
|
|
37
38
|
return funcCalls;
|
|
38
39
|
}
|
|
40
|
+
const AF_FUNCTION_CALL_ID_PREFIX = "adk-";
|
|
41
|
+
function generateClientFunctionCallId() {
|
|
42
|
+
return `${AF_FUNCTION_CALL_ID_PREFIX}${randomUUID()}`;
|
|
43
|
+
}
|
|
44
|
+
function populateClientFunctionCallId(modelResponseEvent) {
|
|
45
|
+
for (const functionCall of getFunctionCalls(modelResponseEvent)) {
|
|
46
|
+
if (!functionCall.id) {
|
|
47
|
+
functionCall.id = generateClientFunctionCallId();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
39
51
|
function getFunctionResponses(event) {
|
|
40
52
|
const funcResponses = [];
|
|
41
53
|
if (event.content && event.content.parts) {
|
|
@@ -65,6 +77,14 @@ function stringifyContent(event) {
|
|
|
65
77
|
return (_a2 = part.text) != null ? _a2 : "";
|
|
66
78
|
}).join("");
|
|
67
79
|
}
|
|
80
|
+
function getEventTokens(event) {
|
|
81
|
+
var _a;
|
|
82
|
+
if (((_a = event.usageMetadata) == null ? void 0 : _a.promptTokenCount) !== void 0) {
|
|
83
|
+
return event.usageMetadata.promptTokenCount;
|
|
84
|
+
}
|
|
85
|
+
const contentStr = stringifyContent(event);
|
|
86
|
+
return Math.ceil(contentStr.length / 4);
|
|
87
|
+
}
|
|
68
88
|
function hasThoughts(event) {
|
|
69
89
|
var _a, _b;
|
|
70
90
|
return !!((_b = (_a = event.content) == null ? void 0 : _a.parts) == null ? void 0 : _b.some((part) => part.thought === true));
|
|
@@ -120,13 +140,17 @@ function transformToSnakeCaseEvent(event) {
|
|
|
120
140
|
return toSnakeCase(event, PRESERVE_KEYS_CAMEL_CASE);
|
|
121
141
|
}
|
|
122
142
|
export {
|
|
143
|
+
AF_FUNCTION_CALL_ID_PREFIX,
|
|
123
144
|
createEvent,
|
|
124
145
|
createNewEventId,
|
|
146
|
+
generateClientFunctionCallId,
|
|
147
|
+
getEventTokens,
|
|
125
148
|
getFunctionCalls,
|
|
126
149
|
getFunctionResponses,
|
|
127
150
|
hasThoughts,
|
|
128
151
|
hasTrailingCodeExecutionResult,
|
|
129
152
|
isFinalResponse,
|
|
153
|
+
populateClientFunctionCallId,
|
|
130
154
|
pruneThoughts,
|
|
131
155
|
stringifyContent,
|
|
132
156
|
transformToCamelCaseEvent,
|
package/dist/esm/index.js
CHANGED
|
@@ -29,11 +29,15 @@ import {
|
|
|
29
29
|
loadSkillFromDir,
|
|
30
30
|
validateSkillDir
|
|
31
31
|
} from "./skills/loader.js";
|
|
32
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
RunSkillInlineScriptErrorCode,
|
|
34
|
+
RunSkillInlineScriptTool
|
|
35
|
+
} from "./tools/skill/run_skill_inline_script_tool.js";
|
|
33
36
|
import { RunSkillScriptTool } from "./tools/skill/run_skill_script_tool.js";
|
|
34
37
|
export * from "./integrations/agent_registry/agent_registry.js";
|
|
35
38
|
export * from "./telemetry/google_cloud.js";
|
|
36
39
|
export * from "./telemetry/setup.js";
|
|
40
|
+
export * from "./tools/mcp/load_mcp_resource_tool.js";
|
|
37
41
|
export * from "./tools/mcp/mcp_session_manager.js";
|
|
38
42
|
export * from "./tools/mcp/mcp_tool.js";
|
|
39
43
|
export * from "./tools/mcp/mcp_toolset.js";
|
|
@@ -47,6 +51,7 @@ export {
|
|
|
47
51
|
GcsArtifactService,
|
|
48
52
|
InvocationContext,
|
|
49
53
|
RemoteA2AAgent,
|
|
54
|
+
RunSkillInlineScriptErrorCode,
|
|
50
55
|
RunSkillInlineScriptTool,
|
|
51
56
|
RunSkillScriptTool,
|
|
52
57
|
UnsafeLocalCodeExecutor,
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
MCPSessionManager
|
|
11
11
|
} from "../../tools/mcp/mcp_session_manager.js";
|
|
12
12
|
import { MCPTool } from "../../tools/mcp/mcp_tool.js";
|
|
13
|
+
import { logger } from "../../utils/logger.js";
|
|
13
14
|
import { GCP_MCP_SERVER_DESTINATION_ID } from "./types.js";
|
|
14
15
|
class AgentRegistrySingleMCPToolset extends BaseToolset {
|
|
15
16
|
/**
|
|
@@ -65,7 +66,14 @@ class AgentRegistrySingleMCPToolset extends BaseToolset {
|
|
|
65
66
|
};
|
|
66
67
|
const sessionManager = new MCPSessionManager(connectionParamsCopy);
|
|
67
68
|
const session = await sessionManager.createSession();
|
|
68
|
-
|
|
69
|
+
let listResult;
|
|
70
|
+
try {
|
|
71
|
+
listResult = await session.listTools();
|
|
72
|
+
} finally {
|
|
73
|
+
await sessionManager.closeSession(session).catch((e) => {
|
|
74
|
+
logger.warn("Failed to close MCP discovery session", e);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
69
77
|
const tools = listResult.tools.map((tool) => {
|
|
70
78
|
const prefixedName = this.prefix ? `${this.prefix}_${tool.name}` : tool.name;
|
|
71
79
|
const mcpTool = new MCPTool(
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {createRequire as topLevelCreateRequire} from 'module';
|
|
2
|
+
const require = topLevelCreateRequire(import.meta.url);
|
|
3
|
+
/**
|
|
4
|
+
* @license
|
|
5
|
+
* Copyright 2026 Google LLC
|
|
6
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
7
|
+
*/
|
|
8
|
+
import { injectSessionState } from "../agents/instructions.js";
|
|
9
|
+
import { ReadonlyContext } from "../agents/readonly_context.js";
|
|
10
|
+
import { BasePlugin } from "./base_plugin.js";
|
|
11
|
+
class GlobalInstructionPlugin extends BasePlugin {
|
|
12
|
+
/**
|
|
13
|
+
* Initializes the GlobalInstructionPlugin.
|
|
14
|
+
*
|
|
15
|
+
* @param globalInstruction The instruction to apply globally. Can be a string
|
|
16
|
+
* or an InstructionProvider function that takes ReadonlyContext and
|
|
17
|
+
* returns a string (sync or async).
|
|
18
|
+
* @param name The name of the plugin (defaults to 'global_instruction').
|
|
19
|
+
*/
|
|
20
|
+
constructor(globalInstruction, name = "global_instruction") {
|
|
21
|
+
super(name);
|
|
22
|
+
this.globalInstruction = globalInstruction;
|
|
23
|
+
}
|
|
24
|
+
async beforeModelCallback(params) {
|
|
25
|
+
if (!this.globalInstruction) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const readonlyContext = new ReadonlyContext(
|
|
29
|
+
params.callbackContext.invocationContext
|
|
30
|
+
);
|
|
31
|
+
const finalGlobalInstruction = await this.resolveGlobalInstruction(readonlyContext);
|
|
32
|
+
if (!finalGlobalInstruction) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (!params.llmRequest.config) {
|
|
36
|
+
params.llmRequest.config = {};
|
|
37
|
+
}
|
|
38
|
+
const existingInstruction = params.llmRequest.config.systemInstruction;
|
|
39
|
+
if (!existingInstruction || Array.isArray(existingInstruction) && existingInstruction.length === 0) {
|
|
40
|
+
params.llmRequest.config.systemInstruction = finalGlobalInstruction;
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (typeof existingInstruction === "string") {
|
|
44
|
+
params.llmRequest.config.systemInstruction = `${finalGlobalInstruction}
|
|
45
|
+
|
|
46
|
+
${existingInstruction}`;
|
|
47
|
+
} else if (Array.isArray(existingInstruction)) {
|
|
48
|
+
params.llmRequest.config.systemInstruction = [
|
|
49
|
+
finalGlobalInstruction,
|
|
50
|
+
...existingInstruction
|
|
51
|
+
];
|
|
52
|
+
} else {
|
|
53
|
+
params.llmRequest.config.systemInstruction = [
|
|
54
|
+
finalGlobalInstruction,
|
|
55
|
+
existingInstruction
|
|
56
|
+
];
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
async resolveGlobalInstruction(readonlyContext) {
|
|
61
|
+
if (typeof this.globalInstruction === "string") {
|
|
62
|
+
return await injectSessionState(this.globalInstruction, readonlyContext);
|
|
63
|
+
}
|
|
64
|
+
return await this.globalInstruction(readonlyContext);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export {
|
|
68
|
+
GlobalInstructionPlugin
|
|
69
|
+
};
|
|
@@ -181,7 +181,7 @@ class Runner {
|
|
|
181
181
|
throw new Error("No parts in the newMessage.");
|
|
182
182
|
}
|
|
183
183
|
if (runConfig.saveInputBlobsAsArtifacts) {
|
|
184
|
-
await this.saveArtifacts(
|
|
184
|
+
newMessage = await this.saveArtifacts(
|
|
185
185
|
invocationContext.invocationId,
|
|
186
186
|
session.userId,
|
|
187
187
|
session.id,
|
|
@@ -269,7 +269,7 @@ class Runner {
|
|
|
269
269
|
}
|
|
270
270
|
/**
|
|
271
271
|
* Saves artifacts from the message parts and replaces the inline data with
|
|
272
|
-
* a file name placeholder.
|
|
272
|
+
* a file name placeholder and optional file reference.
|
|
273
273
|
*
|
|
274
274
|
* @param invocationId The current invocation ID.
|
|
275
275
|
* @param userId The user ID of the session.
|
|
@@ -279,28 +279,67 @@ class Runner {
|
|
|
279
279
|
async saveArtifacts(invocationId, userId, sessionId, message) {
|
|
280
280
|
var _a2;
|
|
281
281
|
if (!this.artifactService || !((_a2 = message.parts) == null ? void 0 : _a2.length)) {
|
|
282
|
-
return;
|
|
282
|
+
return message;
|
|
283
283
|
}
|
|
284
284
|
const sessionKey = {
|
|
285
285
|
appName: this.appName,
|
|
286
286
|
userId,
|
|
287
287
|
sessionId
|
|
288
288
|
};
|
|
289
|
+
const newParts = [];
|
|
290
|
+
let modified = false;
|
|
289
291
|
for (let i = 0; i < message.parts.length; i++) {
|
|
290
292
|
const part = message.parts[i];
|
|
291
293
|
if (!part.inlineData) {
|
|
294
|
+
newParts.push(part);
|
|
292
295
|
continue;
|
|
293
296
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
297
|
+
try {
|
|
298
|
+
const inlineData = part.inlineData;
|
|
299
|
+
const fileName = inlineData.displayName || `artifact_${invocationId}_${i}`;
|
|
300
|
+
const version = await this.artifactService.saveArtifact({
|
|
301
|
+
...sessionKey,
|
|
302
|
+
filename: fileName,
|
|
303
|
+
artifact: part
|
|
304
|
+
});
|
|
305
|
+
newParts.push(createPartFromText(`[Uploaded Artifact: "${fileName}"]`));
|
|
306
|
+
try {
|
|
307
|
+
const artifactVersion = await this.artifactService.getArtifactVersion(
|
|
308
|
+
{
|
|
309
|
+
...sessionKey,
|
|
310
|
+
filename: fileName,
|
|
311
|
+
version
|
|
312
|
+
}
|
|
313
|
+
);
|
|
314
|
+
if ((artifactVersion == null ? void 0 : artifactVersion.canonicalUri) && /^(gs|https?):/i.test(artifactVersion.canonicalUri)) {
|
|
315
|
+
newParts.push({
|
|
316
|
+
fileData: {
|
|
317
|
+
fileUri: artifactVersion.canonicalUri,
|
|
318
|
+
mimeType: artifactVersion.mimeType || inlineData.mimeType || "",
|
|
319
|
+
displayName: fileName
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
} catch (error) {
|
|
324
|
+
logger.warn(
|
|
325
|
+
`Failed to resolve artifact version for ${fileName}:`,
|
|
326
|
+
error
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
modified = true;
|
|
330
|
+
logger.info(`Successfully saved artifact: ${fileName}`);
|
|
331
|
+
} catch (error) {
|
|
332
|
+
logger.error(`Failed to save artifact for part ${i}:`, error);
|
|
333
|
+
newParts.push(part);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (!modified) {
|
|
337
|
+
return message;
|
|
303
338
|
}
|
|
339
|
+
return {
|
|
340
|
+
...message,
|
|
341
|
+
parts: newParts
|
|
342
|
+
};
|
|
304
343
|
}
|
|
305
344
|
/**
|
|
306
345
|
* Determines the next agent to run to continue the session. This is primarily
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {createRequire as topLevelCreateRequire} from 'module';
|
|
2
|
+
const require = topLevelCreateRequire(import.meta.url);
|
|
3
|
+
/**
|
|
4
|
+
* @license
|
|
5
|
+
* Copyright 2026 Google LLC
|
|
6
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
7
|
+
*/
|
|
8
|
+
import {
|
|
9
|
+
isGemini1Model,
|
|
10
|
+
isGeminiModel,
|
|
11
|
+
isGeminiModelIdCheckDisabled
|
|
12
|
+
} from "../utils/model_name.js";
|
|
13
|
+
import { BaseTool } from "./base_tool.js";
|
|
14
|
+
function applyEnterpriseWebSearch(llmRequest) {
|
|
15
|
+
if (!llmRequest.model) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const modelCheckDisabled = isGeminiModelIdCheckDisabled();
|
|
19
|
+
llmRequest.config = llmRequest.config || {};
|
|
20
|
+
llmRequest.config.tools = llmRequest.config.tools || [];
|
|
21
|
+
if (isGeminiModel(llmRequest.model) || modelCheckDisabled) {
|
|
22
|
+
if (isGemini1Model(llmRequest.model) && llmRequest.config.tools.length > 0) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
"Enterprise Web Search tool cannot be used with other tools in Gemini 1.x."
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
llmRequest.config.tools.push({ enterpriseWebSearch: {} });
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Enterprise Web Search tool is not supported for model ${llmRequest.model}`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
class EnterpriseWebSearchTool extends BaseTool {
|
|
35
|
+
constructor() {
|
|
36
|
+
super({
|
|
37
|
+
name: "enterprise_web_search",
|
|
38
|
+
description: "Enterprise Web Search Tool"
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
runAsync() {
|
|
42
|
+
return Promise.resolve();
|
|
43
|
+
}
|
|
44
|
+
async processLlmRequest({
|
|
45
|
+
llmRequest
|
|
46
|
+
}) {
|
|
47
|
+
applyEnterpriseWebSearch(llmRequest);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const ENTERPRISE_WEB_SEARCH = new EnterpriseWebSearchTool();
|
|
51
|
+
export {
|
|
52
|
+
ENTERPRISE_WEB_SEARCH,
|
|
53
|
+
EnterpriseWebSearchTool,
|
|
54
|
+
applyEnterpriseWebSearch
|
|
55
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {createRequire as topLevelCreateRequire} from 'module';
|
|
2
|
+
const require = topLevelCreateRequire(import.meta.url);
|
|
3
|
+
/**
|
|
4
|
+
* @license
|
|
5
|
+
* Copyright 2026 Google LLC
|
|
6
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
7
|
+
*/
|
|
8
|
+
import { buildExampleSi } from "../examples/example_util.js";
|
|
9
|
+
import { appendInstructions } from "../models/llm_request.js";
|
|
10
|
+
import {
|
|
11
|
+
BaseTool
|
|
12
|
+
} from "./base_tool.js";
|
|
13
|
+
class ExampleTool extends BaseTool {
|
|
14
|
+
constructor(examples) {
|
|
15
|
+
super({
|
|
16
|
+
// Name and description are not used because this tool only changes
|
|
17
|
+
// llmRequest.
|
|
18
|
+
name: "example_tool",
|
|
19
|
+
description: "example tool"
|
|
20
|
+
});
|
|
21
|
+
this.examples = examples;
|
|
22
|
+
}
|
|
23
|
+
async runAsync(_request) {
|
|
24
|
+
throw new Error("ExampleTool should not be called by model");
|
|
25
|
+
}
|
|
26
|
+
async processLlmRequest({
|
|
27
|
+
toolContext,
|
|
28
|
+
llmRequest
|
|
29
|
+
}) {
|
|
30
|
+
var _a, _b;
|
|
31
|
+
const parts = (_a = toolContext.userContent) == null ? void 0 : _a.parts;
|
|
32
|
+
if (!parts || !((_b = parts[0]) == null ? void 0 : _b.text)) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
appendInstructions(llmRequest, [
|
|
36
|
+
buildExampleSi(this.examples, parts[0].text, llmRequest.model)
|
|
37
|
+
]);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
ExampleTool
|
|
42
|
+
};
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import {createRequire as topLevelCreateRequire} from 'module';
|
|
2
|
+
const require = topLevelCreateRequire(import.meta.url);
|
|
3
|
+
/**
|
|
4
|
+
* @license
|
|
5
|
+
* Copyright 2026 Google LLC
|
|
6
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
7
|
+
*/
|
|
8
|
+
import { lookup } from "node:dns/promises";
|
|
9
|
+
import { isIP } from "node:net";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
import { FunctionTool } from "./function_tool.js";
|
|
12
|
+
const ALLOWED_SCHEMES = /* @__PURE__ */ new Set(["http:", "https:"]);
|
|
13
|
+
const DEFAULT_TIMEOUT_MS = 3e4;
|
|
14
|
+
const BLOCKED_IPV4_CIDRS = [
|
|
15
|
+
"0.0.0.0/8",
|
|
16
|
+
// "this host on this network"
|
|
17
|
+
"10.0.0.0/8",
|
|
18
|
+
// private
|
|
19
|
+
"100.64.0.0/10",
|
|
20
|
+
// shared address space / CGNAT
|
|
21
|
+
"127.0.0.0/8",
|
|
22
|
+
// loopback
|
|
23
|
+
"169.254.0.0/16",
|
|
24
|
+
// link-local (includes GCP metadata 169.254.169.254)
|
|
25
|
+
"172.16.0.0/12",
|
|
26
|
+
// private
|
|
27
|
+
"192.0.0.0/24",
|
|
28
|
+
// IETF protocol assignments
|
|
29
|
+
"192.0.2.0/24",
|
|
30
|
+
// TEST-NET-1 (documentation)
|
|
31
|
+
"192.88.99.0/24",
|
|
32
|
+
// 6to4 relay anycast (deprecated)
|
|
33
|
+
"192.168.0.0/16",
|
|
34
|
+
// private
|
|
35
|
+
"198.18.0.0/15",
|
|
36
|
+
// benchmarking
|
|
37
|
+
"198.51.100.0/24",
|
|
38
|
+
// TEST-NET-2 (documentation)
|
|
39
|
+
"203.0.113.0/24",
|
|
40
|
+
// TEST-NET-3 (documentation)
|
|
41
|
+
"224.0.0.0/4",
|
|
42
|
+
// multicast
|
|
43
|
+
"240.0.0.0/4"
|
|
44
|
+
// reserved / future use (includes 255.255.255.255)
|
|
45
|
+
].map(parseIpv4Cidr);
|
|
46
|
+
const BLOCKED_IPV6_CIDRS = [
|
|
47
|
+
"::/128",
|
|
48
|
+
// unspecified
|
|
49
|
+
"::1/128",
|
|
50
|
+
// loopback
|
|
51
|
+
"64:ff9b:1::/48",
|
|
52
|
+
// local NAT64
|
|
53
|
+
"100::/64",
|
|
54
|
+
// discard-only
|
|
55
|
+
"2001:db8::/32",
|
|
56
|
+
// documentation
|
|
57
|
+
"fc00::/7",
|
|
58
|
+
// unique-local (ULA, private)
|
|
59
|
+
"fe80::/10",
|
|
60
|
+
// link-local
|
|
61
|
+
"ff00::/8"
|
|
62
|
+
// multicast
|
|
63
|
+
].map(parseIpv6Cidr);
|
|
64
|
+
function failedToFetchMessage(url) {
|
|
65
|
+
return `Failed to fetch url: ${url}`;
|
|
66
|
+
}
|
|
67
|
+
function isBlockedHostname(hostname) {
|
|
68
|
+
const normalized = hostname.replace(/\.+$/, "").toLowerCase();
|
|
69
|
+
return normalized === "localhost" || normalized.endsWith(".localhost");
|
|
70
|
+
}
|
|
71
|
+
function normalizeHost(hostname) {
|
|
72
|
+
return hostname.startsWith("[") ? hostname.slice(1, -1) : hostname;
|
|
73
|
+
}
|
|
74
|
+
function parseIpv4(address) {
|
|
75
|
+
const match = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(address);
|
|
76
|
+
if (!match) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const octets = match.slice(1).map(Number);
|
|
80
|
+
if (octets.some((octet) => octet > 255)) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
return octets;
|
|
84
|
+
}
|
|
85
|
+
function parseIpv6(address) {
|
|
86
|
+
if (isIP(address) !== 6) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const [head, tail] = address.split("::");
|
|
90
|
+
const highGroups = head ? expandHextets(head) : [];
|
|
91
|
+
const lowGroups = tail ? expandHextets(tail) : [];
|
|
92
|
+
const compressed = address.includes("::") ? new Array(8 - highGroups.length - lowGroups.length).fill(0) : [];
|
|
93
|
+
return [...highGroups, ...compressed, ...lowGroups];
|
|
94
|
+
}
|
|
95
|
+
function expandHextets(fragment) {
|
|
96
|
+
const hextets = [];
|
|
97
|
+
for (const group of fragment.split(":")) {
|
|
98
|
+
if (group.includes(".")) {
|
|
99
|
+
const octets = parseIpv4(group);
|
|
100
|
+
hextets.push(octets[0] << 8 | octets[1], octets[2] << 8 | octets[3]);
|
|
101
|
+
} else {
|
|
102
|
+
hextets.push(parseInt(group, 16));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return hextets;
|
|
106
|
+
}
|
|
107
|
+
function ipv4ToInt(octets) {
|
|
108
|
+
return (octets[0] << 24 | octets[1] << 16 | octets[2] << 8 | octets[3]) >>> 0;
|
|
109
|
+
}
|
|
110
|
+
function hextetsToBigInt(hextets) {
|
|
111
|
+
let value = 0n;
|
|
112
|
+
for (const hextet of hextets) {
|
|
113
|
+
value = value << 16n | BigInt(hextet);
|
|
114
|
+
}
|
|
115
|
+
return value;
|
|
116
|
+
}
|
|
117
|
+
function parseIpv4Cidr(cidr) {
|
|
118
|
+
const [address, prefix] = cidr.split("/");
|
|
119
|
+
const mask = 4294967295 << 32 - Number(prefix) >>> 0;
|
|
120
|
+
return { base: (ipv4ToInt(parseIpv4(address)) & mask) >>> 0, mask };
|
|
121
|
+
}
|
|
122
|
+
function parseIpv6Cidr(cidr) {
|
|
123
|
+
const [address, prefix] = cidr.split("/");
|
|
124
|
+
return { base: hextetsToBigInt(parseIpv6(address)), prefix: Number(prefix) };
|
|
125
|
+
}
|
|
126
|
+
function isBlockedIpv4(octets) {
|
|
127
|
+
const value = ipv4ToInt(octets);
|
|
128
|
+
return BLOCKED_IPV4_CIDRS.some(
|
|
129
|
+
({ base, mask }) => (value & mask) >>> 0 === base
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
function isBlockedIpv6(hextets) {
|
|
133
|
+
const value = hextetsToBigInt(hextets);
|
|
134
|
+
if (value >> 32n === 0xffffn) {
|
|
135
|
+
return isBlockedIpv4([
|
|
136
|
+
Number(value >> 24n & 0xffn),
|
|
137
|
+
Number(value >> 16n & 0xffn),
|
|
138
|
+
Number(value >> 8n & 0xffn),
|
|
139
|
+
Number(value & 0xffn)
|
|
140
|
+
]);
|
|
141
|
+
}
|
|
142
|
+
return BLOCKED_IPV6_CIDRS.some(
|
|
143
|
+
({ base, prefix }) => value >> BigInt(128 - prefix) === base >> BigInt(128 - prefix)
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
function isBlockedAddress(address) {
|
|
147
|
+
const octets = parseIpv4(address);
|
|
148
|
+
if (octets) {
|
|
149
|
+
return isBlockedIpv4(octets);
|
|
150
|
+
}
|
|
151
|
+
const hextets = parseIpv6(address);
|
|
152
|
+
if (hextets) {
|
|
153
|
+
return isBlockedIpv6(hextets);
|
|
154
|
+
}
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
async function resolveHostAddresses(hostname) {
|
|
158
|
+
if (isIP(hostname) !== 0) {
|
|
159
|
+
return [hostname];
|
|
160
|
+
}
|
|
161
|
+
const records = await lookup(hostname, { all: true });
|
|
162
|
+
const addresses = [...new Set(records.map((record) => record.address))];
|
|
163
|
+
if (addresses.length === 0) {
|
|
164
|
+
throw new Error(`Unable to resolve host: ${hostname}`);
|
|
165
|
+
}
|
|
166
|
+
return addresses;
|
|
167
|
+
}
|
|
168
|
+
function assertUrlAllowed(url) {
|
|
169
|
+
const parsed = new URL(url);
|
|
170
|
+
if (!ALLOWED_SCHEMES.has(parsed.protocol)) {
|
|
171
|
+
throw new Error(`Unsupported url scheme: ${url}`);
|
|
172
|
+
}
|
|
173
|
+
if (isBlockedHostname(parsed.hostname)) {
|
|
174
|
+
throw new Error(`Blocked host: ${parsed.hostname}`);
|
|
175
|
+
}
|
|
176
|
+
return parsed;
|
|
177
|
+
}
|
|
178
|
+
async function validateResolvedAddresses(hostname) {
|
|
179
|
+
const addresses = await resolveHostAddresses(hostname);
|
|
180
|
+
if (addresses.some(isBlockedAddress)) {
|
|
181
|
+
throw new Error(`Blocked host: ${hostname}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function decodeHtmlEntities(text) {
|
|
185
|
+
return text.replace(/ /g, " ").replace(/'/g, "'").replace(/'/g, "'").replace(/"/g, '"').replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
186
|
+
}
|
|
187
|
+
function htmlToText(html) {
|
|
188
|
+
const withoutCode = html.replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<!--[\s\S]*?-->/g, " ");
|
|
189
|
+
const text = decodeHtmlEntities(withoutCode.replace(/<[^>]+>/g, "\n"));
|
|
190
|
+
return text.split("\n").map((line) => line.trim()).filter((line) => line.split(/\s+/).filter(Boolean).length > 3).join("\n");
|
|
191
|
+
}
|
|
192
|
+
async function loadWebPage(url, options) {
|
|
193
|
+
var _a;
|
|
194
|
+
const timeoutMs = (_a = options == null ? void 0 : options.timeoutMs) != null ? _a : DEFAULT_TIMEOUT_MS;
|
|
195
|
+
try {
|
|
196
|
+
const parsed = assertUrlAllowed(url);
|
|
197
|
+
await validateResolvedAddresses(normalizeHost(parsed.hostname));
|
|
198
|
+
const response = await fetch(url, {
|
|
199
|
+
redirect: "manual",
|
|
200
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
201
|
+
});
|
|
202
|
+
if (response.status !== 200) {
|
|
203
|
+
return failedToFetchMessage(url);
|
|
204
|
+
}
|
|
205
|
+
return htmlToText(await response.text());
|
|
206
|
+
} catch {
|
|
207
|
+
return failedToFetchMessage(url);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const LOAD_WEB_PAGE = new FunctionTool({
|
|
211
|
+
name: "load_web_page",
|
|
212
|
+
description: "Fetches the content at the given URL and returns the readable text extracted from the page.",
|
|
213
|
+
parameters: z.object({
|
|
214
|
+
url: z.string().describe("The URL to fetch and extract text from.")
|
|
215
|
+
}),
|
|
216
|
+
execute: ({ url }) => loadWebPage(url)
|
|
217
|
+
});
|
|
218
|
+
export {
|
|
219
|
+
LOAD_WEB_PAGE,
|
|
220
|
+
loadWebPage
|
|
221
|
+
};
|