Unispec Docs

Agents

Knowledge-grounded assistants with deterministic rules and HTTP tools.

An agent grounds answers on attached Knowledge bases, applies deterministic rules, and can call HTTP tools via a function-calling loop.

Create and attach knowledge

import { createAgentClient } from "@unispec-ai/sdk";
const agents = createAgentClient({ apiKey: process.env.UNISPEC_API_KEY });

await agents.createAgent({
  name: "support",
  instructions: "Answer from knowledge; use tools for orders.",
  knowledge: ["support-docs"],
});
const agent = agents.agent("support");

Rules

Rules are evaluated deterministically around the LLM:

  • escalation — a keyword match short-circuits the LLM and returns the fallback message.
  • forbidden / required — injected as prompt constraints.
await agent.addRule({ type: "escalation", condition: "chargeback,dispute" });

Tools

Register an HTTP endpoint the agent may call. The model fills the parameters; the result is fed back into the answer.

await agent.addTool({
  name: "lookup_order",
  description: "Look up an order by id.",
  http: { url: "https://api.example.com/orders", method: "POST" },
  parameters: { type: "object", properties: { order_id: { type: "string" } }, required: ["order_id"] },
});

Chat

const res = await agent.chat([{ role: "user", content: "Status of order A123?" }]);
console.log(res.message.content);
console.log(res.escalated, res.trace.tool_calls); // rule + tool trace

OpenAI-compatible endpoint

Unispec exposes an OpenAI-compatible POST /v1/chat/completions, so any OpenAI client works out of the box — point the base URL at Unispec and use your agent name as the model. The full server-side pipeline (guardrails, flow, tools, knowledge, tool policy) runs on every request; you don't manage any of it client-side.

import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://unispec.ai/v1",
  apiKey: process.env.UNISPEC_API_KEY, // sent as `Authorization: Bearer …`
});

// Non-streaming
const res = await client.chat.completions.create({
  model: "support-bot", // your agent name
  messages: [{ role: "user", content: "Status of order A123?" }],
});
console.log(res.choices[0].message.content);

// Streaming (Server-Sent Events)
const stream = await client.chat.completions.create({
  model: "support-bot",
  messages: [{ role: "user", content: "Status of order A123?" }],
  stream: true,
});
for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}

Notes:

  • model = agent name (a libra: prefix is tolerated). GET /v1/models lists your agents as models.
  • Streaming uses buffer-then-stream: the full pipeline runs, then the approved answer is replayed as chat.completion.chunk events — so guardrails, the output-guard, and the enterprise risk-hold still apply (a naive token stream couldn't retract a blocked answer).
  • Client system messages are ignored — the agent owns its instructions.
  • The typed extras (ui_actions, citations, escalated) that don't fit the OpenAI schema ride in a non-standard x_libra field (OpenAI clients ignore it). For full fidelity, use the native POST /v1/agents/{name}/chat (SDK agent.chat(...)).

From the Unispec SDK

The Unispec SDK also offers streaming without the openai package, plus helpers to configure an OpenAI client:

import { createAgentClient, openai, openaiConfig } from "@unispec-ai/sdk";

// Stream text deltas over the OpenAI-compatible endpoint (no `openai` dep):
const agent = createAgentClient().agent("support-bot");
for await (const delta of agent.chatStream([{ role: "user", content: "Hi" }], {
  onFinal: (x) => console.log(x.ui_actions, x.escalated), // typed extras on the final frame
})) {
  process.stdout.write(delta);
}

// Or hand back a configured OpenAI client (optional `openai` peer dependency):
const client = await openai();                 // new OpenAI({ baseURL, apiKey })
// …or just the config for your own client:
// const client = new OpenAI(openaiConfig());

Live token streaming (opt-in)

By default, streaming is buffer-then-stream (the pipeline completes, then the approved answer replays as chunks). Set the agent's stream_mode to "live" to stream tokens as the model generates them — first token in well under a second:

await client.configureAgent("support-bot", { streamMode: "live" });

Live mode is guard-gated: it is honored only when the output guard is off and the agent has no tools and no flow (the output guard can't retract tokens that already streamed). Escalation rules and the input guard still run before the first token. When the gate doesn't allow it, streaming silently falls back to buffer-then-stream — same wire format either way.

Visitor memory (memory-native agents)

Pass a memory user id and the turn becomes memory-native: relevant long-term memories for that user are recalled before answering (grounding the reply), and the exchange is persisted after — scoped to this agent + project, powered by the Memory product's LLM fact extraction.

// Native chat
const res = await agent.chat(messages, { memory: { userId: "visitor-123" } });
// res.trace.memory_recalled → how many memories grounded this turn

// OpenAI-compatible endpoint: the standard `user` field activates it
await client.chat.completions.create({
  model: "support-bot",
  messages,
  user: "visitor-123",
});

recall/persist can be toggled individually (memory: { userId, persist: false }). Works on buffered and live streaming (recall runs before the first token); persistence is asynchronous — replies never wait on it.

On this page