@physarum/sdk
v0.2.5
Published
Physarum Intelligence Network TypeScript SDK — MCP tool routing, model selection, and cost optimization
Maintainers
Readme
@physarum/sdk
TypeScript SDK for the Physarum Intelligence Network — real-time MCP tool routing, model selection, and cost optimization powered by network-wide telemetry and Physarum-inspired conductivity algorithms.
What it does
- Routes tool calls to the best-performing implementation based on live success rates, latency, and quality signals collected across all tenants
- Tracks every tool execution with zero-overhead telemetry batching
- Integrates natively with the Vercel AI SDK and LangChain
- Fails open — if the API is unavailable, falls back to your configured static priorities or the first candidate
Installation
npm install @physarum/sdkPeer dependencies (install only what you use):
npm install zod # always recommended
npm install ai # for Vercel AI SDK integration
npm install @langchain/core # for LangChain integrationQuick start
import { PhysarumClient } from "@physarum/sdk";
const client = new PhysarumClient({
apiKey: process.env.PHYSARUM_API_KEY!,
tenantId: process.env.PHYSARUM_TENANT_ID!,
ingestionBaseUrl: "https://api.physarum.network",
recommendationBaseUrl: "https://api.physarum.network",
mode: "SHADOW", // Start with SHADOW, graduate to CONTROLLED
});
// Ask Physarum which tool to use
const decision = await client.selectTool({
taskCategory: "payment_flow",
candidateTools: ["stripe", "paypal", "razorpay"],
actionClass: "SIDE_EFFECT_CRITICAL",
});
console.log(decision.selectedTool); // e.g. "stripe"
console.log(decision.reason); // "controlled_mode" | "shadow_mode" | ...
await client.shutdown();Operating modes
| Mode | Behaviour |
|------|-----------|
| SHADOW | Observes only. Records telemetry but never changes which tool is called. Zero risk, full learning. |
| ADVISORY | Calls the recommendation API and logs the suggestion but still runs your default tool. Use for dashboards/debugging. |
| CONTROLLED | Physarum selects the tool. The network's best recommendation is used for every call. |
Start with SHADOW to accumulate signal, then graduate to CONTROLLED once you trust the data.
Vercel AI SDK integration
Drop-in replacement for the Vercel AI SDK tool() function. Wraps every execution with Physarum telemetry.
import { generateText } from "ai";
import { physarumTool } from "@physarum/sdk/vercel";
// or: import { physarumTool } from "@physarum/sdk";
const tools = {
searchProducts: physarumTool(client, {
name: "searchProducts",
description: "Search the product catalogue by keyword",
parameters: z.object({ query: z.string() }),
taskCategory: "product_search",
actionClass: "READ_ONLY",
execute: async ({ query }) => searchProductsApi(query),
}),
processPayment: physarumTool(client, {
name: "processPayment",
description: "Charge the customer via Stripe",
parameters: z.object({ amount: z.number(), currency: z.string() }),
taskCategory: "payment_flow",
actionClass: "SIDE_EFFECT_CRITICAL",
execute: async ({ amount, currency }) => stripe.charge(amount, currency),
}),
};
const result = await generateText({ model, prompt, tools });Route multiple tools (CONTROLLED mode)
rankToolsForLLM fetches live recommendations and annotates high-performing tool descriptions so the LLM naturally favours the best option:
import { rankToolsForLLM, physarumTool } from "@physarum/sdk/vercel";
const rankedTools = await rankToolsForLLM(
client,
{
stripe: physarumTool(client, { name: "stripe", ... }),
paypal: physarumTool(client, { name: "paypal", ... }),
razorpay: physarumTool(client, { name: "razorpay", ... }),
},
{ taskCategory: "payment_flow", actionClass: "SIDE_EFFECT_CRITICAL" }
);
// In CONTROLLED mode, the best tool's description becomes:
// "[Physarum: recommended, 94% success, improving] Charge via Stripe"
const result = await generateText({ model, prompt, tools: rankedTools });Behaviour by mode:
SHADOW— returns tools unchanged, no API callADVISORY— calls the API (for attribution logging), returns tools unchangedCONTROLLED— annotates top tools (score ≥ 0.7) with performance signal; fails open on any API error
LangChain integration
import { createPhysarumTool, createPhysarumAgentTools } from "@physarum/sdk/langchain";
// or: import { createPhysarumTool } from "@physarum/sdk";
// Single tool
const searchTool = await createPhysarumTool(client, {
name: "searchProducts",
description: "Search the product catalogue by keyword",
schema: z.object({ query: z.string() }),
taskCategory: "product_search",
actionClass: "READ_ONLY",
execute: async ({ query }) => JSON.stringify(await searchProductsApi(query)),
});
// Multiple tools — in CONTROLLED mode, sorted by conductivity rank
const agentTools = await createPhysarumAgentTools(
client,
[
{ name: "stripe", description: "Stripe payment", schema: z.object({ amount: z.number() }), taskCategory: "payment_flow", actionClass: "SIDE_EFFECT_CRITICAL", execute: async ({ amount }) => charge(amount) },
{ name: "paypal", description: "PayPal payment", schema: z.object({ amount: z.number() }), taskCategory: "payment_flow", actionClass: "SIDE_EFFECT_CRITICAL", execute: async ({ amount }) => paypalCharge(amount) },
],
{ taskCategory: "payment_flow", actionClass: "SIDE_EFFECT_CRITICAL" }
);
const agent = await createReactAgent({ llm, tools: agentTools });Manual tool wrapping
Use wrapToolCall to add telemetry to any function without the Vercel/LangChain helpers:
const { result, telemetry } = await client.wrapToolCall({
toolId: "stripe",
toolName: "stripe",
taskCategory: "payment_flow",
actionClass: "SIDE_EFFECT_CRITICAL",
sessionIdHash: hashStable(sessionId, client.getTenantId()),
execute: () => stripe.charge({ amount: 99_00, currency: "usd" }),
});Model routing
const { recommendations } = await client.getModelRoutes({
taskCategory: "code_debug",
candidateModels: ["claude-opus-4-6", "claude-sonnet-4-6", "gpt-4o"],
});
const bestModel = recommendations[0].model_id;Cost optimization
const { recommended_path } = await client.getCostOptimizedPath({
taskCategory: "document_summarization",
candidateTools: ["gpt-4o", "claude-sonnet-4-6", "gemini-flash"],
qualityFloor: 0.8, // minimum acceptable quality score
budgetTokens: 50_000, // max tokens to spend
});Context enrichment
Pass context to improve routing accuracy. Physarum learns per-country, per-domain, and per-locale performance patterns:
const decision = await client.selectTool({
taskCategory: "payment_flow",
candidateTools: ["stripe", "razorpay"],
actionClass: "SIDE_EFFECT_CRITICAL",
context: {
country_code: "IN", // India — Razorpay likely performs better
domain: "e-commerce",
locale: "en-IN",
model_id: "claude-sonnet-4-6",
time_of_day_utc: "14:30",
},
});Static fallback priorities
Configure a deterministic fallback order used when the recommendation API is unavailable:
const client = new PhysarumClient({
// ...
localStaticPriorities: [
{ toolId: "stripe", score: 0.9 },
{ toolId: "paypal", score: 0.7 },
{ toolId: "razorpay", score: 0.5 },
],
// Per task-category overrides:
localStaticPrioritiesByTaskCategory: {
payment_flow_india: [
{ toolId: "razorpay", score: 0.95 },
{ toolId: "stripe", score: 0.6 },
],
},
});Configuration reference
interface PhysarumClientConfig {
apiKey: string;
tenantId: string;
ingestionBaseUrl: string; // Where telemetry is sent
recommendationBaseUrl: string; // Where routing queries go
mode: "SHADOW" | "ADVISORY" | "CONTROLLED";
mcpServerId?: string; // Set when running as an MCP server
requestTimeoutMs?: number; // Default: 5000
telemetryBatchSize?: number; // Default: 50 events per flush
telemetryFlushIntervalMs?: number; // Default: 2000ms
localStaticPriorities?: Array<{ toolId: string; score?: number }>;
localStaticPrioritiesByTaskCategory?: Record<string, Array<{ toolId: string; score?: number }>>;
}Action classes
| Value | Use when |
|-------|----------|
| READ_ONLY | Tool only reads data — search, lookup, fetch |
| IDEMPOTENT_WRITE | Safe to retry — upsert, idempotent create |
| SIDE_EFFECT_CRITICAL | Must not be retried blindly — payment, email send, webhook |
Decision logging (attribution)
Track whether following Physarum's recommendation led to a good outcome:
await client.logDecision({
sessionIdHash,
toolId: "stripe",
taskCategory: "payment_flow",
followedRecommendation: true,
outcome: { success: true, latencyMs: 340 },
});Shutdown
Always call shutdown() before your process exits to flush buffered telemetry:
process.on("SIGTERM", async () => {
await client.shutdown();
process.exit(0);
});License
MIT
