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
3 changes: 3 additions & 0 deletions examples/first-agent/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dist
node_modules
package-lock.json
21 changes: 21 additions & 0 deletions examples/first-agent/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "first-agent",
"private": true,
"main": "dist/index.js",
"type": "module",
"scripts": {
"clean": "rm -rf dist node_modules package-lock.json",
"build": "tsc",
"start": "tsc && node dist/index.js"
},
"workspaces": [
"../../"
],
"dependencies": {
"@strands-agents/sdk": "*"
},
"devDependencies": {
"@types/node": "^20.0.0",
"typescript": "^5.5.0"
}
}
104 changes: 104 additions & 0 deletions examples/first-agent/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { type Tool, type ToolResult, type ToolContext, Agent, BedrockModel } from '@strands-agents/sdk'

// Define the shape of the expected input
type WeatherToolInput = {
location: string
}

// Type Guard: A function that performs a runtime check and informs the TS compiler.
function isValidInput(input: any): input is WeatherToolInput {
return input && typeof input.location === 'string'
}

class WeatherTool implements Tool {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are including an example in the sdk, lets use the zod tools instead of manually declaring it like this.

name = 'get_weather'
description = 'Get the current weather for a specific location.'

toolSpec = {
name: this.name,
description: this.description,
inputSchema: {
type: 'object' as const,
properties: {
location: {
type: 'string' as const,
description: 'The city and state, e.g., San Francisco, CA',
},
},
required: ['location'],
},
}

async *stream(context: ToolContext): AsyncGenerator<never, ToolResult, unknown> {
const input = context.toolUse.input

// Use the type guard for validation
if (!isValidInput(input)) {
throw new Error('Tool input must be an object with a string "location" property.')
}

// After this check, TypeScript knows `input` is `WeatherToolInput`
const location = input.location

console.log(`\n[WeatherTool] Getting weather for ${location}...`)

const fakeWeatherData = {
temperature: '72°F',
conditions: 'sunny',
}

const resultText = `The weather in ${location} is ${fakeWeatherData.temperature} and ${fakeWeatherData.conditions}.`

return {
toolUseId: context.toolUse.toolUseId,
status: 'success' as const,
content: [{ type: 'textBlock', text: resultText }],
}
}
}

/**
* A helper function to run an agent scenario and handle its output stream.
* This avoids repeating the for-await loop and logging logic.
* @param title The title of the scenario to be logged.
* @param agent The agent instance to use.
* @param prompt The user prompt to invoke the agent with.
*/
async function run(title: string, agent: Agent, prompt: string) {
console.log(`--- ${title} ---`)
console.log(`User: ${prompt}`)

const responseStream = agent.invoke(prompt)

console.log('Agent response stream:')
let result = await responseStream.next()
while (!result.done) {
const event = result.value
console.log('[Event]', event)
result = await responseStream.next()
}

// Clean up logging for the next scenario
console.log('\nInvocation complete.\n')
}

async function main() {
// 1. Initialize the components
const model = new BedrockModel()
const weatherTool = new WeatherTool()

// 2. Create agents
const defaultAgent = new Agent()
const agentWithoutTools = new Agent({ model })
const agentWithTools = new Agent({
systemPrompt: 'You are a helpful assistant that provides weather information using the get_weather tool.',
model,
tools: [weatherTool],
})

await run('0: Invocation with default agent (no model or tools)', defaultAgent, 'Hello!')
await run('1: Invocation with a model but no tools', agentWithoutTools, 'Hello!')
await run('2: Invocation that uses a tool', agentWithTools, 'What is the weather in Toronto? Use the weather tool.')
}

main().catch(console.error)
19 changes: 19 additions & 0 deletions examples/first-agent/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022"],
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "tests*"]
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@
"homepage": "https://github.com/strands-agents/sdk-typescript#readme",
"dependencies": {
"@aws-sdk/client-bedrock-runtime": "^3.911.0",
"@modelcontextprotocol/sdk": "^1.20.2",
"uuid": "^13.0.0",
"zod": "^4.1.12"
},
"optionalDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion src/__fixtures__/tool-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function createMockTool(
resultFn: () => ToolResult | AsyncGenerator<never, ToolResult, never>
): Tool {
return {
toolName: name,
name,
description: `Mock tool ${name}`,
toolSpec: {
name,
Expand Down
Loading