@juspay/neurolink
Version:
The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002
265 lines (264 loc) • 11.3 kB
TypeScript
/**
* Local structural types for the optional @juspay/hippocampus integration.
*
* These mirror the public shapes that ship with @juspay/hippocampus's
* `dist/types.d.ts` so NeuroLink's public type surface stays compatible
* for consumers that already configure memory, while the runtime package
* itself becomes an optional peer dependency. The previous setup (a hard
* value import of @juspay/hippocampus) made pnpm pull a registry copy of
* @juspay/neurolink to satisfy Hippocampus's peer, which transitively
* dragged @ai-sdk/google + @ai-sdk/google-vertex into the production
* dependency graph.
*
* Naming:
* - Hippocampus's own `StorageType` and `RedisStorageConfig` collide with
* NeuroLink's in-house Redis manager types in `common.ts` /
* `conversation.ts`. To satisfy the `unique-type-names` ESLint rule,
* the storage variants get a `Memory*` prefix here.
* - `HippocampusMemory` (consumer-facing) and `StorageConfig` (legacy
* re-export) keep their original public names — only their definitions
* move from `import("@juspay/hippocampus").Foo` to local structural form.
*/
import type { TokenUsage } from "./analytics.js";
import type { AdditionalMemoryUser } from "./generate.js";
import type { NeurolinkCredentials } from "./providers.js";
export type MemorySqliteStorageConfig = {
type: "sqlite";
/** Path to SQLite file. Default: ./data/hippocampus.sqlite */
path?: string;
};
export type MemoryRedisStorageConfig = {
type: "redis";
host?: string;
port?: number;
password?: string;
db?: number;
keyPrefix?: string;
ttl?: number;
};
export type MemoryS3StorageConfig = {
type: "s3";
bucket: string;
prefix?: string;
};
export type MemoryCustomStorageConfig = {
type: "custom";
onGet: (ownerId: string) => Promise<string | null>;
onSet: (ownerId: string, memory: string) => Promise<void>;
onDelete: (ownerId: string) => Promise<void>;
onClose?: () => Promise<void>;
};
/**
* Storage configuration accepted by the optional Hippocampus client.
* Re-exported with the legacy `StorageConfig` name from `conversation.ts`
* to preserve the existing public type surface.
*/
export type HippocampusStorageConfig = MemorySqliteStorageConfig | MemoryRedisStorageConfig | MemoryS3StorageConfig | MemoryCustomStorageConfig;
/** Per-call options accepted by `Hippocampus.add`. */
export type HippocampusAddOptions = {
prompt?: string;
maxWords?: number;
};
/**
* The exact request shape Hippocampus's condenser sends to `generate()`
* (`@juspay/hippocampus` `src/client.ts`, the `neurolink.generate({...})`
* call inside `add()`). A full `NeuroLink` instance satisfies this
* structurally; so does any host object that can answer a plain text prompt.
*/
export type HippocampusCondenserGenerateOptions = {
input: {
text: string;
};
provider?: string;
model?: string;
temperature?: number;
disableTools?: boolean;
};
/** The only field Hippocampus reads back from the condensation call. */
export type HippocampusCondenserResult = {
content?: string;
};
/**
* Minimal `generate()`-bearing object Hippocampus can use for condensation
* instead of constructing its own bare `new NeuroLink()`.
*/
export type HippocampusNeurolinkLike = {
generate: (options: HippocampusCondenserGenerateOptions) => Promise<HippocampusCondenserResult>;
};
/**
* Condenser settings forwarded to Hippocampus.
*
* `provider` / `model` / `temperature` select the condensation model.
* `credentials` and `instance` are read by `@juspay/hippocampus` ≥0.2.0;
* the peer floor is 0.2.1, which also substitutes the condensation
* placeholders literally (user content containing `$&` no longer corrupts
* the prompt). NeuroLink always fills `instance` itself — a dedicated
* child instance that carries this instance's `credentials` — unless the
* host supplies one, so condensation stops depending on provider keys being
* present in `process.env`.
*/
export type HippocampusNeurolinkConfig = {
provider?: string;
model?: string;
temperature?: number;
/**
* Credentials for the condenser's own NeuroLink instance. Defaults to the
* parent `new NeuroLink({ credentials })` value.
*/
credentials?: NeurolinkCredentials;
/**
* Host-managed condenser. When set, NeuroLink does not build a child
* instance and passes this object through untouched.
*/
instance?: HippocampusNeurolinkLike;
};
/** Constructor config accepted by the Hippocampus class. */
export type HippocampusConfig = {
storage?: HippocampusStorageConfig;
prompt?: string;
neurolink?: HippocampusNeurolinkConfig;
maxWords?: number;
};
/**
* One completed turn as seen by the memory write hooks. `usage`,
* `finishReason` and `toolsUsed` are best-effort: the generate path has the
* full result, the stream path only knows the provider, the accumulated
* text and this request's own stream result's tool names.
*/
export type MemoryTurn = {
/** The caller's original prompt, before memory context was prepended. */
prompt: string;
/** The assistant's final text, trimmed. */
response: string;
/** Primary memory owner — `context.userId`. */
userId: string;
/** `context.sessionId`, when the call carried one. */
sessionId?: string;
provider?: string;
model?: string;
/** Names of tools that ran during the turn. */
toolsUsed?: string[];
/** Token usage — generate path only. */
usage?: TokenUsage;
/** Provider finish reason — generate path only. */
finishReason?: string;
};
/**
* Gate that decides whether a turn is written to memory at all. Runs inside
* the deferred background write, never on the response path. Returning
* `false` skips every `add()` for the turn (primary owner and
* `additionalUsers`). A thrown error is logged and treated as `false`.
*/
export type MemoryShouldWriteHook = (turn: MemoryTurn) => boolean | Promise<boolean>;
/**
* Transform (or veto) the exact text handed to `Hippocampus.add()`. Receives
* the default `"User: …\nAssistant: …"` rendering; return a replacement
* string, or `null` to skip the write. A thrown error is logged and the
* write is skipped — a missed memory is recoverable, a polluted condensed
* summary is not.
*/
export type MemoryBeforeStoreHook = (content: string, turn: MemoryTurn) => string | null | Promise<string | null>;
/**
* Write-side hooks accepted both per call (`generate({ memory })` /
* `stream({ memory })`) and on the instance
* (`conversationMemory.memory`). When both are set for the same hook, the
* per-call one wins for that call.
*/
export type MemoryWriteHooks = {
shouldWrite?: MemoryShouldWriteHook;
onBeforeStore?: MemoryBeforeStoreHook;
};
/**
* Per-call `memory` block shared by `GenerateOptions` and `StreamOptions`.
*
* Overrides the instance-level memory behaviour for one call. All flags
* default to `true` when the instance memory SDK is enabled; when it is
* disabled this block has no effect.
*/
export type MemoryCallOptions = MemoryWriteHooks & {
/** Master toggle for this call. When false, both read and write are skipped. Defaults to true. */
enabled?: boolean;
/** Whether to read condensed memory and prepend to prompt. Defaults to true. */
read?: boolean;
/** Whether to write (add/condense) the conversation into memory after completion. Defaults to true. */
write?: boolean;
/**
* Additional users whose memory should be retrieved/stored alongside the primary user.
* Each entry can override the condensation prompt and maxWords for that user.
* Primary user is still determined by context.userId.
*/
additionalUsers?: AdditionalMemoryUser[];
/**
* Condensation prompt for the primary owner on this call. Precedence:
* per-call > instance `conversationMemory.memory.prompt` > Hippocampus
* default. Must contain `{{OLD_MEMORY}}` and `{{NEW_CONTENT}}`; a template
* missing either is logged and the instance prompt is used instead. An
* empty or whitespace-only string counts as unset.
*/
prompt?: string;
/** Max words for the primary owner's condensed memory on this call. */
maxWords?: number;
};
/** Placeholder names a condensation prompt template can carry. */
export type CondensationPlaceholder = "OLD_MEMORY" | "NEW_CONTENT" | "MAX_WORDS";
/**
* Result of `validateCondensationPrompt()`. `missing` lists every absent
* placeholder; `fatal` is the subset whose absence breaks memory
* (`OLD_MEMORY`: each turn overwrites the summary; `NEW_CONTENT`: memory
* never grows). A missing `MAX_WORDS` only loses the word cap.
*/
export type CondensationPromptValidation = {
valid: boolean;
missing: CondensationPlaceholder[];
fatal: CondensationPlaceholder[];
};
/**
* Subset of the @juspay/hippocampus client surface that NeuroLink core
* actually calls. Defining this locally lets the initializer / SDK code
* avoid a value or even a type import from the optional package.
*/
export type HippocampusLike = {
add: (ownerId: string, content: string, options?: HippocampusAddOptions) => Promise<string>;
get: (ownerId: string) => Promise<string | null>;
delete: (ownerId: string) => Promise<void>;
close: () => Promise<void>;
};
/** Builds a Hippocampus-compatible client from the config NeuroLink assembled. */
export type HippocampusClientFactory = (config: HippocampusConfig) => HippocampusLike;
/**
* Consumer-facing memory config. The `enabled` flag toggles activation; the
* `HippocampusConfig` part is passed to the Hippocampus constructor when the
* optional package is installed. The remaining fields are NeuroLink-side and
* never reach Hippocampus.
*/
export type HippocampusMemory = HippocampusConfig & MemoryWriteHooks & {
enabled?: boolean;
/**
* Host-managed Hippocampus client (or any object with the same
* `add/get/delete/close` surface). When set, NeuroLink skips loading and
* constructing `@juspay/hippocampus` and uses this instance directly.
* `add()` and `get()` are what core calls and must both be functions;
* a client missing either disables memory with an `error` log.
*
* As a factory it receives the `HippocampusConfig` NeuroLink assembled —
* including the credentialed condenser under `neurolink.instance` — so a
* host can construct its own client (a subclass, a different version, a
* test double) from exactly what `new Hippocampus(config)` would get.
*/
client?: HippocampusLike | HippocampusClientFactory;
/**
* When true, a condensation prompt template (instance `prompt` or
* `HC_CONDENSATION_PROMPT`) missing `{{OLD_MEMORY}}` or `{{NEW_CONTENT}}`
* makes the constructor throw instead of the default disable-and-log
* (at `error`). An empty or whitespace-only template counts as unset.
*/
strictPrompt?: boolean;
};
/**
* Shape of the dynamically-required `@juspay/hippocampus` module surface
* that NeuroLink's lazy initializer reaches for. Only the constructor is
* surfaced here; the rest of the module is irrelevant to core.
*/
export type HippocampusModule = {
Hippocampus: new (config?: HippocampusConfig) => HippocampusLike;
};