diff --git a/docs/src/content/docs/agents/built-in/google-agent.mdx b/docs/src/content/docs/agents/built-in/google-agent.mdx
new file mode 100644
index 00000000..d032a557
--- /dev/null
+++ b/docs/src/content/docs/agents/built-in/google-agent.mdx
@@ -0,0 +1,317 @@
+```markdown
+---
+title: Google AI Agent
+description: Documentation for the Google AI Agent
+---
+
+The `GoogleAIAgent` is a versatile agent class within the Multi-Agent Orchestrator framework, designed to integrate with Google's Gemini API. This agent empowers you to harness Google's advanced language models for a wide range of natural language processing tasks.
+
+## Key Features
+
+- Integration with Google's Gemini API
+- Support for various Gemini models
+- Streaming and non-streaming response options
+- Customizable inference configuration
+- Conversation history management for context-aware interactions
+- Flexible system prompt customization with variable support
+- Support for retrievers to enrich responses with external knowledge
+- Flexible initialization using API key or custom client
+
+## Configuration Options
+
+The `GoogleAIAgentOptions` class, which extends the base `AgentOptions`, provides the following configuration fields:
+
+### Required Fields
+
+- `name`: The agent's name.
+- `description`: A description of the agent's capabilities.
+- Authentication (choose one):
+ - `apiKey`: Your Google Gemini API key.
+ - `client`: A custom Google Gemini client instance.
+
+### Optional Fields
+- `model`: Google Gemini model identifier (e.g., 'gemini-pro'). Defaults to `GOOGLE_MODEL_ID_GEMINI_PRO`.
+- `streaming`: Enables streaming responses. Defaults to `false`.
+- `retriever`: A custom retriever instance for providing additional context.
+- `inferenceConfig`: Configuration for model inference:
+ - `maxOutputTokens`: Maximum tokens to generate (default: 1000).
+ - `temperature`: Controls randomness (0-1).
+ - `topP`: Controls diversity via nucleus sampling.
+ - `stopSequences`: Sequences that stop generation.
+- `customSystemPrompt`: System prompt configuration:
+ - `template`: Template string with optional variable placeholders.
+ - `variables`: Key-value pairs for template variables.
+
+## Creating a GoogleAIAgent
+
+Here are several examples demonstrating different ways to create and configure a `GoogleAIAgent`:
+
+### Basic Examples
+
+**1. Minimal Configuration**
+
+import { Tabs, TabItem } from '@astrojs/starlight/components';
+
+
+
+```typescript
+const agent = new GoogleAIAgent({
+ name: 'Google AI Assistant',
+ description: 'A versatile AI assistant',
+ apiKey: 'your-google-ai-api-key'
+});
+```
+
+
+
+
+
+**2. Using Custom Client**
+
+
+
+```typescript
+import { GoogleGenerativeAI } from '@google/generative-ai';
+const customClient = new GoogleGenerativeAI('your-google-ai-api-key');
+
+const agent = new GoogleAIAgent({
+ name: 'Google AI Assistant',
+ description: 'A versatile AI assistant',
+ client: customClient
+});
+```
+
+
+
+
+
+
+**3. Custom Model and Streaming**
+
+
+
+```typescript
+const agent = new GoogleAIAgent({
+ name: 'Google AI Assistant',
+ description: 'A streaming-enabled assistant',
+ apiKey: 'your-google-ai-api-key',
+ model: 'gemini-pro',
+ streaming: true
+});
+```
+
+
+
+
+
+
+**4. With Inference Configuration**
+
+
+
+```typescript
+const agent = new GoogleAIAgent({
+ name: 'Google AI Assistant',
+ description: 'An assistant with custom inference settings',
+ apiKey: 'your-google-ai-api-key',
+ inferenceConfig: {
+ maxOutputTokens: 500,
+ temperature: 0.7,
+ topP: 0.9,
+ stopSequences: ['Human:', 'AI:']
+ }
+});
+```
+
+
+
+
+
+**5. With Simple System Prompt**
+
+
+
+```typescript
+const agent = new GoogleAIAgent({
+ name: 'Google AI Assistant',
+ description: 'An assistant with custom prompt',
+ apiKey: 'your-google-ai-api-key',
+ customSystemPrompt: {
+ template: 'You are a helpful AI assistant focused on technical support.'
+ }
+});
+```
+
+
+
+
+
+**6. With System Prompt Variables**
+
+
+
+```typescript
+const agent = new GoogleAIAgent({
+ name: 'Google AI Assistant',
+ description: 'An assistant with variable prompt',
+ apiKey: 'your-google-ai-api-key',
+ customSystemPrompt: {
+ template: 'You are an AI assistant specialized in {{DOMAIN}}. Always use a {{TONE}} tone.',
+ variables: {
+ DOMAIN: 'customer support',
+ TONE: 'friendly and helpful'
+ }
+ }
+});
+```
+
+
+
+
+
+**7. With Custom Retriever**
+
+
+
+```typescript
+const retriever = new CustomRetriever({
+ // Retriever configuration
+});
+const agent = new GoogleAIAgent({
+ name: 'Google AI Assistant',
+ description: 'An assistant with retriever',
+ apiKey: 'your-google-ai-api-key',
+ retriever: retriever
+});
+```
+
+
+
+
+**8. Combining Multiple Options**
+
+
+
+```typescript
+const agent = new GoogleAIAgent({
+ name: 'Google AI Assistant',
+ description: 'An assistant with multiple options',
+ apiKey: 'your-google-ai-api-key',
+ model: 'gemini-pro',
+ streaming: true,
+ inferenceConfig: {
+ maxOutputTokens: 500,
+ temperature: 0.7
+ },
+ customSystemPrompt: {
+ template: 'You are an AI assistant specialized in {{DOMAIN}}.',
+ variables: {
+ DOMAIN: 'technical support'
+ }
+ }
+});
+```
+
+
+
+
+**9. Complete Example with All Options**
+
+Here's a comprehensive example demonstrating all available configuration options:
+
+
+
+```typescript
+import { GoogleAIAgent } from 'multi-agent-orchestrator';
+
+const agent = new GoogleAIAgent({
+ // Required fields
+ name: 'Advanced Google AI Assistant',
+ description: 'A fully configured AI assistant powered by Google Gemini',
+ apiKey: 'your-google-ai-api-key',
+
+ // Optional fields
+ model: 'gemini-pro', // Choose Google Gemini model
+ streaming: true, // Enable streaming responses
+ retriever: customRetriever, // Custom retriever for additional context
+
+ // Inference configuration
+ inferenceConfig: {
+ maxOutputTokens: 500, // Maximum tokens to generate
+ temperature: 0.7, // Control randomness (0-1)
+ topP: 0.9, // Control diversity via nucleus sampling
+ stopSequences: ['Human:', 'AI:'] // Sequences that stop generation
+ },
+
+ // Custom system prompt with variables
+ customSystemPrompt: {
+ template: `You are an AI assistant specialized in {{DOMAIN}}.
+ Your core competencies:
+ {{SKILLS}}
+
+ Communication style:
+ - Maintain a {{TONE}} tone
+ - Focus on {{FOCUS}}
+ - Prioritize {{PRIORITY}}`,
+ variables: {
+ DOMAIN: 'scientific research',
+ SKILLS: [
+ '- Advanced data analysis',
+ '- Statistical methodology',
+ '- Research design',
+ '- Technical writing'
+ ],
+ TONE: 'professional and academic',
+ FOCUS: 'accuracy and clarity',
+ PRIORITY: 'evidence-based insights'
+ }
+ }
+});
+```
+
+
+
+## Using the GoogleAIAgent
+
+You can use the `GoogleAIAgent` either directly or within the Multi-Agent Orchestrator framework.
+
+### Direct Usage
+
+Call the agent directly when you need to use a single agent without orchestrator routing:
+
+
+
+```typescript
+const classifierResult = {
+ selectedAgent: agent,
+ confidence: 1.0
+};
+
+const response = await orchestrator.agentProcessRequest(
+ "What is the capital of France?",
+ "user123",
+ "session456",
+ classifierResult
+);
+```
+
+
+
+### Using with the Orchestrator
+
+Add the agent to the Multi-Agent Orchestrator for use in a multi-agent system:
+
+
+
+```typescript
+const orchestrator = new MultiAgentOrchestrator();
+orchestrator.addAgent(agent);
+
+const response = await orchestrator.routeRequest(
+ "What is the capital of France?",
+ "user123",
+ "session456"
+);
+```
+
+
diff --git a/typescript/package-lock.json b/typescript/package-lock.json
index 494c1b35..ef098ea9 100644
--- a/typescript/package-lock.json
+++ b/typescript/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "multi-agent-orchestrator",
- "version": "0.1.1",
+ "version": "0.1.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "multi-agent-orchestrator",
- "version": "0.1.1",
+ "version": "0.1.2",
"license": "Apache-2.0",
"dependencies": {
"@anthropic-ai/sdk": "^0.24.3",
@@ -18,8 +18,8 @@
"@aws-sdk/client-lex-runtime-v2": "^3.621.0",
"@aws-sdk/lib-dynamodb": "^3.621.0",
"@aws-sdk/util-dynamodb": "^3.621.0",
+ "@google/generative-ai": "^0.21.0",
"axios": "^1.7.2",
- "chai": "^5.1.2",
"eslint-config-prettier": "^9.1.0",
"natural": "^7.0.7",
"openai": "^4.52.7",
@@ -2524,6 +2524,14 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
+ "node_modules/@google/generative-ai": {
+ "version": "0.21.0",
+ "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.21.0.tgz",
+ "integrity": "sha512-7XhUbtnlkSEZK15kN3t+tzIMxsbKm/dSkKBFalj+20NvPKe1kBY7mR2P7vuijEn+f06z5+A8bVGKO0v39cr6Wg==",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
"node_modules/@humanwhocodes/config-array": {
"version": "0.11.14",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
@@ -4271,14 +4279,6 @@
"node": ">=8"
}
},
- "node_modules/assertion-error": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
- "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
- "engines": {
- "node": ">=12"
- }
- },
"node_modules/async": {
"version": "2.6.4",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
@@ -4598,21 +4598,6 @@
}
]
},
- "node_modules/chai": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz",
- "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==",
- "dependencies": {
- "assertion-error": "^2.0.1",
- "check-error": "^2.1.1",
- "deep-eql": "^5.0.1",
- "loupe": "^3.1.0",
- "pathval": "^2.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -4637,14 +4622,6 @@
"node": ">=10"
}
},
- "node_modules/check-error": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
- "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
- "engines": {
- "node": ">= 16"
- }
- },
"node_modules/ci-info": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
@@ -4825,14 +4802,6 @@
}
}
},
- "node_modules/deep-eql": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
- "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -6861,11 +6830,6 @@
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
},
- "node_modules/loupe": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz",
- "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg=="
- },
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -7498,14 +7462,6 @@
"node": ">=8"
}
},
- "node_modules/pathval": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz",
- "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==",
- "engines": {
- "node": ">= 14.16"
- }
- },
"node_modules/pg": {
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.12.0.tgz",
diff --git a/typescript/package.json b/typescript/package.json
index 0b57005d..848e51fc 100644
--- a/typescript/package.json
+++ b/typescript/package.json
@@ -36,6 +36,7 @@
"@aws-sdk/client-lex-runtime-v2": "^3.621.0",
"@aws-sdk/lib-dynamodb": "^3.621.0",
"@aws-sdk/util-dynamodb": "^3.621.0",
+ "@google/generative-ai": "^0.21.0",
"axios": "^1.7.2",
"eslint-config-prettier": "^9.1.0",
"natural": "^7.0.7",
diff --git a/typescript/src/agents/googleAIAgent.ts b/typescript/src/agents/googleAIAgent.ts
new file mode 100644
index 00000000..2360fb1b
--- /dev/null
+++ b/typescript/src/agents/googleAIAgent.ts
@@ -0,0 +1,88 @@
+import { Agent, type AgentOptions } from "./agent";;
+import { type ConversationMessage, ParticipantRole } from "../types";
+import { GenerativeModel, GoogleGenerativeAI } from "@google/generative-ai";
+import { Logger } from "../utils/logger";
+
+export interface GoogleAIAgentOptions extends AgentOptions {
+ apiKey: string;
+ modelId?: string;
+ baseUrl?: string;
+ streaming?: boolean;
+ inferenceConfig?: {
+ maxOutputTokens?: number;
+ temperature?: number;
+ topP?: number;
+ stopSequences?: string[];
+ };
+}
+
+const DEFAULT_MAX_OUTPUT_TOKENS = 1000;
+
+export class GoogleAIAgent extends Agent {
+ private genAI: GoogleGenerativeAI;
+ private modelId: string;
+ private client: GenerativeModel;
+ private baseUrl: string | undefined;
+ private streaming: boolean;
+ private inferenceConfig: {
+ maxOutputTokens?: number;
+ temperature?: number;
+ topP?: number;
+ stopSequences?: string[];
+ };
+
+ constructor(options: GoogleAIAgentOptions) {
+ super(options);
+ this.genAI = new GoogleGenerativeAI(options.apiKey);
+ this.modelId = options.modelId ?? 'gemini-pro';
+ this.baseUrl = options.baseUrl;
+ this.client = this.genAI.getGenerativeModel({ model: this.modelId }, {baseUrl: this.baseUrl});
+ this.streaming = options.streaming ?? false;
+ this.inferenceConfig = {
+ maxOutputTokens: options.inferenceConfig?.maxOutputTokens ?? DEFAULT_MAX_OUTPUT_TOKENS,
+ temperature: options.inferenceConfig?.temperature,
+ topP: options.inferenceConfig?.topP,
+ stopSequences: options.inferenceConfig?.stopSequences,
+ };
+ }
+ /* eslint-disable @typescript-eslint/no-unused-vars */
+ async processRequest(
+ inputText: string,
+ userId: string,
+ sessionId: string,
+ chatHistory: ConversationMessage[],
+ additionalParams?: Record
+ ): Promise> {
+ const chat = this.client.startChat();
+
+ for (const msg of chatHistory) {
+ chat.sendMessage(msg.role.toLowerCase(), msg.content ? msg.content[0]?.text || '' : '');
+ }
+
+ const { maxOutputTokens, temperature, topP, stopSequences } = this.inferenceConfig;
+ const generationConfig = { maxOutputTokens, temperature, topP, stopSequences };
+ if (this.streaming) {
+ return this.handleStreamingResponse(chat, inputText, generationConfig);
+ } else {
+ return this.handleSingleResponse(chat, inputText, generationConfig);
+ }
+ }
+
+ private async handleSingleResponse(chat: any, inputText: string, generationConfig: any): Promise {
+ try {
+ const result = await chat.sendMessage(inputText, generationConfig);
+ const response = result.response;
+ return { role: ParticipantRole.ASSISTANT, content: [{ text: response.text() }] };
+ } catch (error) {
+ Logger.logger.error('Error in Google Generative AI call:', error);
+ throw error;
+ }
+ }
+
+ private async *handleStreamingResponse(chat: any, inputText: string, generationConfig: any): AsyncIterable {
+ const result = await chat.sendMessageStream(inputText, generationConfig);
+ for await (const chunk of result.stream) {
+ yield chunk.text();
+ }
+ }
+}
diff --git a/typescript/src/classifiers/googleAIClassifier.ts b/typescript/src/classifiers/googleAIClassifier.ts
new file mode 100644
index 00000000..7f61c4ed
--- /dev/null
+++ b/typescript/src/classifiers/googleAIClassifier.ts
@@ -0,0 +1,111 @@
+import { GenerativeModel, GoogleGenerativeAI, SchemaType, type Tool } from "@google/generative-ai";
+import { type ConversationMessage } from "../types";
+import { isClassifierToolInput } from "../utils/helpers";
+import { Logger } from "../utils/logger";
+import { Classifier, type ClassifierResult } from "./classifier"
+
+const GOOGLE_GENERATIVE_AI_MODEL_ID = 'gemini-pro';
+
+export interface GoogleGenerativeAIClassifierOptions {
+ modelId?: string;
+ inferenceConfig?: {
+ maxOutputTokens?: number;
+ temperature?: number;
+ topP?: number;
+ stopSequences?: string[];
+ };
+ apiKey: string;
+ baseUrl?: string;
+}
+
+export class GoogleAIClassifier extends Classifier {
+ private genAI: GoogleGenerativeAI;
+ private client: GenerativeModel;
+ protected inferenceConfig: {
+ maxOutputTokens?: number;
+ temperature?: number;
+ topP?: number;
+ stopSequences?: string[];
+ };
+ private baseUrl: string | undefined;
+ private tools: Tool[];
+
+ constructor(options: GoogleGenerativeAIClassifierOptions) {
+ super();
+
+ if (!options.apiKey) {
+ throw new Error("Google Generative AI API key is required");
+ }
+ this.genAI = new GoogleGenerativeAI(options.apiKey);
+ this.modelId = options.modelId || GOOGLE_GENERATIVE_AI_MODEL_ID;
+
+ const defaultMaxOutputTokens = 1000;
+ this.inferenceConfig = {
+ maxOutputTokens: options.inferenceConfig?.maxOutputTokens ?? defaultMaxOutputTokens,
+ temperature: options.inferenceConfig?.temperature,
+ topP: options.inferenceConfig?.topP,
+ stopSequences: options.inferenceConfig?.stopSequences,
+ };
+ this.baseUrl = options.baseUrl;
+ this.client = this.genAI.getGenerativeModel({ model: this.modelId }, {baseUrl: this.baseUrl});
+ this.tools = [{
+ functionDeclarations: [{
+ name: 'analyzePrompt',
+ description: 'Analyze the user input and provide structured output',
+ parameters: {
+ type: SchemaType.OBJECT,
+ properties: {
+ userinput: { type: SchemaType.STRING },
+ selected_agent: { type: SchemaType.STRING },
+ confidence: { type: SchemaType.NUMBER },
+ },
+ required: ['userinput', 'selected_agent', 'confidence'],
+ },
+ }],
+ }];
+ }
+ /* eslint-disable @typescript-eslint/no-unused-vars */
+ async processRequest(
+ inputText: string,
+ chatHistory: ConversationMessage[]
+ ): Promise {
+ const chat = this.client.startChat({
+ tools: this.tools,
+ systemInstruction: {
+ role: "system",
+ parts: [{text: this.systemPrompt}]
+ }
+ });
+
+ try {
+ const result = await chat.sendMessage(inputText, {
+ // maxOutputTokens: this.inferenceConfig.maxOutputTokens,
+ // temperature: this.inferenceConfig.temperature,
+ // topP: this.inferenceConfig.topP,
+ // stopSequences: this.inferenceConfig.stopSequences,
+ });
+
+ const toolCall = result.response.functionCalls()?.[0];
+
+ if (!toolCall || toolCall.name !== "analyzePrompt") {
+ throw new Error("No valid tool call found in the response");
+}
+
+ const toolInput = toolCall.args;
+
+ if (!isClassifierToolInput(toolInput)) {
+ throw new Error("Tool input does not match expected structure");
+ }
+
+ const intentClassifierResult: ClassifierResult = {
+ selectedAgent: this.getAgentById(toolInput.selected_agent),
+ confidence: parseFloat(toolInput.confidence),
+ };
+ return intentClassifierResult;
+
+ } catch (error) {
+ Logger.logger.error("Error processing request:", error);
+ throw error;
+ }
+ }
+}