Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions nodejs/vercel-sdk/sample-agent/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Anthropic Configuration
ANTHROPIC_API_KEY=

# Environment Settings
NODE_ENV=development

# Telemetry and Tracing Configuration
DEBUG=agents:*
AZURE_EXPERIMENTAL_ENABLE_ACTIVITY_SOURCE=true
AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED=true
OPENAI_AGENTS_DISABLE_TRACING=false
OTEL_SDK_DISABLED=false
CONNECTION_STRING=

# Use Agentic Authentication rather than OBO
USE_AGENTIC_AUTH=false

# Service Connection Settings
connections__service_connection__settings__clientId=
connections__service_connection__settings__clientSecret=
connections__service_connection__settings__tenantId=

# Set service connection as default
connectionsMap__0__serviceUrl=*
connectionsMap__0__connection=service_connection

# AgenticAuthentication Options
agentic_type=agentic
agentic_altBlueprintConnectionName=service_connection
agentic_scopes=ea9ffc3e-8a23-4a7d-836d-234d7c7565c1/.default # Prod Agentic scope
43 changes: 43 additions & 0 deletions nodejs/vercel-sdk/sample-agent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Sample Agent - Node.js Vercel AI SDK

This directory contains a sample agent implementation using Node.js and Vercel AI SDK.

## Demonstrates

This sample demonstrates how to build an agent using the Agent365 framework with Node.js and Vercel AI SDK.

## Prerequisites

- Node.js 18+
- Vercel AI SDK
- Agents SDK

## How to run this sample

1. **Setup environment variables**
```bash
# Copy the example environment file
cp .env.example .env
```

2. **Install dependencies**
```bash
npm install
```

3. **Build the project**
```bash
npm run build
```

4. **Start the agent**
```bash
npm start
```

5. **Optionally, while testing you can run in dev mode**
```bash
npm run dev
```

The agent will start and be ready to receive requests through the configured hosting mechanism.
40 changes: 40 additions & 0 deletions nodejs/vercel-sdk/sample-agent/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "vercel-sdk-sample",
"version": "2025.11.6",
"description": "Sample agent integrating Vercel AI SDK Agents with Microsoft 365 Agents SDK and Agent365 SDK",
"main": "src/index.ts",
"scripts": {
"preinstall": "node preinstall-local-packages.js",
"start": "node dist/index.js",
"dev": "nodemon --watch src/*.ts --exec ts-node src/index.ts",
"test-tool": "agentsplayground",
"eval": "node --env-file .env src/evals/index.js",
"build": "tsc"
},
"keywords": [
"vercel-ai-sdk",
"microsoft-365",
"agent",
"ai"
],
"license": "MIT",
"dependencies": {
"@ai-sdk/anthropic": "^2.0.31",
"@microsoft/agents-activity": "^1.1.0-alpha.85",
"@microsoft/agents-hosting": "^1.1.0-alpha.85",
"ai": "^5.0.72",
"dotenv": "^17.2.3",
"express": "^5.1.0",
"node-fetch": "^3.3.2",
"uuid": "^9.0.0"
},
"devDependencies": {
"@azure/monitor-opentelemetry-exporter": "^1.0.0-beta.32",
"@babel/cli": "^7.28.3",
"@babel/core": "^7.28.4",
"@babel/preset-env": "^7.28.3",
"@microsoft/m365agentsplayground": "^0.2.16",
"nodemon": "^3.1.10",
"ts-node": "^10.9.2"
}
}
72 changes: 72 additions & 0 deletions nodejs/vercel-sdk/sample-agent/preinstall-local-packages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/env node

import { readdir } from 'fs/promises';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import { execSync } from 'child_process';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// Look for *.tgz files two directories above
const tgzDir = join(__dirname, '../../');

// Define the installation order
const installOrder = [
'microsoft-agents-a365-runtime-',
'microsoft-agents-a365-notifications-',
'microsoft-agents-a365-observability-',
'microsoft-agents-a365-tooling-',
'microsoft-agents-a365-tooling-extensions-claude-'
];

async function findTgzFiles() {
try {
const files = await readdir(tgzDir);
return files.filter(file => file.endsWith('.tgz'));
} catch (error) {
console.log('No tgz directory found or no files to install');
return [];
}
}

function findFileForPattern(files, pattern) {
return files.find(file => file.startsWith(pattern));
}

async function installPackages() {
const tgzFiles = await findTgzFiles();

if (tgzFiles.length === 0) {
console.log('No .tgz files found in', tgzDir);
return;
}

console.log('Found .tgz files:', tgzFiles);

for (const pattern of installOrder) {
const file = findFileForPattern(tgzFiles, pattern);
if (file) {
const filePath = join(tgzDir, file);
console.log(`Installing ${file}...`);
try {
execSync(`npm install "${filePath}"`, {
stdio: 'inherit',
cwd: __dirname
});
console.log(`✓ Successfully installed ${file}`);
} catch (error) {
console.error(`✗ Failed to install ${file}:`, error.message);
process.exit(1);
}
} else {
console.log(`No file found matching pattern: ${pattern}`);
}
}
}

// Run the installation
installPackages().catch(error => {
console.error('Error during package installation:', error);
process.exit(1);
});
62 changes: 62 additions & 0 deletions nodejs/vercel-sdk/sample-agent/src/agent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { TurnState, AgentApplication, TurnContext, MemoryStorage } from '@microsoft/agents-hosting';
import { ActivityTypes } from '@microsoft/agents-activity';

// Notification Imports
import '@microsoft/agents-a365-notifications';
import { AgentNotificationActivity } from '@microsoft/agents-a365-notifications';

import { Client, getClient } from './client';

export class A365Agent extends AgentApplication<TurnState> {
agentName = "A365 Agent";

constructor() {
super({
startTypingTimer: true,
storage: new MemoryStorage(),
authorization: {
agentic: {
type: 'agentic',
} // scopes set in the .env file...
}
});

// Route agent notifications
this.onAgentNotification("agents:*", async (context: TurnContext, state: TurnState, agentNotificationActivity: AgentNotificationActivity) => {
await this.handleAgentNotificationActivity(context, state, agentNotificationActivity);
});

this.onActivity(ActivityTypes.Message, async (context: TurnContext, state: TurnState) => {
await this.handleAgentMessageActivity(context, state);
});
}

/**
* Handles incoming user messages and sends responses.
*/
async handleAgentMessageActivity(turnContext: TurnContext, state: TurnState): Promise<void> {
const userMessage = turnContext.activity.text?.trim() || '';

if (!userMessage) {
await turnContext.sendActivity('Please send me a message and I\'ll help you!');
return;
}

try {
const client: Client = await getClient();
const response = await client.invokeAgentWithScope(userMessage);
await turnContext.sendActivity(response);
} catch (error) {
console.error('LLM query error:', error);
const err = error as any;
await turnContext.sendActivity(`Error: ${err.message || err}`);
}
}

async handleAgentNotificationActivity(context: TurnContext, state: TurnState, agentNotificationActivity: AgentNotificationActivity) {
context.sendActivity("Recieved an AgentNotification!");
/* your logic here... */
}
}

export const agentApplication = new A365Agent();
115 changes: 115 additions & 0 deletions nodejs/vercel-sdk/sample-agent/src/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { Experimental_Agent as Agent } from "ai";
import { anthropic } from '@ai-sdk/anthropic';


// Observability Imports
import {
ObservabilityManager,
InferenceScope,
Builder,
InferenceOperationType,
AgentDetails,
TenantDetails,
InferenceDetails
} from '@microsoft/agents-a365-observability';

const modelName = 'claude-sonnet-4-20250514';

export interface Client {
invokeAgentWithScope(prompt: string): Promise<string>;
}

const sdk = ObservabilityManager.configure(
(builder: Builder) =>
builder
.withService('Vercel AI SDK Sample Agent', '1.0.0')
);

sdk.start();

/**
* Creates and configures a Vercel AI SDK client with anthropic model.
*
* This factory function initializes a Vercel AI SDK React agent with access to
*
* @returns Promise<Client> - Configured Vercel AI SDK client ready for agent interactions
*
* @example
* ```typescript
* const client = await getClient();
* const response = await client.invokeAgent("Hello, how are you?");
* ```
*/
export async function getClient(): Promise<Client> {
// Create the model
const model = anthropic(modelName)

// Create the agent
const agent = new Agent({
model: model,
});

return new VercelAiClient(agent);
}

/**
* VercelAiClient provides an interface to interact with Vercel AI SDK agents.
* It creates a React agent with tools and exposes an invokeAgent method.
*/
class VercelAiClient implements Client {
private agent: Agent<any, any, any>;

constructor(agent: any) {
this.agent = agent;
}

/**
* Sends a user message to the Vercel AI SDK agent and returns the AI's response.
* Handles streaming results and error reporting.
*
* @param {string} userMessage - The message or prompt to send to the agent.
* @returns {Promise<string>} The response from the agent, or an error message if the query fails.
*/
async invokeAgent(userMessage: string): Promise<string> {
const { text: agentMessage } = await this.agent.generate({
prompt: userMessage
});

if (!agentMessage) {
return "Sorry, I couldn't get a response from the agent :(";
}

return agentMessage;
}

async invokeAgentWithScope(prompt: string) {
const inferenceDetails: InferenceDetails = {
operationName: InferenceOperationType.CHAT,
model: modelName,
};

const agentDetails: AgentDetails = {
agentId: 'typescript-compliance-agent',
agentName: 'TypeScript Compliance Agent',
conversationId: 'conv-12345',
};

const tenantDetails: TenantDetails = {
tenantId: 'typescript-sample-tenant',
};

const scope = InferenceScope.start(inferenceDetails, agentDetails, tenantDetails);

const response = await this.invokeAgent(prompt);

// Record the inference response with token usage
scope?.recordOutputMessages([response]);
scope?.recordInputMessages([prompt]);
scope?.recordResponseId(`resp-${Date.now()}`);
scope?.recordInputTokens(45);
scope?.recordOutputTokens(78);
scope?.recordFinishReasons(['stop']);

return response;
}
}
Loading