agentfn
v1.0.9
Published
Lightweight agentic AI SDK for building tool-using, workflow-driven AI agents.
Maintainers
Readme
agentfn
The world's lightest agentic AI SDK for building tool-using, workflow-driven AI agents.
✨ Features
- Agentic AI: Create agents that reason, use tools, and adapt to user needs.
- Tool Registration: Register any function, API, or workflow as a tool.
- Schema Validation: Automatic parameter validation for safe, robust tools.
- Workflows: Chain tools and logic for multi-step automation.
- promptTool: Instantly turn prompts into tools—no code required.
- Streaming: Get partial/final responses as they're generated.
- Custom Hooks: onStart, onFinish, onError for every tool.
- Memory & Context: Plug in your own memory for persistent agents.
- Agentic Modes: Switch between creative, balanced, or precise reasoning.
- Type-safe Response Formats: Markdown, HTML, JSON, plaintext.
🚀 Install
npm install agentfn🏁 Quick Start
import { Client, Agent, RESPONSE_TYPE } from 'agentfn';
import { AzureOpenAiAdapter } from 'agentfn/dist/azureOpenAiAdapter';
const client = new Client();
const aiAdapter = new AzureOpenAiAdapter({
endpoint: 'https://your-azure-endpoint',
apiKey: 'sk-...',
deployment: 'gpt-4',
apiVersion: '2024-05-01-preview'
});
client.registerTool({
name: 'add',
description: 'Adds two numbers.',
schema: { type: 'object', properties: { a: { type: 'number' }, b: { type: 'number' } }, required: ['a', 'b'] },
type: 'function',
execute: async ({ a, b }) => ({ sum: a + b })
});
const agent = new Agent({
name: 'MathBot',
tools: client.getLocalTools(),
responseType: RESPONSE_TYPE.MARKDOWN,
aiAdapter
});
(async () => {
const result = await agent.process(client, 'What is 2 + 2? Use your add tool.');
console.log(result);
})();Output:
{
"answer": "The sum of 2 and 2 is 4.",
"messages": [
{ "role": "user", "content": "What is 2 + 2? Use your add tool." },
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_...",
"function": { "name": "add", "arguments": "{\"a\":2,\"b\":2}" }
}
]
},
{
"role": "tool",
"tool_call_id": "call_...",
"content": "{\"sum\":4}"
},
{
"role": "assistant",
"content": "The sum of 2 and 2 is 4."
}
]
}🧠 Registering Powerful Tools
Any Function or API
client.registerTool({
name: 'weather',
description: 'Gets the weather for a city.',
schema: { type: 'object', properties: { city: { type: 'string' } }, required: ['city'] },
type: 'function',
execute: async ({ city }) => {
// Call your weather API here
return { forecast: 'Sunny in ' + city };
}
});promptTool: Turn Any Prompt Into a Tool
import { promptTool, RESPONSE_TYPE } from 'agentfn';
client.registerTool({
name: 'summarize',
description: 'Summarizes any text.',
schema: { type: 'object', properties: { text: { type: 'string' } }, required: ['text'] },
type: 'function',
execute: promptTool({
prompt: ({ text }) => `Summarize this: ${text}`,
responseType: RESPONSE_TYPE.MARKDOWN
})
});🛠️ Workflows: Chain Tools and Logic
import { Workflow } from 'agentfn';
const wf = new Workflow()
.step(async (ctx) => {
ctx.weather = await client.callTool('weather', { city: 'London' });
})
.step(async (ctx) => {
ctx.summary = await client.callTool('summarize', { text: ctx.weather.forecast });
});
const ctx = await wf.run({ client });
console.log(ctx.summary);🤖 Streaming Responses
for await (const msg of agent.processStream(client, [
{ role: 'user', content: 'Summarize this: The quick brown fox jumps over the lazy dog.' }
])) {
if (msg.role === 'assistant') {
console.log('Partial:', msg.content);
}
if (msg.isFinal) {
console.log('Final:', msg.content);
}
}🦾 Advanced: Custom Memory, Tool Policies, and More
Custom Memory Module
const memory = {
store: {},
async save(key, value) { this.store[key] = value; },
async load(key) { return this.store[key]; }
};
const agent = new Agent({
// ...
memory
});Custom Tool Use Policy
const agent = new Agent({
// ...
toolUsePolicy: (tool) => tool.name !== 'delete_all_data'
});📝 promptTool: The Fastest Way to Make an AI Tool
client.registerTool({
name: 'translate',
description: 'Translates text to French.',
schema: { type: 'object', properties: { text: { type: 'string' } }, required: ['text'] },
type: 'function',
execute: promptTool({
prompt: ({ text }) => `Translate this to French: ${text}`,
responseType: RESPONSE_TYPE.JSON,
outputSchema: {
type: 'object',
properties: { translation: { type: 'string' } },
required: ['translation']
},
sample: { translation: 'Bonjour!' }
})
});
// If the AI returns { translation: "Bonjour!" }, it passes validation.
// If the AI returns a string or an object without 'translation', an error is thrown.🧩 Using the sample Option
You can provide a sample output (object or array) when creating a prompt tool. This helps the AI by showing a concrete example, and the SDK will validate your sample against the outputSchema:
client.registerTool({
name: 'weather_json',
description: 'Returns weather as JSON.',
schema: { type: 'object', properties: { city: { type: 'string' } }, required: ['city'] },
type: 'function',
execute: promptTool({
prompt: ({ city }) => `Get the weather for ${city}.`,
responseType: RESPONSE_TYPE.JSON,
outputSchema: {
type: 'object',
properties: { forecast: { type: 'string' }, tempC: { type: 'number' } },
required: ['forecast', 'tempC']
},
sample: { forecast: 'Sunny', tempC: 25 }
})
});- If your sample does not match the schema, the SDK will log a warning in the console, but will not throw an error.
- If your sample is valid, it will be included in the prompt as an example for the AI.
- You can also provide an array of samples for more complex tools.
// Example with an invalid sample (logs a warning):
client.registerTool({
name: 'bad_sample',
description: 'Example with invalid sample.',
schema: { type: 'object', properties: {}, required: [] },
type: 'function',
execute: promptTool({
prompt: 'Say hello',
responseType: RESPONSE_TYPE.JSON,
outputSchema: { type: 'object', properties: { answer: { type: 'string' } }, required: ['answer'] },
sample: { wrongField: 123 } // This will log a warning
})
});🧪 Example: Agentic Reasoning Chain
client.registerTool({
name: 'web_search',
description: 'Searches the web.',
schema: { type: 'object', properties: { query: { type: 'string' } }, required: ['query'] },
type: 'function',
execute: async ({ query }) => {
// Call your web search API
return { results: [{ title: '...', url: '...' }] };
}
});
client.registerTool({
name: 'send_email',
description: 'Sends an email.',
schema: { type: 'object', properties: { to: { type: 'string' }, subject: { type: 'string' }, body: { type: 'string' } }, required: ['to', 'subject', 'body'] },
type: 'function',
execute: async ({ to, subject, body }) => {
// Send email logic
return { sent: true };
}
});
// Now the agent can search, summarize, and email in one reasoning chain!Author
Tochi Mba
License
MIT
