npm package: @physarum/sdk
Description: Physarum Intelligence Network TypeScript SDK — MCP tool routing, model selection, and cost optimization
Latest version: 0.2.5

# @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.

[![npm version](https://img.shields.io/npm/v/@physarum/sdk)](https://www.npmjs.com/package/@physarum/sdk)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

## 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

```bash
npm install @physarum/sdk
```

Peer dependencies (install only what you use):

```bash
npm install zod                   # always recommended
npm install ai                    # for Vercel AI SDK integration
npm install @langchain/core       # for LangChain integration
```

## Quick start

```typescript
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.

```typescript
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:

```typescript
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 call
- `ADVISORY` — calls the API (for attribution logging), returns tools unchanged
- `CONTROLLED` — annotates top tools (score ≥ 0.7) with performance signal; fails open on any API error

## LangChain integration

```typescript
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:

```typescript
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

```typescript
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

```typescript
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:

```typescript
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:

```typescript
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

```typescript
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:

```typescript
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:

```typescript
process.on("SIGTERM", async () => {
  await client.shutdown();
  process.exit(0);
});
```

## License

MIT