├── docs ├── .gitkeep ├── examples │ └── typescript │ │ └── snippets │ │ ├── agents │ │ └── workflow-agents │ │ │ └── requirements.txt │ │ ├── get-started │ │ └── google-search-agent │ │ │ ├── index.ts │ │ │ └── agent.ts │ │ ├── tools │ │ ├── overview │ │ │ └── UserPreference.ts │ │ └── built-in-tools │ │ │ └── GoogleSearch.ts │ │ └── callbacks │ │ └── CallbackBasic.ts ├── assets │ ├── adk-run.png │ ├── adk-stack.png │ ├── event-loop.png │ ├── idx-logo.png │ ├── loop-agent.png │ ├── quickstart.png │ ├── agent-types.png │ ├── custom-agent.png │ ├── deploy-agent.png │ ├── multi-agent.png │ ├── adk-api-server.png │ ├── adk-components.png │ ├── adk-interfaces.png │ ├── adk-lifecycle.png │ ├── adk-social-card.png │ ├── adk-streaming.png │ ├── agent-tool-call.png │ ├── agent-workflow.png │ ├── callback_flow.png │ ├── evaluate_agent.png │ ├── invocation-flow.png │ ├── oauth-tool-flow.png │ ├── parallel-agent.png │ ├── session-state.png │ ├── weather-example.png │ ├── agent-evaluation.png │ ├── custom-agent-flow.png │ ├── sequential-agent.png │ ├── session_lifecycle.png │ ├── what-is-an-agent.png │ ├── adk-web-dev-ui-audio.png │ ├── adk-web-dev-ui-chat.png │ ├── publish-integration.png │ ├── quickstart-flow-tool.png │ ├── agent-development-kit.png │ ├── quickstart-streaming-tool.png │ ├── adk-web-dev-ui-function-call.png │ ├── use-connection-tool-template.png │ └── application-integration-overview.png ├── stylesheets │ └── custom.css ├── tutorials │ └── index.md ├── deploy │ └── index.md ├── overrides │ └── main.html ├── get-started │ ├── index.md │ └── installation.md └── agents │ └── workflow-agents │ ├── index.md │ └── sequential-agents.md ├── tests ├── integration │ ├── fixture │ │ ├── context_update_test │ │ │ ├── OWNERS │ │ │ ├── test-config.json │ │ │ └── agent.ts │ │ ├── agent_with_config │ │ │ ├── test-config.json │ │ │ └── agent.ts │ │ ├── trip_planner_agent │ │ │ ├── test_config.json │ │ │ ├── test_files │ │ │ │ ├── test_config.json │ │ │ │ ├── initial.session.json │ │ │ │ └── trip_inquiry_sub_agent.test.json │ │ │ ├── agent.d.ts │ │ │ ├── index.ts │ │ │ ├── initial.session.json │ │ │ └── trip_inquiry.test.json │ │ ├── customer_support_ma │ │ │ ├── test-config.json │ │ │ └── customer-support.test.json │ │ ├── flow_complex_spark │ │ │ └── test-config.json │ │ ├── home_automation_agent │ │ │ ├── test_config.json │ │ │ ├── test_files │ │ │ │ ├── test_config.json │ │ │ │ ├── memorizing_past_events │ │ │ │ │ ├── test_config.json │ │ │ │ │ └── eval_data.test.json │ │ │ │ ├── simple_test2.test.json │ │ │ │ ├── simple_test.test.json │ │ │ │ ├── dependent_tool_calls.test.json │ │ │ │ └── simple_multi_turn_conversation.test.json │ │ │ ├── test-config.json │ │ │ ├── index.ts │ │ │ ├── simple_test.test.json │ │ │ └── simple_test2.test.json │ │ ├── hello_world_agent │ │ │ ├── test-config.json │ │ │ └── roll-die.test.json │ │ ├── tool_agent │ │ │ ├── file │ │ │ │ └── Agent_test_plan.pdf │ │ │ └── agent.d.ts │ │ ├── ecommerce_customer_service_agent │ │ │ ├── test_config.json │ │ │ ├── test-config.json │ │ │ └── order_query.test.json │ │ └── index.ts │ ├── models │ │ └── index.ts │ ├── .env.example │ ├── singleAgent.test.ts │ ├── testConfig.ts │ └── multiAgent.test.ts └── unittests │ ├── artifacts │ └── index.ts │ ├── agents │ └── index.ts │ ├── cli │ └── utils │ │ └── index.ts │ └── models │ └── models.test.ts ├── src ├── cli │ ├── browser │ │ ├── assets │ │ │ ├── config │ │ │ │ └── runtime-config.json │ │ │ ├── audio-processor.js │ │ │ └── ADK-512-color.svg │ │ ├── chunk-EQDQRRRY.js │ │ └── adk_favicon.svg │ ├── utils │ │ ├── index.ts │ │ ├── envs.ts │ │ └── init.ts │ ├── HelpfulCommand.ts │ └── runAgent.ts ├── version.ts ├── events │ ├── index.ts │ └── EventActions.ts ├── tools │ ├── apihub-tool │ │ ├── clients │ │ │ └── index.ts │ │ └── index.ts │ ├── openapi-tool │ │ ├── index.ts │ │ ├── auth │ │ │ └── credential_exchangers │ │ │ │ └── BaseCredentialExchanger.ts │ │ ├── openapi-spec-parser │ │ │ └── ToolAuthHandler.ts │ │ └── README.md │ ├── retrieval │ │ ├── index.ts │ │ ├── LlamaIndexRetrieval.ts │ │ ├── FilesRetrieval.ts │ │ └── BaseRetrievalTool.ts │ ├── toolActions.ts │ ├── mcp-tool │ │ └── index.ts │ ├── google-api-tool │ │ ├── index.ts │ │ └── GoogleApiTool.ts │ ├── memoryEntryUtils.ts │ ├── LongRunningTool.ts │ ├── ExitLoopTool.ts │ ├── BaseToolset.ts │ ├── TransferToAgentTool.ts │ ├── application-integration-tool │ │ └── IntegrationClient.ts │ ├── GetUserChoiceTool.ts │ ├── LoadMemoryTool.ts │ ├── BuiltInCodeExecutionTool.ts │ └── GoogleSearchTool.ts ├── flows │ ├── index.ts │ └── llm_flows │ │ ├── index.ts │ │ ├── BaseLlmProcessor.ts │ │ ├── identity.ts │ │ ├── AutoFlow.ts │ │ └── SingleFlow.ts ├── types │ ├── js-yaml.d.ts │ └── index.ts ├── planners │ ├── index.ts │ └── BasePlanner.ts ├── sessions │ ├── index.ts │ ├── sessionUtils.ts │ └── types.ts ├── evaluation │ ├── EvaluationConstants.ts │ ├── index.ts │ ├── EvalSetsManager.ts │ ├── Evaluator.ts │ └── EvalSet.ts ├── artifacts │ ├── index.ts │ └── BaseArtifactService.ts ├── auth │ ├── AuthPreprocessor.ts │ ├── AuthScheme.ts │ ├── index.ts │ └── AuthConfig.ts ├── models │ ├── index.ts │ ├── BaseLlm.ts │ ├── BaseLlmConnection.ts │ └── LlmRegistry.ts ├── memory │ ├── utils.ts │ ├── MemoryEntry.ts │ └── index.ts ├── agents │ ├── TranscriptionEntry.ts │ ├── index.ts │ ├── ReadonlyContext.ts │ ├── ActiveStreamingTool.ts │ ├── LiveRequestQueue.ts │ ├── RunConfig.ts │ ├── LoopAgent.ts │ └── RemoteAgent.ts ├── code-executors │ ├── index.ts │ └── BaseCodeExecutor.ts ├── utils │ └── index.ts └── index.ts ├── assets ├── agent-development-kit.png └── adk-web-dev-ui-function-call.png ├── .env.example ├── .npmignore ├── jest.config.js ├── setup-dev.bat ├── setup-dev.sh ├── tsconfig.json ├── .gitignore ├── .eslintrc.js ├── .github └── workflows │ ├── test.yml │ └── publish-docs.yml ├── examples ├── non_llm_sequential │ └── agent.ts └── fields_output_schema │ └── agent.ts ├── CONTRIBUTING.md ├── typedoc.json └── todo.md /docs/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/integration/fixture/context_update_test/OWNERS: -------------------------------------------------------------------------------- 1 | gkcng 2 | -------------------------------------------------------------------------------- /docs/examples/typescript/snippets/agents/workflow-agents/requirements.txt: -------------------------------------------------------------------------------- 1 | google-adk -------------------------------------------------------------------------------- /src/cli/browser/assets/config/runtime-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "backendUrl": "" 3 | } -------------------------------------------------------------------------------- /src/version.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * ADK TypeScript version 3 | */ 4 | export const VERSION = '1.0.4'; 5 | -------------------------------------------------------------------------------- /tests/unittests/artifacts/index.ts: -------------------------------------------------------------------------------- 1 | // Export all artifact tests 2 | import './artifactService.test'; -------------------------------------------------------------------------------- /docs/assets/adk-run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/adk-run.png -------------------------------------------------------------------------------- /docs/assets/adk-stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/adk-stack.png -------------------------------------------------------------------------------- /docs/assets/event-loop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/event-loop.png -------------------------------------------------------------------------------- /docs/assets/idx-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/idx-logo.png -------------------------------------------------------------------------------- /docs/assets/loop-agent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/loop-agent.png -------------------------------------------------------------------------------- /docs/assets/quickstart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/quickstart.png -------------------------------------------------------------------------------- /docs/assets/agent-types.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/agent-types.png -------------------------------------------------------------------------------- /docs/assets/custom-agent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/custom-agent.png -------------------------------------------------------------------------------- /docs/assets/deploy-agent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/deploy-agent.png -------------------------------------------------------------------------------- /docs/assets/multi-agent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/multi-agent.png -------------------------------------------------------------------------------- /src/events/index.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | export { Event } from './Event'; 4 | export { EventActions } from './EventActions'; -------------------------------------------------------------------------------- /src/tools/apihub-tool/clients/index.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | export * from './APIHubClient'; 4 | export * from './SecretClient'; -------------------------------------------------------------------------------- /docs/assets/adk-api-server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/adk-api-server.png -------------------------------------------------------------------------------- /docs/assets/adk-components.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/adk-components.png -------------------------------------------------------------------------------- /docs/assets/adk-interfaces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/adk-interfaces.png -------------------------------------------------------------------------------- /docs/assets/adk-lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/adk-lifecycle.png -------------------------------------------------------------------------------- /docs/assets/adk-social-card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/adk-social-card.png -------------------------------------------------------------------------------- /docs/assets/adk-streaming.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/adk-streaming.png -------------------------------------------------------------------------------- /docs/assets/agent-tool-call.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/agent-tool-call.png -------------------------------------------------------------------------------- /docs/assets/agent-workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/agent-workflow.png -------------------------------------------------------------------------------- /docs/assets/callback_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/callback_flow.png -------------------------------------------------------------------------------- /docs/assets/evaluate_agent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/evaluate_agent.png -------------------------------------------------------------------------------- /docs/assets/invocation-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/invocation-flow.png -------------------------------------------------------------------------------- /docs/assets/oauth-tool-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/oauth-tool-flow.png -------------------------------------------------------------------------------- /docs/assets/parallel-agent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/parallel-agent.png -------------------------------------------------------------------------------- /docs/assets/session-state.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/session-state.png -------------------------------------------------------------------------------- /docs/assets/weather-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/weather-example.png -------------------------------------------------------------------------------- /tests/integration/fixture/agent_with_config/test-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "rootAgent": "agent1", 3 | "failFast": true 4 | } -------------------------------------------------------------------------------- /assets/agent-development-kit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/assets/agent-development-kit.png -------------------------------------------------------------------------------- /docs/assets/agent-evaluation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/agent-evaluation.png -------------------------------------------------------------------------------- /docs/assets/custom-agent-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/custom-agent-flow.png -------------------------------------------------------------------------------- /docs/assets/sequential-agent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/sequential-agent.png -------------------------------------------------------------------------------- /docs/assets/session_lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/session_lifecycle.png -------------------------------------------------------------------------------- /docs/assets/what-is-an-agent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/what-is-an-agent.png -------------------------------------------------------------------------------- /docs/assets/adk-web-dev-ui-audio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/adk-web-dev-ui-audio.png -------------------------------------------------------------------------------- /docs/assets/adk-web-dev-ui-chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/adk-web-dev-ui-chat.png -------------------------------------------------------------------------------- /docs/assets/publish-integration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/publish-integration.png -------------------------------------------------------------------------------- /docs/assets/quickstart-flow-tool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/quickstart-flow-tool.png -------------------------------------------------------------------------------- /docs/assets/agent-development-kit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/agent-development-kit.png -------------------------------------------------------------------------------- /tests/integration/fixture/context_update_test/test-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "rootAgent": "contextUpdateRootAgent", 3 | "failFast": true 4 | } -------------------------------------------------------------------------------- /tests/integration/fixture/trip_planner_agent/test_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "criteria": { 3 | "response_match_score": 0.4 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /assets/adk-web-dev-ui-function-call.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/assets/adk-web-dev-ui-function-call.png -------------------------------------------------------------------------------- /docs/assets/quickstart-streaming-tool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/quickstart-streaming-tool.png -------------------------------------------------------------------------------- /tests/integration/fixture/customer_support_ma/test-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "rootAgent": "customerSupportRootAgent", 3 | "failFast": true 4 | } -------------------------------------------------------------------------------- /tests/integration/fixture/flow_complex_spark/test-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "rootAgent": "flowComplexSparkRootAgent", 3 | "failFast": true 4 | } -------------------------------------------------------------------------------- /tests/integration/fixture/home_automation_agent/test_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "criteria": { 3 | "tool_trajectory_avg_score": 1.0 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /docs/assets/adk-web-dev-ui-function-call.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/adk-web-dev-ui-function-call.png -------------------------------------------------------------------------------- /docs/assets/use-connection-tool-template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/use-connection-tool-template.png -------------------------------------------------------------------------------- /docs/assets/application-integration-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/docs/assets/application-integration-overview.png -------------------------------------------------------------------------------- /tests/integration/fixture/home_automation_agent/test_files/test_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "criteria": { 3 | "tool_trajectory_avg_score": 1.0 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/flows/index.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * Exports for flows module. 5 | */ 6 | import * as llmFlows from './llm_flows'; 7 | 8 | export { 9 | llmFlows 10 | }; -------------------------------------------------------------------------------- /tests/integration/fixture/hello_world_agent/test-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "criteria": { 3 | "tool_trajectory_avg_score": 1.0, 4 | "response_match_score": 0.5 5 | } 6 | } -------------------------------------------------------------------------------- /src/types/js-yaml.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'js-yaml' { 2 | export function load(input: string, options?: any): any; 3 | export function dump(obj: any, options?: any): string; 4 | } -------------------------------------------------------------------------------- /tests/integration/fixture/tool_agent/file/Agent_test_plan.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njraladdin/adk-typescript/HEAD/tests/integration/fixture/tool_agent/file/Agent_test_plan.pdf -------------------------------------------------------------------------------- /tests/integration/models/index.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * Models module for integration tests 5 | */ 6 | 7 | // Re-export models test files 8 | // Add more exports as they are ported -------------------------------------------------------------------------------- /tests/integration/fixture/home_automation_agent/test-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "rootAgent": "homeAutomationRootAgent", 3 | "criteria": { 4 | "tool_trajectory_avg_score": 1.0 5 | } 6 | } -------------------------------------------------------------------------------- /tests/integration/fixture/trip_planner_agent/test_files/test_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "criteria": { 3 | "tool_trajectory_avg_score": 1, 4 | "response_match_score": 0.35 5 | } 6 | } -------------------------------------------------------------------------------- /src/planners/index.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | export { BasePlanner } from './BasePlanner'; 4 | export { BuiltInPlanner } from './BuiltInPlanner'; 5 | export { PlanReActPlanner } from './PlanReActPlanner'; -------------------------------------------------------------------------------- /tests/integration/fixture/ecommerce_customer_service_agent/test_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "criteria": { 3 | "tool_trajectory_avg_score": 0.7, 4 | "response_match_score": 0.5 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/cli/utils/index.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | // Re-export utility functions from module files 4 | export * from './envs'; 5 | export * from './evals'; 6 | export * from './logs'; 7 | export * from './init'; -------------------------------------------------------------------------------- /tests/integration/fixture/tool_agent/agent.d.ts: -------------------------------------------------------------------------------- 1 | import { LlmAgent as Agent } from '../../../../src/agents/LlmAgent'; 2 | 3 | export const singleFunctionAgent: Agent; 4 | export const toolAgent: Agent; -------------------------------------------------------------------------------- /tests/integration/fixture/home_automation_agent/test_files/memorizing_past_events/test_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "criteria": { 3 | "tool_trajectory_avg_score": 1.0, 4 | "response_match_score": 0.5 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/sessions/index.ts: -------------------------------------------------------------------------------- 1 | export * from './types'; 2 | export * from './State'; 3 | export * from './BaseSessionService'; 4 | export * from './InMemorySessionService'; 5 | export * from './DatabaseSessionService'; 6 | export * from './Session'; -------------------------------------------------------------------------------- /tests/integration/fixture/ecommerce_customer_service_agent/test-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "rootAgent": "ecommerceCustomerServiceAgent", 3 | "criteria": { 4 | "tool_trajectory_avg_score": 0.7, 5 | "response_match_score": 0.5 6 | } 7 | } -------------------------------------------------------------------------------- /tests/integration/fixture/trip_planner_agent/agent.d.ts: -------------------------------------------------------------------------------- 1 | import { LlmAgent as Agent } from '../../../../src/agents/LlmAgent'; 2 | 3 | export const identify_agent: Agent; 4 | export const gatherAgent: Agent; 5 | export const planAgent: Agent; 6 | export const rootAgent: Agent; -------------------------------------------------------------------------------- /tests/integration/fixture/home_automation_agent/index.ts: -------------------------------------------------------------------------------- 1 | // Export the agent from the agent.ts file 2 | import { rootAgent, reset_data } from './agent'; 3 | 4 | // Export in the structure expected by EvaluationGenerator 5 | export const agent = { 6 | rootAgent, 7 | reset_data 8 | }; -------------------------------------------------------------------------------- /src/tools/apihub-tool/index.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * API Hub Tool module 5 | * 6 | * This module provides integration with Google Cloud API Hub to automatically 7 | * generate tools from API specifications stored in API Hub. 8 | */ 9 | 10 | export * from './APIHubToolset'; 11 | export * from './clients'; -------------------------------------------------------------------------------- /tests/unittests/agents/index.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | // Export all agent tests 4 | import './BaseAgent.test'; 5 | import './LlmAgent.test'; 6 | import './LlmAgentCallbacks.test'; 7 | import './LoopAgent.test'; 8 | import './ParallelAgent.test'; 9 | import './SequentialAgent.test'; 10 | import './LanggraphAgent.test'; -------------------------------------------------------------------------------- /tests/integration/fixture/home_automation_agent/simple_test.test.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "query": "Turn off device_2 in the Bedroom.", 3 | "expected_tool_use": [{"tool_name": "set_device_info", "tool_input": {"location": "Bedroom", "device_id": "device_2", "status": "OFF"}}], 4 | "reference": "I have set the device_2 status to off." 5 | }] 6 | -------------------------------------------------------------------------------- /tests/integration/fixture/home_automation_agent/simple_test2.test.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "query": "Turn off device_3 in the Bedroom.", 3 | "expected_tool_use": [{"tool_name": "set_device_info", "tool_input": {"location": "Bedroom", "device_id": "device_3", "status": "OFF"}}], 4 | "reference": "I have set the device_3 status to off." 5 | }] 6 | -------------------------------------------------------------------------------- /tests/integration/fixture/home_automation_agent/test_files/simple_test2.test.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "query": "Turn off device_3 in the Bedroom.", 3 | "expected_tool_use": [{"tool_name": "set_device_info", "tool_input": {"location": "Bedroom", "device_id": "device_3", "status": "OFF"}}], 4 | "reference": "I have set the device_3 status to off." 5 | }] 6 | -------------------------------------------------------------------------------- /tests/integration/fixture/trip_planner_agent/index.ts: -------------------------------------------------------------------------------- 1 | // Export the agent from the agent.ts file 2 | import { agent, rootAgent, identifyAgent, gatherAgent, planAgent, reset_data } from './agent'; 3 | 4 | // Re-export in the structure expected by EvaluationGenerator 5 | export { agent, rootAgent, identifyAgent, gatherAgent, planAgent, reset_data }; -------------------------------------------------------------------------------- /tests/unittests/cli/utils/index.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * Index file for CLI utility tests. 5 | * 6 | * This file exports all CLI-related test modules for easy importing. 7 | */ 8 | 9 | // Export test modules 10 | export * from './cli.test'; 11 | export * from './cliCreate.test'; 12 | export * from './cliDeploy.test'; 13 | export * from './cliTools.test'; -------------------------------------------------------------------------------- /docs/examples/typescript/snippets/get-started/google-search-agent/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * TypeScript equivalent of the google_search_agent/__init__.py 3 | * 4 | * This file acts as the main entry point for the google-search-agent module, 5 | * re-exporting the agent for use by other modules. 6 | */ 7 | 8 | // Re-export the agent from the agent.ts file 9 | export { agent } from './agent'; -------------------------------------------------------------------------------- /src/evaluation/EvaluationConstants.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Holds constants for evaluation file keys. 3 | */ 4 | export const EvalConstants = { 5 | QUERY: "query", 6 | EXPECTED_TOOL_USE: "expected_tool_use", 7 | RESPONSE: "response", 8 | REFERENCE: "reference", 9 | TOOL_NAME: "tool_name", 10 | TOOL_INPUT: "tool_input", 11 | MOCK_TOOL_OUTPUT: "mock_tool_output", 12 | } as const; -------------------------------------------------------------------------------- /tests/integration/fixture/trip_planner_agent/initial.session.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "test_id", 3 | "app_name": "trip_planner_agent", 4 | "user_id": "test_user", 5 | "state": { 6 | "origin": "San Francisco", 7 | "interests": "Food, Shopping, Museums", 8 | "range": "1000 miles", 9 | "cities": "" 10 | }, 11 | "events": [], 12 | "last_update_time": 1741218714.258285 13 | } 14 | -------------------------------------------------------------------------------- /tests/integration/fixture/trip_planner_agent/test_files/initial.session.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "test_id", 3 | "app_name": "trip_planner_agent", 4 | "user_id": "test_user", 5 | "state": { 6 | "origin": "San Francisco", 7 | "interests": "Food, Shopping, Museums", 8 | "range": "1000 miles", 9 | "cities": "" 10 | }, 11 | "events": [], 12 | "last_update_time": 1741218714.258285 13 | } 14 | -------------------------------------------------------------------------------- /src/artifacts/index.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { ArtifactParams, BaseArtifactService } from './BaseArtifactService'; 4 | import { GcsArtifactService } from './GcsArtifactService'; 5 | import { InMemoryArtifactService } from './InMemoryArtifactService'; 6 | 7 | // Export all components 8 | export type { 9 | ArtifactParams, 10 | BaseArtifactService, 11 | }; 12 | export { 13 | GcsArtifactService, 14 | InMemoryArtifactService, 15 | }; -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # Google API key for GOOGLE_AI backend 2 | GOOGLE_API_KEY=your_api_key_here 3 | 4 | # Google Cloud settings for VERTEX backend 5 | GOOGLE_CLOUD_PROJECT=your_gcp_project_id 6 | GOOGLE_CLOUD_LOCATION=us-central1 7 | 8 | # Backend configuration for tests 9 | # Use GOOGLE_AI_ONLY, VERTEX_ONLY, or BOTH 10 | TEST_BACKEND=BOTH 11 | 12 | # Optional: Set this to 1 to force use of Vertex AI, 0 for regular Google AI 13 | # GOOGLE_GENAI_USE_VERTEXAI=0 -------------------------------------------------------------------------------- /tests/integration/.env.example: -------------------------------------------------------------------------------- 1 | # Google API key for GOOGLE_AI backend 2 | GOOGLE_API_KEY=your_api_key_here 3 | 4 | # Google Cloud settings for VERTEX backend 5 | GOOGLE_CLOUD_PROJECT=your_gcp_project_id 6 | GOOGLE_CLOUD_LOCATION=us-central1 7 | 8 | # Backend configuration for tests 9 | # Use GOOGLE_AI_ONLY, VERTEX_ONLY, or BOTH 10 | TEST_BACKEND=BOTH 11 | 12 | # Optional: Set this to 1 to force use of Vertex AI, 0 for regular Google AI 13 | # GOOGLE_GENAI_USE_VERTEXAI=0 -------------------------------------------------------------------------------- /src/evaluation/index.ts: -------------------------------------------------------------------------------- 1 | export * from './EvaluationConstants'; 2 | export * from './EvaluationGenerator'; 3 | export * from './ResponseEvaluator'; 4 | export * from './TrajectoryEvaluator'; 5 | export { AgentEvaluator } from './AgentEvaluator'; 6 | export type { EvaluationParams, EvaluationCriteria, EvaluationResult } from './AgentEvaluator'; 7 | export * from './EvalCase'; 8 | export * from './EvalSet'; 9 | export * from './EvalSetsManager'; 10 | export * from './LocalEvalSetsManager'; -------------------------------------------------------------------------------- /src/tools/openapi-tool/index.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * OpenAPI tool module 5 | * 6 | * This module provides tools for working with OpenAPI specifications and creating 7 | * tools that interact with REST APIs documented with OpenAPI. 8 | */ 9 | 10 | // Export from OpenAPI spec parser 11 | export * from './openapi-spec-parser/RestApiTool'; 12 | export * from './openapi-spec-parser/OpenAPIToolset'; 13 | 14 | // Export commonly used types and utilities 15 | export * from './common/common'; -------------------------------------------------------------------------------- /src/auth/AuthPreprocessor.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { AuthConfig } from './AuthConfig'; 4 | 5 | /** 6 | * Preprocessor for authentication logic (stub). 7 | */ 8 | export class AuthPreprocessor { 9 | config: AuthConfig; 10 | 11 | constructor(config: AuthConfig) { 12 | this.config = config; 13 | } 14 | 15 | /** 16 | * Example method to preprocess authentication (stub). 17 | */ 18 | preprocess(): boolean { 19 | // TODO: Implement preprocessing logic 20 | return true; 21 | } 22 | } -------------------------------------------------------------------------------- /src/tools/retrieval/index.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * Retrieval tools module - Tools for retrieving information 5 | */ 6 | 7 | export * from './BaseRetrievalTool'; 8 | export * from './WebSearchTool'; 9 | export * from './LlamaIndexRetrieval'; 10 | export * from './FilesRetrieval'; 11 | export * from './VertexAiRagRetrieval'; 12 | 13 | // Note: When importing VertexAiRagRetrieval, make sure the Vertex AI SDK is installed. 14 | // If the SDK is not installed, the module will throw an error when initialized. -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Source code (since we compile to dist) 2 | src/ 3 | 4 | # Development files 5 | tests/ 6 | coverage/ 7 | docs/ 8 | examples/ 9 | assets/ 10 | .git/ 11 | .github/ 12 | adk-python/ 13 | 14 | # Build and config files that aren't needed 15 | .env 16 | .env.example 17 | tsconfig.json 18 | jest.config.js 19 | .gitignore 20 | .eslintrc.js 21 | .prettierrc 22 | 23 | # Editor configs 24 | .vscode/ 25 | .idea/ 26 | 27 | # Logs 28 | *.log 29 | npm-debug.log* 30 | yarn-debug.log* 31 | yarn-error.log* 32 | 33 | # Typescript source maps 34 | *.map 35 | 36 | # Don't ignore .d.ts files 37 | !**/*.d.ts -------------------------------------------------------------------------------- /tests/integration/fixture/customer_support_ma/customer-support.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "tests": [ 3 | { 4 | "name": "Basic customer support query", 5 | "turns": [ 6 | { 7 | "user": "How were you built?", 8 | "model": "I was built with the best agent framework." 9 | } 10 | ] 11 | }, 12 | { 13 | "name": "Blocked query about expedia", 14 | "turns": [ 15 | { 16 | "user": "Can you tell me about expedia flights?", 17 | "model": "Sorry, I can't answer this question." 18 | } 19 | ] 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /src/tools/toolActions.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * Interface for tool actions that can be applied in a tool context 5 | */ 6 | export interface ToolActions { 7 | /** 8 | * Whether to escalate the current flow (used by ExitLoopTool) 9 | */ 10 | escalate?: boolean; 11 | 12 | /** 13 | * Agent name to transfer to (used by TransferToAgentTool) 14 | */ 15 | transferToAgent?: string; 16 | 17 | /** 18 | * Whether to skip summarization (used by GetUserChoiceTool) 19 | */ 20 | skipSummarization?: boolean; 21 | 22 | /** 23 | * Optional properties for other actions 24 | */ 25 | [key: string]: any; 26 | } -------------------------------------------------------------------------------- /tests/integration/fixture/trip_planner_agent/test_files/trip_inquiry_sub_agent.test.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "query": "Based on my interests, where should I go, Yosemite national park or Los Angeles?", 4 | "expected_tool_use": [], 5 | "reference": "Given your interests in food, shopping, and museums, Los Angeles would be a better choice than Yosemite National Park. Yosemite is primarily focused on outdoor activities and natural landscapes, while Los Angeles offers a diverse range of culinary experiences, shopping districts, and world-class museums. I will now gather information to create an in-depth guide for your trip to Los Angeles.\n" 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */ 2 | module.exports = { 3 | preset: 'ts-jest', 4 | testEnvironment: 'node', 5 | roots: ['/src/', '/tests/'], 6 | testMatch: ['**/*.test.ts'], 7 | transform: { 8 | '^.+\\.tsx?$': 'ts-jest' 9 | }, 10 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 11 | collectCoverage: false, 12 | coverageDirectory: 'coverage', 13 | collectCoverageFrom: [ 14 | 'src/**/*.{ts,tsx}', 15 | '!src/**/*.d.ts', 16 | '!**/node_modules/**' 17 | ], 18 | // Limit the number of workers to reduce API request frequency 19 | maxWorkers: 2 20 | }; -------------------------------------------------------------------------------- /setup-dev.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM Setup script for local development on Windows 3 | REM This links the local adk-typescript package and starts auto-rebuild 4 | 5 | echo Building the project... 6 | call npm run build 7 | 8 | echo Linking adk-typescript globally... 9 | call npm link 10 | 11 | echo. 12 | echo Setup complete! 13 | echo. 14 | echo Starting watch mode for auto-rebuild on changes... 15 | echo Press Ctrl+C to stop watching. 16 | echo. 17 | echo In another terminal, you can now run examples: 18 | echo npx ts-node examples/quickstart/agent.ts 19 | echo. 20 | echo Changes to src/ will auto-rebuild! 21 | echo. 22 | 23 | call npm run build:watch 24 | -------------------------------------------------------------------------------- /setup-dev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Setup script for local development 4 | # This links the local adk-typescript package and starts auto-rebuild 5 | 6 | echo "Building the project..." 7 | npm run build 8 | 9 | echo "Linking adk-typescript globally..." 10 | npm link 11 | 12 | echo "" 13 | echo "✅ Setup complete!" 14 | echo "" 15 | echo "Starting watch mode for auto-rebuild on changes..." 16 | echo "Press Ctrl+C to stop watching." 17 | echo "" 18 | echo "In another terminal, you can now run examples:" 19 | echo " npx ts-node examples/quickstart/agent.ts" 20 | echo "" 21 | echo "Changes to src/ will auto-rebuild!" 22 | echo "" 23 | 24 | npm run build:watch 25 | -------------------------------------------------------------------------------- /tests/integration/fixture/home_automation_agent/test_files/simple_test.test.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "query": "Turn off device_2 in the Bedroom.", 4 | "expected_tool_use": [ 5 | { 6 | "tool_name": "set_device_info", 7 | "tool_input": {"location": "Bedroom", "device_id": "device_2", "status": "OFF"} 8 | } 9 | ], 10 | "reference": "OK. I've turned off device_2 in the Bedroom. Anything else?\n" 11 | }, 12 | { 13 | "query": "What's the command I just issued?", 14 | "expected_tool_use": [], 15 | "reference": "You asked me to turn off device_2 in the Bedroom.\n" 16 | } 17 | ] 18 | -------------------------------------------------------------------------------- /src/auth/AuthScheme.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * Enum representing different authentication schemes 5 | */ 6 | export enum AuthScheme { 7 | /** No authentication */ 8 | NONE = 'NONE', 9 | 10 | /** API key authentication */ 11 | API_KEY = 'API_KEY', 12 | 13 | /** Basic authentication with username and password */ 14 | BASIC = 'BASIC', 15 | 16 | /** OAuth 2.0 authentication */ 17 | OAUTH2 = 'OAUTH2', 18 | 19 | /** OpenID Connect authentication */ 20 | OIDC = 'OIDC', 21 | 22 | /** Google service account authentication */ 23 | SERVICE_ACCOUNT = 'SERVICE_ACCOUNT', 24 | 25 | /** Custom authentication mechanism */ 26 | CUSTOM = 'CUSTOM' 27 | } -------------------------------------------------------------------------------- /tests/integration/fixture/home_automation_agent/test_files/memorizing_past_events/eval_data.test.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "query": "Turn off device_2 in the Bedroom.", 4 | "expected_tool_use": [ 5 | { 6 | "tool_name": "set_device_info", 7 | "tool_input": {"location": "Bedroom", "device_id": "device_2", "status": "OFF"} 8 | } 9 | ], 10 | "reference": "OK. I've turned off device_2 in the Bedroom. Anything else?\n" 11 | }, 12 | { 13 | "query": "What's the command I just issued?", 14 | "expected_tool_use": [], 15 | "reference": "You asked me to turn off device_2 in the Bedroom.\n" 16 | } 17 | ] 18 | -------------------------------------------------------------------------------- /tests/integration/fixture/home_automation_agent/test_files/dependent_tool_calls.test.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "query": "Turn off device_2 in the Bedroom.", 4 | "expected_tool_use": [{ 5 | "tool_name": "set_device_info", 6 | "tool_input": {"location": "Bedroom", "status": "OFF", "device_id": "device_2"} 7 | }], 8 | "reference": "I have set the device 2 status to off." 9 | }, 10 | { 11 | "query": "What's the status of device_2 in the Bedroom?", 12 | "expected_tool_use": [{ 13 | "tool_name": "get_device_info", 14 | "tool_input": {"device_id": "device_2"} 15 | }], 16 | "reference": "Status of device_2 is off." 17 | } 18 | ] 19 | -------------------------------------------------------------------------------- /tests/integration/fixture/home_automation_agent/test_files/simple_multi_turn_conversation.test.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "query": "Turn off device_2 in the Bedroom.", 4 | "expected_tool_use": [{ 5 | "tool_name": "set_device_info", 6 | "tool_input": {"location": "Bedroom", "device_id": "device_2", "status": "OFF"} 7 | }], 8 | "reference": "I have set the device 2 status to off." 9 | }, 10 | { 11 | "query": "Turn on device_2 in the Bedroom.", 12 | "expected_tool_use": [{ 13 | "tool_name": "set_device_info", 14 | "tool_input": {"location": "Bedroom", "status": "ON", "device_id": "device_2"} 15 | }], 16 | "reference": "I have set the device 2 status to on." 17 | } 18 | ] 19 | -------------------------------------------------------------------------------- /src/tools/mcp-tool/index.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | // Export MCP Session Manager and related types 4 | export type { 5 | ClientSession, 6 | ListToolsResult, 7 | McpBaseTool, 8 | StdioServerParameters, 9 | SseServerParams, 10 | } from './MCPSessionManager'; 11 | export { 12 | AsyncExitStack, 13 | MCPSessionManager, 14 | ClosedResourceError, 15 | retryOnClosedResource, 16 | } from './MCPSessionManager'; 17 | 18 | // Export MCP Tool 19 | export { MCPTool } from './MCPTool'; 20 | 21 | // Export MCP Toolset 22 | export { MCPToolset } from './MCPToolset'; 23 | 24 | // Export conversion utilities 25 | export type { 26 | SchemaType, 27 | GeminiSchema, 28 | JSONSchema, 29 | } from './ConversionUtils'; 30 | export { 31 | adkToMcpToolType, 32 | geminiToJsonSchema, 33 | } from './ConversionUtils'; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "Node16", 5 | "declaration": true, 6 | "outDir": "./dist", 7 | "rootDir": "./src", 8 | "strict": true, 9 | "esModuleInterop": true, 10 | "skipLibCheck": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "resolveJsonModule": true, 13 | "moduleResolution": "node16", 14 | "experimentalDecorators": true, 15 | "emitDecoratorMetadata": true, 16 | "isolatedModules": true, 17 | "typeRoots": ["./node_modules/@types", "./src/types"], 18 | "baseUrl": ".", 19 | "paths": { 20 | "@/*": ["src/*"], 21 | "adk-typescript": ["src/index.ts"], 22 | "adk-typescript/*": ["src/*"] 23 | } 24 | }, 25 | "include": ["src/**/*"], 26 | "exclude": ["node_modules", "**/*.test.ts"] 27 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependency directories 2 | node_modules/ 3 | jspm_packages/ 4 | 5 | # TypeScript output directory 6 | dist/ 7 | build/ 8 | lib/ 9 | 10 | # Testing 11 | coverage/ 12 | .nyc_output/ 13 | 14 | # Environment files 15 | .env 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | .cursorignore 21 | 22 | # IDE and editor files 23 | .idea/ 24 | .vscode/ 25 | *.swp 26 | *.swo 27 | .DS_Store 28 | 29 | # Logs 30 | logs 31 | *.log 32 | npm-debug.log* 33 | yarn-debug.log* 34 | yarn-error.log* 35 | lerna-debug.log* 36 | 37 | # Cache 38 | .npm 39 | .eslintcache 40 | 41 | coverage/ 42 | 43 | adk-python/ 44 | 45 | # npm related files 46 | npm-debug.log* 47 | yarn-debug.log* 48 | yarn-error.log* 49 | lerna-debug.log* 50 | 51 | package-lock.json 52 | *.tgz 53 | test-adk 54 | docs_python 55 | 56 | 57 | test.py 58 | tmp/ -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Basic type definitions for the ADK TypeScript port 3 | */ 4 | 5 | /** 6 | * Part represents a content part, which can be text, a function call, or a function response 7 | */ 8 | export class Part { 9 | text?: string; 10 | functionCall?: any; 11 | functionResponse?: any; 12 | 13 | constructor(data: { text?: string; functionCall?: any; functionResponse?: any }) { 14 | this.text = data.text; 15 | this.functionCall = data.functionCall; 16 | this.functionResponse = data.functionResponse; 17 | } 18 | } 19 | 20 | /** 21 | * Content represents a message with a role and parts 22 | */ 23 | export interface Content { 24 | role: string; 25 | parts: Part[]; 26 | } 27 | 28 | /** 29 | * Event represents an interaction in a session 30 | */ 31 | export interface Event { 32 | author: string; 33 | content: Content; 34 | } -------------------------------------------------------------------------------- /tests/integration/fixture/hello_world_agent/roll-die.test.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "query": "Hi who are you?", 4 | "expected_tool_use": [], 5 | "reference": "I am a data processing agent. I can roll dice and check if the results are prime numbers. What would you like me to do? \n" 6 | }, 7 | { 8 | "query": "What can you do?", 9 | "expected_tool_use": [], 10 | "reference": "I can roll dice for you of different sizes, and I can check if the results are prime numbers. I can also remember previous rolls if you'd like to check those for primes as well. What would you like me to do? \n" 11 | }, 12 | { 13 | "query": "Can you roll a die with 6 sides", 14 | "expected_tool_use": [ 15 | { 16 | "tool_name": "roll_die", 17 | "tool_input": { 18 | "sides": 6 19 | } 20 | } 21 | ], 22 | "reference": null 23 | } 24 | ] -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | extends: [ 4 | 'eslint:recommended', 5 | 'plugin:@typescript-eslint/recommended', 6 | ], 7 | plugins: ['@typescript-eslint'], 8 | env: { 9 | node: true, 10 | es6: true, 11 | jest: true, 12 | }, 13 | parserOptions: { 14 | ecmaVersion: 2020, 15 | sourceType: 'module', 16 | }, 17 | rules: { 18 | // Downgrade errors to warnings for initial release 19 | '@typescript-eslint/no-explicit-any': 'off', 20 | '@typescript-eslint/no-unused-vars': 'off', 21 | 'no-constant-condition': 'warn', 22 | 'prefer-const': 'warn', 23 | '@typescript-eslint/no-var-requires': 'off', 24 | '@typescript-eslint/no-namespace': 'off', 25 | '@typescript-eslint/ban-ts-comment': 'off', 26 | 'no-dupe-else-if': 'warn', 27 | }, 28 | ignorePatterns: ['dist/', 'node_modules/', 'coverage/'], 29 | }; -------------------------------------------------------------------------------- /src/flows/llm_flows/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Exports for LLM flow components. 3 | */ 4 | import { BaseLlmRequestProcessor, BaseLlmResponseProcessor } from './BaseLlmProcessor'; 5 | import { BaseLlmFlow } from './BaseLlmFlow'; 6 | import { SingleFlow } from './SingleFlow'; 7 | import { AutoFlow } from './AutoFlow'; 8 | import * as identity from './identity'; 9 | import * as basic from './basic'; 10 | import * as instructions from './instructions'; 11 | import * as agentTransfer from './agentTransfer'; 12 | import * as contents from './contents'; 13 | import * as nlPlanning from './NlPlanning'; 14 | import * as codeExecution from './CodeExecution'; 15 | 16 | export type { 17 | BaseLlmRequestProcessor, 18 | BaseLlmResponseProcessor, 19 | }; 20 | export { 21 | BaseLlmFlow, 22 | SingleFlow, 23 | AutoFlow, 24 | basic, 25 | identity, 26 | instructions, 27 | agentTransfer, 28 | contents, 29 | nlPlanning, 30 | codeExecution 31 | }; -------------------------------------------------------------------------------- /src/tools/google-api-tool/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Google API Tool module 3 | * 4 | * This module provides tools for interacting with Google APIs through OpenAPI specifications. 5 | */ 6 | 7 | // Export all classes and interfaces from the GoogleApiTool module 8 | export * from './GoogleApiTool'; 9 | export * from './GoogleApiToolSet'; 10 | export * from './GoogleApiToolSets'; 11 | export * from './GoogleApiToOpenApiConverter'; 12 | 13 | // Export commonly used toolsets 14 | export { 15 | getBigqueryToolSet as bigqueryToolSet, 16 | getCalendarToolSet as calendarToolSet, 17 | getGmailToolSet as gmailToolSet, 18 | getYoutubeToolSet as youtubeToolSet, 19 | getSlidesToolSet as slidesToolSet, 20 | getSheetsToolSet as sheetsToolSet, 21 | getDocsToolSet as docsToolSet, 22 | BigQueryToolset, 23 | CalendarToolset, 24 | GmailToolset, 25 | YoutubeToolset, 26 | SlidesToolset, 27 | SheetsToolset, 28 | DocsToolset, 29 | } from './GoogleApiToolSets'; -------------------------------------------------------------------------------- /src/models/index.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * Defines the interface to support a model. 5 | */ 6 | 7 | // Import all models 8 | import { Claude } from './AnthropicLlm'; 9 | import { Gemini } from './GoogleLlm'; 10 | import { LlmRegistry } from './LlmRegistry'; 11 | 12 | // Export base classes 13 | export { BaseLlm } from './BaseLlm'; 14 | export { BaseLlmConnection } from './BaseLlmConnection'; 15 | export { LlmRequest } from './LlmRequest'; 16 | export { LlmResponse } from './LlmResponse'; 17 | export { LlmRegistry } from './LlmRegistry'; 18 | export { LiteLlm, TextChunk, FunctionChunk } from './LiteLlm'; 19 | export { Claude } from './AnthropicLlm'; 20 | export { Gemini } from './GoogleLlm'; 21 | export { GeminiLlmConnection } from './GeminiLlmConnection'; 22 | 23 | // Export types 24 | export * from './types'; 25 | 26 | // Auto-register models 27 | // Register the Claude model 28 | LlmRegistry.register(Claude); 29 | // Register the Gemini model 30 | LlmRegistry.register(Gemini); -------------------------------------------------------------------------------- /tests/integration/fixture/index.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * Fixture module for integration tests 5 | */ 6 | 7 | // Export all agent fixtures 8 | export * from './hello_world_agent/agent'; 9 | export * from './context_variable_agent/agent'; 10 | export * from './callback_agent/agent'; 11 | export * from './tool_agent/agent'; // Note: tool_agent has limited implementation of agent tools due to API differences 12 | export * from './customer_support_ma/agent'; // Customer support multi-agent 13 | export * from './agent_with_config/agent'; // Agent with different configuration options 14 | export * from './context_update_test/agent'; // Context update test for state management 15 | export * from './ecommerce_customer_service_agent/agent'; // E-commerce customer service agent 16 | export * from './flow_complex_spark/agent'; // Complex flow with spark agents 17 | export * from './home_automation_agent/agent'; // Home automation agent 18 | 19 | // Add more fixture exports as they are ported -------------------------------------------------------------------------------- /src/memory/utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * Formats the timestamp of the memory entry. 19 | * @param timestamp The timestamp to format (in seconds since epoch) 20 | * @returns The formatted timestamp in ISO format 21 | */ 22 | export function formatTimestamp(timestamp: number): string { 23 | return new Date(timestamp * 1000).toISOString(); 24 | } -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Run Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - master 8 | pull_request: 9 | branches: 10 | - main 11 | - master 12 | 13 | jobs: 14 | test: 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [20.x] 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | 24 | - name: Setup Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v4 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | cache: 'npm' 29 | 30 | - name: Install dependencies 31 | run: npm ci 32 | 33 | - name: Run unit tests 34 | run: npm run test:unit 35 | env: 36 | CI: true 37 | 38 | - name: Upload coverage reports (optional) 39 | if: matrix.node-version == '20.x' 40 | uses: codecov/codecov-action@v3 41 | with: 42 | fail_ci_if_error: false 43 | -------------------------------------------------------------------------------- /docs/stylesheets/custom.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | /* Index page styling */ 4 | 5 | .md-grid { max-width: 80%; } 6 | .footer { padding-bottom: 30vh; } 7 | .centered-logo-text-group { display: inline-flex; align-items: center; gap: 1.5em; margin-bottom: 0.5em; vertical-align: middle; } 8 | .centered-logo-text-group img { height: auto; } 9 | .centered-logo-text-group h1 { margin: 0; text-align: left; } 10 | .install-command-container { max-width: 600px; margin: 2.5em auto; padding: 1.5em 2em; background-color: var(--md-code-bg-color, #f5f5f5); border-radius: 8px; text-align: center; box-shadow: 0 3px 6px rgba(0,0,0,0.05); border-left: 5px solid var(--md-primary-fg-color, #526cfe); margin-top: 30px; } 11 | .install-command-container p { font-size: 1.1em; color: var(--md-default-fg-color); margin-bottom: -10px; margin-top: -10px; } 12 | .install-command-container p code { font-size: 1.1em; font-weight: 600; padding: 0.3em 0.6em; background-color: var(--md-code-fg-color--light); border-radius: 4px; display: inline-block; line-height: 1.4; } 13 | -------------------------------------------------------------------------------- /examples/non_llm_sequential/agent.ts: -------------------------------------------------------------------------------- 1 | import { LlmAgent, SequentialAgent } from 'adk-typescript/agents'; 2 | import { runAgent } from 'adk-typescript'; 3 | 4 | const subAgent1 = new LlmAgent({ 5 | name: 'sub_agent_1', 6 | description: 'No.1 sub agent.', 7 | model: 'gemini-2.0-flash', 8 | instruction: 'JUST SAY 1.', 9 | }); 10 | 11 | const subAgent2 = new LlmAgent({ 12 | name: 'sub_agent_2', 13 | description: 'No.2 sub agent.', 14 | model: 'gemini-2.0-flash', 15 | instruction: 'JUST SAY 2.', 16 | }); 17 | 18 | // Now the interface exactly matches the Python version: SequentialAgent(name='...', sub_agents=[...]) 19 | const sequentialAgent = new SequentialAgent({ 20 | name: 'sequential_agent', 21 | subAgents: [subAgent1, subAgent2] 22 | }); 23 | 24 | export const rootAgent = sequentialAgent; 25 | 26 | // Run agent directly when this file is executed 27 | // Usage: npx ts-node examples/non_llm_sequential/agent.ts 28 | if (require.main === module) { 29 | runAgent(rootAgent as any).catch(console.error); 30 | } 31 | -------------------------------------------------------------------------------- /tests/integration/fixture/trip_planner_agent/trip_inquiry.test.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "query": "Hi, who are you? What can you do?", 4 | "expected_tool_use": [], 5 | "reference": "I am trip_planner, and my goal is to plan the best trip ever. I can describe why a city was chosen, list its top attractions, and provide a detailed itinerary for each day of the trip.\n" 6 | }, 7 | { 8 | "query": "I want to travel from San Francisco to an European country in fall next year. I am considering London and Paris. What is your advice?", 9 | "expected_tool_use": [ 10 | { 11 | "tool_name": "transfer_to_agent", 12 | "tool_input": { 13 | "agent_name": "indentify_agent" 14 | } 15 | } 16 | ], 17 | "reference": "Okay, I can help you analyze London and Paris to determine which city is better for your trip next fall. I will consider weather patterns, seasonal events, travel costs (including flights from San Francisco), and your interests (food, shopping, and museums). After gathering this information, I'll provide a detailed report on my chosen city.\n" 18 | } 19 | ] 20 | -------------------------------------------------------------------------------- /docs/tutorials/index.md: -------------------------------------------------------------------------------- 1 | # ADK Tutorials! 2 | 3 | Get started with the Agent Development Kit (ADK) through our collection of 4 | practical guides. These tutorials are designed in a simple, progressive, 5 | step-by-step fashion, introducing you to different ADK features and 6 | capabilities. 7 | 8 | This approach allows you to learn and build incrementally – starting with 9 | foundational concepts and gradually tackling more advanced agent development 10 | techniques. You'll explore how to apply these features effectively across 11 | various use cases, equipping you to build your own sophisticated agentic 12 | applications with ADK. Explore our collection below and happy building: 13 | 14 |
15 | 16 | - :material-console-line: **Agent Team** 17 | 18 | --- 19 | 20 | Learn to build an intelligent multi-agent weather bot and master key ADK 21 | features: defining Tools, using multiple LLMs (Gemini, GPT, Claude) with 22 | LiteLLM, orchestrating agent delegation, adding memory with session state, 23 | and ensuring safety via callbacks. 24 | 25 | [:octicons-arrow-right-24: Start learning here](agent-team.md) 26 | 27 |
-------------------------------------------------------------------------------- /src/agents/TranscriptionEntry.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A transcription entry represents audio or text content that needs to be transcribed. 3 | */ 4 | export class TranscriptionEntry { 5 | /** Role of the transcription entry (e.g., 'user', 'model') */ 6 | role?: string; 7 | 8 | /** Audio data as a binary buffer or a string */ 9 | audioData?: ArrayBuffer | string; 10 | 11 | /** Text content */ 12 | textContent?: string; 13 | 14 | /** Metadata for the transcription */ 15 | metadata?: Record; 16 | 17 | /** Data property for compatibility with Python implementation */ 18 | data?: any; 19 | 20 | /** 21 | * Creates a new instance of TranscriptionEntry. 22 | * 23 | * @param params The parameters for the transcription entry. 24 | */ 25 | constructor(params: { 26 | role?: string; 27 | audioData?: ArrayBuffer | string; 28 | textContent?: string; 29 | metadata?: Record; 30 | data?: any; 31 | }) { 32 | this.role = params.role; 33 | this.audioData = params.audioData; 34 | this.textContent = params.textContent; 35 | this.metadata = params.metadata; 36 | this.data = params.data; 37 | } 38 | } -------------------------------------------------------------------------------- /src/auth/index.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * Auth module - Provides interfaces and implementations for authentication 5 | */ 6 | 7 | // We'll use named exports to avoid ambiguities with existing exports 8 | // Export from original credential/scheme files (different naming to avoid conflicts) 9 | export type { 10 | AuthCredential as SimpleAuthCredential, 11 | } from './AuthCredential'; 12 | export { 13 | ApiKeyCredential, 14 | BearerCredential, 15 | BasicCredential 16 | } from './AuthCredential'; 17 | 18 | export type { 19 | AuthScheme as SimpleAuthScheme, 20 | } from './AuthSchemes'; 21 | export { 22 | ApiKeyAuthScheme, 23 | BearerAuthScheme, 24 | BasicAuthScheme 25 | } from './AuthSchemes'; 26 | 27 | // Export auth components - consolidate exports to avoid naming conflicts 28 | export * from './AuthCredential'; 29 | export * from './AuthHandler'; 30 | export * from './AuthSchemes'; // This includes AuthScheme enum 31 | 32 | // Export from AuthTool with renamed AuthConfig to avoid ambiguity 33 | import * as AuthToolExports from './AuthTool'; 34 | export { 35 | AuthToolExports as AuthToolModule 36 | }; 37 | 38 | export * from './AuthPreprocessor'; 39 | export * from './AuthConfig'; -------------------------------------------------------------------------------- /src/agents/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Agents module - Defines different agent implementations 3 | */ 4 | 5 | // Core agent interfaces and contexts 6 | export { BaseAgent } from './BaseAgent'; 7 | export type { BeforeAgentCallback, AfterAgentCallback } from './BaseAgent'; 8 | export { ReadonlyContext } from './ReadonlyContext'; 9 | export { CallbackContext } from './CallbackContext'; 10 | export { InvocationContext, LlmCallsLimitExceededError } from './InvocationContext'; 11 | 12 | // Agent runtime support 13 | export { RunConfig } from './RunConfig'; 14 | export { LiveRequestQueue } from './LiveRequestQueue'; 15 | export { ActiveStreamingTool } from './ActiveStreamingTool'; 16 | export { TranscriptionEntry } from './TranscriptionEntry'; 17 | 18 | // Agent implementations 19 | export { LlmAgent } from './LlmAgent'; // Main LLM-based Agent 20 | export { SequentialAgent } from './SequentialAgent'; 21 | export { RemoteAgent } from './RemoteAgent'; 22 | export { LoopAgent } from './LoopAgent'; 23 | export { ParallelAgent } from './ParallelAgent'; 24 | export { LanggraphAgent } from './LanggraphAgent'; 25 | 26 | // Legacy types for backward compatibility 27 | export { ReasoningAgent, PlanningAgent } from './LegacyAgents'; -------------------------------------------------------------------------------- /src/code-executors/index.ts: -------------------------------------------------------------------------------- 1 | import { BaseCodeExecutor } from './BaseCodeExecutor'; 2 | import { BuiltInCodeExecutor } from './BuiltInCodeExecutor'; 3 | import { CodeExecutorContext } from './CodeExecutorContext'; 4 | import { UnsafeLocalCodeExecutor } from './UnsafeLocalCodeExecutor'; 5 | import { 6 | CodeExecutionInput, 7 | CodeExecutionResult, 8 | CodeExecutionUtils, 9 | File 10 | } from './CodeExecutionUtils'; 11 | import { VertexAiCodeExecutor, VertexAiCodeExecutorOptions } from './VertexAiCodeExecutor'; 12 | 13 | // Export all components 14 | export type { 15 | CodeExecutionInput, 16 | CodeExecutionResult, 17 | File, 18 | VertexAiCodeExecutorOptions 19 | }; 20 | export { 21 | BaseCodeExecutor, 22 | BuiltInCodeExecutor, 23 | CodeExecutorContext, 24 | UnsafeLocalCodeExecutor, 25 | VertexAiCodeExecutor, 26 | CodeExecutionUtils, 27 | }; 28 | 29 | // Note: The following executors would need to be implemented if needed: 30 | // - ContainerCodeExecutor 31 | 32 | // These would need their own TypeScript implementations and appropriate 33 | // dependencies installed. They'd be imported and exported here when available. 34 | // Similar to the Python implementation which conditionally exports them. -------------------------------------------------------------------------------- /src/tools/memoryEntryUtils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { MemoryEntry } from '../memory/MemoryEntry'; 18 | 19 | /** 20 | * Extracts the text from the memory entry. 21 | * @param memory The memory entry to extract text from 22 | * @param splitter The string to use to join text parts (default: ' ') 23 | * @returns The extracted text content 24 | */ 25 | export function extractText(memory: MemoryEntry, splitter: string = ' '): string { 26 | if (!memory.content.parts) { 27 | return ''; 28 | } 29 | 30 | return memory.content.parts 31 | .filter(part => part.text) 32 | .map(part => part.text!) 33 | .join(splitter); 34 | } -------------------------------------------------------------------------------- /src/cli/browser/chunk-EQDQRRRY.js: -------------------------------------------------------------------------------- 1 | var p=Object.create;var j=Object.defineProperty,q=Object.defineProperties,r=Object.getOwnPropertyDescriptor,s=Object.getOwnPropertyDescriptors,t=Object.getOwnPropertyNames,g=Object.getOwnPropertySymbols,u=Object.getPrototypeOf,k=Object.prototype.hasOwnProperty,m=Object.prototype.propertyIsEnumerable;var l=(a,b,c)=>b in a?j(a,b,{enumerable:!0,configurable:!0,writable:!0,value:c}):a[b]=c,w=(a,b)=>{for(var c in b||={})k.call(b,c)&&l(a,c,b[c]);if(g)for(var c of g(b))m.call(b,c)&&l(a,c,b[c]);return a},x=(a,b)=>q(a,s(b));var y=(a,b)=>{var c={};for(var d in a)k.call(a,d)&&b.indexOf(d)<0&&(c[d]=a[d]);if(a!=null&&g)for(var d of g(a))b.indexOf(d)<0&&m.call(a,d)&&(c[d]=a[d]);return c};var z=(a,b)=>()=>(b||a((b={exports:{}}).exports,b),b.exports);var v=(a,b,c,d)=>{if(b&&typeof b=="object"||typeof b=="function")for(let e of t(b))!k.call(a,e)&&e!==c&&j(a,e,{get:()=>b[e],enumerable:!(d=r(b,e))||d.enumerable});return a};var A=(a,b,c)=>(c=a!=null?p(u(a)):{},v(b||!a||!a.__esModule?j(c,"default",{value:a,enumerable:!0}):c,a));var B=(a,b,c)=>new Promise((d,e)=>{var n=f=>{try{h(c.next(f))}catch(i){e(i)}},o=f=>{try{h(c.throw(f))}catch(i){e(i)}},h=f=>f.done?d(f.value):Promise.resolve(f.value).then(n,o);h((c=c.apply(a,b)).next())});export{w as a,x as b,y as c,z as d,A as e,B as f}; 2 | -------------------------------------------------------------------------------- /examples/fields_output_schema/agent.ts: -------------------------------------------------------------------------------- 1 | import { LlmAgent as Agent } from 'adk-typescript/agents'; 2 | import { runAgent } from 'adk-typescript'; 3 | 4 | class WeatherData { 5 | temperature: string; 6 | humidity: string; 7 | wind_speed: string; 8 | 9 | constructor(data: { 10 | temperature: string; 11 | humidity: string; 12 | wind_speed: string; 13 | }) { 14 | this.temperature = data.temperature; 15 | this.humidity = data.humidity; 16 | this.wind_speed = data.wind_speed; 17 | } 18 | } 19 | 20 | export const rootAgent = new Agent({ 21 | name: 'root_agent', 22 | model: 'gemini-2.0-flash', 23 | instruction: `Answer user's questions based on the data you have. 24 | 25 | If you don't have the data, you can just say you don't know. 26 | 27 | Here are the data you have for San Jose 28 | 29 | * temperature: 26 C 30 | * humidity: 20% 31 | * wind_speed: 29 mph 32 | 33 | Here are the data you have for Cupertino 34 | 35 | * temperature: 16 C 36 | * humidity: 10% 37 | * wind_speed: 13 mph 38 | `, 39 | outputSchema: WeatherData, 40 | outputKey: 'weather_data', 41 | }); 42 | 43 | // Run agent directly when this file is executed 44 | // Usage: npx ts-node examples/fields_output_schema/agent.ts 45 | if (require.main === module) { 46 | runAgent(rootAgent).catch(console.error); 47 | } 48 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to ADK TypeScript 2 | 3 | Thanks for your interest in contributing! This is an unofficial community port, and contributions are what make it better. 4 | 5 | ## How to Contribute 6 | 7 | ### Reporting Issues 8 | 9 | - Check existing issues before creating a new one 10 | - Include version info, code snippets, and error messages 11 | - Describe expected vs actual behavior 12 | 13 | ### Pull Requests 14 | 15 | 1. Fork the repo and create a branch from `main` 16 | 2. Make your changes with clear commit messages 17 | 3. Add tests if applicable 18 | 4. Ensure tests pass: `npm test` 19 | 5. Submit a PR with a clear description 20 | 21 | ### Development Setup 22 | 23 | ```bash 24 | git clone https://github.com/njraladdin/adk-typescript.git 25 | cd adk-typescript 26 | npm install 27 | npm test 28 | ``` 29 | 30 | ### Code Style 31 | 32 | - Follow existing TypeScript conventions 33 | - Use meaningful variable names 34 | - Add JSDoc comments for public APIs 35 | - Keep changes focused and atomic 36 | 37 | ### Areas We Need Help 38 | 39 | - Bug fixes and stability improvements 40 | - Documentation improvements 41 | - Test coverage 42 | - Feature parity with Python ADK 43 | - Performance optimizations 44 | 45 | ## Code of Conduct 46 | 47 | Be respectful and constructive. This is a learning community building cool AI stuff together. -------------------------------------------------------------------------------- /docs/examples/typescript/snippets/get-started/google-search-agent/agent.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * TypeScript port of the google_search_agent/agent.py example from the Python ADK library 3 | * 4 | * This example demonstrates how to create an agent with the built-in Google Search tool 5 | * to help answer questions with internet search results. 6 | * 7 | * NOTE: This is a template file that demonstrates how to use the ADK TypeScript library. 8 | * You'll see TypeScript errors in your IDE until you install the actual 'adk-typescript' package. 9 | * The structure and patterns shown here match how you would use the library in a real project. 10 | */ 11 | 12 | import { Agent, googleSearch } from 'adk-typescript'; 13 | 14 | // Create the agent with Google Search tool 15 | const rootAgent = new Agent("google_search_agent", { 16 | // The Large Language Model (LLM) that agent will use 17 | model: "gemini-2.0-flash", 18 | // A short description of the agent's purpose 19 | description: "Agent to answer questions using Google Search.", 20 | // Instructions to set the agent's behavior 21 | instruction: "You are an expert researcher. You always stick to the facts.", 22 | // Add google_search tool to perform grounding with Google search 23 | tools: [googleSearch] 24 | }); 25 | 26 | // Export the agent for use in other modules 27 | export const agent = rootAgent; -------------------------------------------------------------------------------- /src/flows/llm_flows/BaseLlmProcessor.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * Defines the processor interfaces used for BaseLlmFlow. 5 | */ 6 | import { InvocationContext } from '../../agents/InvocationContext'; 7 | import { Event } from '../../events/Event'; 8 | import { LlmRequest } from '../../models/LlmRequest'; 9 | import { LlmResponse } from '../../models/LlmResponse'; 10 | 11 | /** 12 | * Base interface for LLM request processor. 13 | */ 14 | export interface BaseLlmRequestProcessor { 15 | /** 16 | * Runs the processor asynchronously. 17 | * 18 | * @param invocationContext The invocation context 19 | * @param llmRequest The LLM request to process 20 | * @returns An async generator yielding events 21 | */ 22 | runAsync( 23 | invocationContext: InvocationContext, 24 | llmRequest: LlmRequest 25 | ): AsyncGenerator; 26 | } 27 | 28 | /** 29 | * Base interface for LLM response processor. 30 | */ 31 | export interface BaseLlmResponseProcessor { 32 | /** 33 | * Processes the LLM response asynchronously. 34 | * 35 | * @param invocationContext The invocation context 36 | * @param llmResponse The LLM response to process 37 | * @returns An async generator yielding events 38 | */ 39 | runAsync( 40 | invocationContext: InvocationContext, 41 | llmResponse: LlmResponse 42 | ): AsyncGenerator; 43 | } -------------------------------------------------------------------------------- /src/tools/LongRunningTool.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { FunctionTool, ToolFunction, FunctionToolOptions } from './FunctionTool'; 4 | 5 | /** 6 | * Options for creating a long-running function tool 7 | */ 8 | export interface LongRunningFunctionToolOptions extends Omit { 9 | /** 10 | * The function to execute 11 | */ 12 | fn: ToolFunction; 13 | } 14 | 15 | /** 16 | * A function tool that returns the result asynchronously. 17 | * 18 | * This tool is used for long-running operations that may take a significant 19 | * amount of time to complete. The framework will call the function. Once the 20 | * function returns, the response will be returned asynchronously to the 21 | * framework which is identified by the function_call_id. 22 | * 23 | * Example: 24 | * ```typescript 25 | * const tool = new LongRunningFunctionTool({ 26 | * name: 'long_running_function', 27 | * description: 'A long running function', 28 | * fn: aLongRunningFunction 29 | * }); 30 | * ``` 31 | */ 32 | export class LongRunningFunctionTool extends FunctionTool { 33 | /** 34 | * Creates a new long-running function tool 35 | * 36 | * @param options Options for the long-running function tool 37 | */ 38 | constructor(options: LongRunningFunctionToolOptions) { 39 | super({ 40 | ...options, 41 | isLongRunning: true 42 | }); 43 | } 44 | } -------------------------------------------------------------------------------- /src/planners/BasePlanner.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { Part } from '../models/types'; 4 | import { ReadonlyContext } from '../agents/ReadonlyContext'; 5 | import { CallbackContext } from '../agents/CallbackContext'; 6 | import { LlmRequest } from '../models/LlmRequest'; 7 | 8 | /** 9 | * Abstract base class for all planners. 10 | * 11 | * The planner allows the agent to generate plans for the queries to guide its 12 | * action. 13 | */ 14 | export abstract class BasePlanner { 15 | /** 16 | * Builds the system instruction to be appended to the LLM request for planning. 17 | * 18 | * @param readonlyContext The readonly context of the invocation. 19 | * @param llmRequest The LLM request. Readonly. 20 | * @returns The planning system instruction, or undefined if no instruction is needed. 21 | */ 22 | abstract buildPlanningInstruction( 23 | readonlyContext: ReadonlyContext, 24 | llmRequest: LlmRequest 25 | ): string | undefined; 26 | 27 | /** 28 | * Processes the LLM response for planning. 29 | * 30 | * @param callbackContext The callback context of the invocation. 31 | * @param responseParts The LLM response parts. Readonly. 32 | * @returns The processed response parts, or undefined if no processing is needed. 33 | */ 34 | abstract processPlanningResponse( 35 | callbackContext: CallbackContext, 36 | responseParts: Part[] 37 | ): Part[] | undefined; 38 | } -------------------------------------------------------------------------------- /src/tools/openapi-tool/auth/credential_exchangers/BaseCredentialExchanger.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Base Credential Exchanger 3 | * 4 | * Base class for all credential exchangers 5 | */ 6 | 7 | import { AuthCredential } from '../../../../auth/AuthCredential'; 8 | 9 | /** 10 | * Error thrown when credentials are missing 11 | */ 12 | export class AuthCredentialMissingError extends Error { 13 | constructor(message: string) { 14 | super(message); 15 | this.name = 'AuthCredentialMissingError'; 16 | } 17 | } 18 | 19 | /** 20 | * Base class for credential exchangers 21 | */ 22 | export abstract class BaseCredentialExchanger { 23 | /** 24 | * Checks if the scheme and credential types are compatible 25 | * @param authScheme The authentication scheme 26 | * @param authCredential The authentication credentials 27 | */ 28 | protected abstract _checkSchemeCredentialType( 29 | authScheme: any, 30 | authCredential: AuthCredential | null 31 | ): void; 32 | 33 | /** 34 | * Exchanges one credential type for another 35 | * @param authScheme The auth scheme 36 | * @param authCredential The auth credential to exchange 37 | * @returns The exchanged credential, or null if no credential was provided 38 | */ 39 | public abstract exchangeCredential( 40 | authScheme: any, 41 | authCredential: AuthCredential | null 42 | ): Promise | AuthCredential | null; 43 | } -------------------------------------------------------------------------------- /src/evaluation/EvalSetsManager.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { EvalCase } from './EvalCase'; 18 | import { EvalSet } from './EvalSet'; 19 | 20 | /** 21 | * An interface to manage Eval Sets. 22 | */ 23 | export abstract class EvalSetsManager { 24 | /** 25 | * Returns an EvalSet identified by an app_name and eval_set_id. 26 | */ 27 | abstract getEvalSet(appName: string, evalSetId: string): EvalSet; 28 | 29 | /** 30 | * Creates an empty EvalSet given the app_name and eval_set_id. 31 | */ 32 | abstract createEvalSet(appName: string, evalSetId: string): void; 33 | 34 | /** 35 | * Returns a list of EvalSets that belong to the given app_name. 36 | */ 37 | abstract listEvalSets(appName: string): string[]; 38 | 39 | /** 40 | * Adds the given EvalCase to an existing EvalSet identified by app_name and eval_set_id. 41 | */ 42 | abstract addEvalCase(appName: string, evalSetId: string, evalCase: EvalCase): void; 43 | } -------------------------------------------------------------------------------- /src/flows/llm_flows/identity.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Gives the agent identity from the framework. 3 | */ 4 | import { InvocationContext } from '../../agents/InvocationContext'; 5 | import { Event } from '../../events/Event'; 6 | import { LlmRequest } from '../../models/LlmRequest'; 7 | import { BaseLlmRequestProcessor } from './BaseLlmProcessor'; 8 | 9 | /** 10 | * Gives the agent identity from the framework. 11 | */ 12 | class IdentityLlmRequestProcessor implements BaseLlmRequestProcessor { 13 | /** 14 | * Runs the processor asynchronously. 15 | * 16 | * @param invocationContext The invocation context 17 | * @param llmRequest The LLM request to process 18 | * @returns An async generator yielding events 19 | */ 20 | async *runAsync( 21 | invocationContext: InvocationContext, 22 | llmRequest: LlmRequest 23 | ): AsyncGenerator { 24 | const agent = invocationContext.agent; 25 | const instructions = [`You are an agent. Your internal name is "${agent.name}".`]; 26 | 27 | if (agent.description) { 28 | instructions.push(` The description about you is "${agent.description}"`); 29 | } 30 | 31 | llmRequest.appendInstructions(instructions); 32 | 33 | // This is a proper way to maintain the AsyncGenerator contract without actually yielding anything 34 | // The condition is always false, but TypeScript doesn't flag this specific pattern as an error 35 | if (Math.random() < 0) { 36 | yield {} as Event; 37 | } 38 | } 39 | } 40 | 41 | // Export the processor instance 42 | export const requestProcessor = new IdentityLlmRequestProcessor(); -------------------------------------------------------------------------------- /src/agents/ReadonlyContext.ts: -------------------------------------------------------------------------------- 1 | import { InvocationContext } from './InvocationContext'; 2 | import { Content } from '../models/types'; 3 | import { State } from '../sessions/State'; 4 | 5 | /** 6 | * Readonly context for agent invocations. 7 | * Provides read-only access to the agent's state and context. 8 | */ 9 | export class ReadonlyContext { 10 | protected invocationContext: InvocationContext; 11 | 12 | constructor(invocationContext: InvocationContext) { 13 | this.invocationContext = invocationContext; 14 | } 15 | 16 | /** 17 | * The user content that started this invocation. READONLY field. 18 | */ 19 | get userContent(): Content | undefined { 20 | return this.invocationContext.userContent; 21 | } 22 | 23 | /** 24 | * The current invocation id. 25 | */ 26 | get invocationId(): string { 27 | return this.invocationContext.invocationId; 28 | } 29 | 30 | /** 31 | * The name of the agent that is currently running. 32 | */ 33 | get agentName(): string { 34 | return this.invocationContext.agent.name; 35 | } 36 | 37 | /** 38 | * The state of the current session. READONLY field. 39 | */ 40 | get state(): Readonly { 41 | // Create a read-only wrapper around the state 42 | const sessionState = this.invocationContext.session.state; 43 | return new Proxy(sessionState, { 44 | set() { 45 | throw new Error('Cannot modify state through ReadonlyContext'); 46 | }, 47 | deleteProperty() { 48 | throw new Error('Cannot delete state properties through ReadonlyContext'); 49 | } 50 | }) as Readonly; 51 | } 52 | } -------------------------------------------------------------------------------- /src/tools/ExitLoopTool.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { FunctionTool } from './FunctionTool'; 4 | import { ToolContext } from './ToolContext'; 5 | import { ToolActions } from './toolActions'; 6 | 7 | /** 8 | * Function to exit a loop in an agent's execution 9 | * 10 | * @param params Parameters for the function (not used) 11 | * @param context The tool context 12 | * @returns A message confirming the loop exit 13 | */ 14 | export function exitLoop( 15 | params: Record, 16 | context: ToolContext 17 | ): Promise { 18 | // Set the escalate action on the context 19 | if (context.get) { 20 | const actions = (context.get('actions') || {}) as ToolActions; 21 | actions.escalate = true; 22 | context.set('actions', actions); 23 | } else if ((context as any).actions) { 24 | (context as any).actions.escalate = true; 25 | } 26 | 27 | return Promise.resolve('Exiting the loop as requested'); 28 | } 29 | 30 | /** 31 | * Tool for exiting loops in an agent's execution 32 | */ 33 | export class ExitLoopTool extends FunctionTool { 34 | /** 35 | * Creates a new exit loop tool 36 | */ 37 | constructor() { 38 | super({ 39 | name: 'exit_loop', 40 | description: 'Exits the loop. Call this function only when you are instructed to do so.', 41 | fn: exitLoop, 42 | functionDeclaration: { 43 | name: 'exit_loop', 44 | description: 'Exits the loop. Call this function only when you are instructed to do so.', 45 | parameters: { 46 | type: 'object', 47 | properties: {}, 48 | required: [] 49 | } 50 | } 51 | }); 52 | } 53 | } -------------------------------------------------------------------------------- /src/auth/AuthConfig.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { AuthCredential } from './AuthCredential'; 4 | import { AuthScheme } from './AuthSchemes'; 5 | 6 | /** 7 | * The auth config sent by tool asking client to collect auth credentials and 8 | * adk and client will help to fill in the response 9 | */ 10 | export interface AuthConfig { 11 | /** The auth scheme used to collect credentials */ 12 | authScheme: AuthScheme; 13 | /** 14 | * The raw auth credential used to collect credentials. The raw auth 15 | * credentials are used in some auth scheme that needs to exchange auth 16 | * credentials. e.g. OAuth2 and OIDC. For other auth scheme, it could be None. 17 | */ 18 | rawAuthCredential?: AuthCredential | null; 19 | /** 20 | * The exchanged auth credential used to collect credentials. adk and client 21 | * will work together to fill it. For those auth scheme that doesn't need to 22 | * exchange auth credentials, e.g. API key, service account etc. It's filled by 23 | * client directly. For those auth scheme that need to exchange auth credentials, 24 | * e.g. OAuth2 and OIDC, it's first filled by adk. If the raw credentials 25 | * passed by tool only has client id and client credential, adk will help to 26 | * generate the corresponding authorization uri and state and store the processed 27 | * credential in this field. If the raw credentials passed by tool already has 28 | * authorization uri, state, etc. then it's copied to this field. Client will use 29 | * this field to guide the user through the OAuth2 flow and fill auth response in 30 | * this field 31 | */ 32 | exchangedAuthCredential?: AuthCredential | null; 33 | } -------------------------------------------------------------------------------- /src/events/EventActions.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { AuthConfig } from '../auth/AuthConfig'; 4 | 5 | /** 6 | * Represents the actions attached to an event. 7 | */ 8 | export class EventActions { 9 | /** 10 | * If true, it won't call model to summarize function response. 11 | * Only used for function_response event. 12 | */ 13 | skipSummarization?: boolean; 14 | 15 | /** 16 | * Indicates that the event is updating the state with the given delta. 17 | */ 18 | stateDelta: Record = {}; 19 | 20 | /** 21 | * Indicates that the event is updating an artifact. key is the filename, 22 | * value is the version. 23 | */ 24 | artifactDelta: Record = {}; 25 | 26 | /** 27 | * If set, the event transfers to the specified agent. 28 | */ 29 | transferToAgent?: string; 30 | 31 | /** 32 | * The agent is escalating to a higher level agent. 33 | */ 34 | escalate?: boolean; 35 | 36 | /** 37 | * Will only be set by a tool response indicating tool request euc. 38 | * Map key is the function call id since one function call response (from model) 39 | * could correspond to multiple function calls. 40 | * Map value is the required auth config. 41 | */ 42 | requestedAuthConfigs: Map = new Map(); 43 | 44 | constructor(params: Partial = {}) { 45 | this.skipSummarization = params.skipSummarization; 46 | this.stateDelta = params.stateDelta || {}; 47 | this.artifactDelta = params.artifactDelta || {}; 48 | this.transferToAgent = params.transferToAgent; 49 | this.escalate = params.escalate; 50 | this.requestedAuthConfigs = params.requestedAuthConfigs || new Map(); 51 | } 52 | } -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": ["src/index.ts"], 3 | "entryPointStrategy": "expand", 4 | "out": "docs/api-reference", 5 | "includeVersion": true, 6 | "excludePrivate": true, 7 | "excludeProtected": false, 8 | "excludeExternals": true, 9 | "externalPattern": ["**/node_modules/**"], 10 | "readme": "README.md", 11 | "name": "ADK TypeScript API Reference", 12 | "sort": ["static-first", "alphabetical"], 13 | "categoryOrder": ["Core", "Agents", "Models", "Tools", "Sessions", "Memory", "*"], 14 | "cleanOutputDir": true, 15 | "validation": { 16 | "invalidLink": true, 17 | "notDocumented": true 18 | }, 19 | "visibilityFilters": { 20 | "protected": true, 21 | "private": false, 22 | "inherited": true, 23 | "external": false 24 | }, 25 | "plugin": ["typedoc-plugin-markdown"], 26 | "theme": "default", 27 | "treatWarningsAsErrors": false, 28 | "searchInComments": true, 29 | "disableSources": false, 30 | "sourceLinkTemplate": "https://github.com/njraladdin/adk-typescript/blob/main/{path}#L{line}", 31 | "navigationLinks": { 32 | "GitHub": "https://github.com/njraladdin/adk-typescript", 33 | "Examples": "https://github.com/njraladdin/adk-typescript/tree/main/examples" 34 | }, 35 | "sidebarLinks": { 36 | "Home": "https://github.com/njraladdin/adk-typescript#readme" 37 | }, 38 | "categorizeByGroup": true, 39 | "searchCategoryBoosts": { 40 | "Core": 1.5, 41 | "Agent": 1.3, 42 | "Tool": 1.2 43 | }, 44 | "jsDocCompatibility": true, 45 | "excludeNotDocumented": false, 46 | "groupOrder": [ 47 | "Classes", 48 | "Interfaces", 49 | "Enumerations", 50 | "Functions", 51 | "Variables", 52 | "TypeAliases" 53 | ] 54 | } -------------------------------------------------------------------------------- /src/models/BaseLlm.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { BaseLlmConnection } from './BaseLlmConnection'; 4 | import { LlmRequest } from './LlmRequest'; 5 | import { LlmResponse } from './LlmResponse'; 6 | 7 | /** 8 | * The BaseLlm abstract class. 9 | * 10 | * Attributes: 11 | * model: The name of the LLM, e.g. gemini-2.0-flash or gemini-2.0-flash-001. 12 | */ 13 | export abstract class BaseLlm { 14 | /** 15 | * The name of the LLM, e.g. gemini-2.0-flash or gemini-2.0-flash-001. 16 | */ 17 | model: string; 18 | 19 | constructor(model: string) { 20 | this.model = model; 21 | } 22 | 23 | /** 24 | * Returns a list of supported models in regex for LlmRegistry. 25 | */ 26 | static supportedModels(): string[] { 27 | return []; 28 | } 29 | 30 | /** 31 | * Generates one content from the given contents and tools. 32 | * 33 | * @param llmRequest The request to send to the LLM. 34 | * @param stream Whether to do streaming call. 35 | * @returns An async generator of LlmResponse. 36 | * 37 | * For non-streaming call, it will only yield one LlmResponse. 38 | * For streaming call, it may yield more than one response, but all yielded 39 | * responses should be treated as one by merging the parts list. 40 | */ 41 | abstract generateContentAsync( 42 | llmRequest: LlmRequest, 43 | stream?: boolean 44 | ): AsyncGenerator; 45 | 46 | /** 47 | * Creates a live connection to the LLM. 48 | * 49 | * @param llmRequest The request to send to the LLM. 50 | * @returns The connection to the LLM. 51 | */ 52 | connect(llmRequest: LlmRequest): BaseLlmConnection { 53 | throw new Error(`Live connection is not supported for ${this.model}.`); 54 | } 55 | } -------------------------------------------------------------------------------- /src/evaluation/Evaluator.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { Invocation } from './EvalCase'; 18 | 19 | export enum EvalStatus { 20 | PASSED = 1, 21 | FAILED = 2, 22 | NOT_EVALUATED = 3, 23 | } 24 | 25 | /** 26 | * Metric evaluation score per invocation. 27 | */ 28 | export interface PerInvocationResult { 29 | actualInvocation: Invocation; 30 | expectedInvocation: Invocation; 31 | score?: number; 32 | evalStatus: EvalStatus; 33 | } 34 | 35 | export interface EvaluationResult { 36 | /** Overall score, based on each invocation. */ 37 | overallScore?: number; 38 | 39 | /** Overall status, based on each invocation. */ 40 | overallEvalStatus: EvalStatus; 41 | 42 | perInvocationResults: PerInvocationResult[]; 43 | } 44 | 45 | /** 46 | * A metrics evaluator interface. 47 | */ 48 | export abstract class Evaluator { 49 | /** 50 | * Returns EvaluationResult after performing evaluations using actual and expected invocations. 51 | */ 52 | abstract evaluateInvocations( 53 | actualInvocations: Invocation[], 54 | expectedInvocations: Invocation[] 55 | ): EvaluationResult; 56 | } -------------------------------------------------------------------------------- /src/agents/ActiveStreamingTool.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Represents an active streaming tool that is running. 3 | */ 4 | export class ActiveStreamingTool { 5 | /** The task Promise */ 6 | task: Promise | Promise | null; 7 | 8 | /** Whether the task is done */ 9 | done: boolean = false; 10 | 11 | /** Whether the task has been cancelled */ 12 | cancelled: boolean = false; 13 | 14 | /** Function name */ 15 | name?: string; 16 | 17 | /** Function arguments */ 18 | args?: Record; 19 | 20 | /** Function ID */ 21 | id?: string; 22 | 23 | /** The result of the function, if completed */ 24 | result?: any; 25 | 26 | /** 27 | * Creates a new instance of ActiveStreamingTool. 28 | * 29 | * @param task The task Promise 30 | * @param options Additional options 31 | */ 32 | constructor( 33 | task: Promise | Promise | null, 34 | options?: { 35 | name?: string; 36 | args?: Record; 37 | id?: string; 38 | done?: boolean; 39 | cancelled?: boolean; 40 | } 41 | ) { 42 | this.task = task; 43 | 44 | if (options) { 45 | this.name = options.name; 46 | this.args = options.args; 47 | this.id = options.id; 48 | this.done = options.done || false; 49 | this.cancelled = options.cancelled || false; 50 | } 51 | } 52 | 53 | /** 54 | * Marks the streaming tool as completed. 55 | * 56 | * @param result The result of the function. 57 | */ 58 | complete(result: any): void { 59 | this.done = true; 60 | this.result = result; 61 | } 62 | 63 | /** 64 | * Marks the streaming tool as cancelled. 65 | */ 66 | cancel(): void { 67 | this.cancelled = true; 68 | } 69 | } -------------------------------------------------------------------------------- /todo.md: -------------------------------------------------------------------------------- 1 | TODO: 2 | - port all flows, done 3 | - problem with _preprocessAsync in BaseLlmFlow, it's not properly updating the llmRequest with proper request stuff, done 4 | - fix not runnign the function calls in the response, done 5 | - implement cli, run an agent from the cli, done 6 | - implement simple web ui, done 7 | - implement the same ui in the original python project (obfuscated js), done 8 | - fix message after the agnet responds with tool call being empty and causing 400 error, done 9 | - prepare npm package, done 10 | - make sure all cli commands work correctly (adk-ts create, run, web), done 11 | - convert docs examples to typescript, done 12 | - add README, done 13 | - update all docs, done 14 | - host docs on github pages, done 15 | - fix file names to typescript conventions, done git 16 | - fix docs : tutorials, done 17 | - fix linting, done 18 | - make unit tests pass, done 19 | - make integration tests pass, done 20 | - port changes from python project v0.3.0, done 21 | - port changes from python project v0.4.0, done 22 | - port all unit tests and fix them, done 23 | - publish as npm package and create github release, done 24 | - test quickstart doc examples, done 25 | - create simple agent that would detect commits on the original python project and create issues on the github repo, done 26 | - get project up to date with v0.5.0, done 27 | - fix output schema not being used, done 28 | - v1.0.0: get project up to date with v1.0.0, make agent interface more similar, add all sample agnets and run them, fix web ui, done 29 | - global install: remove global install 'npm -g' and each agent having it's own node_modules, done 30 | - fix issue with update session state, done 31 | 32 | - implement proper logging with winston 33 | -------------------------------------------------------------------------------- /src/evaluation/EvalSet.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { EvalCase } from './EvalCase'; 18 | 19 | /** 20 | * A set of eval cases. 21 | */ 22 | export interface EvalSet { 23 | /** 24 | * Unique identifier for the eval set. 25 | */ 26 | evalSetId: string; 27 | 28 | /** 29 | * Name of the dataset. 30 | */ 31 | name?: string; 32 | 33 | /** 34 | * Description of the dataset. 35 | */ 36 | description?: string; 37 | 38 | /** 39 | * List of eval cases in the dataset. Each case represents a single 40 | * interaction to be evaluated. 41 | */ 42 | evalCases: EvalCase[]; 43 | 44 | /** 45 | * The time at which this eval set was created. 46 | */ 47 | creationTimestamp: number; 48 | } 49 | 50 | /** 51 | * Factory function to create a default EvalSet instance. 52 | */ 53 | export function createEvalSet( 54 | evalSetId: string, 55 | overrides: Partial> = {} 56 | ): EvalSet { 57 | return { 58 | evalSetId, 59 | evalCases: [], 60 | creationTimestamp: Date.now() / 1000, // Convert to seconds to match Python timestamp format 61 | ...overrides 62 | }; 63 | } -------------------------------------------------------------------------------- /src/cli/utils/envs.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import * as fs from 'fs'; 4 | import * as path from 'path'; 5 | import * as dotenv from 'dotenv'; 6 | 7 | /** 8 | * Walk up directory structure until the specified file is found 9 | * 10 | * @param folder Starting folder to search from 11 | * @param filename Filename to look for 12 | * @returns Path to the file if found, empty string if not found 13 | */ 14 | function walkToRootUntilFound(folder: string, filename: string): string { 15 | const checkpath = path.join(folder, filename); 16 | if (fs.existsSync(checkpath) && fs.statSync(checkpath).isFile()) { 17 | return checkpath; 18 | } 19 | 20 | const parentFolder = path.dirname(folder); 21 | if (parentFolder === folder) { // reached the root 22 | return ''; 23 | } 24 | 25 | return walkToRootUntilFound(parentFolder, filename); 26 | } 27 | 28 | /** 29 | * Loads the .env file for the agent module 30 | * 31 | * @param agentName Name of the agent 32 | * @param agentParentFolder Parent folder of the agent 33 | * @param filename Name of the environment file, defaults to .env 34 | */ 35 | export function loadDotenvForAgent( 36 | agentName: string, 37 | agentParentFolder: string, 38 | filename: string = '.env' 39 | ): void { 40 | // Get the absolute path of the agent folder as starting point 41 | const startingFolder = path.resolve( 42 | path.join(agentParentFolder, agentName) 43 | ); 44 | 45 | const dotenvFilePath = walkToRootUntilFound(startingFolder, filename); 46 | 47 | if (dotenvFilePath) { 48 | dotenv.config({ path: dotenvFilePath, override: true }); 49 | console.log( 50 | `Loaded ${filename} file for ${agentName} at ${dotenvFilePath}` 51 | ); 52 | } else { 53 | console.log(`No ${filename} file found for ${agentName}`); 54 | } 55 | } -------------------------------------------------------------------------------- /src/models/BaseLlmConnection.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { Blob, Content } from './types'; 4 | import { LlmResponse } from './LlmResponse'; 5 | 6 | /** 7 | * The base class for a live model connection. 8 | */ 9 | export abstract class BaseLlmConnection { 10 | /** 11 | * Sends the conversation history to the model. 12 | * 13 | * You call this method right after setting up the model connection. 14 | * The model will respond if the last content is from user, otherwise it will 15 | * wait for new user input before responding. 16 | * 17 | * @param history The conversation history to send to the model. 18 | */ 19 | abstract sendHistory(history: Content[]): Promise; 20 | 21 | /** 22 | * Sends a user content to the model. 23 | * 24 | * The model will respond immediately upon receiving the content. 25 | * If you send function responses, all parts in the content should be function 26 | * responses. 27 | * 28 | * @param content The content to send to the model. 29 | */ 30 | abstract sendContent(content: Content): Promise; 31 | 32 | /** 33 | * Sends a chunk of audio or a frame of video to the model in realtime. 34 | * 35 | * The model may not respond immediately upon receiving the blob. It will do 36 | * voice activity detection and decide when to respond. 37 | * 38 | * @param blob The blob to send to the model. 39 | */ 40 | abstract sendRealtime(blob: Blob): Promise; 41 | 42 | /** 43 | * Receives the model response using the llm server connection. 44 | * 45 | * @returns An async generator yielding LlmResponse objects. 46 | */ 47 | abstract receive(): AsyncGenerator; 48 | 49 | /** 50 | * Closes the llm server connection. 51 | */ 52 | abstract close(): Promise; 53 | } -------------------------------------------------------------------------------- /tests/unittests/models/models.test.ts: -------------------------------------------------------------------------------- 1 | import { BaseLlm, LlmRegistry, LlmRequest } from '../../../src/models'; 2 | import { Gemini } from '../../../src/models/GoogleLlm'; 3 | import { Claude } from '../../../src/models/AnthropicLlm'; 4 | 5 | describe('LlmRegistry', () => { 6 | beforeAll(() => { 7 | // Register Gemini by default (like the Python implementation) 8 | LlmRegistry.register(Gemini); 9 | }); 10 | 11 | describe('Gemini model matching', () => { 12 | const geminiModels = [ 13 | 'gemini-2.0-flash', 14 | 'projects/123456/locations/us-central1/endpoints/123456', // finetuned vertex gemini endpoint 15 | 'projects/123456/locations/us-central1/publishers/google/models/gemini-2.0-flash-exp', // vertex gemini long name 16 | ]; 17 | 18 | test.each(geminiModels)('matches %s to Gemini class', (modelName) => { 19 | const modelClass = LlmRegistry.resolve(modelName); 20 | expect(modelClass).toBe(Gemini); 21 | }); 22 | }); 23 | 24 | describe('Claude model matching', () => { 25 | beforeAll(() => { 26 | // Register Claude for these tests 27 | LlmRegistry.register(Claude); 28 | }); 29 | 30 | const claudeModels = [ 31 | 'claude-3-5-haiku@20241022', 32 | 'claude-3-5-sonnet-v2@20241022', 33 | 'claude-3-5-sonnet@20240620', 34 | 'claude-3-haiku@20240307', 35 | 'claude-3-opus@20240229', 36 | 'claude-3-sonnet@20240229', 37 | ]; 38 | 39 | test.each(claudeModels)('matches %s to Claude class', (modelName) => { 40 | const modelClass = LlmRegistry.resolve(modelName); 41 | expect(modelClass).toBe(Claude); 42 | }); 43 | }); 44 | 45 | describe('Error handling', () => { 46 | test('throws error for non-existent model', () => { 47 | expect(() => { 48 | LlmRegistry.resolve('non-exist-model'); 49 | }).toThrow('Model non-exist-model not found.'); 50 | }); 51 | }); 52 | }); -------------------------------------------------------------------------------- /docs/deploy/index.md: -------------------------------------------------------------------------------- 1 | # Deploying Your Agent 2 | 3 | Once you've built and tested your agent using ADK TypeScript, 4 | the next step is to deploy it so it can be accessed, queried, and used in 5 | production or integrated with other applications. Deployment moves your agent 6 | from your local development machine to a scalable and reliable environment. 7 | 8 | Deploying your agent 9 | 10 | ## Deployment Options 11 | 12 | Your ADK TypeScript agent can be deployed to a range of different environments based 13 | on your needs for production readiness or custom flexibility: 14 | 15 | ### Agent Engine in Vertex AI 16 | 17 | [Agent Engine](agent-engine.md) is a fully managed auto-scaling service on Google Cloud 18 | specifically designed for deploying, managing, and scaling AI agents built with 19 | frameworks such as ADK TypeScript. It provides a serverless deployment experience with 20 | automatic scaling, high availability, and simple management. 21 | 22 | Learn more about [deploying your agent to Vertex AI Agent Engine](agent-engine.md). 23 | 24 | ### Cloud Run 25 | 26 | [Cloud Run](https://cloud.google.com/run) is a managed auto-scaling compute platform on 27 | Google Cloud that enables you to run your agent as a container-based 28 | application. It's a great option for when you need more control over your deployment 29 | while still benefiting from managed infrastructure. 30 | 31 | Learn more about [deploying your agent to Cloud Run](cloud-run.md). 32 | 33 | ### Google Kubernetes Engine (GKE) 34 | 35 | [GKE](https://cloud.google.com/kubernetes-engine) is Google Cloud's managed Kubernetes service 36 | that gives you the highest degree of control and customization for your agent deployment. This is 37 | ideal for complex production scenarios with specific networking, scaling, or integration requirements. 38 | 39 | Learn more about [deploying your agent to GKE](gke.md). 40 | -------------------------------------------------------------------------------- /docs/overrides/main.html: -------------------------------------------------------------------------------- 1 | 16 | 17 | {% extends "base.html" %} 18 | 19 | {% block extrahead %} 20 | 21 | Agent Development Kit 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | {% endblock %} 39 | -------------------------------------------------------------------------------- /src/tools/BaseToolset.ts: -------------------------------------------------------------------------------- 1 | import { BaseTool } from './BaseTool'; 2 | import { ReadonlyContext } from '../agents/ReadonlyContext'; 3 | 4 | /** 5 | * Protocol for a predicate that defines the interface to decide whether a 6 | * tool should be exposed to LLM. Toolset implementer could consider whether to 7 | * accept such instance in the toolset's constructor and apply the predicate in 8 | * getTools method. 9 | */ 10 | export interface ToolPredicate { 11 | /** 12 | * Decide whether the passed-in tool should be exposed to LLM based on the 13 | * current context. True if the tool is usable by the LLM. 14 | * 15 | * It's used to filter tools in the toolset. 16 | * 17 | * @param tool The tool to evaluate 18 | * @param readonlyContext Context used to filter tools 19 | * @returns True if the tool should be exposed to the LLM 20 | */ 21 | (tool: BaseTool, readonlyContext?: ReadonlyContext): boolean; 22 | } 23 | 24 | /** 25 | * Base class for toolset. 26 | * 27 | * A toolset is a collection of tools that can be used by an agent. 28 | */ 29 | export abstract class BaseToolset { 30 | /** 31 | * Return all tools in the toolset based on the provided context. 32 | * 33 | * @param readonlyContext Context used to filter tools available to the agent. 34 | * If undefined, all tools in the toolset are returned. 35 | * @returns A list of tools available under the specified context. 36 | */ 37 | abstract getTools(readonlyContext?: ReadonlyContext): Promise; 38 | 39 | /** 40 | * Performs cleanup and releases resources held by the toolset. 41 | * 42 | * NOTE: This method is invoked, for example, at the end of an agent server's 43 | * lifecycle or when the toolset is no longer needed. Implementations 44 | * should ensure that any open connections, files, or other managed 45 | * resources are properly released to prevent leaks. 46 | */ 47 | abstract close(): Promise; 48 | } -------------------------------------------------------------------------------- /docs/get-started/index.md: -------------------------------------------------------------------------------- 1 | 2 | # Get Started with ADK TypeScript 3 | 4 | Agent Development Kit (ADK) for TypeScript is designed to empower developers to build, manage, evaluate, and deploy AI-powered agents using TypeScript. It provides a robust and flexible environment for creating both conversational and non-conversational agents, capable of handling complex tasks and workflows. 5 | 6 |
7 | 8 | - :material-nodejs: **Installation** 9 | 10 | --- 11 | 12 | Install `adk-typescript` with `npm` or `yarn` and get set up in minutes. 13 | 14 | [:octicons-arrow-right-24: More information](installation.md) 15 | 16 | - :material-console-line: **Quickstart** 17 | 18 | --- 19 | 20 | Create your first ADK TypeScript agent with tools in minutes. 21 | 22 | [:octicons-arrow-right-24: More information](quickstart.md) 23 | 24 | - :material-web: **Quickstart (Streaming)** 25 | 26 | --- 27 | 28 | Create your first real-time streaming ADK TypeScript agent using Express.js and Socket.IO. 29 | 30 | [:octicons-arrow-right-24: More information](quickstart-streaming.md) 31 | 32 | - :material-account-group-outline: **Tutorial** 33 | 34 | --- 35 | 36 | Build your first ADK TypeScript multi-agent system with memory and safety features. 37 | 38 | [:octicons-arrow-right-24: More information](tutorial.md) 39 | 40 | - :material-rocket-launch-outline: **Discover sample agents** 41 | 42 | --- 43 | 44 | Discover sample agents for various use cases. *(Note: Current samples may primarily be in Python, but demonstrate concepts applicable to TypeScript.)* 45 | 46 | [:octicons-arrow-right-24: Discover adk-samples](https://github.com/google/adk-samples){:target="_blank"} 47 | 48 | - :material-graph: **About ADK TypeScript** 49 | 50 | --- 51 | 52 | Learn about the key components and concepts for building and deploying ADK TypeScript agents. 53 | 54 | [:octicons-arrow-right-24: More information](about.md) 55 | 56 |
-------------------------------------------------------------------------------- /src/cli/browser/assets/audio-processor.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | class AudioProcessor extends AudioWorkletProcessor { 18 | constructor() { 19 | super(); 20 | this.targetSampleRate = 22000; // Change to your desired rate 21 | this.originalSampleRate = sampleRate; // Browser's sample rate 22 | this.resampleRatio = this.originalSampleRate / this.targetSampleRate; 23 | } 24 | 25 | process(inputs, outputs, parameters) { 26 | const input = inputs[0]; 27 | if (input.length > 0) { 28 | let audioData = input[0]; // Get first channel's data 29 | 30 | if (this.resampleRatio !== 1) { 31 | audioData = this.resample(audioData); 32 | } 33 | 34 | this.port.postMessage(audioData); 35 | } 36 | return true; // Keep processor alive 37 | } 38 | 39 | resample(audioData) { 40 | const newLength = Math.round(audioData.length / this.resampleRatio); 41 | const resampled = new Float32Array(newLength); 42 | 43 | for (let i = 0; i < newLength; i++) { 44 | const srcIndex = Math.floor(i * this.resampleRatio); 45 | resampled[i] = audioData[srcIndex]; // Nearest neighbor resampling 46 | } 47 | return resampled; 48 | } 49 | } 50 | 51 | registerProcessor('audio-processor', AudioProcessor); 52 | -------------------------------------------------------------------------------- /src/memory/MemoryEntry.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { Content } from '../types'; 18 | 19 | /** 20 | * Represent one memory entry. 21 | */ 22 | export interface MemoryEntry { 23 | /** 24 | * The main content of the memory. 25 | */ 26 | content: Content; 27 | 28 | /** 29 | * The author of the memory. 30 | */ 31 | author?: string; 32 | 33 | /** 34 | * The timestamp when the original content of this memory happened. 35 | * 36 | * This string will be forwarded to LLM. Preferred format is ISO 8601 format. 37 | */ 38 | timestamp?: string; 39 | } 40 | 41 | /** 42 | * Implementation of MemoryEntry as a class for cases where you need to instantiate it. 43 | */ 44 | export class MemoryEntryImpl implements MemoryEntry { 45 | content: Content; 46 | author?: string; 47 | timestamp?: string; 48 | 49 | constructor(data: { 50 | content: Content; 51 | author?: string; 52 | timestamp?: string; 53 | }) { 54 | this.content = data.content; 55 | this.author = data.author; 56 | this.timestamp = data.timestamp; 57 | } 58 | 59 | /** 60 | * Create a MemoryEntry with the current timestamp. 61 | */ 62 | static withCurrentTimestamp(content: Content, author?: string): MemoryEntryImpl { 63 | return new MemoryEntryImpl({ 64 | content, 65 | author, 66 | timestamp: new Date().toISOString() 67 | }); 68 | } 69 | } -------------------------------------------------------------------------------- /src/tools/TransferToAgentTool.ts: -------------------------------------------------------------------------------- 1 | import { FunctionTool } from './FunctionTool'; 2 | import { ToolContext } from './ToolContext'; 3 | 4 | /** 5 | * Interface for tool actions 6 | */ 7 | interface ToolActions { 8 | transferToAgent?: string; 9 | [key: string]: any; 10 | } 11 | 12 | /** 13 | * Function to transfer control to another agent 14 | * 15 | * @param params Parameters for the function 16 | * @param params.agentName The name of the agent to transfer to 17 | * @param context The tool context 18 | * @returns A message about the transfer 19 | */ 20 | export function transferToAgent( 21 | params: Record, 22 | context: ToolContext 23 | ): Promise { 24 | const agentName = params.agentName; 25 | 26 | // Set the transfer_to_agent action on the context if available 27 | if (context.get) { 28 | const actions = (context.get('actions') || {}) as ToolActions; 29 | actions.transferToAgent = agentName; 30 | context.set('actions', actions); 31 | } else if ((context as any).actions) { 32 | (context as any).actions.transferToAgent = agentName; 33 | } 34 | 35 | return Promise.resolve(`Transferring to agent: ${agentName}`); 36 | } 37 | 38 | /** 39 | * Tool for transferring control to another agent 40 | */ 41 | export class TransferToAgentTool extends FunctionTool { 42 | /** 43 | * Creates a new transfer to agent tool 44 | */ 45 | constructor() { 46 | super({ 47 | name: 'transfer_to_agent', 48 | description: 'Transfers the question to another agent', 49 | fn: transferToAgent, 50 | functionDeclaration: { 51 | name: 'transfer_to_agent', 52 | description: 'Transfers the question to another agent', 53 | parameters: { 54 | type: 'object', 55 | properties: { 56 | agentName: { 57 | type: 'string', 58 | description: 'The name of the agent to transfer to' 59 | } 60 | }, 61 | required: ['agentName'] 62 | } 63 | } 64 | }); 65 | } 66 | } -------------------------------------------------------------------------------- /src/artifacts/BaseArtifactService.ts: -------------------------------------------------------------------------------- 1 | import { Part } from '../models/types'; 2 | 3 | /** 4 | * Parameters for artifact operations. 5 | */ 6 | export interface ArtifactParams { 7 | /** The application name */ 8 | appName: string; 9 | 10 | /** The user ID */ 11 | userId: string; 12 | 13 | /** The session ID */ 14 | sessionId: string; 15 | 16 | /** The filename of the artifact */ 17 | filename: string; 18 | 19 | /** The version of the artifact (for load operations) */ 20 | version?: number; 21 | 22 | /** The artifact content (for save operations) */ 23 | artifact?: Part; 24 | } 25 | 26 | /** 27 | * Interface for artifact services. 28 | * Artifact services provide functionality to store and retrieve artifacts. 29 | */ 30 | export interface BaseArtifactService { 31 | /** 32 | * Loads an artifact. 33 | * 34 | * @param params The artifact parameters 35 | * @returns The loaded artifact, or undefined if not found 36 | */ 37 | loadArtifact(params: ArtifactParams): Promise; 38 | 39 | /** 40 | * Saves an artifact. 41 | * 42 | * @param params The artifact parameters 43 | * @returns The version of the saved artifact 44 | */ 45 | saveArtifact(params: ArtifactParams): Promise; 46 | 47 | /** 48 | * Deletes an artifact. 49 | * 50 | * @param params The artifact parameters 51 | * @returns A promise that resolves when the operation is complete 52 | */ 53 | deleteArtifact(params: ArtifactParams): Promise; 54 | 55 | /** 56 | * Lists all the artifact filenames within a session. 57 | * 58 | * @param params The artifact parameters 59 | * @returns A list of all artifact filenames within a session 60 | */ 61 | listArtifactKeys(params: ArtifactParams): Promise; 62 | 63 | /** 64 | * Lists all versions of an artifact. 65 | * 66 | * @param params The artifact parameters 67 | * @returns A list of all available versions of the artifact 68 | */ 69 | listVersions(params: ArtifactParams): Promise; 70 | } -------------------------------------------------------------------------------- /tests/integration/fixture/ecommerce_customer_service_agent/order_query.test.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "query": "Send an email to user user_a whose email address is alice@example.com", 4 | "expected_tool_use": [ 5 | { 6 | "tool_name": "send_email", 7 | "tool_input": { 8 | "email": "alice@example.com", 9 | "user_id": "user_a" 10 | } 11 | } 12 | ], 13 | "reference": "Email sent to alice@example.com for user id user_a." 14 | }, 15 | { 16 | "query": "Can you tell me the status of my order with ID 1?", 17 | "expected_tool_use": [ 18 | { 19 | "tool_name": "get_order_status", 20 | "tool_input": { 21 | "order_id": "1" 22 | } 23 | } 24 | ], 25 | "reference": "Your order with ID 1 is FINISHED." 26 | }, 27 | { 28 | "query": "Cancel all pending order for the user with user id user_a", 29 | "expected_tool_use": [ 30 | { 31 | "tool_name": "get_order_ids_for_user", 32 | "tool_input": { 33 | "user_id": "user_a" 34 | } 35 | }, 36 | { 37 | "tool_name": "get_order_status", 38 | "tool_input": { 39 | "order_id": "1" 40 | } 41 | }, 42 | { 43 | "tool_name": "get_order_status", 44 | "tool_input": { 45 | "order_id": "4" 46 | } 47 | }, 48 | { 49 | "tool_name": "cancel_order", 50 | "tool_input": { 51 | "order_id": "4" 52 | } 53 | } 54 | ], 55 | "reference": "I have checked your orders and order 4 was in pending status, so I have cancelled it. Order 1 was already finished and couldn't be cancelled.\n" 56 | }, 57 | { 58 | "query": "What orders have I placed under the username user_b?", 59 | "expected_tool_use": [ 60 | { 61 | "tool_name": "get_order_ids_for_user", 62 | "tool_input": { 63 | "user_id": "user_b" 64 | } 65 | } 66 | ], 67 | "reference": "User user_b has placed one order with order ID 2.\n" 68 | } 69 | ] 70 | -------------------------------------------------------------------------------- /src/cli/utils/init.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { BaseAgent } from '../../agents/BaseAgent'; 4 | import { LlmAgent } from '../../agents/LlmAgent'; 5 | 6 | /** 7 | * Recursively creates an empty state for all state variables 8 | * referenced in agent instructions 9 | * 10 | * @param agent The agent to process 11 | * @param allState Object to collect the state variables in 12 | */ 13 | function _createEmptyState(agent: BaseAgent, allState: Record): void { 14 | // Process sub-agents first 15 | for (const subAgent of agent.subAgents) { 16 | _createEmptyState(subAgent, allState); 17 | } 18 | 19 | // If this is an LLM agent with an instruction that has variables, add them to the state 20 | if (agent instanceof LlmAgent && agent.instruction && typeof agent.instruction === 'string') { 21 | // Find all variables in the format {variableName} 22 | const matches = agent.instruction.match(/{([\w]+)}/g) || []; 23 | 24 | for (const match of matches) { 25 | // Extract the variable name without the braces 26 | const key = match.substring(1, match.length - 1); 27 | allState[key] = ''; 28 | } 29 | } 30 | } 31 | 32 | /** 33 | * Creates empty strings for all non-initialized state variables 34 | * referenced in agent instructions 35 | * 36 | * @param agent The root agent to process 37 | * @param initializedStates Optional object with already initialized states 38 | * @returns An object with empty strings for all non-initialized state variables 39 | */ 40 | export function createEmptyState( 41 | agent: BaseAgent, 42 | initializedStates: Record = {} 43 | ): Record { 44 | const nonInitializedStates: Record = {}; 45 | 46 | // Find all state variables in the agent hierarchy 47 | _createEmptyState(agent, nonInitializedStates); 48 | 49 | // Remove any variables that are already initialized 50 | for (const key in initializedStates) { 51 | if (key in nonInitializedStates) { 52 | delete nonInitializedStates[key]; 53 | } 54 | } 55 | 56 | return nonInitializedStates; 57 | } -------------------------------------------------------------------------------- /src/code-executors/BaseCodeExecutor.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { InvocationContext } from "../agents/InvocationContext"; 4 | import { CodeExecutionInput, CodeExecutionResult } from "./CodeExecutionUtils"; 5 | 6 | /** 7 | * Abstract base class for all code executors. 8 | * 9 | * The code executor allows the agent to execute code blocks from model responses 10 | * and incorporate the execution results into the final response. 11 | */ 12 | export abstract class BaseCodeExecutor { 13 | /** 14 | * If true, extract and process data files from the model request 15 | * and attach them to the code executor. 16 | * Supported data file MimeTypes are [text/csv]. 17 | * 18 | * Default to false. 19 | */ 20 | optimizeDataFile: boolean = false; 21 | 22 | /** 23 | * Whether the code executor is stateful. Default to false. 24 | */ 25 | stateful: boolean = false; 26 | 27 | /** 28 | * The number of attempts to retry on consecutive code execution errors. Default to 2. 29 | */ 30 | errorRetryAttempts: number = 2; 31 | 32 | /** 33 | * The list of the enclosing delimiters to identify the code blocks. 34 | * For example, the delimiter ['```python\n', '\n```'] can be 35 | * used to identify code blocks with the following format: 36 | * 37 | * ```python 38 | * print("hello") 39 | * ``` 40 | */ 41 | codeBlockDelimiters: [string, string][] = [ 42 | ['```tool_code\n', '\n```'], 43 | ['```python\n', '\n```'], 44 | ]; 45 | 46 | /** 47 | * The delimiters to format the code execution result. 48 | */ 49 | executionResultDelimiters: [string, string] = ['```tool_output\n', '\n```']; 50 | 51 | /** 52 | * Executes code and return the code execution result. 53 | * 54 | * @param invocationContext - The invocation context of the code execution. 55 | * @param codeExecutionInput - The code execution input. 56 | * @returns The code execution result. 57 | */ 58 | abstract executeCode( 59 | invocationContext: InvocationContext, 60 | codeExecutionInput: CodeExecutionInput, 61 | ): Promise; 62 | } -------------------------------------------------------------------------------- /src/agents/LiveRequestQueue.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * A queue for live requests in streaming sessions. 5 | */ 6 | import { Content } from '../models/types'; 7 | 8 | /** 9 | * Represents a live request within a streaming session. 10 | */ 11 | export interface LiveRequest { 12 | /** Indicates if the connection should be closed */ 13 | close?: boolean; 14 | 15 | /** Binary blob data for audio/video streaming */ 16 | blob?: Uint8Array; 17 | 18 | /** Content data */ 19 | content?: Content; 20 | } 21 | 22 | /** 23 | * A queue for managing live requests in streaming sessions. 24 | */ 25 | export class LiveRequestQueue { 26 | private queue: LiveRequest[] = []; 27 | private resolvers: ((request: LiveRequest) => void)[] = []; 28 | 29 | /** 30 | * Adds a request to close the connection to the queue. 31 | */ 32 | sendClose(): void { 33 | this.enqueue({ close: true }); 34 | } 35 | 36 | /** 37 | * Adds a blob request to the queue. 38 | * 39 | * @param blob The binary blob data 40 | */ 41 | sendBlob(blob: Uint8Array): void { 42 | this.enqueue({ blob }); 43 | } 44 | 45 | /** 46 | * Adds a content request to the queue. 47 | * 48 | * @param content The content data 49 | */ 50 | sendContent(content: Content): void { 51 | this.enqueue({ content }); 52 | } 53 | 54 | /** 55 | * Gets the next request from the queue. 56 | * 57 | * @returns A promise that resolves to the next request 58 | */ 59 | async get(): Promise { 60 | if (this.queue.length > 0) { 61 | return this.queue.shift()!; 62 | } 63 | 64 | return new Promise((resolve) => { 65 | this.resolvers.push(resolve); 66 | }); 67 | } 68 | 69 | /** 70 | * Adds a request to the queue. 71 | * 72 | * @param request The request to add 73 | */ 74 | private enqueue(request: LiveRequest): void { 75 | if (this.resolvers.length > 0) { 76 | const resolve = this.resolvers.shift()!; 77 | resolve(request); 78 | } else { 79 | this.queue.push(request); 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /.github/workflows/publish-docs.yml: -------------------------------------------------------------------------------- 1 | name: Publish Docs 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - main 7 | permissions: 8 | contents: write 9 | jobs: 10 | deploy: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Configure Git Credentials 15 | run: | 16 | git config user.name github-actions[bot] 17 | git config user.email 41898282+github-actions[bot]@users.noreply.github.com 18 | 19 | # Set up Node.js for TypeDoc generation 20 | # Note: We need Node.js 20+ for some of our dev dependencies 21 | - name: Setup Node.js 22 | uses: actions/setup-node@v4 23 | with: 24 | node-version: '20' 25 | cache: 'npm' 26 | 27 | # Install dependencies and generate TypeDoc documentation 28 | # Note: We generate the API reference docs during the build process 29 | # rather than committing them to the repository 30 | 31 | # Now set up Python for MkDocs 32 | - uses: actions/setup-python@v5 33 | with: 34 | python-version: 3.x 35 | - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV 36 | - uses: actions/cache@v4 37 | with: 38 | key: mkdocs-material-${{ env.cache_id }} 39 | path: .cache 40 | restore-keys: | 41 | mkdocs-material- 42 | - name: Install MkDocs dependencies 43 | run: | 44 | pip install mkdocs-material mkdocs-redirects 45 | - name: Debug directory structure 46 | run: | 47 | echo "Checking if overrides directory exists:" 48 | if [ -d "docs/overrides" ]; then 49 | echo "✅ docs/overrides directory exists" 50 | ls -la docs/overrides/ 51 | else 52 | echo "❌ docs/overrides directory does not exist" 53 | echo "Creating it..." 54 | mkdir -p docs/overrides 55 | echo "{% extends 'base.html' %}" > docs/overrides/main.html 56 | fi 57 | - name: Deploy documentation 58 | run: mkdocs gh-deploy --force -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Utils module - Provides utility functions and helpers 3 | */ 4 | 5 | /** 6 | * Format a date to ISO string 7 | * @param date Date to format 8 | * @returns Formatted date string 9 | */ 10 | export function formatDate(date: Date = new Date()): string { 11 | return date.toISOString(); 12 | } 13 | 14 | /** 15 | * Sleep for a specified number of milliseconds 16 | * @param ms Milliseconds to sleep 17 | * @returns Promise that resolves after the specified time 18 | */ 19 | export function sleep(ms: number): Promise { 20 | return new Promise(resolve => setTimeout(resolve, ms)); 21 | } 22 | 23 | /** 24 | * Deep clone an object 25 | * @param obj Object to clone 26 | * @returns Cloned object 27 | */ 28 | export function deepClone(obj: T): T { 29 | return JSON.parse(JSON.stringify(obj)); 30 | } 31 | 32 | /** 33 | * Generate a random ID 34 | * @param length Length of the ID (default: 10) 35 | * @returns Random ID string 36 | */ 37 | export function generateId(length: number = 10): string { 38 | const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 39 | let result = ''; 40 | 41 | for (let i = 0; i < length; i++) { 42 | result += characters.charAt(Math.floor(Math.random() * characters.length)); 43 | } 44 | 45 | return result; 46 | } 47 | 48 | /** 49 | * Logger utility 50 | */ 51 | export class Logger { 52 | private prefix: string; 53 | 54 | constructor(prefix: string = 'ADK') { 55 | this.prefix = prefix; 56 | } 57 | 58 | /** 59 | * Log an informational message 60 | * @param message Message to log 61 | */ 62 | info(message: string): void { 63 | console.log(`[${this.prefix}] [INFO] ${message}`); 64 | } 65 | 66 | /** 67 | * Log a warning message 68 | * @param message Message to log 69 | */ 70 | warn(message: string): void { 71 | console.warn(`[${this.prefix}] [WARN] ${message}`); 72 | } 73 | 74 | /** 75 | * Log an error message 76 | * @param message Message to log 77 | */ 78 | error(message: string): void { 79 | console.error(`[${this.prefix}] [ERROR] ${message}`); 80 | } 81 | } -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * ADK TypeScript - Agent Development Kit 3 | * A TypeScript port of Google's ADK Python library 4 | * 5 | * This is the main entry point for the ADK library. 6 | * Provides both flat direct exports and namespaced exports, 7 | * similar to Python's import structure. 8 | * 9 | * Users can import in different ways: 10 | * 11 | * 1. Default import: 12 | * import ADK from 'adk-typescript'; 13 | * const agent = new ADK.agents.LlmAgent({...}); 14 | * 15 | * 2. Named module import (like Python): 16 | * import { agents, tools } from 'adk-typescript'; 17 | * const agent = new agents.LlmAgent({...}); 18 | * const tool = new tools.FunctionTool({...}); 19 | * 20 | * 3. Direct component import: 21 | * import { LlmAgent } from 'adk-typescript/agents'; 22 | * import { FunctionTool } from 'adk-typescript/tools'; 23 | */ 24 | 25 | // Import version 26 | import { VERSION } from './version'; 27 | 28 | // Import modules to re-export 29 | import * as agentsModule from './agents'; 30 | import * as toolsModule from './tools'; 31 | import * as modelsModule from './models'; 32 | import * as flowsModule from './flows'; 33 | import * as runnersModule from './runners'; 34 | import * as sessionsModule from './sessions'; 35 | import * as memoryModule from './memory'; 36 | import * as utilsModule from './utils'; 37 | 38 | // Export version 39 | export { VERSION }; 40 | 41 | // Import CLI utilities 42 | export { runAgent } from './cli/runAgent'; 43 | 44 | // Export namespaced modules (similar to Python's import structure) 45 | export const agents = agentsModule; 46 | export const tools = toolsModule; 47 | export const models = modelsModule; 48 | export const flows = flowsModule; 49 | export const runners = runnersModule; 50 | export const sessions = sessionsModule; 51 | export const memory = memoryModule; 52 | export const utils = utilsModule; 53 | 54 | // Export the default object for default imports 55 | export default { 56 | // Version information 57 | VERSION, 58 | 59 | // Namespaced modules (organized access) 60 | agents, 61 | tools, 62 | models, 63 | flows, 64 | runners, 65 | sessions, 66 | memory, 67 | utils 68 | }; -------------------------------------------------------------------------------- /src/tools/application-integration-tool/IntegrationClient.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * Client for Integration service 19 | */ 20 | export class IntegrationClient { 21 | /** 22 | * Creates a new IntegrationClient 23 | * 24 | * @param project GCP project ID 25 | * @param location GCP location 26 | * @param integration Integration name 27 | * @param triggers List of trigger names 28 | * @param connection Connection name 29 | * @param entityOperations List of entity operations 30 | * @param actions List of actions 31 | * @param serviceAccountJson Service account JSON credentials 32 | */ 33 | constructor( 34 | private readonly project: string, 35 | private readonly location: string, 36 | private readonly integration: string | null, 37 | private readonly triggers: string[] | null, 38 | private readonly connection: string | null, 39 | private readonly entityOperations: string[] | null, 40 | private readonly actions: string[] | null, 41 | private readonly serviceAccountJson: string | null 42 | ) {} 43 | 44 | /** 45 | * Gets OpenAPI spec for the integration 46 | * @returns OpenAPI spec 47 | */ 48 | getOpenApiSpecForIntegration(): any { 49 | return { openapi: '3.0.0' }; 50 | } 51 | 52 | /** 53 | * Gets OpenAPI spec for the connection 54 | * @param toolName Tool name 55 | * @param toolInstructions Tool instructions 56 | * @returns OpenAPI spec 57 | */ 58 | getOpenApiSpecForConnection(toolName: string, toolInstructions: string): any { 59 | return { openapi: '3.0.0' }; 60 | } 61 | } -------------------------------------------------------------------------------- /src/cli/HelpfulCommand.ts: -------------------------------------------------------------------------------- 1 | import { Command } from 'commander'; 2 | 3 | /** 4 | * Command that shows full help on error instead of just the error message. 5 | * 6 | * A custom Commander Command class that overrides the default error handling 7 | * behavior to display the full help text when a required argument is missing, 8 | * followed by the error message. This provides users with better context 9 | * about command usage without needing to run a separate --help command. 10 | */ 11 | export class HelpfulCommand extends Command { 12 | /** 13 | * Override the error handling to show help text before errors. 14 | * This is called when Commander encounters an error during parsing. 15 | */ 16 | error(message: string, options?: { exitCode?: number; code?: string }): never { 17 | // Show the help text first 18 | this.outputHelp(); 19 | 20 | // Then show the error message in red 21 | console.error(`\n\x1b[31mError: ${message}\x1b[0m`); 22 | 23 | // Exit with error code (defaults to 1, but can be overridden) 24 | process.exit(options?.exitCode ?? 1); 25 | } 26 | 27 | /** 28 | * Override the missing argument handling to show help text. 29 | * This is called when a required argument is missing. 30 | */ 31 | missingArgument(name: string): never { 32 | // Show the help text first 33 | this.outputHelp(); 34 | 35 | // Then show the missing argument error in red 36 | console.error(`\n\x1b[31mError: Missing required argument '${name}'\x1b[0m`); 37 | 38 | // Exit with error code 2 (similar to Python's ctx.exit(2)) 39 | process.exit(2); 40 | } 41 | 42 | /** 43 | * Override parse to catch missing arguments and show help. 44 | */ 45 | parse(argv?: readonly string[], options?: { from: 'node' | 'electron' | 'user' }): this { 46 | try { 47 | return super.parse(argv, options); 48 | } catch (error: any) { 49 | // If it's a missing argument error, show help 50 | if (error.code === 'commander.missingArgument') { 51 | this.missingArgument(error.argument); 52 | } 53 | 54 | // If it's any other error, use our custom error handler 55 | this.error(error.message); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /src/tools/retrieval/LlamaIndexRetrieval.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { BaseRetrievalTool, RetrievalToolOptions } from './BaseRetrievalTool'; 4 | import { ToolContext } from '../ToolContext'; 5 | 6 | /** 7 | * Options for creating a LlamaIndex retrieval tool 8 | */ 9 | export interface LlamaIndexRetrievalOptions extends RetrievalToolOptions { 10 | /** 11 | * The LlamaIndex retriever to use 12 | */ 13 | retriever: any; // Type as any for now, would be properly typed if LlamaIndex had TypeScript definitions 14 | } 15 | 16 | /** 17 | * Retrieval tool that uses LlamaIndex to retrieve information 18 | */ 19 | export class LlamaIndexRetrieval extends BaseRetrievalTool { 20 | /** 21 | * The LlamaIndex retriever 22 | */ 23 | private retriever: any; 24 | 25 | /** 26 | * Create a new LlamaIndex retrieval tool 27 | * 28 | * @param options Options for the LlamaIndex retrieval tool 29 | */ 30 | constructor(options: LlamaIndexRetrievalOptions) { 31 | super(options); 32 | this.retriever = options.retriever; 33 | } 34 | 35 | /** 36 | * Retrieve information using LlamaIndex 37 | * 38 | * @param query The query to retrieve information for 39 | * @param maxResults Maximum number of results to return 40 | * @param context The context for the retrieval 41 | * @returns The retrieved information 42 | */ 43 | protected async retrieve( 44 | query: string, 45 | maxResults: number, 46 | context: ToolContext 47 | ): Promise { 48 | try { 49 | // This is a simplified implementation since we don't have direct LlamaIndex bindings in TypeScript 50 | // In a real implementation, this would use the LlamaIndex TypeScript bindings if available 51 | const results = await this.retriever.retrieve(query); 52 | 53 | if (!results || results.length === 0) { 54 | return "No results found"; 55 | } 56 | 57 | // Return the first result's text 58 | // In a real implementation with TypeScript bindings, this would be properly typed 59 | return results[0].text; 60 | } catch (error: unknown) { 61 | console.error("Error retrieving from LlamaIndex:", error); 62 | return `Error retrieving information: ${error instanceof Error ? error.message : String(error)}`; 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /src/flows/llm_flows/AutoFlow.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Implementation of AutoFlow. 3 | */ 4 | import * as agentTransfer from './agentTransfer'; 5 | import { SingleFlow } from './SingleFlow'; 6 | import { BaseLlmRequestProcessor, BaseLlmResponseProcessor } from './BaseLlmProcessor'; 7 | 8 | /** 9 | * AutoFlow is SingleFlow with agent transfer capability. 10 | * 11 | * Agent transfer is allowed in the following directions: 12 | * 13 | * 1. from parent to sub-agent; 14 | * 2. from sub-agent to parent; 15 | * 3. from sub-agent to its peer agents; 16 | * 17 | * For peer-agent transfers, it's only enabled when all below conditions are met: 18 | * 19 | * - The parent agent is also of AutoFlow; 20 | * - `allowTransferToPeer` option of this agent is not set to false (default is true). 21 | * 22 | * Depending on the target agent flow type, the transfer may be automatically 23 | * reversed. The condition is as below: 24 | * 25 | * - If the flow type of the transferee agent is also auto, transferee agent will 26 | * remain as the active agent. The transferee agent will respond to the user's 27 | * next message directly. 28 | * - If the flow type of the transferee agent is not auto, the active agent will 29 | * be reversed back to previous agent. 30 | * 31 | * Note: AutoFlow inherits _runOneStepAsync from BaseLlmFlow via SingleFlow, which 32 | * properly handles function calls including transfer_to_agent. This matches the 33 | * Python implementation where both classes inherit this method from BaseLlmFlow. 34 | */ 35 | export class AutoFlow extends SingleFlow { 36 | /** 37 | * Creates a new AutoFlow instance with agent transfer capability. 38 | * 39 | * @param additionalRequestProcessors Additional request processors to use 40 | * @param additionalResponseProcessors Additional response processors to use 41 | */ 42 | constructor( 43 | additionalRequestProcessors: BaseLlmRequestProcessor[] = [], 44 | additionalResponseProcessors: BaseLlmResponseProcessor[] = [] 45 | ) { 46 | // Pass the additional processors to the parent constructor 47 | super(additionalRequestProcessors, additionalResponseProcessors); 48 | 49 | // Add the agent transfer processor to enable transfer capabilities 50 | this.requestProcessors.push(agentTransfer.requestProcessor); 51 | } 52 | } -------------------------------------------------------------------------------- /tests/integration/singleAgent.test.ts: -------------------------------------------------------------------------------- 1 | import { setBackendEnvironment, restoreBackendEnvironment } from './testConfig'; 2 | import { AgentEvaluator } from '../../src/evaluation'; 3 | import { rootAgent } from './fixture/home_automation_agent/agent'; 4 | 5 | /** 6 | * This test checks basic agent evaluation functionality 7 | */ 8 | describe('Single Agent Tests', () => { 9 | // Run tests against both backends if configured 10 | const backends: ('GOOGLE_AI' | 'VERTEX')[] = 11 | process.env.TEST_BACKEND === 'BOTH' 12 | ? ['GOOGLE_AI', 'VERTEX'] 13 | : [process.env.TEST_BACKEND as 'GOOGLE_AI' | 'VERTEX']; 14 | 15 | backends.forEach(backend => { 16 | describe(`Using ${backend} backend`, () => { 17 | let originalBackend: string | undefined; 18 | 19 | beforeAll(() => { 20 | originalBackend = setBackendEnvironment(backend); 21 | }); 22 | 23 | afterAll(() => { 24 | restoreBackendEnvironment(originalBackend); 25 | }); 26 | 27 | it('should evaluate an agent with test dataset', async () => { 28 | // Skip this test if environment not properly configured 29 | if (backend === 'GOOGLE_AI' && !process.env.GOOGLE_API_KEY) { 30 | console.warn('Skipping test: GOOGLE_API_KEY not set'); 31 | return; 32 | } 33 | 34 | if (backend === 'VERTEX' && (!process.env.GOOGLE_CLOUD_PROJECT || !process.env.GOOGLE_CLOUD_LOCATION)) { 35 | console.warn('Skipping test: GOOGLE_CLOUD_PROJECT or GOOGLE_CLOUD_LOCATION not set'); 36 | return; 37 | } 38 | 39 | try { 40 | // Mimic the Python test but with TypeScript paths 41 | const results = await AgentEvaluator.evaluate({ 42 | agent: rootAgent, 43 | evalDatasetFilePathOrDir: 'tests/integration/fixture/home_automation_agent/simple_test.test.json', 44 | numRuns: 4 45 | }); 46 | 47 | // Assert that results were generated correctly 48 | expect(results).toBeDefined(); 49 | expect(results.length).toBe(4); 50 | expect(results.every(result => result.success)).toBe(true); 51 | } catch (error) { 52 | console.error('Test failed with error:', error); 53 | throw error; 54 | } 55 | }); 56 | }); 57 | }); 58 | }); -------------------------------------------------------------------------------- /src/agents/RunConfig.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Available streaming modes for agent responses 3 | */ 4 | export enum StreamingMode { 5 | /** 6 | * No streaming - wait for complete response 7 | */ 8 | NONE = 'NONE', 9 | 10 | /** 11 | * Server-Sent Events streaming 12 | */ 13 | SSE = 'SSE', 14 | 15 | /** 16 | * WebSocket streaming 17 | */ 18 | WEBSOCKET = 'WEBSOCKET' 19 | } 20 | 21 | /** 22 | * Configuration for audio transcription 23 | */ 24 | export interface AudioTranscriptionConfig { 25 | /** 26 | * Whether to enable transcription 27 | */ 28 | enabled?: boolean; 29 | 30 | /** 31 | * Additional configuration options 32 | */ 33 | [key: string]: any; 34 | } 35 | 36 | /** 37 | * Configuration for running an agent 38 | */ 39 | export class RunConfig { 40 | /** 41 | * The streaming mode to use for agent responses 42 | */ 43 | streamingMode?: StreamingMode; 44 | 45 | /** 46 | * Whether to trace execution 47 | */ 48 | trace?: boolean; 49 | 50 | /** 51 | * Timeout in milliseconds 52 | */ 53 | timeoutMs?: number; 54 | 55 | /** 56 | * Specific tools to enable (if undefined, all tools are enabled) 57 | */ 58 | enabledTools?: string[]; 59 | 60 | /** 61 | * Specific tools to disable 62 | */ 63 | disabledTools?: string[]; 64 | 65 | /** 66 | * Whether to save input blobs as artifacts 67 | */ 68 | saveInputBlobsAsArtifacts?: boolean; 69 | 70 | /** 71 | * Whether to support code function calling 72 | */ 73 | supportCfc?: boolean; 74 | 75 | /** 76 | * Maximum number of LLM calls to make before raising an error 77 | */ 78 | maxLlmCalls?: number; 79 | 80 | /** 81 | * Response modalities for multi-modal output 82 | * e.g., ['TEXT', 'AUDIO'] 83 | */ 84 | responseModalities?: string[]; 85 | 86 | /** 87 | * Configuration for output audio transcription 88 | */ 89 | outputAudioTranscription?: AudioTranscriptionConfig; 90 | 91 | /** 92 | * Configuration for input audio transcription 93 | */ 94 | inputAudioTranscription?: AudioTranscriptionConfig; 95 | 96 | /** 97 | * Creates a new RunConfig with default values 98 | */ 99 | constructor(config: Partial = {}) { 100 | Object.assign(this, config); 101 | } 102 | } -------------------------------------------------------------------------------- /docs/agents/workflow-agents/index.md: -------------------------------------------------------------------------------- 1 | # Workflow Agents 2 | 3 | This section introduces "*workflow agents*" - **specialized agents that control the execution flow of its sub-agents**. 4 | 5 | Workflow agents are specialized components in ADK designed purely for **orchestrating the execution flow of sub-agents**. Their primary role is to manage *how* and *when* other agents run, defining the control flow of a process. 6 | 7 | Unlike [LLM Agents](../llm-agents.md), which use Large Language Models for dynamic reasoning and decision-making, Workflow Agents operate based on **predefined logic**. They determine the execution sequence according to their type (e.g., sequential, parallel, loop) without consulting an LLM for the orchestration itself. This results in **deterministic and predictable execution patterns**. 8 | 9 | ADK provides three core workflow agent types, each implementing a distinct execution pattern: 10 | 11 |
12 | 13 | - :material-console-line: **Sequential Agents** 14 | 15 | --- 16 | 17 | Executes sub-agents one after another, in **sequence**. 18 | 19 | [:octicons-arrow-right-24: Learn more](sequential-agents.md) 20 | 21 | - :material-console-line: **Loop Agents** 22 | 23 | --- 24 | 25 | **Repeatedly** executes its sub-agents until a specific termination condition is met. 26 | 27 | [:octicons-arrow-right-24: Learn more](loop-agents.md) 28 | 29 | - :material-console-line: **Parallel Agents** 30 | 31 | --- 32 | 33 | Executes multiple sub-agents in **parallel**. 34 | 35 | [:octicons-arrow-right-24: Learn more](parallel-agents.md) 36 | 37 |
38 | 39 | ## Why Use Workflow Agents? 40 | 41 | Workflow agents are essential when you need explicit control over how a series of tasks or agents are executed. They provide: 42 | 43 | * **Predictability:** The flow of execution is guaranteed based on the agent type and configuration. 44 | * **Reliability:** Ensures tasks run in the required order or pattern consistently. 45 | * **Structure:** Allows you to build complex processes by composing agents within clear control structures. 46 | 47 | While the workflow agent manages the control flow deterministically, the sub-agents it orchestrates can themselves be any type of agent, including intelligent `LlmAgent` instances. This allows you to combine structured process control with flexible, LLM-powered task execution. 48 | -------------------------------------------------------------------------------- /src/cli/browser/assets/ADK-512-color.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/tools/openapi-tool/openapi-spec-parser/ToolAuthHandler.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { ToolContext } from '../../ToolContext'; 18 | import { AuthCredential } from '../../../auth/AuthCredential'; 19 | import { AuthScheme } from '../auth/AuthTypes'; 20 | 21 | export interface AuthResult { 22 | state: 'pending' | 'done'; 23 | authCredential?: AuthCredential; 24 | message?: string; 25 | } 26 | 27 | /** 28 | * Handles authentication for tools. 29 | */ 30 | export class ToolAuthHandler { 31 | private constructor( 32 | private readonly toolContext: ToolContext, 33 | private readonly authScheme?: AuthScheme, 34 | private readonly authCredential?: AuthCredential, 35 | ) {} 36 | 37 | /** 38 | * Creates a ToolAuthHandler from the tool context. 39 | * @param toolContext The tool context. 40 | * @param authScheme The auth scheme. 41 | * @param authCredential The auth credential. 42 | * @returns A new ToolAuthHandler. 43 | */ 44 | static fromToolContext( 45 | toolContext: ToolContext, 46 | authScheme?: AuthScheme, 47 | authCredential?: AuthCredential, 48 | ): ToolAuthHandler { 49 | return new ToolAuthHandler(toolContext, authScheme, authCredential); 50 | } 51 | 52 | /** 53 | * Prepares the auth credentials. 54 | * @returns The auth result. 55 | */ 56 | async prepareAuthCredentials(): Promise { 57 | // This is a simplified mock. 58 | // In a real implementation, this would handle OAuth flows. 59 | if (this.authCredential) { 60 | return { 61 | state: 'done', 62 | authCredential: this.authCredential, 63 | }; 64 | } 65 | return { 66 | state: 'done', 67 | }; 68 | } 69 | } -------------------------------------------------------------------------------- /src/tools/openapi-tool/README.md: -------------------------------------------------------------------------------- 1 | # OpenAPI Tool Module 2 | 3 | This module provides tools for working with OpenAPI specifications and creating tools that interact with REST APIs documented with OpenAPI. 4 | 5 | ## Features 6 | 7 | - Parse OpenAPI specifications into a set of tools 8 | - Automatically generate tool parameters from OpenAPI operations 9 | - Support for authentication with various schemes (API key, OAuth2, etc.) 10 | - Convert between OpenAPI schema and Gemini schema formats 11 | - Execute REST API calls with proper parameter handling 12 | 13 | ## Usage 14 | 15 | ```typescript 16 | import { OpenAPIToolset } from '@google/adk/tools/openapi_tool'; 17 | 18 | // Create a toolset from an OpenAPI specification string 19 | const openApiSpec = ` 20 | openapi: 3.0.0 21 | info: 22 | title: Pet Store API 23 | version: 1.0.0 24 | paths: 25 | /pets: 26 | get: 27 | operationId: listPets 28 | summary: List all pets 29 | responses: 30 | '200': 31 | description: A list of pets 32 | `; 33 | 34 | // Create a toolset from a YAML OpenAPI spec 35 | const petStoreToolset = new OpenAPIToolset({ 36 | specStr: openApiSpec, 37 | specStrType: 'yaml' 38 | }); 39 | 40 | // Get all tools from the toolset 41 | const tools = petStoreToolset.getTools(); 42 | 43 | // Find a specific tool 44 | const listPetsTool = petStoreToolset.getTool('list_pets'); 45 | 46 | // Use the tool 47 | const result = await listPetsTool.execute({}, toolContext); 48 | ``` 49 | 50 | ## Components 51 | 52 | The module is organized into the following components: 53 | 54 | 1. **OpenAPIToolset** - Main class for creating a toolset from an OpenAPI specification 55 | 2. **RestApiTool** - Tool implementation for a single REST API operation 56 | 3. **OpenApiSpecParser** - Parser for OpenAPI specifications 57 | 4. **Common Utilities** - Helper functions for schema conversion, type hints, etc. 58 | 5. **Authentication** - Support for various authentication schemes 59 | 60 | ## Integration with ADK 61 | 62 | This module integrates with the ADK framework, allowing you to use OpenAPI-based tools in your agents: 63 | 64 | ```typescript 65 | import { Agent } from '@google/adk'; 66 | import { OpenAPIToolset } from '@google/adk/tools/openapi_tool'; 67 | 68 | // Create an agent with OpenAPI tools 69 | const agent = new Agent({ 70 | name: 'PetStoreAgent', 71 | tools: [ 72 | ...new OpenAPIToolset({ 73 | specStr: petStoreSpec, 74 | specStrType: 'yaml' 75 | }).getTools() 76 | ] 77 | }); 78 | ``` -------------------------------------------------------------------------------- /src/tools/GetUserChoiceTool.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { LongRunningFunctionTool } from './LongRunningTool'; 4 | import { ToolContext } from './ToolContext'; 5 | import { ToolActions } from './toolActions'; 6 | 7 | /** 8 | * Function to get user choice from a list of options 9 | * 10 | * @param params Parameters for the function 11 | * @param params.options List of options to present to the user 12 | * @param context The tool context 13 | * @returns null (the actual response will be sent asynchronously) 14 | */ 15 | export async function getUserChoice( 16 | params: Record, 17 | context: ToolContext 18 | ): Promise { 19 | const options = params.options; 20 | 21 | // Validate options 22 | if (!options || !Array.isArray(options) || options.length === 0) { 23 | return 'Error: options must be a non-empty array of strings'; 24 | } 25 | 26 | // Set the skip_summarization flag on the context's actions 27 | if (context.get) { 28 | const actions = (context.get('actions') || {}) as ToolActions; 29 | actions.skipSummarization = true; 30 | context.set('actions', actions); 31 | } else if ((context as any).actions) { 32 | (context as any).actions.skipSummarization = true; 33 | } 34 | 35 | // Return null because this is a long-running tool 36 | // The actual response will be sent asynchronously via the framework 37 | return null; 38 | } 39 | 40 | /** 41 | * Tool for collecting user choice from a list of options 42 | */ 43 | export class GetUserChoiceTool extends LongRunningFunctionTool { 44 | /** 45 | * Creates a new get user choice tool 46 | */ 47 | constructor() { 48 | super({ 49 | name: 'get_user_choice', 50 | description: 'Provides the options to the user and asks them to choose one', 51 | fn: getUserChoice, 52 | functionDeclaration: { 53 | name: 'get_user_choice', 54 | description: 'Provides the options to the user and asks them to choose one', 55 | parameters: { 56 | type: 'object', 57 | properties: { 58 | options: { 59 | type: 'array', 60 | items: { 61 | type: 'string' 62 | }, 63 | description: 'List of options to present to the user' 64 | } 65 | }, 66 | required: ['options'] 67 | } 68 | } 69 | }); 70 | } 71 | } 72 | 73 | /** 74 | * Singleton instance of the Get User Choice tool 75 | */ 76 | export const getUserChoiceTool = new GetUserChoiceTool(); -------------------------------------------------------------------------------- /docs/agents/workflow-agents/sequential-agents.md: -------------------------------------------------------------------------------- 1 | # Sequential agents 2 | 3 | ## The `SequentialAgent` 4 | 5 | The `SequentialAgent` is a [workflow agent](index.md) that executes its sub-agents in the order they are specified in the list. 6 | 7 | Use the `SequentialAgent` when you want the execution to occur in a fixed, strict order. 8 | 9 | ### Example 10 | 11 | * You want to build an agent that can summarize any webpage, using two tools: `get_page_contents` and `summarize_page`. Because the agent must always call `get_page_contents` before calling `summarize_page` (you can't summarize from nothing!), you should build your agent using a `SequentialAgent`. 12 | 13 | As with other [workflow agents](index.md), the `SequentialAgent` is not powered by an LLM, and is thus deterministic in how it executes. That being said, workflow agents are only concerned only with their execution (i.e. in sequence), and not their internal logic; the tools or sub-agents of a workflow agent may or may not utilize LLMs. 14 | 15 | ### How it works 16 | 17 | When the `SequentialAgent`'s `run_async()` method is called, it performs the following actions: 18 | 19 | 1. **Iteration:** It iterates through the `sub_agents` list in the order they were provided. 20 | 2. **Sub-Agent Execution:** For each sub-agent in the list, it calls the sub-agent's `run_async()` method. 21 | 22 | ![Sequential Agent](../../assets/sequential-agent.png){: width="600"} 23 | 24 | ### Full Example: Code Development Pipeline 25 | 26 | Consider a simplified code development pipeline: 27 | 28 | * **Code Writer Agent:** An `LlmAgent` that generates initial code based on a specification. 29 | * **Code Reviewer Agent:** An `LlmAgent` that reviews the generated code for errors, style issues, and adherence to best practices. It receives the output of the Code Writer Agent. 30 | * **Code Refactorer Agent:** An `LlmAgent` that takes the reviewed code (and the reviewer's comments) and refactors it to improve quality and address issues. 31 | 32 | A `SequentialAgent` is perfect for this: 33 | 34 | ```py 35 | SequentialAgent(sub_agents=[CodeWriterAgent, CodeReviewerAgent, CodeRefactorerAgent]) 36 | ``` 37 | 38 | This ensures the code is written, *then* reviewed, and *finally* refactored, in a strict, dependable order. **The output from each sub-agent is passed to the next by storing them in state via [`output_key`](../llm-agents.md)**. 39 | 40 | ???+ "Code" 41 | 42 | ```py 43 | --8<-- "examples/python/snippets/agents/workflow-agents/sequential_agent_code_development_agent.py" 44 | ``` 45 | -------------------------------------------------------------------------------- /src/tools/retrieval/FilesRetrieval.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { RetrievalToolOptions } from './BaseRetrievalTool'; 4 | import { LlamaIndexRetrieval } from './LlamaIndexRetrieval'; 5 | 6 | /** 7 | * Options for creating a Files retrieval tool 8 | */ 9 | export interface FilesRetrievalOptions extends RetrievalToolOptions { 10 | /** 11 | * Directory containing the files to index 12 | */ 13 | inputDir: string; 14 | } 15 | 16 | /** 17 | * Retrieval tool that uses LlamaIndex to retrieve information from files 18 | */ 19 | export class FilesRetrieval extends LlamaIndexRetrieval { 20 | /** 21 | * Directory containing the indexed files 22 | */ 23 | private inputDir: string; 24 | 25 | /** 26 | * Create a new Files retrieval tool 27 | * 28 | * @param options Options for the Files retrieval tool 29 | */ 30 | constructor(options: FilesRetrievalOptions) { 31 | console.log(`Loading data from ${options.inputDir}`); 32 | 33 | // In the Python implementation, this uses SimpleDirectoryReader and VectorStoreIndex 34 | // Since we don't have direct LlamaIndex bindings in TypeScript, we create a placeholder 35 | // retriever object that would be replaced with actual implementation when available 36 | const retriever = createVectorIndexFromDirectory(options.inputDir); 37 | 38 | super({ 39 | ...options, 40 | retriever 41 | }); 42 | 43 | this.inputDir = options.inputDir; 44 | } 45 | } 46 | 47 | /** 48 | * Create a vector index from a directory 49 | * 50 | * This is a placeholder function that would be replaced with actual LlamaIndex 51 | * implementation when TypeScript bindings are available 52 | * 53 | * @param inputDir Directory containing files to index 54 | * @returns A retriever object 55 | */ 56 | function createVectorIndexFromDirectory(inputDir: string): any { 57 | // This is a placeholder implementation 58 | // In a real implementation, this would: 59 | // 1. Use SimpleDirectoryReader to load documents from the directory 60 | // 2. Create a VectorStoreIndex from those documents 61 | // 3. Return the index as a retriever 62 | 63 | console.log(`Creating vector index from directory: ${inputDir}`); 64 | 65 | return { 66 | retrieve: async (query: string) => { 67 | console.log(`Querying vector index for: ${query}`); 68 | return [ 69 | { 70 | text: `This is a placeholder response for "${query}". In a real implementation, ` + 71 | `this would search documents in "${inputDir}" using LlamaIndex.` 72 | } 73 | ]; 74 | } 75 | }; 76 | } -------------------------------------------------------------------------------- /docs/examples/typescript/snippets/tools/overview/UserPreference.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * TypeScript port of the user_preference.py example from the Python ADK library 3 | * 4 | * This example demonstrates how to use ToolContext to update user-specific preferences 5 | * in the session state when a tool is invoked. 6 | * 7 | * NOTE: This is a template file that demonstrates how to use the ADK TypeScript library. 8 | * You'll see TypeScript errors in your IDE until you install the actual 'adk-typescript' package. 9 | * The structure and patterns shown here match how you would use the library in a real project. 10 | */ 11 | 12 | import { ToolContext, FunctionTool } from 'adk-typescript/tools'; 13 | 14 | /** 15 | * Updates a user-specific preference in the session state. 16 | * 17 | * @param preference The preference name to update 18 | * @param value The value to set for the preference 19 | * @param toolContext The context for the tool execution, providing access to state 20 | * @returns A status object indicating success and which preference was updated 21 | */ 22 | function updateUserPreference( 23 | preference: string, 24 | value: string, 25 | toolContext: ToolContext 26 | ): Record { 27 | const userPrefsKey = "user:preferences"; 28 | 29 | // Get current preferences or initialize if none exist 30 | const preferences = toolContext.state.get(userPrefsKey, {}); 31 | 32 | // Update the specific preference 33 | preferences[preference] = value; 34 | 35 | // Write the updated dictionary back to the state 36 | toolContext.state[userPrefsKey] = preferences; 37 | 38 | console.log(`Tool: Updated user preference '${preference}' to '${value}'`); 39 | 40 | return { 41 | "status": "success", 42 | "updated_preference": preference 43 | }; 44 | } 45 | 46 | 47 | // Export for use in an Agent 48 | export const userPreferenceTool = updateUserPreference; 49 | 50 | /** 51 | * Usage example in an Agent: 52 | * 53 | * ```typescript 54 | * import { Agent } from 'adk-typescript'; 55 | * import { userPreferenceTool } from './user-preference'; 56 | * 57 | * const myAgent = new Agent("preference_agent", { 58 | * model: "gemini-2.0-flash", 59 | * instruction: "You can update user preferences when asked.", 60 | * tools: [userPreferenceTool] 61 | * }); 62 | * ``` 63 | * 64 | * When the LLM calls updateUserPreference(preference='theme', value='dark', ...): 65 | * - The toolContext.state will be updated with {'user:preferences': {'theme': 'dark'}} 66 | * - The change will be part of the resulting tool response event's actions.state_delta 67 | */ -------------------------------------------------------------------------------- /src/agents/LoopAgent.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { Content } from '../models/types'; 4 | import { Event } from '../events/Event'; 5 | import { BaseAgent, AgentOptions } from './BaseAgent'; 6 | import { InvocationContext } from './InvocationContext'; 7 | 8 | /** 9 | * Options for the LoopAgent. 10 | */ 11 | export interface LoopAgentOptions extends AgentOptions { 12 | /** The maximum number of iterations to run the loop agent */ 13 | maxIterations?: number; 14 | } 15 | 16 | /** 17 | * A shell agent that runs its sub-agents in a loop. 18 | * 19 | * When sub-agent generates an event with escalate or max_iterations are 20 | * reached, the loop agent will stop. 21 | */ 22 | export class LoopAgent extends BaseAgent { 23 | /** 24 | * The maximum number of iterations to run the loop agent. 25 | * If not set, the loop agent will run indefinitely until a sub-agent escalates. 26 | */ 27 | private readonly maxIterations?: number; 28 | 29 | /** 30 | * Creates a new LoopAgent. 31 | * 32 | * @param name The name of the agent 33 | * @param options Options for the agent 34 | */ 35 | constructor(name: string, options: LoopAgentOptions = {}) { 36 | super(name, options); 37 | this.maxIterations = options.maxIterations; 38 | } 39 | 40 | /** 41 | * @inheritdoc 42 | */ 43 | protected async* runAsyncImpl(ctx: InvocationContext): AsyncGenerator { 44 | let timesLooped = 0; 45 | 46 | while (!this.maxIterations || timesLooped < this.maxIterations) { 47 | for (const subAgent of this.subAgents) { 48 | for await (const event of subAgent.runAsync(ctx)) { 49 | yield event; 50 | 51 | if (event.actions.escalate) { 52 | return; 53 | } 54 | } 55 | } 56 | 57 | timesLooped += 1; 58 | } 59 | } 60 | 61 | /** 62 | * @inheritdoc 63 | */ 64 | protected async* runLiveImpl(ctx: InvocationContext): AsyncGenerator { 65 | throw new Error('The behavior for runLive is not defined yet for LoopAgent.'); 66 | 67 | // Return early - this code is unreachable but satisfies TypeScript's return type 68 | return; 69 | yield {} as Event; // Unreachable code to satisfy AsyncGenerator type 70 | } 71 | 72 | /** 73 | * @inheritdoc 74 | */ 75 | setUserContent(content: Content, invocationContext: InvocationContext): void { 76 | // For LoopAgent, we pass the content to all sub-agents 77 | for (const subAgent of this.subAgents) { 78 | subAgent.setUserContent(content, invocationContext); 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /tests/integration/testConfig.ts: -------------------------------------------------------------------------------- 1 | import * as dotenv from 'dotenv'; 2 | import * as path from 'path'; 3 | 4 | /** 5 | * Load environment variables for tests 6 | */ 7 | export function loadEnvForTests(): void { 8 | const dotenvPath = path.join(__dirname, '.env'); 9 | 10 | try { 11 | dotenv.config({ path: dotenvPath }); 12 | } catch (error) { 13 | console.warn(`Missing .env file at ${dotenvPath}. See .env.example for an example.`); 14 | } 15 | 16 | // Check for required environment variables 17 | if (!process.env.GOOGLE_API_KEY) { 18 | console.warn('Missing GOOGLE_API_KEY in the environment variables. GOOGLE_AI backend integration tests will fail.'); 19 | } 20 | 21 | const requiredCloudVars = [ 22 | 'GOOGLE_CLOUD_PROJECT', 23 | 'GOOGLE_CLOUD_LOCATION' 24 | ]; 25 | 26 | for (const envVar of requiredCloudVars) { 27 | if (!process.env[envVar]) { 28 | console.warn(`Missing ${envVar} in the environment variables. Vertex backend integration tests will fail.`); 29 | } 30 | } 31 | } 32 | 33 | /** 34 | * Get the backend type for tests 35 | * @returns The backend type (GOOGLE_AI or VERTEX) 36 | */ 37 | export function getBackendType(): 'GOOGLE_AI' | 'VERTEX' | 'BOTH' { 38 | const testBackend = process.env.TEST_BACKEND || 'BOTH'; 39 | 40 | if (testBackend === 'GOOGLE_AI_ONLY') { 41 | return 'GOOGLE_AI'; 42 | } else if (testBackend === 'VERTEX_ONLY') { 43 | return 'VERTEX'; 44 | } else if (testBackend === 'BOTH') { 45 | return 'BOTH'; 46 | } else { 47 | throw new Error(`Invalid TEST_BACKEND value: ${testBackend}, should be one of [GOOGLE_AI_ONLY, VERTEX_ONLY, BOTH]`); 48 | } 49 | } 50 | 51 | /** 52 | * Set the backend environment variable 53 | * @param backendType The backend type to set 54 | * @returns The original backend setting 55 | */ 56 | export function setBackendEnvironment(backendType: 'GOOGLE_AI' | 'VERTEX'): string | undefined { 57 | const originalVal = process.env.GOOGLE_GENAI_USE_VERTEXAI; 58 | 59 | if (backendType === 'GOOGLE_AI') { 60 | process.env.GOOGLE_GENAI_USE_VERTEXAI = '0'; 61 | } else { 62 | process.env.GOOGLE_GENAI_USE_VERTEXAI = '1'; 63 | } 64 | 65 | return originalVal; 66 | } 67 | 68 | /** 69 | * Restore the backend environment variable 70 | * @param originalVal The original value to restore 71 | */ 72 | export function restoreBackendEnvironment(originalVal: string | undefined): void { 73 | if (originalVal === undefined) { 74 | delete process.env.GOOGLE_GENAI_USE_VERTEXAI; 75 | } else { 76 | process.env.GOOGLE_GENAI_USE_VERTEXAI = originalVal; 77 | } 78 | } 79 | 80 | // Initialize environment variables 81 | loadEnvForTests(); -------------------------------------------------------------------------------- /src/agents/RemoteAgent.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import axios from 'axios'; 4 | import { Content } from '../models/types'; 5 | import { Event } from '../events/Event'; 6 | import { BaseAgent, AgentOptions } from './BaseAgent'; 7 | import { InvocationContext } from './InvocationContext'; 8 | 9 | /** 10 | * Options for the RemoteAgent. 11 | */ 12 | export interface RemoteAgentOptions extends AgentOptions { 13 | /** The URL to send requests to */ 14 | url: string; 15 | } 16 | 17 | /** 18 | * Remote agent that delegates processing to an external service. 19 | * Experimental, do not use. 20 | */ 21 | export class RemoteAgent extends BaseAgent { 22 | /** The URL to send requests to */ 23 | private readonly url: string; 24 | 25 | /** 26 | * Creates a new RemoteAgent. 27 | * 28 | * @param name The name of the agent 29 | * @param options Options for the agent 30 | */ 31 | constructor(name: string, options: RemoteAgentOptions) { 32 | super(name, options); 33 | this.url = options.url; 34 | } 35 | 36 | /** 37 | * @inheritdoc 38 | */ 39 | protected async* runAsyncImpl(ctx: InvocationContext): AsyncGenerator { 40 | const data = { 41 | invocation_id: ctx.invocationId, 42 | session: ctx.session, 43 | }; 44 | 45 | const response = await axios.post(this.url, JSON.stringify(data), { 46 | headers: { 47 | 'Content-Type': 'application/json', 48 | }, 49 | timeout: 120000, // 120 seconds (same as Python's 120 timeout) 50 | }); 51 | 52 | if (response.status !== 200) { 53 | throw new Error(`Remote agent request failed with status: ${response.status}`); 54 | } 55 | 56 | for (const eventData of response.data) { 57 | const event = new Event({ 58 | ...eventData, 59 | author: this.name, 60 | }); 61 | yield event; 62 | } 63 | } 64 | 65 | /** 66 | * @inheritdoc 67 | */ 68 | protected async* runLiveImpl(ctx: InvocationContext): AsyncGenerator { 69 | // For live implementation, we simply delegate to the async implementation 70 | yield* this.runAsyncImpl(ctx); 71 | } 72 | 73 | /** 74 | * @inheritdoc 75 | */ 76 | setUserContent(content: Content, invocationContext: InvocationContext): void { 77 | // Remote agent doesn't need to store user content locally 78 | // It will be sent as part of the session data 79 | } 80 | 81 | /** 82 | * Override the addSubAgent method to prevent adding sub-agents. 83 | * Sub-agents are disabled in RemoteAgent. 84 | */ 85 | addSubAgent(agent: BaseAgent): this { 86 | console.warn('Sub-agents are disabled in RemoteAgent'); 87 | return this; 88 | } 89 | } -------------------------------------------------------------------------------- /src/tools/retrieval/BaseRetrievalTool.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { BaseTool, BaseToolOptions } from '../BaseTool'; 4 | import { ToolContext } from '../ToolContext'; 5 | 6 | /** 7 | * Options for creating a retrieval tool 8 | */ 9 | export interface RetrievalToolOptions extends BaseToolOptions { 10 | /** 11 | * Maximum number of results to return (default: 5) 12 | */ 13 | maxResults?: number; 14 | } 15 | 16 | /** 17 | * Base class for retrieval tools that search for information 18 | */ 19 | export abstract class BaseRetrievalTool extends BaseTool { 20 | /** 21 | * Maximum number of results to return 22 | */ 23 | protected maxResults: number; 24 | 25 | /** 26 | * Create a new retrieval tool 27 | * 28 | * @param options Options for the retrieval tool 29 | */ 30 | constructor(options: RetrievalToolOptions) { 31 | super(options); 32 | this.maxResults = options.maxResults || 5; 33 | } 34 | 35 | /** 36 | * Get the function declaration for retrieval tools 37 | * 38 | * @returns The function declaration 39 | */ 40 | protected _getDeclaration() { 41 | return { 42 | name: this.name, 43 | description: this.description, 44 | parameters: { 45 | type: 'object', 46 | properties: { 47 | query: { 48 | type: 'string', 49 | description: 'The query to retrieve information for.' 50 | }, 51 | maxResults: { 52 | type: 'integer', 53 | description: `Maximum number of results to return (default: ${this.maxResults}).` 54 | } 55 | }, 56 | required: ['query'] 57 | } 58 | }; 59 | } 60 | 61 | /** 62 | * Execute the retrieval tool 63 | * 64 | * @param params The parameters for the tool execution 65 | * @param context The context for the tool execution 66 | * @returns The result of the tool execution 67 | */ 68 | async execute( 69 | params: Record, 70 | context: ToolContext 71 | ): Promise { 72 | const query = params.query; 73 | const maxResults = params.maxResults || this.maxResults; 74 | 75 | return this.retrieve(query, maxResults, context); 76 | } 77 | 78 | /** 79 | * Retrieve information based on the query 80 | * 81 | * This method must be implemented by subclasses. 82 | * 83 | * @param query The query to retrieve information for 84 | * @param maxResults Maximum number of results to return 85 | * @param context The context for the retrieval 86 | * @returns The retrieved information 87 | */ 88 | protected abstract retrieve( 89 | query: string, 90 | maxResults: number, 91 | context: ToolContext 92 | ): Promise; 93 | } -------------------------------------------------------------------------------- /src/tools/google-api-tool/GoogleApiTool.ts: -------------------------------------------------------------------------------- 1 | import { BaseTool } from '../BaseTool'; 2 | import { ToolContext } from '../ToolContext'; 3 | 4 | /** 5 | * Auth credential types enum 6 | */ 7 | export enum AuthCredentialTypes { 8 | OPEN_ID_CONNECT = 'open_id_connect', 9 | } 10 | 11 | /** 12 | * OAuth2 Auth interface 13 | */ 14 | export interface OAuth2Auth { 15 | clientId: string; 16 | clientSecret: string; 17 | accessToken?: string; 18 | refreshToken?: string; 19 | } 20 | 21 | /** 22 | * Auth credential interface 23 | */ 24 | export interface AuthCredential { 25 | authType: AuthCredentialTypes; 26 | oauth2: OAuth2Auth; 27 | } 28 | 29 | /** 30 | * RestApiTool interface (placeholder for actual implementation) 31 | */ 32 | export interface RestApiTool { 33 | name: string; 34 | description: string; 35 | isLongRunning: boolean; 36 | execute: (params: Record, context: ToolContext) => Promise; 37 | getDeclaration: () => any; 38 | authCredential?: AuthCredential; 39 | } 40 | 41 | /** 42 | * GoogleApiTool class 43 | * A wrapper around RestApiTool that adds Google API specific functionality 44 | */ 45 | export class GoogleApiTool extends BaseTool { 46 | private restApiTool: RestApiTool; 47 | 48 | /** 49 | * Create a new GoogleApiTool 50 | * @param restApiTool The underlying RestApiTool to wrap 51 | */ 52 | constructor(restApiTool: RestApiTool) { 53 | super({ 54 | name: restApiTool.name, 55 | description: restApiTool.description, 56 | isLongRunning: restApiTool.isLongRunning 57 | }); 58 | this.restApiTool = restApiTool; 59 | } 60 | 61 | /** 62 | * Get the function declaration 63 | * @returns The function declaration from the underlying RestApiTool 64 | */ 65 | protected _getDeclaration(): any { 66 | return this.restApiTool.getDeclaration(); 67 | } 68 | 69 | /** 70 | * Execute the tool 71 | * @param params The parameters for the tool execution 72 | * @param context The context for the tool execution 73 | * @returns The result of executing the underlying RestApiTool 74 | */ 75 | async execute( 76 | params: Record, 77 | context: ToolContext 78 | ): Promise { 79 | return await this.restApiTool.execute(params, context); 80 | } 81 | 82 | /** 83 | * Configure authentication for the tool 84 | * @param clientId The OAuth2 client ID 85 | * @param clientSecret The OAuth2 client secret 86 | */ 87 | configureAuth(clientId: string, clientSecret: string): void { 88 | this.restApiTool.authCredential = { 89 | authType: AuthCredentialTypes.OPEN_ID_CONNECT, 90 | oauth2: { 91 | clientId, 92 | clientSecret 93 | } 94 | }; 95 | } 96 | } -------------------------------------------------------------------------------- /src/sessions/sessionUtils.ts: -------------------------------------------------------------------------------- 1 | import { Content, Part } from './types'; 2 | 3 | /** 4 | * Encodes content for storage in the database 5 | * Converts binary data to base64 strings for JSON serialization 6 | */ 7 | export function encodeContent(content: Content): Record { 8 | if (!content || !content.parts) { 9 | return content as unknown as Record; 10 | } 11 | 12 | const encodedContent: Record = { 13 | role: content.role, 14 | parts: [] 15 | }; 16 | 17 | // Deep copy the parts array to avoid modifying the original 18 | encodedContent.parts = content.parts.map(part => { 19 | const encodedPart: Record = { ...part }; 20 | 21 | if (encodedPart.data && encodedPart.mimeType) { 22 | // Ensure data is a Uint8Array before encoding 23 | const dataArray = encodedPart.data instanceof Uint8Array 24 | ? encodedPart.data 25 | : new Uint8Array(encodedPart.data as any); 26 | 27 | // Convert Uint8Array to base64 string for storage 28 | encodedPart.data = Buffer.from(dataArray).toString('base64'); 29 | } 30 | 31 | return encodedPart; 32 | }); 33 | 34 | return encodedContent; 35 | } 36 | 37 | /** 38 | * Decodes content from the database 39 | * Converts base64 strings back to binary data 40 | */ 41 | export function decodeContent(content: Record | null | undefined): Content { 42 | if (!content || !content.parts) { 43 | // Return default Content structure if content is missing required properties 44 | return { 45 | role: 'user', 46 | parts: [] 47 | }; 48 | } 49 | 50 | const decodedContent: Content = { 51 | role: content.role, 52 | parts: [] 53 | }; 54 | 55 | if (Array.isArray(content.parts)) { 56 | decodedContent.parts = content.parts.map(part => { 57 | const decodedPart: Part = {}; 58 | 59 | if (part.text !== undefined) { 60 | decodedPart.text = part.text; 61 | } 62 | 63 | if (part.data && part.mimeType) { 64 | // If data is a string (base64 encoded), convert it to Uint8Array 65 | if (typeof part.data === 'string') { 66 | decodedPart.data = new Uint8Array(Buffer.from(part.data, 'base64')); 67 | decodedPart.mimeType = part.mimeType; 68 | } 69 | // If data is a Buffer object from JSON serialization 70 | else if (typeof part.data === 'object' && part.data.type === 'Buffer' && Array.isArray(part.data.data)) { 71 | decodedPart.data = new Uint8Array(part.data.data); 72 | decodedPart.mimeType = part.mimeType; 73 | } 74 | } 75 | 76 | return decodedPart; 77 | }); 78 | } 79 | 80 | return decodedContent; 81 | } -------------------------------------------------------------------------------- /src/sessions/types.ts: -------------------------------------------------------------------------------- 1 | import { State } from './State'; 2 | 3 | export interface Part { 4 | text?: string; 5 | data?: Uint8Array; 6 | mimeType?: string; 7 | } 8 | 9 | // Create a namespace with the same name as the interface to add static methods 10 | export namespace Part { 11 | export function fromBytes(data: Uint8Array, mimeType: string): Part { 12 | return { 13 | data, 14 | mimeType 15 | }; 16 | } 17 | } 18 | 19 | export interface Content { 20 | role: string; 21 | parts: Part[]; 22 | } 23 | 24 | // Session interface definitions (merged from interfaces.ts) 25 | 26 | /** 27 | * Actions that can be triggered by an event 28 | */ 29 | export interface EventActions { 30 | stateDelta?: Record; 31 | artifactDelta?: Record; 32 | transferToAgent?: string; 33 | escalate?: boolean; 34 | skipSummarization?: boolean; 35 | requestedAuthConfigs?: Record; 36 | } 37 | 38 | /** 39 | * Represents an event in a session 40 | */ 41 | export interface Event { 42 | id?: string; 43 | invocationId: string; 44 | author: string; 45 | content: Content; 46 | turnComplete?: boolean; 47 | partial?: boolean; 48 | actions?: EventActions; 49 | longRunningToolIds?: Set; 50 | errorCode?: string; 51 | errorMessage?: string; 52 | interrupted?: boolean; 53 | timestamp?: number; 54 | branch?: string; 55 | groundingMetadata?: Record; 56 | } 57 | 58 | /** 59 | * Represents a session interface (formerly from interfaces.ts) 60 | */ 61 | export interface SessionInterface { 62 | id: string; 63 | appName: string; 64 | userId: string; 65 | state: State; 66 | events: Event[]; 67 | lastUpdateTime?: number; 68 | } 69 | 70 | /** 71 | * Represents a list of sessions 72 | */ 73 | export interface SessionsList { 74 | sessions: SessionInterface[]; 75 | } 76 | 77 | /** 78 | * Interface for session services 79 | */ 80 | export interface SessionService { 81 | createSession(options: { 82 | appName: string; 83 | userId: string; 84 | sessionId?: string; 85 | state?: Record; 86 | }): Promise; 87 | 88 | getSession(options: { 89 | appName: string; 90 | userId: string; 91 | sessionId: string; 92 | config?: import('./BaseSessionService').GetSessionConfig; 93 | }): Promise; 94 | 95 | listSessions(options: { 96 | appName: string; 97 | userId: string; 98 | }): Promise; 99 | 100 | deleteSession(options: { 101 | appName: string; 102 | userId: string; 103 | sessionId: string; 104 | }): Promise; 105 | 106 | appendEvent(options: { 107 | session: SessionInterface; 108 | event: Event; 109 | }): Promise; 110 | } -------------------------------------------------------------------------------- /docs/examples/typescript/snippets/tools/built-in-tools/GoogleSearch.ts: -------------------------------------------------------------------------------- 1 | import { LlmAgent as Agent } from 'adk-typescript/agents'; 2 | import { runners } from 'adk-typescript'; 3 | import { Content } from 'adk-typescript/types'; 4 | import { InMemorySessionService } from 'adk-typescript/sessions'; 5 | import { googleSearch } from 'adk-typescript/tools'; 6 | 7 | // Constants for the app 8 | const APP_NAME = "google_search_agent"; 9 | const USER_ID = "user1234"; 10 | const SESSION_ID = "1234"; 11 | 12 | // Configure logging (simplified version for TypeScript) 13 | const logger = { 14 | info: (message: string, ...args: any[]) => console.info(message, ...args), 15 | error: (message: string, ...args: any[]) => console.error(message, ...args) 16 | }; 17 | 18 | // Create the agent with Google Search tool 19 | const rootAgent = new Agent({ 20 | name: "basic_search_agent", 21 | model: "gemini-2.0-flash", 22 | description: "Agent to answer questions using Google Search.", 23 | instruction: "I can answer your questions by searching the internet. Just ask me anything!", 24 | // googleSearch is a pre-built tool which allows the agent to perform Google searches. 25 | tools: [googleSearch] 26 | }); 27 | 28 | // Create Session and Runner 29 | const sessionService = new InMemorySessionService(); 30 | const session = sessionService.createSession({ 31 | appName: APP_NAME, 32 | userId: USER_ID, 33 | sessionId: SESSION_ID 34 | }); 35 | 36 | const runner = new runners.Runner({ 37 | agent: rootAgent, 38 | appName: APP_NAME, 39 | sessionService: sessionService 40 | }); 41 | 42 | /** 43 | * Helper function to call the agent with a query. 44 | * 45 | * @param query The user's query to send to the agent 46 | */ 47 | function callAgent(query: string): void { 48 | // Create content for the request 49 | const content: Content = { 50 | role: 'user', 51 | parts: [{ text: query }] 52 | }; 53 | 54 | // Run the agent and collect results 55 | (async () => { 56 | try { 57 | const events = runner.run({ 58 | userId: USER_ID, 59 | sessionId: SESSION_ID, 60 | newMessage: content 61 | }); 62 | 63 | for await (const event of events) { 64 | if (event.isFinalResponse() && event.content && event.content.parts && event.content.parts[0].text) { 65 | const finalResponse = event.content.parts[0].text; 66 | console.log("Agent Response: ", finalResponse); 67 | } 68 | } 69 | } catch (error) { 70 | console.error("Error running agent:", error); 71 | } 72 | })(); 73 | } 74 | 75 | // Execute with a sample query if run directly 76 | if (require.main === module) { 77 | callAgent("what's the latest ai news?"); 78 | } 79 | 80 | // Export for external use 81 | export const agent = rootAgent; 82 | export function runGoogleSearchDemo(query: string): void { 83 | callAgent(query); 84 | } -------------------------------------------------------------------------------- /tests/integration/fixture/agent_with_config/agent.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { LlmAgent as Agent } from '../../../../src/agents/LlmAgent'; 4 | import { Content, Part } from '../../../../src/types'; 5 | import { LlmRegistry } from '../../../../src/models'; 6 | import { FunctionTool } from '../../../../src/tools/FunctionTool'; 7 | 8 | export const newMessage: Content = { 9 | role: 'user', 10 | parts: [{ text: 'Count a number' }] 11 | }; 12 | 13 | // Create model instance 14 | const geminiFlashModel = LlmRegistry.newLlm('gemini-2.0-flash'); 15 | 16 | /** 17 | * First agent with basic configuration 18 | */ 19 | export const agent1 = new Agent({ 20 | name: 'agent_1', 21 | description: 'The first agent in the team.', 22 | instruction: 'Just say 1', 23 | model: geminiFlashModel 24 | }); 25 | 26 | /** 27 | * Second agent with safety settings 28 | */ 29 | export const agent2 = new Agent({ 30 | name: 'agent_2', 31 | description: 'The second agent in the team.', 32 | instruction: 'Just say 2', 33 | model: geminiFlashModel, 34 | safetySettings: [{ 35 | category: 'HARM_CATEGORY_HATE_SPEECH', 36 | threshold: 'BLOCK_ONLY_HIGH' 37 | }] 38 | }); 39 | 40 | /** 41 | * Third agent with different safety settings 42 | */ 43 | export const agent3 = new Agent({ 44 | name: 'agent_3', 45 | description: 'The third agent in the team.', 46 | instruction: 'Just say 3', 47 | model: geminiFlashModel, 48 | safetySettings: [{ 49 | category: 'HARM_CATEGORY_DANGEROUS_CONTENT', 50 | threshold: 'BLOCK_NONE' 51 | }] 52 | }); 53 | 54 | /** 55 | * Agent with instruction in config 56 | */ 57 | export const agentWithInstructionInConfig = new Agent({ 58 | name: 'agent', 59 | model: geminiFlashModel, 60 | instruction: 'Count 1' 61 | }); 62 | 63 | /** 64 | * Simple function for tool demonstration 65 | */ 66 | function simpleFunction(): void { 67 | // Implementation would go here 68 | } 69 | 70 | /** 71 | * Agent with tools in config 72 | */ 73 | export const agentWithToolsInConfig = new Agent({ 74 | name: 'agent', 75 | model: geminiFlashModel, 76 | tools: [ 77 | new FunctionTool({ 78 | name: 'simpleFunction', 79 | description: 'A simple function for tool demonstration', 80 | fn: async () => simpleFunction(), 81 | functionDeclaration: { 82 | name: 'simpleFunction', 83 | description: 'A simple function for tool demonstration', 84 | parameters: { 85 | type: 'object', 86 | properties: {} 87 | } 88 | } 89 | }) 90 | ] 91 | }); 92 | 93 | /** 94 | * Agent with response schema in config 95 | */ 96 | export const agentWithResponseSchemaInConfig = new Agent({ 97 | name: 'agent', 98 | model: geminiFlashModel, 99 | outputSchema: { type: 'object', properties: {} } as const 100 | }); -------------------------------------------------------------------------------- /docs/examples/typescript/snippets/callbacks/CallbackBasic.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | // --8<-- [start:callback_basic] 4 | import { 5 | LlmAgent, 6 | CallbackContext, 7 | } from 'adk-typescript/agents'; 8 | import { 9 | LlmRequest, 10 | LlmResponse, 11 | LlmRegistry, 12 | Content, 13 | } from 'adk-typescript/models'; 14 | import { runners } from 'adk-typescript'; 15 | import { InMemorySessionService } from 'adk-typescript/sessions'; 16 | 17 | 18 | // --- Define your callback function --- 19 | function myBeforeModelLogic( 20 | callbackContext: CallbackContext, 21 | llmRequest: LlmRequest 22 | ): LlmResponse | undefined { 23 | console.log(`Callback running before model call for agent: ${callbackContext.agentName}`); 24 | // ... your custom logic here ... 25 | return undefined; // Allow the model call to proceed 26 | } 27 | 28 | // --- Register it during Agent creation --- 29 | const myAgent = new LlmAgent({ 30 | name: "MyCallbackAgent", 31 | model: LlmRegistry.newLlm("gemini-2.0-flash"), // Or your desired model 32 | instruction: "Be helpful.", 33 | // Other agent parameters... 34 | beforeModelCallback: myBeforeModelLogic // Pass the function here 35 | }); 36 | // --8<-- [end:callback_basic] 37 | 38 | const APP_NAME = "guardrail_app"; 39 | const USER_ID = "user_1"; 40 | const SESSION_ID = "session_001"; 41 | 42 | // Session and Runner 43 | const sessionService = new InMemorySessionService(); 44 | sessionService.createSession({ 45 | appName: APP_NAME, 46 | userId: USER_ID, 47 | sessionId: SESSION_ID 48 | }); 49 | 50 | const runner = new runners.Runner({ 51 | agent: myAgent, 52 | appName: APP_NAME, 53 | sessionService: sessionService 54 | }); 55 | 56 | // Agent Interaction 57 | async function callAgent(query: string): Promise { 58 | // Create content for the request 59 | const content: Content = { 60 | role: 'user', 61 | parts: [{ text: query }] 62 | }; 63 | 64 | // Run the agent and collect results 65 | try { 66 | const events = runner.run({ 67 | userId: USER_ID, 68 | sessionId: SESSION_ID, 69 | newMessage: content 70 | }); 71 | 72 | for await (const event of events) { 73 | if (event.isFinalResponse() && event.content && event.content.parts && event.content.parts[0].text) { 74 | const finalResponse = event.content.parts[0].text; 75 | console.log("Agent Response: ", finalResponse); 76 | } else if (event.errorCode) { 77 | console.log(`Error Event: [${event.errorCode}] ${event.errorMessage}`); 78 | } 79 | } 80 | } catch (error) { 81 | console.error("Error running agent:", error); 82 | } 83 | } 84 | 85 | callAgent("callback example"); 86 | 87 | // Export the agent and interaction function for external use 88 | export const agent = myAgent; 89 | export async function runCallbackBasicDemo(query: string): Promise { 90 | await callAgent(query); 91 | } -------------------------------------------------------------------------------- /src/cli/runAgent.ts: -------------------------------------------------------------------------------- 1 | import * as readline from 'readline'; 2 | import { config } from 'dotenv'; 3 | import { LlmAgent } from '../agents/LlmAgent'; 4 | import { Runner } from '../runners'; 5 | import { InMemorySessionService } from '../sessions/InMemorySessionService'; 6 | import { InMemoryArtifactService } from '../artifacts/InMemoryArtifactService'; 7 | import { Content, Part } from '../models/types'; 8 | 9 | // Load environment variables from .env file 10 | config(); 11 | 12 | /** 13 | * Run an agent interactively from any file 14 | * 15 | * Usage in your agent file: 16 | * ```typescript 17 | * import { runAgent } from '../src/cli/runAgent'; 18 | * 19 | * export const rootAgent = new LlmAgent({ ... }); 20 | * 21 | * if (require.main === module) { 22 | * runAgent(rootAgent); 23 | * } 24 | * ``` 25 | * 26 | * @param agent The agent to run 27 | * @param options Optional configuration 28 | */ 29 | export async function runAgent( 30 | agent: LlmAgent, 31 | options: { 32 | appName?: string; 33 | userId?: string; 34 | initialState?: Record; 35 | } = {} 36 | ): Promise { 37 | const { 38 | appName = 'test-agent', 39 | userId = 'test-user', 40 | initialState = {} 41 | } = options; 42 | console.log('running agent') 43 | const sessionService = new InMemorySessionService(); 44 | const artifactService = new InMemoryArtifactService(); 45 | 46 | const session = await sessionService.createSession({ 47 | appName, 48 | userId, 49 | state: initialState, 50 | }); 51 | 52 | const runner = new Runner({ 53 | appName, 54 | agent, 55 | artifactService, 56 | sessionService, 57 | }); 58 | 59 | const rl = readline.createInterface({ 60 | input: process.stdin, 61 | output: process.stdout, 62 | }); 63 | 64 | const ask = (q: string) => new Promise(resolve => rl.question(q, resolve)); 65 | 66 | console.log(`Agent "${agent.name}" started! Type "exit" to quit.\n`); 67 | 68 | let isRunning = true; 69 | while (isRunning) { 70 | const query = (await ask('[user]: ')).trim(); 71 | if (!query) continue; 72 | if (query === 'exit') { 73 | isRunning = false; 74 | break; 75 | } 76 | 77 | const content: Content = { 78 | role: 'user', 79 | parts: [{ text: query } as Part] 80 | }; 81 | 82 | for await (const event of runner.runAsync({ 83 | userId: session.userId, 84 | sessionId: session.id, 85 | newMessage: content, 86 | })) { 87 | if (event.content && event.content.parts) { 88 | const text = event.content.parts 89 | .map((part: Part) => part.text || '') 90 | .join(''); 91 | 92 | if (text) { 93 | console.log(`[${event.author}]: ${text}`); 94 | } 95 | } 96 | } 97 | } 98 | 99 | rl.close(); 100 | } 101 | -------------------------------------------------------------------------------- /src/tools/LoadMemoryTool.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { FunctionTool } from './FunctionTool'; 18 | import { ToolContext } from './ToolContext'; 19 | import { MemoryEntry } from '../memory/MemoryEntry'; 20 | import { LlmRequest } from '../models/LlmRequest'; 21 | import { FunctionDeclaration } from './BaseTool'; 22 | 23 | /** 24 | * Response from loading memory. 25 | */ 26 | export interface LoadMemoryResponse { 27 | memories: MemoryEntry[]; 28 | } 29 | 30 | /** 31 | * Loads the memory for the current user. 32 | * 33 | * @param params The function parameters 34 | * @param toolContext The tool context 35 | * @returns A LoadMemoryResponse containing memory entries 36 | */ 37 | async function loadMemory( 38 | params: Record, 39 | toolContext: ToolContext 40 | ): Promise { 41 | const query = params.query; 42 | const searchMemoryResponse = await toolContext.searchMemory(query); 43 | return { memories: searchMemoryResponse.memories }; 44 | } 45 | 46 | /** 47 | * A tool that loads the memory for the current user. 48 | * 49 | * NOTE: Currently this tool only uses text part from the memory. 50 | */ 51 | export class LoadMemoryTool extends FunctionTool { 52 | constructor() { 53 | super({ 54 | name: 'load_memory', 55 | description: 'Loads the memory for the current user', 56 | fn: loadMemory, 57 | functionDeclaration: { 58 | name: 'load_memory', 59 | description: 'Loads the memory for the current user', 60 | parameters: { 61 | type: 'object', 62 | properties: { 63 | query: { 64 | type: 'string', 65 | description: 'The query to search memory for' 66 | } 67 | }, 68 | required: ['query'] 69 | } 70 | } 71 | }); 72 | } 73 | 74 | async processLlmRequest(params: { 75 | toolContext: ToolContext; 76 | llmRequest: LlmRequest; 77 | }): Promise { 78 | await super.processLlmRequest(params); 79 | 80 | // Tell the model about the memory 81 | params.llmRequest.appendInstructions([ 82 | 'You have memory. You can use it to answer questions. If any questions need you to look up the memory, you should call load_memory function with a query.' 83 | ]); 84 | } 85 | } 86 | 87 | export const loadMemoryTool = new LoadMemoryTool(); -------------------------------------------------------------------------------- /src/tools/BuiltInCodeExecutionTool.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { BaseTool } from './BaseTool'; 4 | import { ToolContext } from './ToolContext'; 5 | 6 | /** 7 | * A built-in code execution tool that is automatically invoked by Gemini 2 models. 8 | * 9 | * This tool operates internally within the model and does not require or perform 10 | * local code execution. 11 | * 12 | * @deprecated No longer supported. Please use the new BuiltInCodeExecutor instead. 13 | */ 14 | export class BuiltInCodeExecutionTool extends BaseTool { 15 | /** 16 | * Creates a new built-in code execution tool 17 | */ 18 | constructor() { 19 | // Name and description are not used because this is a model built-in tool 20 | super({ 21 | name: 'code_execution', 22 | description: 'A built-in tool that enables Gemini models to execute code' 23 | }); 24 | } 25 | 26 | /** 27 | * Process the LLM request to enable code execution capability 28 | * 29 | * @param params Parameters for processing 30 | * @param params.toolContext The tool context 31 | * @param params.llmRequest The LLM request to process 32 | */ 33 | async processLlmRequest({ 34 | toolContext, 35 | llmRequest 36 | }: { 37 | toolContext: ToolContext, 38 | llmRequest: any 39 | }): Promise { 40 | console.warn( 41 | 'BuiltInCodeExecutionTool is deprecated. Please use the new BuiltInCodeExecutor instead.' 42 | ); 43 | 44 | // Ensure the model is Gemini 2.x 45 | const model = llmRequest.model || ''; 46 | 47 | if (!model.startsWith('gemini-2')) { 48 | throw new Error(`Code execution tool is not supported for model ${model}`); 49 | } 50 | 51 | // Ensure config exists 52 | if (!llmRequest.config) { 53 | llmRequest.config = {}; 54 | } 55 | 56 | // Ensure tools array exists 57 | if (!llmRequest.config.tools) { 58 | llmRequest.config.tools = []; 59 | } 60 | 61 | // Add code execution tool to the tools array 62 | llmRequest.config.tools.push({ 63 | codeExecution: {} // Empty object for default configuration 64 | }); 65 | } 66 | 67 | /** 68 | * Execute the built-in code execution tool 69 | * 70 | * This method doesn't actually execute code locally. The code execution happens 71 | * internally within the model. 72 | * 73 | * @param params The parameters for tool execution 74 | * @param context The context for tool execution 75 | * @returns A placeholder response 76 | */ 77 | async execute( 78 | params: Record, 79 | context: ToolContext 80 | ): Promise { 81 | // This tool doesn't actually execute code locally - it's handled by the model 82 | return { 83 | status: 'error', 84 | message: 'BuiltInCodeExecutionTool cannot be executed directly. It is handled internally by the model.' 85 | }; 86 | } 87 | } 88 | 89 | /** 90 | * Singleton instance of the Built-in Code Execution tool 91 | */ 92 | export const builtInCodeExecution = new BuiltInCodeExecutionTool(); -------------------------------------------------------------------------------- /tests/integration/multiAgent.test.ts: -------------------------------------------------------------------------------- 1 | import { setBackendEnvironment, restoreBackendEnvironment } from './testConfig'; 2 | import { AgentEvaluator } from '../../src/evaluation'; 3 | import { rootAgent } from './fixture/trip_planner_agent/agent'; 4 | /** 5 | * Tests for evaluating multi-agent systems 6 | */ 7 | describe('Multi-Agent Tests', () => { 8 | // Run tests against both backends if configured 9 | const backends: ('GOOGLE_AI' | 'VERTEX')[] = 10 | process.env.TEST_BACKEND === 'BOTH' 11 | ? ['GOOGLE_AI', 'VERTEX'] 12 | : [process.env.TEST_BACKEND as 'GOOGLE_AI' | 'VERTEX']; 13 | 14 | backends.forEach(backend => { 15 | describe(`Using ${backend} backend`, () => { 16 | let originalBackend: string | undefined; 17 | 18 | beforeAll(() => { 19 | originalBackend = setBackendEnvironment(backend); 20 | }); 21 | 22 | afterAll(async () => { 23 | // Add a delay to ensure all operations complete before restoring the environment 24 | await new Promise(resolve => setTimeout(resolve, 1000)); 25 | restoreBackendEnvironment(originalBackend); 26 | }); 27 | 28 | beforeEach(() => { 29 | // Skip these tests if environment not properly configured 30 | if (backend === 'GOOGLE_AI' && !process.env.GOOGLE_API_KEY) { 31 | console.warn('Skipping test: GOOGLE_API_KEY not set'); 32 | return; 33 | } 34 | 35 | if (backend === 'VERTEX' && (!process.env.GOOGLE_CLOUD_PROJECT || !process.env.GOOGLE_CLOUD_LOCATION)) { 36 | console.warn('Skipping test: GOOGLE_CLOUD_PROJECT or GOOGLE_CLOUD_LOCATION not set'); 37 | return; 38 | } 39 | }); 40 | 41 | it('should evaluate a multi-agent system', async () => { 42 | try { 43 | // Mimic the Python test 44 | const results = await AgentEvaluator.evaluate({ 45 | agent: rootAgent, 46 | evalDatasetFilePathOrDir: 'tests/integration/fixture/trip_planner_agent/trip_inquiry.test.json', 47 | initialSessionFile: 'tests/integration/fixture/trip_planner_agent/initial.session.json', 48 | numRuns: 4 49 | }); 50 | 51 | // Assert that results were generated correctly 52 | expect(results).toBeDefined(); 53 | expect(results.length).toBeGreaterThan(0); 54 | expect(results.every(result => result.success)).toBe(true); 55 | 56 | // Add a short delay at the end to ensure all async operations complete 57 | await new Promise(resolve => setTimeout(resolve, 500)); 58 | } catch (error) { 59 | console.error('Test failed with error:', error); 60 | throw error; 61 | } 62 | }, 60000); // Increase timeout to 60 seconds for this test 63 | }); 64 | }); 65 | 66 | // Add a final delay after all tests complete to ensure pending operations complete 67 | afterAll(async () => { 68 | // This prevents the "Cannot log after tests are done" errors 69 | await new Promise(resolve => setTimeout(resolve, 2000)); 70 | }); 71 | }); -------------------------------------------------------------------------------- /src/memory/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Memory module - Provides memory systems for agents 3 | */ 4 | 5 | // Export the BaseMemoryService interface and implementations 6 | export type { BaseMemoryService, MemoryResult, SearchMemoryResponse } from './BaseMemoryService'; 7 | export { InMemoryMemoryService } from './InMemoryMemoryService'; 8 | export { VertexAiRagMemoryService } from './VertexAiRagMemoryService'; 9 | export type { VertexAiRagConfig } from './VertexAiRagMemoryService'; 10 | 11 | // Export the MemoryEntry interface and implementation 12 | export type { MemoryEntry } from './MemoryEntry'; 13 | export { MemoryEntryImpl } from './MemoryEntry'; 14 | 15 | // Export utility functions 16 | export { formatTimestamp } from './utils'; 17 | 18 | /** 19 | * Base Memory interface that all memory implementations should implement 20 | */ 21 | export interface BaseMemory { 22 | add(item: any): void; 23 | get(query?: any): any; 24 | clear(): void; 25 | [key: string]: any; 26 | } 27 | 28 | /** 29 | * Default memory implementation 30 | */ 31 | export class Memory implements BaseMemory { 32 | private items: any[] = []; 33 | 34 | /** 35 | * Add an item to memory 36 | * @param item The item to add to memory 37 | */ 38 | add(item: any): void { 39 | this.items.push({ 40 | timestamp: Date.now(), 41 | content: item 42 | }); 43 | } 44 | 45 | /** 46 | * Get items from memory, optionally filtered by a query 47 | * @param query Optional query to filter memory items 48 | * @returns Matching memory items 49 | */ 50 | get(query?: any): any { 51 | if (!query) { 52 | return [...this.items]; 53 | } 54 | 55 | // Simple implementation - will be expanded in the future 56 | return this.items.filter(item => 57 | JSON.stringify(item).includes(JSON.stringify(query)) 58 | ); 59 | } 60 | 61 | /** 62 | * Clear all items from memory 63 | */ 64 | clear(): void { 65 | this.items = []; 66 | } 67 | } 68 | 69 | /** 70 | * Conversation memory specialized for chat history 71 | */ 72 | export class ConversationMemory implements BaseMemory { 73 | private messages: any[] = []; 74 | 75 | /** 76 | * Add a message to the conversation 77 | * @param item Message to add 78 | */ 79 | add(item: any): void { 80 | this.messages.push({ 81 | timestamp: Date.now(), 82 | role: item.role || 'user', 83 | content: item.content 84 | }); 85 | } 86 | 87 | /** 88 | * Get conversation history 89 | * @param query Optional query to filter messages 90 | * @returns Conversation messages 91 | */ 92 | get(query?: any): any { 93 | if (!query) { 94 | return [...this.messages]; 95 | } 96 | 97 | // Simple implementation - will be expanded in the future 98 | return this.messages.filter(msg => 99 | JSON.stringify(msg).includes(JSON.stringify(query)) 100 | ); 101 | } 102 | 103 | /** 104 | * Clear conversation history 105 | */ 106 | clear(): void { 107 | this.messages = []; 108 | } 109 | } -------------------------------------------------------------------------------- /src/tools/GoogleSearchTool.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { BaseTool } from './BaseTool'; 4 | import { ToolContext } from './ToolContext'; 5 | 6 | /** 7 | * A built-in tool that is automatically invoked by Gemini models to retrieve search results from Google Search. 8 | * 9 | * This tool operates internally within the model and does not require or perform 10 | * local code execution. 11 | */ 12 | export class GoogleSearchTool extends BaseTool { 13 | /** 14 | * Creates a new Google Search tool 15 | */ 16 | constructor() { 17 | // Name and description are not used because this is a model built-in tool 18 | super({ 19 | name: 'google_search', 20 | description: 'A built-in tool that retrieves search results from Google Search' 21 | }); 22 | } 23 | 24 | /** 25 | * Process the LLM request to configure Google Search capability 26 | * 27 | * @param params Parameters for processing 28 | * @param params.toolContext The tool context 29 | * @param params.llmRequest The LLM request to process 30 | */ 31 | async processLlmRequest({ 32 | toolContext, 33 | llmRequest 34 | }: { 35 | toolContext: ToolContext, 36 | llmRequest: any 37 | }): Promise { 38 | // Ensure config exists 39 | if (!llmRequest.config) { 40 | llmRequest.config = {}; 41 | } 42 | 43 | // Ensure tools array exists 44 | if (!llmRequest.config.tools) { 45 | llmRequest.config.tools = []; 46 | } 47 | 48 | // Configure based on model 49 | const model = llmRequest.model || ''; 50 | 51 | if (model.startsWith('gemini-1')) { 52 | // Gemini 1.x doesn't support Google Search alongside other tools 53 | if (llmRequest.config.tools.length > 0) { 54 | throw new Error('Google search tool cannot be used with other tools in Gemini 1.x.'); 55 | } 56 | 57 | // Add Google Search Retrieval for Gemini 1.x 58 | llmRequest.config.tools.push({ 59 | googleSearchRetrieval: {} 60 | }); 61 | } else if (model.startsWith('gemini-2')) { 62 | // Add Google Search for Gemini 2.x 63 | llmRequest.config.tools.push({ 64 | googleSearch: {} 65 | }); 66 | } else { 67 | throw new Error(`Google search tool is not supported for model ${model}`); 68 | } 69 | } 70 | 71 | /** 72 | * Execute the Google Search tool 73 | * 74 | * This is a placeholder as the actual execution happens internally in the model. 75 | * 76 | * @param params The parameters for tool execution 77 | * @param context The context for tool execution 78 | * @returns A placeholder response 79 | */ 80 | async execute( 81 | params: Record, 82 | context: ToolContext 83 | ): Promise { 84 | // This tool doesn't actually execute locally - it's handled by the model 85 | return { 86 | status: 'error', 87 | message: 'GoogleSearchTool cannot be executed directly. It is handled internally by the model.' 88 | }; 89 | } 90 | } 91 | 92 | /** 93 | * Singleton instance of the Google Search tool 94 | */ 95 | export const googleSearch = new GoogleSearchTool(); -------------------------------------------------------------------------------- /src/cli/browser/adk_favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /docs/get-started/installation.md: -------------------------------------------------------------------------------- 1 | # Installing ADK TypeScript 2 | 3 | This guide explains how to install the Agent Development Kit (ADK) for TypeScript and set up your development environment. 4 | 5 | ## Prerequisites 6 | 7 | ADK TypeScript requires: 8 | - Node.js (v18 or higher) 9 | - npm (included with Node.js) or yarn 10 | 11 | If you don't have Node.js installed, download and install it from [nodejs.org](https://nodejs.org/). 12 | 13 | ## Installation 14 | 15 | Install ADK TypeScript locally in your project and use `npx` to run commands. This ensures consistent versions across development and production environments. 16 | 17 | ```bash 18 | # Create your project directory 19 | mkdir my-adk-project 20 | cd my-adk-project 21 | 22 | # Create your first agent (this will set up everything automatically) 23 | npx adk create my-weather-agent 24 | ``` 25 | 26 | The `npx adk create` command will automatically: 27 | - Initialize your project with `package.json` and `tsconfig.json` (if not already present) 28 | - Install all necessary dependencies including ADK TypeScript 29 | - Set up the project structure 30 | - Create your agent with sample code 31 | - Walk you through configuring your LLM backend 32 | 33 | ## Using the ADK Commands 34 | 35 | Use `npx adk` to run all commands: 36 | 37 | ```bash 38 | # Create a new agent project 39 | npx adk create my-weather-agent 40 | 41 | # Run an agent in terminal 42 | npx adk run my-weather-agent 43 | 44 | # Start the web UI 45 | npx adk web my-weather-agent 46 | 47 | # Run the API server 48 | npx adk api_server --agent_dir my-weather-agent 49 | 50 | # Evaluate an agent 51 | npx adk eval my-weather-agent eval_data.test.json 52 | 53 | # Generate agent graph 54 | npx adk graph my-weather-agent --output graph.png 55 | ``` 56 | 57 | ## Project Structure 58 | 59 | When you create an agent with `npx adk create`, it generates an efficient, scalable project structure: 60 | 61 | ```console 62 | my-adk-project/ # Your parent project folder 63 | ├── my-weather-agent/ # Your agent's code folder 64 | │ ├── agent.ts # Agent definition 65 | │ └── .env # API keys for this agent 66 | ├── package.json # SHARED Node.js project manifest 67 | ├── tsconfig.json # SHARED TypeScript configuration 68 | └── dist/ # (Created after build) Compiled output 69 | ``` 70 | 71 | This structure allows multiple agents to share dependencies while keeping their code and configuration separate. 72 | 73 | ## Building and Running 74 | 75 | After creating your agent, you need to build and run it: 76 | 77 | ```bash 78 | # Install dependencies (only needed once, or if you add more agents) 79 | npm install 80 | 81 | # Build your TypeScript code 82 | npm run build 83 | 84 | # Run your agent 85 | npx adk run my-weather-agent 86 | # or start the web UI 87 | npx adk web my-weather-agent 88 | ``` 89 | 90 | ## Next Steps 91 | 92 | - Jump into the [**Quickstart**](./quickstart.md) to create your first agent 93 | - Learn about [**ADK TypeScript Concepts**](../agents/index.md) to understand the core components 94 | - Explore the [**Tutorial**](./tutorial.md) for building multi-agent systems -------------------------------------------------------------------------------- /src/models/LlmRegistry.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { BaseLlm } from './BaseLlm'; 4 | 5 | /** 6 | * Type for LLM constructor classes that have the supportedModels static method 7 | */ 8 | type LlmConstructor = { 9 | new (model: string): BaseLlm; 10 | supportedModels(): string[]; 11 | }; 12 | 13 | /** 14 | * Registry for LLMs. 15 | * Key is the regex that matches the model name. 16 | * Value is the constructor class that implements the model. 17 | */ 18 | const llmRegistryDict: Map = new Map(); 19 | 20 | /** 21 | * Registry for LLMs. 22 | */ 23 | export class LlmRegistry { 24 | /** 25 | * Creates a new LLM instance. 26 | * @param model The model name. 27 | * @returns The LLM instance. 28 | */ 29 | static newLlm(model: string): BaseLlm { 30 | return new (LlmRegistry.resolve(model))(model); 31 | } 32 | 33 | /** 34 | * Registers a new LLM class. 35 | * @param modelNameRegex The regex that matches the model name. 36 | * @param llmClass The constructor class that implements the model. 37 | */ 38 | private static _register( 39 | modelNameRegex: string, 40 | llmClass: LlmConstructor 41 | ): void { 42 | if (llmRegistryDict.has(modelNameRegex)) { 43 | const existingClass = llmRegistryDict.get(modelNameRegex); 44 | console.info( 45 | `Updating LLM class for ${modelNameRegex} from ${existingClass?.name || 'unknown'} to ${llmClass.name}` 46 | ); 47 | } 48 | 49 | llmRegistryDict.set(modelNameRegex, llmClass); 50 | } 51 | 52 | /** 53 | * Registers a new LLM class. 54 | * @param llmClass The constructor class that implements the model. 55 | */ 56 | static register(llmClass: LlmConstructor): void { 57 | for (const regex of llmClass.supportedModels()) { 58 | LlmRegistry._register(regex, llmClass); 59 | } 60 | } 61 | 62 | /** 63 | * Simple LRU cache implementation 64 | */ 65 | private static resolveCache: Map = new Map(); 66 | private static readonly MAX_CACHE_SIZE = 32; 67 | 68 | /** 69 | * Resolves the model to a BaseLlm constructor. 70 | * @param model The model name. 71 | * @returns The BaseLlm constructor. 72 | * @throws Error if the model is not found. 73 | */ 74 | static resolve(model: string): LlmConstructor { 75 | // Check cache first 76 | const cachedConstructor = LlmRegistry.resolveCache.get(model); 77 | if (cachedConstructor) { 78 | return cachedConstructor; 79 | } 80 | 81 | // Search for matching regex 82 | for (const [regex, llmClass] of llmRegistryDict.entries()) { 83 | if (new RegExp(`^${regex}$`).test(model)) { 84 | // Add to cache 85 | if (LlmRegistry.resolveCache.size >= LlmRegistry.MAX_CACHE_SIZE) { 86 | // Remove oldest entry if cache is full 87 | const firstKey = LlmRegistry.resolveCache.keys().next().value; 88 | if (firstKey) { 89 | LlmRegistry.resolveCache.delete(firstKey); 90 | } 91 | } 92 | LlmRegistry.resolveCache.set(model, llmClass); 93 | return llmClass; 94 | } 95 | } 96 | 97 | throw new Error(`Model ${model} not found.`); 98 | } 99 | } -------------------------------------------------------------------------------- /tests/integration/fixture/context_update_test/agent.ts: -------------------------------------------------------------------------------- 1 | import { LlmAgent as Agent } from '../../../../src/agents'; 2 | import { ToolContext } from '../../../../src/tools'; 3 | import { LlmRegistry } from '../../../../src/models'; 4 | import { FunctionTool } from '../../../../src/tools/FunctionTool'; 5 | import { AutoFlow } from '../../../../src/flows/llm_flows/AutoFlow'; 6 | 7 | /** 8 | * Updates context variables with provided data 9 | * @param dataOne First data element (string) 10 | * @param dataTwo Second data element (number or string) 11 | * @param dataThree Third data element (array of strings) 12 | * @param dataFour Fourth data element (array of numbers or strings) 13 | * @param toolContext The tool context 14 | */ 15 | function updateContext( 16 | dataOne: string, 17 | dataTwo: number | string, 18 | dataThree: string[], 19 | dataFour: (number | string)[], 20 | toolContext: ToolContext 21 | ): void { 22 | toolContext.actions.stateDelta['data_one'] = dataOne; 23 | toolContext.actions.stateDelta['data_two'] = dataTwo; 24 | toolContext.actions.stateDelta['data_three'] = dataThree; 25 | toolContext.actions.stateDelta['data_four'] = dataFour; 26 | } 27 | 28 | // Create model instance 29 | const geminiModel = LlmRegistry.newLlm('gemini-2.0-flash'); 30 | 31 | // Create flow instance 32 | const autoFlow = new AutoFlow(); 33 | 34 | /** 35 | * Root agent for context update testing 36 | */ 37 | export const contextUpdateRootAgent = new Agent({ 38 | name: 'root_agent', 39 | model: geminiModel, 40 | instruction: 'Call tools', 41 | flow: autoFlow, 42 | tools: [ 43 | new FunctionTool({ 44 | name: 'update_fc', 45 | description: 'Simply ask to update these variables in the context', 46 | fn: async (params) => updateContext( 47 | params.data_one, 48 | params.data_two, 49 | params.data_three, 50 | params.data_four, 51 | params.tool_context 52 | ), 53 | functionDeclaration: { 54 | name: 'update_fc', 55 | description: 'Simply ask to update these variables in the context', 56 | parameters: { 57 | type: 'object', 58 | properties: { 59 | data_one: { 60 | type: 'string', 61 | description: 'First data element (string)' 62 | }, 63 | data_two: { 64 | type: 'string', // Using string type to handle both number and string 65 | description: 'Second data element (number or string)' 66 | }, 67 | data_three: { 68 | type: 'array', 69 | items: { 70 | type: 'string' 71 | }, 72 | description: 'Third data element (array of strings)' 73 | }, 74 | data_four: { 75 | type: 'array', 76 | items: { 77 | type: 'string' // Using string type to handle both number and string 78 | }, 79 | description: 'Fourth data element (array of numbers or strings)' 80 | } 81 | }, 82 | required: ['data_one', 'data_two', 'data_three', 'data_four'] 83 | } 84 | } 85 | }) 86 | ] 87 | }); -------------------------------------------------------------------------------- /src/flows/llm_flows/SingleFlow.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A simple flow that only executes one LLM call. 3 | */ 4 | import { InvocationContext } from '../../agents/InvocationContext'; 5 | import { Event } from '../../events/Event'; 6 | import { LlmRequest } from '../../models/LlmRequest'; 7 | import { BaseLlmFlow } from './BaseLlmFlow'; 8 | import { BaseLlmRequestProcessor, BaseLlmResponseProcessor } from './BaseLlmProcessor'; 9 | import * as basic from './basic'; 10 | import * as identity from './identity'; 11 | import * as instructions from './instructions'; 12 | import * as contents from './contents'; 13 | import * as nlPlanning from './NlPlanning'; 14 | import * as codeExecution from './CodeExecution'; 15 | 16 | /** 17 | * A simple flow that only executes one LLM call. 18 | * 19 | * This is the TypeScript equivalent of the Python SingleFlow implementation, 20 | * which automatically includes several request and response processors. 21 | */ 22 | export class SingleFlow extends BaseLlmFlow { 23 | /** 24 | * Constructs a new SingleFlow with the given request processors. 25 | * 26 | * @param additionalRequestProcessors Additional request processors to use 27 | * @param additionalResponseProcessors Additional response processors to use 28 | */ 29 | constructor( 30 | additionalRequestProcessors: BaseLlmRequestProcessor[] = [], 31 | additionalResponseProcessors: BaseLlmResponseProcessor[] = [] 32 | ) { 33 | super(); 34 | 35 | // Initialize with default request processors (matching Python implementation) 36 | this.requestProcessors = [ 37 | basic.requestProcessor, // Adds user content 38 | // auth_preprocessor.requestProcessor is not included in TS implementation yet 39 | instructions.requestProcessor, // Adds agent instructions 40 | identity.requestProcessor, // Adds agent identity 41 | contents.requestProcessor, // Adds content 42 | // Some implementations of NL Planning mark planning contents as thoughts 43 | // in the post processor. Since these need to be unmarked, NL Planning 44 | // should be after contents. 45 | nlPlanning.requestProcessor, // Adds NL planning 46 | // Code execution should be after the contents as it mutates the contents 47 | // to optimize data files. 48 | codeExecution.requestProcessor // Adds code execution 49 | ]; 50 | 51 | // Initialize response processors (matching Python implementation) 52 | this.responseProcessors = [ 53 | nlPlanning.responseProcessor, // Processes NL planning 54 | codeExecution.responseProcessor // Processes code execution 55 | ]; 56 | 57 | // Add any additional request processors 58 | if (additionalRequestProcessors.length > 0) { 59 | this.requestProcessors.push(...additionalRequestProcessors); 60 | } 61 | 62 | // Add any additional response processors 63 | if (additionalResponseProcessors.length > 0) { 64 | this.responseProcessors.push(...additionalResponseProcessors); 65 | } 66 | } 67 | 68 | // The _runOneStepAsync method is intentionally removed to inherit the implementation 69 | // from BaseLlmFlow, which matches the Python implementation and properly handles 70 | // function calls and response processing 71 | } --------------------------------------------------------------------------------