@alexkroman1/aai 0.9.3 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal-types.d.ts +49 -22
- package/dist/_internal-types.js +43 -1
- package/dist/_mock-ws.d.ts +1 -2
- package/dist/_run-code.d.ts +31 -0
- package/dist/_session-ctx.d.ts +73 -0
- package/dist/_session-otel.d.ts +43 -0
- package/dist/_session-persist.d.ts +30 -0
- package/dist/_ssrf.d.ts +30 -0
- package/dist/_ssrf.js +123 -0
- package/dist/_utils.d.ts +25 -0
- package/dist/_utils.js +54 -1
- package/dist/builtin-tools.d.ts +5 -34
- package/dist/direct-executor-Ca0wt5H0.js +572 -0
- package/dist/direct-executor.d.ts +34 -5
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -2
- package/dist/kv.d.ts +30 -38
- package/dist/kv.js +19 -86
- package/dist/matchers.d.ts +20 -0
- package/dist/matchers.js +41 -0
- package/dist/memory-tools.d.ts +39 -0
- package/dist/middleware-core.d.ts +47 -0
- package/dist/middleware-core.js +107 -0
- package/dist/middleware.d.ts +37 -0
- package/dist/protocol.d.ts +44 -24
- package/dist/protocol.js +34 -14
- package/dist/runtime.d.ts +26 -2
- package/dist/runtime.js +44 -7
- package/dist/s2s.d.ts +19 -29
- package/dist/s2s.js +117 -87
- package/dist/server.d.ts +31 -3
- package/dist/server.js +102 -28
- package/dist/session-BkN9u0ni.js +683 -0
- package/dist/session.d.ts +55 -28
- package/dist/session.js +2 -312
- package/dist/sqlite-kv.d.ts +34 -0
- package/dist/sqlite-kv.js +133 -0
- package/dist/sqlite-vector.d.ts +58 -0
- package/dist/sqlite-vector.js +149 -0
- package/dist/system-prompt.d.ts +21 -0
- package/dist/telemetry.d.ts +49 -0
- package/dist/telemetry.js +95 -0
- package/dist/testing-MRl3SXsI.js +519 -0
- package/dist/testing.d.ts +299 -0
- package/dist/testing.js +2 -0
- package/dist/types.d.ts +324 -39
- package/dist/types.js +62 -9
- package/dist/vector.d.ts +18 -22
- package/dist/vector.js +41 -48
- package/dist/worker-entry.d.ts +11 -3
- package/dist/worker-entry.js +19 -8
- package/dist/ws-handler.d.ts +7 -3
- package/dist/ws-handler.js +64 -12
- package/package.json +55 -8
- package/dist/_mock-ws.js +0 -158
- package/dist/builtin-tools.js +0 -270
- package/dist/direct-executor.js +0 -125
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
//#region sqlite-vector.ts
|
|
3
|
+
/**
|
|
4
|
+
* SQLite-backed vector store with local embeddings.
|
|
5
|
+
*
|
|
6
|
+
* Persists data across restarts using a local SQLite database file.
|
|
7
|
+
* Uses brute-force cosine similarity over `node:sqlite` — no native
|
|
8
|
+
* extensions required. Fast enough for local dev (sub-ms for <10k vectors).
|
|
9
|
+
* Embeddings are computed locally via `all-MiniLM-L6-v2` (384 dims) —
|
|
10
|
+
* no external API key required. The model is downloaded on first use
|
|
11
|
+
* (~86 MB) and cached in `.aai/models/`.
|
|
12
|
+
*/
|
|
13
|
+
const DEFAULT_DIMENSIONS = 384;
|
|
14
|
+
const DEFAULT_MODEL = "Xenova/all-MiniLM-L6-v2";
|
|
15
|
+
/**
|
|
16
|
+
* Create a local embedding function using `all-MiniLM-L6-v2`.
|
|
17
|
+
*
|
|
18
|
+
* The model is downloaded on first use (~86 MB) and cached locally.
|
|
19
|
+
* Subsequent calls load from cache in ~90ms. Each embedding takes <2ms.
|
|
20
|
+
*/
|
|
21
|
+
function createLocalEmbedFn(cacheDir) {
|
|
22
|
+
let pipelinePromise = null;
|
|
23
|
+
async function getPipeline() {
|
|
24
|
+
if (!pipelinePromise) pipelinePromise = (async () => {
|
|
25
|
+
const { pipeline, env } = await import("@huggingface/transformers");
|
|
26
|
+
env.cacheDir = cacheDir;
|
|
27
|
+
return pipeline("feature-extraction", DEFAULT_MODEL);
|
|
28
|
+
})();
|
|
29
|
+
return pipelinePromise;
|
|
30
|
+
}
|
|
31
|
+
return async (text) => {
|
|
32
|
+
const output = await (await getPipeline())(text, {
|
|
33
|
+
pooling: "mean",
|
|
34
|
+
normalize: true
|
|
35
|
+
});
|
|
36
|
+
return Array.from(output.data);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Create a deterministic hash-based embedding function for testing.
|
|
41
|
+
*
|
|
42
|
+
* Produces repeatable vectors where similar text yields similar embeddings.
|
|
43
|
+
* Not suitable for production — use the default local model instead.
|
|
44
|
+
*
|
|
45
|
+
* @param dimensions - Vector dimensions (default: 384).
|
|
46
|
+
*/
|
|
47
|
+
function createTestEmbedFn(dimensions = DEFAULT_DIMENSIONS) {
|
|
48
|
+
return async (text) => {
|
|
49
|
+
const vec = new Float32Array(dimensions);
|
|
50
|
+
const words = text.toLowerCase().split(/\s+/).filter(Boolean);
|
|
51
|
+
for (const word of words) {
|
|
52
|
+
let hash = 0;
|
|
53
|
+
for (let i = 0; i < word.length; i++) hash = (hash << 5) - hash + word.charCodeAt(i) | 0;
|
|
54
|
+
for (let i = 0; i < 8; i++) {
|
|
55
|
+
const idx = Math.abs((hash + i * 31337) % dimensions);
|
|
56
|
+
vec[idx] = (vec[idx] ?? 0) + 1;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
let norm = 0;
|
|
60
|
+
for (let i = 0; i < dimensions; i++) norm += (vec[i] ?? 0) * (vec[i] ?? 0);
|
|
61
|
+
norm = Math.sqrt(norm) || 1;
|
|
62
|
+
return Array.from(vec, (v) => v / norm);
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/** Cosine similarity between two vectors stored as Buffers of float32. */
|
|
66
|
+
function cosineSimilarity(a, b) {
|
|
67
|
+
const fa = new Float32Array(a.buffer, a.byteOffset, a.byteLength / 4);
|
|
68
|
+
const fb = new Float32Array(b.buffer, b.byteOffset, b.byteLength / 4);
|
|
69
|
+
let dot = 0;
|
|
70
|
+
let normA = 0;
|
|
71
|
+
let normB = 0;
|
|
72
|
+
for (let i = 0; i < fa.length; i++) {
|
|
73
|
+
const ai = fa[i] ?? 0;
|
|
74
|
+
const bi = fb[i] ?? 0;
|
|
75
|
+
dot += ai * bi;
|
|
76
|
+
normA += ai * ai;
|
|
77
|
+
normB += bi * bi;
|
|
78
|
+
}
|
|
79
|
+
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
80
|
+
return denom === 0 ? 0 : dot / denom;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Create a SQLite-backed vector store with local embeddings.
|
|
84
|
+
*
|
|
85
|
+
* Data persists to a local SQLite file (default: `.aai/vectors.db`).
|
|
86
|
+
* Embeddings are computed locally using `all-MiniLM-L6-v2` by default —
|
|
87
|
+
* no API key required. The model auto-downloads on first use (~86 MB).
|
|
88
|
+
*
|
|
89
|
+
* Vector search uses brute-force cosine similarity over all stored
|
|
90
|
+
* embeddings. This is fast for local dev workloads (<10k vectors).
|
|
91
|
+
*
|
|
92
|
+
* @param options - See {@link SqliteVecVectorStoreOptions}.
|
|
93
|
+
* @returns A {@link VectorStore} instance.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```ts
|
|
97
|
+
* import { createSqliteVectorStore } from "@alexkroman1/aai/sqlite-vector";
|
|
98
|
+
*
|
|
99
|
+
* const vector = createSqliteVectorStore();
|
|
100
|
+
* await vector.upsert("doc-1", "The capital of France is Paris.");
|
|
101
|
+
* const results = await vector.query("France capital");
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
function createSqliteVectorStore(options) {
|
|
105
|
+
const dbPath = options?.path ?? ".aai/vectors.db";
|
|
106
|
+
const cacheDir = options?.modelCacheDir ?? ".aai/models";
|
|
107
|
+
const embedFn = options?.embedFn ?? createLocalEmbedFn(cacheDir);
|
|
108
|
+
const db = new DatabaseSync(dbPath);
|
|
109
|
+
db.exec("PRAGMA journal_mode = WAL");
|
|
110
|
+
db.exec(`
|
|
111
|
+
CREATE TABLE IF NOT EXISTS vectors (
|
|
112
|
+
id TEXT PRIMARY KEY,
|
|
113
|
+
data TEXT NOT NULL,
|
|
114
|
+
metadata TEXT NOT NULL DEFAULT '',
|
|
115
|
+
embedding BLOB NOT NULL
|
|
116
|
+
)
|
|
117
|
+
`);
|
|
118
|
+
const stmtUpsert = db.prepare("INSERT OR REPLACE INTO vectors (id, data, metadata, embedding) VALUES (?, ?, ?, ?)");
|
|
119
|
+
const stmtDelete = db.prepare("DELETE FROM vectors WHERE id = ?");
|
|
120
|
+
const stmtAll = db.prepare("SELECT id, data, metadata, embedding FROM vectors");
|
|
121
|
+
return {
|
|
122
|
+
async upsert(id, data, metadata) {
|
|
123
|
+
const vector = await embedFn(data);
|
|
124
|
+
const embedding = Buffer.from(new Float32Array(vector).buffer);
|
|
125
|
+
const metaJson = metadata ? JSON.stringify(metadata) : "";
|
|
126
|
+
stmtUpsert.run(id, data, metaJson, embedding);
|
|
127
|
+
},
|
|
128
|
+
async query(text, queryOptions) {
|
|
129
|
+
const topK = queryOptions?.topK ?? 10;
|
|
130
|
+
if (!text.trim()) return [];
|
|
131
|
+
const queryVec = await embedFn(text);
|
|
132
|
+
const queryBuf = Buffer.from(new Float32Array(queryVec).buffer);
|
|
133
|
+
const scored = stmtAll.all().map((row) => ({
|
|
134
|
+
id: row.id,
|
|
135
|
+
score: cosineSimilarity(queryBuf, row.embedding),
|
|
136
|
+
data: row.data,
|
|
137
|
+
metadata: row.metadata && row.metadata !== "" ? JSON.parse(row.metadata) : void 0
|
|
138
|
+
}));
|
|
139
|
+
scored.sort((a, b) => b.score - a.score);
|
|
140
|
+
return scored.slice(0, topK);
|
|
141
|
+
},
|
|
142
|
+
async delete(ids) {
|
|
143
|
+
const idArray = Array.isArray(ids) ? ids : [ids];
|
|
144
|
+
for (const id of idArray) stmtDelete.run(id);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
//#endregion
|
|
149
|
+
export { createSqliteVectorStore, createTestEmbedFn };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System prompt builder for S2S sessions.
|
|
3
|
+
*/
|
|
4
|
+
import type { AgentConfig } from "./_internal-types.ts";
|
|
5
|
+
/**
|
|
6
|
+
* Build the system prompt sent to the LLM from the agent configuration.
|
|
7
|
+
*
|
|
8
|
+
* Assembles the default instructions, today's date, agent-specific instructions,
|
|
9
|
+
* and optional sections for tool usage preamble and voice output rules.
|
|
10
|
+
*
|
|
11
|
+
* @param config - The serializable agent configuration (name, instructions, etc.).
|
|
12
|
+
* @param opts.hasTools - When `true`, appends a preamble instructing the LLM to
|
|
13
|
+
* speak a brief phrase before each tool call to fill silence.
|
|
14
|
+
* @param opts.voice - When `true`, appends strict voice-specific output rules
|
|
15
|
+
* (no markdown, no bullet points, conversational tone, concise responses).
|
|
16
|
+
* @returns The assembled system prompt string.
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildSystemPrompt(config: AgentConfig, opts: {
|
|
19
|
+
hasTools: boolean;
|
|
20
|
+
voice?: boolean;
|
|
21
|
+
}): string;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenTelemetry instrumentation helpers for the AAI SDK.
|
|
3
|
+
*
|
|
4
|
+
* Uses `@opentelemetry/api` only — consumers bring their own SDK and
|
|
5
|
+
* exporters. When no SDK is configured the API returns no-op instances,
|
|
6
|
+
* so the overhead in uninstrumented environments is negligible.
|
|
7
|
+
*
|
|
8
|
+
* Provides:
|
|
9
|
+
* - `tracer` — a pre-scoped `Tracer` for creating spans
|
|
10
|
+
* - `meter` — a pre-scoped `Meter` for recording metrics
|
|
11
|
+
* - Pre-built counters, histograms, and up/down counters covering the
|
|
12
|
+
* STT → LLM → TTS pipeline
|
|
13
|
+
*/
|
|
14
|
+
import { type Meter, type Span, type Tracer } from "@opentelemetry/api";
|
|
15
|
+
/** Tracer scoped to the AAI SDK. */
|
|
16
|
+
export declare const tracer: Tracer;
|
|
17
|
+
/** Meter scoped to the AAI SDK. */
|
|
18
|
+
export declare const meter: Meter;
|
|
19
|
+
/** Total sessions opened. */
|
|
20
|
+
export declare const sessionCounter: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
21
|
+
/** Currently active sessions. */
|
|
22
|
+
export declare const activeSessionsUpDown: import("@opentelemetry/api").UpDownCounter<import("@opentelemetry/api").Attributes>;
|
|
23
|
+
/** Total user turns (speech → transcript). */
|
|
24
|
+
export declare const turnCounter: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
25
|
+
/** Total tool calls executed. */
|
|
26
|
+
export declare const toolCallCounter: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
27
|
+
/** Tool call execution duration in seconds. */
|
|
28
|
+
export declare const toolCallDuration: import("@opentelemetry/api").Histogram<import("@opentelemetry/api").Attributes>;
|
|
29
|
+
/** Total tool call errors. */
|
|
30
|
+
export declare const toolCallErrorCounter: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
31
|
+
/** S2S WebSocket connection duration in seconds. */
|
|
32
|
+
export declare const s2sConnectionDuration: import("@opentelemetry/api").Histogram<import("@opentelemetry/api").Attributes>;
|
|
33
|
+
/** Total S2S errors. */
|
|
34
|
+
export declare const s2sErrorCounter: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
35
|
+
/** Number of agentic loop steps (tool calls) per completed turn. */
|
|
36
|
+
export declare const turnStepsHistogram: import("@opentelemetry/api").Histogram<import("@opentelemetry/api").Attributes>;
|
|
37
|
+
/** Total barge-in (reply interrupted) events. */
|
|
38
|
+
export declare const bargeInCounter: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
39
|
+
/** Messages silently dropped because the WebSocket was closed. */
|
|
40
|
+
export declare const wsSendDroppedCounter: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
41
|
+
/** Sessions closed due to S2S idle timeout. */
|
|
42
|
+
export declare const idleTimeoutCounter: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
43
|
+
/**
|
|
44
|
+
* Run `fn` inside a new span. The span is automatically ended and its
|
|
45
|
+
* status set based on whether `fn` throws.
|
|
46
|
+
*/
|
|
47
|
+
export declare function withSpan<T>(name: string, fn: (span: Span) => T): T;
|
|
48
|
+
export type { Meter, Span, Tracer } from "@opentelemetry/api";
|
|
49
|
+
export { context, metrics, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { SpanStatusCode, SpanStatusCode as SpanStatusCode$1, context, metrics, metrics as metrics$1, trace, trace as trace$1 } from "@opentelemetry/api";
|
|
3
|
+
//#region telemetry.ts
|
|
4
|
+
/**
|
|
5
|
+
* OpenTelemetry instrumentation helpers for the AAI SDK.
|
|
6
|
+
*
|
|
7
|
+
* Uses `@opentelemetry/api` only — consumers bring their own SDK and
|
|
8
|
+
* exporters. When no SDK is configured the API returns no-op instances,
|
|
9
|
+
* so the overhead in uninstrumented environments is negligible.
|
|
10
|
+
*
|
|
11
|
+
* Provides:
|
|
12
|
+
* - `tracer` — a pre-scoped `Tracer` for creating spans
|
|
13
|
+
* - `meter` — a pre-scoped `Meter` for recording metrics
|
|
14
|
+
* - Pre-built counters, histograms, and up/down counters covering the
|
|
15
|
+
* STT → LLM → TTS pipeline
|
|
16
|
+
*/
|
|
17
|
+
const SCOPE = "aai";
|
|
18
|
+
const _require = createRequire(import.meta.url);
|
|
19
|
+
let VERSION = "0.0.0";
|
|
20
|
+
try {
|
|
21
|
+
VERSION = _require("./package.json").version;
|
|
22
|
+
} catch {
|
|
23
|
+
VERSION = _require("../package.json").version;
|
|
24
|
+
}
|
|
25
|
+
/** Tracer scoped to the AAI SDK. */
|
|
26
|
+
const tracer = trace$1.getTracer(SCOPE, VERSION);
|
|
27
|
+
/** Meter scoped to the AAI SDK. */
|
|
28
|
+
const meter = metrics$1.getMeter(SCOPE, VERSION);
|
|
29
|
+
/** Total sessions opened. */
|
|
30
|
+
const sessionCounter = meter.createCounter("aai.session.count", { description: "Total voice sessions opened" });
|
|
31
|
+
/** Currently active sessions. */
|
|
32
|
+
const activeSessionsUpDown = meter.createUpDownCounter("aai.session.active", { description: "Currently active voice sessions" });
|
|
33
|
+
/** Total user turns (speech → transcript). */
|
|
34
|
+
const turnCounter = meter.createCounter("aai.turn.count", { description: "Total user turns" });
|
|
35
|
+
/** Total tool calls executed. */
|
|
36
|
+
const toolCallCounter = meter.createCounter("aai.tool.call.count", { description: "Total tool calls executed" });
|
|
37
|
+
/** Tool call execution duration in seconds. */
|
|
38
|
+
const toolCallDuration = meter.createHistogram("aai.tool.call.duration", {
|
|
39
|
+
description: "Tool call execution duration in seconds",
|
|
40
|
+
unit: "s"
|
|
41
|
+
});
|
|
42
|
+
/** Total tool call errors. */
|
|
43
|
+
const toolCallErrorCounter = meter.createCounter("aai.tool.call.error.count", { description: "Total tool call errors" });
|
|
44
|
+
/** S2S WebSocket connection duration in seconds. */
|
|
45
|
+
const s2sConnectionDuration = meter.createHistogram("aai.s2s.connection.duration", {
|
|
46
|
+
description: "S2S WebSocket connection duration in seconds",
|
|
47
|
+
unit: "s"
|
|
48
|
+
});
|
|
49
|
+
/** Total S2S errors. */
|
|
50
|
+
const s2sErrorCounter = meter.createCounter("aai.s2s.error.count", { description: "Total S2S errors" });
|
|
51
|
+
/** Number of agentic loop steps (tool calls) per completed turn. */
|
|
52
|
+
const turnStepsHistogram = meter.createHistogram("aai.turn.steps", { description: "Number of agentic loop steps per completed turn" });
|
|
53
|
+
/** Total barge-in (reply interrupted) events. */
|
|
54
|
+
const bargeInCounter = meter.createCounter("aai.turn.bargein.count", { description: "Total barge-in (reply interrupted) events" });
|
|
55
|
+
/** Messages silently dropped because the WebSocket was closed. */
|
|
56
|
+
const wsSendDroppedCounter = meter.createCounter("aai.ws.send_dropped", { description: "Messages silently dropped because the WebSocket was closed" });
|
|
57
|
+
/** Sessions closed due to S2S idle timeout. */
|
|
58
|
+
const idleTimeoutCounter = meter.createCounter("aai.session.idle.timeout.count", { description: "Sessions closed due to S2S idle timeout" });
|
|
59
|
+
/**
|
|
60
|
+
* Run `fn` inside a new span. The span is automatically ended and its
|
|
61
|
+
* status set based on whether `fn` throws.
|
|
62
|
+
*/
|
|
63
|
+
function withSpan(name, fn) {
|
|
64
|
+
return tracer.startActiveSpan(name, (span) => {
|
|
65
|
+
try {
|
|
66
|
+
const result = fn(span);
|
|
67
|
+
if (result instanceof Promise) return result.then((v) => {
|
|
68
|
+
span.setStatus({ code: SpanStatusCode$1.OK });
|
|
69
|
+
span.end();
|
|
70
|
+
return v;
|
|
71
|
+
}).catch((err) => {
|
|
72
|
+
span.setStatus({
|
|
73
|
+
code: SpanStatusCode$1.ERROR,
|
|
74
|
+
message: err instanceof Error ? err.message : String(err)
|
|
75
|
+
});
|
|
76
|
+
span.recordException(err instanceof Error ? err : new Error(String(err)));
|
|
77
|
+
span.end();
|
|
78
|
+
throw err;
|
|
79
|
+
});
|
|
80
|
+
span.setStatus({ code: SpanStatusCode$1.OK });
|
|
81
|
+
span.end();
|
|
82
|
+
return result;
|
|
83
|
+
} catch (err) {
|
|
84
|
+
span.setStatus({
|
|
85
|
+
code: SpanStatusCode$1.ERROR,
|
|
86
|
+
message: err instanceof Error ? err.message : String(err)
|
|
87
|
+
});
|
|
88
|
+
span.recordException(err instanceof Error ? err : new Error(String(err)));
|
|
89
|
+
span.end();
|
|
90
|
+
throw err;
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
//#endregion
|
|
95
|
+
export { SpanStatusCode, activeSessionsUpDown, bargeInCounter, context, idleTimeoutCounter, meter, metrics, s2sConnectionDuration, s2sErrorCounter, sessionCounter, toolCallCounter, toolCallDuration, toolCallErrorCounter, trace, tracer, turnCounter, turnStepsHistogram, withSpan, wsSendDroppedCounter };
|