psadk
v1.4.1
Published
ps adk adapter
Readme
PSADK
ps adk adapter
- samples : sample agents using PSADK
Installation
$ npm install psadkFeatures
- Multi-Provider LLM Support: OpenAI, Anthropic, Google Gemini, AWS Bedrock, Azure OpenAI, and more
- A2A Protocol Compatible: Build agents that work with the Agent-to-Agent protocol
- Prompt Caching: Reduce costs and latency with built-in prompt caching support (90% cost reduction!)
- Custom Tools: Create and integrate custom tools with your agents
- Type-Safe: Full TypeScript support with comprehensive type definitions
- Authentication: Built-in JWT authentication and token management
Documentation
The classes are documented here Documentation
New: Prompt Caching Guide - Learn how to reduce costs and latency by 90% with prompt caching
Agent Core Module
The agent-core module provides a low-level, event-driven agent runtime built on @earendil-works/pi-ai. This module is designed for advanced use cases where you need fine-grained control over agent execution, tool calling, and message handling.
Note: The
agent-coremodule adds additional dependencies including LLM provider SDKs. It's imported separately viapsadk/agent-coreto keep the main package lightweight.
Installation
import { Agent } from 'psadk/agent-core';Key Features
- Stateful Agent Execution: Lifecycle management with states (idle, running, stopped, error)
- Event-Driven Architecture: Hooks for messages, thinking, tool calls, and errors
- Tool Execution: Sequential and parallel tool calling modes
- Message History: Automatic tracking and state management
- LLM Proxy: Server-side routing for LLM calls
- Type Safety: Comprehensive TypeScript types with runtime validation using TypeBox
Basic Example
import { Agent } from 'psadk/agent-core';
const agent = new Agent({
name: 'MyAgent',
model: 'gpt-4',
systemPrompt: 'You are a helpful assistant.',
tools: [
{
name: 'get_weather',
description: 'Get the current weather for a location',
inputSchema: {
type: 'object',
properties: {
location: { type: 'string' },
},
required: ['location'],
},
execute: async ({ location }) => {
// Implementation
return { temperature: 72, conditions: 'sunny' };
},
},
],
onMessage: (msg) => console.log('Agent:', msg.content),
onThinking: (thinking) => console.log('Thinking:', thinking),
onToolCall: (toolCall) => console.log('Tool:', toolCall.name),
onError: (error) => console.error('Error:', error),
});
// Start the agent and send a message
await agent.run("What's the weather in San Francisco?");Agent Configuration
interface AgentConfig {
name: string;
model: string;
systemPrompt?: string;
tools?: Tool[];
parallel?: boolean; // Enable parallel tool execution
maxSteps?: number; // Maximum number of execution steps
onMessage?: (message: Message) => void;
onThinking?: (thinking: string) => void;
onToolCall?: (toolCall: ToolCall) => void;
onToolResult?: (result: ToolResult) => void;
onError?: (error: Error) => void;
onStateChange?: (state: AgentState) => void;
}Advanced: Using the Proxy
The proxy module allows you to route LLM calls through a server:
import { ProxyStream } from 'psadk/agent-core';
const proxy = new ProxyStream({
endpoint: 'https://your-server.com/llm',
apiKey: 'your-api-key',
});
const stream = await proxy.createCompletion({
model: 'gpt-4',
messages: [{ role: 'user', content: 'Hello!' }],
});
for await (const chunk of stream) {
console.log(chunk);
}Agent Lifecycle
The agent follows a state machine:
- idle: Initial state, ready to accept tasks
- running: Actively processing a task
- stopped: Gracefully stopped
- error: Encountered an error
agent.onStateChange((state) => {
console.log(`Agent state changed to: ${state}`);
});
// Stop the agent
await agent.stop();
// Reset to idle state
await agent.reset();Error Handling
const agent = new Agent({
name: 'SafeAgent',
model: 'gpt-4',
onError: (error) => {
console.error('Agent error:', error.message);
// Handle error (e.g., retry, alert, log)
},
});
try {
await agent.run('Process this task');
} catch (error) {
// Errors are also thrown, so you can handle them here
console.error('Task failed:', error);
}Developing with PSADK
A2A Server
hello agent - simple chat agent
clone of https://github.com/a2aproject/a2a-samples/blob/main/samples/js/src/agents/coder/index.ts
import { getServiceToken, PSAgent, PSAgentLogger } from 'psadk';
import dotenv from 'dotenv';
dotenv.config();
async function main() {
const token = await getServiceToken({});
const HelloAgent = new PSAgent({
name: 'HelloAgent',
description: 'A simple hello world agent',
instruction: 'You are a friendly assistant that greets users.',
token,
port: 41241,
log: new PSAgentLogger({ prefix: 'HelloAgent' }),
});
const { app } = HelloAgent.getA2AServer();
app.listen(41241, () => {
console.log('HelloAgent is running on http://localhost:41241');
});
}
main().catch((error) => {
console.error(error);
process.exit(1);
});create a .env file with the appropriate keys
PS_API_URL=https://dev.lionis.ai
PS_APP_KEY=sk_0abb4....
PS_CLIENT_ID=b5cc89b7-....
PS_PROJECT_ID=15a2e....
PS_WORKSPACE_ID=4ffba209-xxx-xxx-xxx-....
PS_SVC_CLIENT_ID=0483....
PS_SVC_CLIENT_SECRET=gfjt678...
PS_SVC_APP_KEY=sk_5f2967....
DEBUG=*
movie info agent - example with custom tools
clone of https://github.com/a2aproject/a2a-samples/blob/main/samples/js/src/agents/movie-agent/index.ts
import { PSAI, PSAgent, PSAgentLogger } from 'psadk';
import dotenv from 'dotenv';
import { movie_search_tool, movie_searchpeople_tool } from './tmdb_tools.js';
import { prompt } from './movie_agent_prompts.js';
dotenv.config();
async function main() {
// const token = await PSAI.getServiceToken({});
const token = (await PSAI.getAuthToken()).accessToken;
const instruction = prompt.replace('{{now}}', new Date().toLocaleString());
const MovieAgent = new PSAgent({
name: 'MovieAgent',
description: 'An agent that can answer questions about movies using TMDB.',
instruction,
model: 'gpt-4.1-mini',
token,
port: 41241,
log: new PSAgentLogger({ prefix: 'MovieAgent' }),
tools: [movie_search_tool, movie_searchpeople_tool],
});
const { app, port } = MovieAgent.getA2AServer();
app.listen(port, () => {
console.log(`MovieAgent is running on http://localhost:${port}`);
});
}
main().catch((error) => {
console.error(error);
process.exit(1);
});Use can use a2a inspector ui just enter the agent card url http://localhost:41241/.well-known/agent-card.json to interact with the agent
Custom tool
import { PSAgentTool } from 'psadk';
import { z } from 'zod/v4';
export const movie_search_tool = new PSAgentTool({
name: 'search_movies',
description: 'search TMDB for movies by title',
type: 'API',
local_execution: true,
hitl: true,
inputschema: {
title: `search_movies_inputs.`,
description: 'Inputs for search_movies tool.',
url: 'http://localhost/search_movies',
method: 'POST',
requestBody: z.toJSONSchema(
z
.object({
query: z.string().meta({
title: 'query',
description: 'The search query string for movies.',
}),
})
.meta({
title: 'search_movies_request_body',
description:
'The request body parameters for the search_movies tool.',
}),
{ io: 'input' },
),
},
runCallback: async ({ query }) => {
console.log('[tmdb:searchMovies]', JSON.stringify(query));
try {
const data = await callTmdbApi('movie', query);
// Only modify image paths to be full URLs
const results = data.results.map((movie: any) => {
if (movie.poster_path) {
movie.poster_path = `https://image.tmdb.org/t/p/w500${movie.poster_path}`;
}
if (movie.backdrop_path) {
movie.backdrop_path = `https://image.tmdb.org/t/p/w500${movie.backdrop_path}`;
}
return movie;
});
return {
status: 'success',
output: {
...data,
results,
},
};
} catch (error) {
console.error('Error searching movies:', error);
// Re-throwing allows the caller to handle it appropriately
throw error;
}
},
});Client: Sending a Message
The A2AClient makes it easy to communicate with any A2A-compliant agent.
// client.ts
import { A2AClient, SendMessageSuccessResponse } from '@a2a-js/sdk/client';
import { Message, MessageSendParams } from '@a2a-js/sdk';
import { v4 as uuidv4 } from 'uuid';
async function run() {
// Create a client pointing to the agent's Agent Card URL.
const client = await A2AClient.fromCardUrl(
'http://localhost:41241/.well-known/agent-card.json',
);
const sendParams: MessageSendParams = {
message: {
messageId: uuidv4(),
role: 'user',
parts: [{ kind: 'text', text: 'Hi there!' }],
kind: 'message',
},
};
const response = await client.sendMessage(sendParams);
if ('error' in response) {
console.error('Error:', response.error.message);
} else {
const result = (response as SendMessageSuccessResponse).result as Message;
console.log('Agent response:', result.parts[0].text); // "Hello, world!"
}
}
await run();