@yh-ui/ai-sdk 0.1.21

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.
@@ -0,0 +1,112 @@
1
+ /**
2
+ * YH-UI AI SDK - 可观测性导出
3
+ *
4
+ * 支持 OpenTelemetry JSON 导出与 LangSmith 风格遥测。
5
+ */
6
+ import type { TraceSpan, TraceEvent } from './future';
7
+ /** OpenTelemetry Span 类型(兼容 OTLP) */
8
+ export interface OTelSpan {
9
+ traceId: string;
10
+ spanId: string;
11
+ parentSpanId?: string;
12
+ name: string;
13
+ kind: 'INTERNAL' | 'SERVER' | 'CLIENT' | 'PRODUCER' | 'CONSUMER';
14
+ startTimeUnixNano: number;
15
+ endTimeUnixNano?: number;
16
+ status?: {
17
+ code: 0 | 1 | 2;
18
+ message?: string;
19
+ };
20
+ attributes: Array<{
21
+ key: string;
22
+ value: {
23
+ stringValue?: string;
24
+ intValue?: number;
25
+ boolValue?: boolean;
26
+ };
27
+ }>;
28
+ events?: Array<{
29
+ timeUnixNano: number;
30
+ name: string;
31
+ attributes?: Array<{
32
+ key: string;
33
+ value: {
34
+ stringValue?: string;
35
+ };
36
+ }>;
37
+ }>;
38
+ }
39
+ /** OpenTelemetry Resource */
40
+ export interface OTelResource {
41
+ attributes: Array<{
42
+ key: string;
43
+ value: {
44
+ stringValue?: string;
45
+ };
46
+ }>;
47
+ }
48
+ /** OpenTelemetry Export Payload */
49
+ export interface OTelExportPayload {
50
+ resourceSpans: Array<{
51
+ resource: OTelResource;
52
+ scopeSpans: Array<{
53
+ spans: OTelSpan[];
54
+ }>;
55
+ }>;
56
+ }
57
+ /** LangSmith run 类型 */
58
+ export type LangSmithRunType = 'llm' | 'chain' | 'tool' | 'retriever';
59
+ /** LangSmith 遥测数据 */
60
+ export interface LangSmithRun {
61
+ id: string;
62
+ name: string;
63
+ run_type: LangSmithRunType;
64
+ start_time: number;
65
+ end_time?: number;
66
+ inputs: Record<string, unknown>;
67
+ outputs?: Record<string, unknown>;
68
+ error?: string;
69
+ parent_run_id?: string;
70
+ tags?: string[];
71
+ metadata?: Record<string, unknown>;
72
+ }
73
+ /** LangSmith 客户端配置 */
74
+ export interface LangSmithClientConfig {
75
+ apiUrl?: string;
76
+ apiKey?: string;
77
+ projectName?: string;
78
+ }
79
+ /** 导出器接口 */
80
+ export interface TraceExporter {
81
+ /** 导出 spans */
82
+ exportSpans(spans: OTelSpan[]): Promise<void>;
83
+ /** 导出事件 */
84
+ exportEvents?(events: TraceEvent[]): Promise<void>;
85
+ }
86
+ /**
87
+ * OpenTelemetry JSON Console Exporter(开发调试用)
88
+ */
89
+ export declare function createOTelConsoleExporter(): TraceExporter;
90
+ /**
91
+ * LangSmith Exporter(可对接官方 API)
92
+ */
93
+ export declare function createLangSmithExporter(config: LangSmithClientConfig): TraceExporter;
94
+ /**
95
+ * 将 internal TraceSpan 转换为 OTelSpan
96
+ */
97
+ export declare function toOTelSpan(span: TraceSpan, traceId: string): OTelSpan;
98
+ /**
99
+ * 创建可观测性管理器(整合 tracer + exporter)
100
+ */
101
+ export interface ObservabilityManager {
102
+ /** 添加导出器 */
103
+ addExporter(exporter: TraceExporter): void;
104
+ /** 刷新所有导出器 */
105
+ flush(): Promise<void>;
106
+ /** 收集并导出当前 trace */
107
+ export(tracerSpans: TraceSpan[]): Promise<void>;
108
+ }
109
+ export declare function createObservabilityManager(config?: {
110
+ exporters?: TraceExporter[];
111
+ traceId?: string;
112
+ }): ObservabilityManager;
@@ -0,0 +1,117 @@
1
+ export function createOTelConsoleExporter() {
2
+ return {
3
+ async exportSpans(spans) {
4
+ const payload = {
5
+ resourceSpans: [
6
+ {
7
+ resource: {
8
+ attributes: [{ key: "service.name", value: { stringValue: "yh-ai-sdk" } }]
9
+ },
10
+ scopeSpans: [{ spans }]
11
+ }
12
+ ]
13
+ };
14
+ console.log("[OTel Export]", JSON.stringify(payload, null, 2));
15
+ }
16
+ };
17
+ }
18
+ export function createLangSmithExporter(config) {
19
+ const { apiUrl = "https://api.langsmith.com/v1/runs", apiKey, projectName = "yh-ai-sdk" } = config;
20
+ return {
21
+ async exportSpans(spans) {
22
+ if (!apiKey) {
23
+ console.warn("[LangSmith] No API key configured, skipping export");
24
+ return;
25
+ }
26
+ const runs = spans.map((span) => {
27
+ const inputs = {};
28
+ const outputs = {};
29
+ let runType = "chain";
30
+ for (const attr of span.attributes) {
31
+ const key = attr.key;
32
+ const val = attr.value.stringValue ?? attr.value.intValue ?? attr.value.boolValue;
33
+ if (key.startsWith("input.")) {
34
+ inputs[key.slice(6)] = val;
35
+ } else if (key.startsWith("output.")) {
36
+ outputs[key.slice(7)] = val;
37
+ } else if (key === "run_type") {
38
+ runType = val;
39
+ }
40
+ }
41
+ return {
42
+ id: span.spanId,
43
+ name: span.name,
44
+ run_type: runType,
45
+ start_time: span.startTimeUnixNano / 1e6,
46
+ end_time: span.endTimeUnixNano ? span.endTimeUnixNano / 1e6 : Date.now(),
47
+ inputs,
48
+ outputs: Object.keys(outputs).length ? outputs : void 0,
49
+ error: span.status?.code === 2 ? span.status.message : void 0,
50
+ parent_run_id: span.parentSpanId,
51
+ metadata: { project: projectName }
52
+ };
53
+ });
54
+ try {
55
+ await fetch(apiUrl, {
56
+ method: "POST",
57
+ headers: {
58
+ "Content-Type": "application/json",
59
+ Authorization: `Bearer ${apiKey}`
60
+ },
61
+ body: JSON.stringify({ runs })
62
+ });
63
+ } catch (err) {
64
+ console.error("[LangSmith] Export failed:", err);
65
+ }
66
+ }
67
+ };
68
+ }
69
+ export function toOTelSpan(span, traceId) {
70
+ const startNs = span.startTime.getTime() * 1e6;
71
+ const endNs = span.endTime ? span.endTime.getTime() * 1e6 : Date.now() * 1e6;
72
+ const attrs = Object.entries(span.attributes).map(([key, value]) => ({
73
+ key,
74
+ value: typeof value === "string" ? { stringValue: value } : typeof value === "number" ? { intValue: value } : { boolValue: Boolean(value) }
75
+ }));
76
+ const events = span.events.map((e) => ({
77
+ timeUnixNano: e.timestamp.getTime() * 1e6,
78
+ name: e.type,
79
+ attributes: Object.entries(e.data).map(([k, v]) => ({
80
+ key: k,
81
+ value: { stringValue: String(v) }
82
+ }))
83
+ }));
84
+ return {
85
+ traceId,
86
+ spanId: span.id,
87
+ name: span.name,
88
+ kind: "INTERNAL",
89
+ startTimeUnixNano: startNs,
90
+ endTimeUnixNano: endNs,
91
+ status: span.events.some((e) => e.type === "error") ? { code: 2, message: "Error occurred" } : { code: 0 },
92
+ attributes: attrs,
93
+ events
94
+ };
95
+ }
96
+ export function createObservabilityManager(config) {
97
+ const exporters = config?.exporters || [];
98
+ const traceId = config?.traceId || Math.random().toString(16).slice(2).padStart(32, "0");
99
+ return {
100
+ addExporter(exporter) {
101
+ exporters.push(exporter);
102
+ },
103
+ async flush() {
104
+ await Promise.all(
105
+ exporters.map(async (ex) => {
106
+ if ("flush" in ex && typeof ex.flush === "function") {
107
+ await ex.flush();
108
+ }
109
+ })
110
+ );
111
+ },
112
+ async export(tracerSpans) {
113
+ const otelSpans = tracerSpans.map((s) => toOTelSpan(s, traceId));
114
+ await Promise.all(exporters.map((ex) => ex.exportSpans(otelSpans)));
115
+ }
116
+ };
117
+ }
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.createProductionRAG = createProductionRAG;
7
+ function createProductionRAG(config) {
8
+ const {
9
+ namespace = "default",
10
+ topK = 5,
11
+ similarityThreshold = 0.5,
12
+ includeSources = true,
13
+ vectorStore,
14
+ embed
15
+ } = config;
16
+ async function addDocuments(documents) {
17
+ const vectors = [];
18
+ for (let i = 0; i < documents.length; i++) {
19
+ const doc = documents[i];
20
+ const embedding = await embed(doc.content);
21
+ vectors.push({
22
+ id: `chunk-${Date.now()}-${i}`,
23
+ content: doc.content,
24
+ embedding,
25
+ metadata: doc.metadata || {}
26
+ });
27
+ }
28
+ await vectorStore.add(vectors, namespace);
29
+ return vectors.map(v => ({
30
+ id: v.id,
31
+ content: v.content,
32
+ metadata: v.metadata,
33
+ embedding: v.embedding
34
+ }));
35
+ }
36
+ async function retrieve(query2, k = topK) {
37
+ const queryEmbedding = await embed(query2);
38
+ const results = await vectorStore.search(queryEmbedding, {
39
+ topK: k,
40
+ threshold: similarityThreshold,
41
+ namespace
42
+ });
43
+ return results.map(r => ({
44
+ id: r.id,
45
+ content: r.content,
46
+ metadata: r.metadata || {},
47
+ score: r.score,
48
+ embedding: r.embedding
49
+ }));
50
+ }
51
+ async function query(question, llm) {
52
+ const relevantDocs = await retrieve(question);
53
+ if (relevantDocs.length === 0) {
54
+ return {
55
+ answer: "\u62B1\u6B49\uFF0C\u77E5\u8BC6\u5E93\u4E2D\u6CA1\u6709\u627E\u5230\u76F8\u5173\u4FE1\u606F\u3002",
56
+ sources: [],
57
+ contextUsed: ""
58
+ };
59
+ }
60
+ const context = relevantDocs.map((doc, i) => `[\u6587\u6863 ${i + 1}]
61
+ ${doc.content}`).join("\n\n");
62
+ const prompt = `\u57FA\u4E8E\u4EE5\u4E0B\u77E5\u8BC6\u5E93\u5185\u5BB9\uFF0C\u7528\u4E2D\u6587\u56DE\u7B54\u7528\u6237\u7684\u95EE\u9898\u3002\u5982\u679C\u77E5\u8BC6\u5E93\u4E2D\u7684\u4FE1\u606F\u4E0D\u80FD\u56DE\u7B54\u95EE\u9898\uFF0C\u8BF7\u8BF4\u660E\u3002
63
+
64
+ \u77E5\u8BC6\u5E93\u5185\u5BB9:
65
+ ${context}
66
+
67
+ \u7528\u6237\u95EE\u9898: ${question}
68
+
69
+ \u8BF7\u7ED9\u51FA\u56DE\u7B54\uFF1A`;
70
+ const answer = await llm(prompt);
71
+ return {
72
+ answer,
73
+ sources: includeSources ? relevantDocs.map(doc => ({
74
+ content: doc.content.slice(0, 200) + (doc.content.length > 200 ? "..." : ""),
75
+ metadata: doc.metadata,
76
+ score: doc.score ?? 0
77
+ })) : [],
78
+ contextUsed: context.slice(0, 500)
79
+ };
80
+ }
81
+ async function clear() {
82
+ await vectorStore.clear(namespace);
83
+ }
84
+ return {
85
+ addDocuments,
86
+ retrieve,
87
+ query,
88
+ clear,
89
+ config: {
90
+ topK,
91
+ similarityThreshold,
92
+ namespace
93
+ }
94
+ };
95
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * YH-UI AI SDK - 生产级 RAG
3
+ *
4
+ * 依赖向量存储抽象与可选嵌入函数,可与文档加载器配合使用。
5
+ */
6
+ import type { IVectorStore } from './vector-store';
7
+ import type { RAGResult, DocumentChunk } from './future';
8
+ export interface ProductionRAGConfig {
9
+ /** 知识库/命名空间 */
10
+ namespace?: string;
11
+ /** 检索数量 */
12
+ topK?: number;
13
+ /** 相似度阈值 */
14
+ similarityThreshold?: number;
15
+ /** 是否返回源信息 */
16
+ includeSources?: boolean;
17
+ /** 向量存储(必填) */
18
+ vectorStore: IVectorStore;
19
+ /** 嵌入函数(必填,用于 query 与 addDocuments) */
20
+ embed: (text: string) => Promise<number[]>;
21
+ }
22
+ /**
23
+ * 生产级 RAG:使用真实向量存储与嵌入
24
+ */
25
+ export declare function createProductionRAG(config: ProductionRAGConfig): {
26
+ addDocuments: (documents: Array<{
27
+ content: string;
28
+ metadata?: Record<string, unknown>;
29
+ }>) => Promise<{
30
+ id: string;
31
+ content: string;
32
+ metadata: Record<string, unknown> | undefined;
33
+ embedding: number[];
34
+ }[]>;
35
+ retrieve: (query: string, k?: number) => Promise<DocumentChunk[]>;
36
+ query: (question: string, llm: (prompt: string) => Promise<string>) => Promise<RAGResult>;
37
+ clear: () => Promise<void>;
38
+ config: {
39
+ topK: number;
40
+ similarityThreshold: number;
41
+ namespace: string;
42
+ };
43
+ };
@@ -0,0 +1,85 @@
1
+ export function createProductionRAG(config) {
2
+ const {
3
+ namespace = "default",
4
+ topK = 5,
5
+ similarityThreshold = 0.5,
6
+ includeSources = true,
7
+ vectorStore,
8
+ embed
9
+ } = config;
10
+ async function addDocuments(documents) {
11
+ const vectors = [];
12
+ for (let i = 0; i < documents.length; i++) {
13
+ const doc = documents[i];
14
+ const embedding = await embed(doc.content);
15
+ vectors.push({
16
+ id: `chunk-${Date.now()}-${i}`,
17
+ content: doc.content,
18
+ embedding,
19
+ metadata: doc.metadata || {}
20
+ });
21
+ }
22
+ await vectorStore.add(vectors, namespace);
23
+ return vectors.map((v) => ({
24
+ id: v.id,
25
+ content: v.content,
26
+ metadata: v.metadata,
27
+ embedding: v.embedding
28
+ }));
29
+ }
30
+ async function retrieve(query2, k = topK) {
31
+ const queryEmbedding = await embed(query2);
32
+ const results = await vectorStore.search(queryEmbedding, {
33
+ topK: k,
34
+ threshold: similarityThreshold,
35
+ namespace
36
+ });
37
+ return results.map((r) => ({
38
+ id: r.id,
39
+ content: r.content,
40
+ metadata: r.metadata || {},
41
+ score: r.score,
42
+ embedding: r.embedding
43
+ }));
44
+ }
45
+ async function query(question, llm) {
46
+ const relevantDocs = await retrieve(question);
47
+ if (relevantDocs.length === 0) {
48
+ return {
49
+ answer: "\u62B1\u6B49\uFF0C\u77E5\u8BC6\u5E93\u4E2D\u6CA1\u6709\u627E\u5230\u76F8\u5173\u4FE1\u606F\u3002",
50
+ sources: [],
51
+ contextUsed: ""
52
+ };
53
+ }
54
+ const context = relevantDocs.map((doc, i) => `[\u6587\u6863 ${i + 1}]
55
+ ${doc.content}`).join("\n\n");
56
+ const prompt = `\u57FA\u4E8E\u4EE5\u4E0B\u77E5\u8BC6\u5E93\u5185\u5BB9\uFF0C\u7528\u4E2D\u6587\u56DE\u7B54\u7528\u6237\u7684\u95EE\u9898\u3002\u5982\u679C\u77E5\u8BC6\u5E93\u4E2D\u7684\u4FE1\u606F\u4E0D\u80FD\u56DE\u7B54\u95EE\u9898\uFF0C\u8BF7\u8BF4\u660E\u3002
57
+
58
+ \u77E5\u8BC6\u5E93\u5185\u5BB9:
59
+ ${context}
60
+
61
+ \u7528\u6237\u95EE\u9898: ${question}
62
+
63
+ \u8BF7\u7ED9\u51FA\u56DE\u7B54\uFF1A`;
64
+ const answer = await llm(prompt);
65
+ return {
66
+ answer,
67
+ sources: includeSources ? relevantDocs.map((doc) => ({
68
+ content: doc.content.slice(0, 200) + (doc.content.length > 200 ? "..." : ""),
69
+ metadata: doc.metadata,
70
+ score: doc.score ?? 0
71
+ })) : [],
72
+ contextUsed: context.slice(0, 500)
73
+ };
74
+ }
75
+ async function clear() {
76
+ await vectorStore.clear(namespace);
77
+ }
78
+ return {
79
+ addDocuments,
80
+ retrieve,
81
+ query,
82
+ clear,
83
+ config: { topK, similarityThreshold, namespace }
84
+ };
85
+ }
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.createRateLimitMiddleware = createRateLimitMiddleware;
7
+ exports.createRateLimiter = createRateLimiter;
8
+ const defaultKey = "__default__";
9
+ function createRateLimiter(config) {
10
+ const {
11
+ windowMs,
12
+ max,
13
+ keyBy = () => defaultKey,
14
+ message = "Too Many Requests"
15
+ } = config;
16
+ const store = /* @__PURE__ */new Map();
17
+ function getKey(cfg) {
18
+ if (typeof keyBy === "function") return keyBy(cfg) || defaultKey;
19
+ const h = cfg.headers || {};
20
+ if (keyBy === "userId") return cfg.userId ?? h["x-user-id"] ?? defaultKey;
21
+ if (keyBy === "apiKey") return h["x-api-key"] ?? h["authorization"] ?? defaultKey;
22
+ if (keyBy === "ip") return h["x-forwarded-for"] ?? h["x-real-ip"] ?? defaultKey;
23
+ return defaultKey;
24
+ }
25
+ function check(key) {
26
+ const now = Date.now();
27
+ let state = store.get(key);
28
+ if (!state || state.resetAt <= now) {
29
+ state = {
30
+ count: 0,
31
+ resetAt: now + windowMs
32
+ };
33
+ store.set(key, state);
34
+ }
35
+ state.count += 1;
36
+ const allowed = state.count <= max;
37
+ const remaining = Math.max(0, max - state.count);
38
+ return {
39
+ allowed,
40
+ remaining,
41
+ resetAt: state.resetAt
42
+ };
43
+ }
44
+ return {
45
+ check,
46
+ getKey,
47
+ /** 创建 XRequest 用的中间件 */
48
+ middleware() {
49
+ return {
50
+ name: "rate-limit",
51
+ async onRequest(requestConfig) {
52
+ const key = getKey(requestConfig);
53
+ const {
54
+ allowed,
55
+ remaining,
56
+ resetAt
57
+ } = check(key);
58
+ if (!allowed) {
59
+ const err = new Error(message);
60
+ err.statusCode = 429;
61
+ err.remaining = remaining;
62
+ err.resetAt = resetAt;
63
+ throw err;
64
+ }
65
+ return requestConfig;
66
+ }
67
+ };
68
+ }
69
+ };
70
+ }
71
+ function createRateLimitMiddleware(config) {
72
+ return createRateLimiter(config).middleware();
73
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * YH-UI AI SDK - 限流中间件
3
+ *
4
+ * 支持按用户/API Key 的令牌桶或滑动窗口,可与 AI Gateway 策略对齐。
5
+ */
6
+ export interface RateLimitConfig {
7
+ /** 时间窗口(毫秒) */
8
+ windowMs: number;
9
+ /** 窗口内最大请求数 */
10
+ max: number;
11
+ /** 标识键:从 config 或 headers 中取,如 'userId' | 'apiKey',默认同一 key 共享限额 */
12
+ keyBy?: 'userId' | 'apiKey' | 'ip' | ((config: {
13
+ headers?: Record<string, string>;
14
+ [k: string]: unknown;
15
+ }) => string);
16
+ /** 超限时返回的消息 */
17
+ message?: string;
18
+ }
19
+ /**
20
+ * 内存版限流器(单进程有效)
21
+ */
22
+ export declare function createRateLimiter(config: RateLimitConfig): {
23
+ check: (key: string) => {
24
+ allowed: boolean;
25
+ remaining: number;
26
+ resetAt: number;
27
+ };
28
+ getKey: (cfg: {
29
+ headers?: Record<string, string>;
30
+ [k: string]: unknown;
31
+ }) => string;
32
+ /** 创建 XRequest 用的中间件 */
33
+ middleware(): {
34
+ name: string;
35
+ onRequest(requestConfig: {
36
+ headers?: Record<string, string>;
37
+ [k: string]: unknown;
38
+ }): Promise<{
39
+ [k: string]: unknown;
40
+ headers?: Record<string, string>;
41
+ }>;
42
+ };
43
+ };
44
+ /** 限流中间件(与 XRequest / registerMiddleware 兼容) */
45
+ export interface RateLimitMiddleware {
46
+ name: string;
47
+ onRequest?: (config: {
48
+ headers?: Record<string, string>;
49
+ [k: string]: unknown;
50
+ }) => unknown | Promise<unknown>;
51
+ }
52
+ /**
53
+ * 创建限流中间件(便捷方法)
54
+ */
55
+ export declare function createRateLimitMiddleware(config: RateLimitConfig): RateLimitMiddleware;
@@ -0,0 +1,51 @@
1
+ const defaultKey = "__default__";
2
+ export function createRateLimiter(config) {
3
+ const { windowMs, max, keyBy = () => defaultKey, message = "Too Many Requests" } = config;
4
+ const store = /* @__PURE__ */ new Map();
5
+ function getKey(cfg) {
6
+ if (typeof keyBy === "function") return keyBy(cfg) || defaultKey;
7
+ const h = cfg.headers || {};
8
+ if (keyBy === "userId")
9
+ return cfg.userId ?? h["x-user-id"] ?? defaultKey;
10
+ if (keyBy === "apiKey") return h["x-api-key"] ?? h["authorization"] ?? defaultKey;
11
+ if (keyBy === "ip") return h["x-forwarded-for"] ?? h["x-real-ip"] ?? defaultKey;
12
+ return defaultKey;
13
+ }
14
+ function check(key) {
15
+ const now = Date.now();
16
+ let state = store.get(key);
17
+ if (!state || state.resetAt <= now) {
18
+ state = { count: 0, resetAt: now + windowMs };
19
+ store.set(key, state);
20
+ }
21
+ state.count += 1;
22
+ const allowed = state.count <= max;
23
+ const remaining = Math.max(0, max - state.count);
24
+ return { allowed, remaining, resetAt: state.resetAt };
25
+ }
26
+ return {
27
+ check,
28
+ getKey,
29
+ /** 创建 XRequest 用的中间件 */
30
+ middleware() {
31
+ return {
32
+ name: "rate-limit",
33
+ async onRequest(requestConfig) {
34
+ const key = getKey(requestConfig);
35
+ const { allowed, remaining, resetAt } = check(key);
36
+ if (!allowed) {
37
+ const err = new Error(message);
38
+ err.statusCode = 429;
39
+ err.remaining = remaining;
40
+ err.resetAt = resetAt;
41
+ throw err;
42
+ }
43
+ return requestConfig;
44
+ }
45
+ };
46
+ }
47
+ };
48
+ }
49
+ export function createRateLimitMiddleware(config) {
50
+ return createRateLimiter(config).middleware();
51
+ }
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.createInMemoryVectorStore = createInMemoryVectorStore;
7
+ function createInMemoryVectorStore(_config) {
8
+ const store = /* @__PURE__ */new Map();
9
+ function cosineSimilarity(a, b) {
10
+ if (a.length !== b.length) return 0;
11
+ let dot = 0;
12
+ let normA = 0;
13
+ let normB = 0;
14
+ for (let i = 0; i < a.length; i++) {
15
+ dot += a[i] * b[i];
16
+ normA += a[i] * a[i];
17
+ normB += b[i] * b[i];
18
+ }
19
+ const denom = Math.sqrt(normA) * Math.sqrt(normB);
20
+ return denom === 0 ? 0 : dot / denom;
21
+ }
22
+ return {
23
+ async add(documents, namespace = "default") {
24
+ const list = store.get(namespace) || [];
25
+ for (const doc of documents) {
26
+ const idx = list.findIndex(d => d.id === doc.id);
27
+ if (idx >= 0) list[idx] = doc;else list.push(doc);
28
+ }
29
+ store.set(namespace, list);
30
+ },
31
+ async search(queryEmbedding, options = {}) {
32
+ const topK = options.topK ?? 10;
33
+ const threshold = options.threshold ?? 0;
34
+ const namespaceOpt = options.namespace ?? "default";
35
+ const list = store.get(namespaceOpt) || [];
36
+ const scored = list.map(doc => ({
37
+ ...doc,
38
+ score: cosineSimilarity(doc.embedding, queryEmbedding)
39
+ })).filter(d => d.score >= threshold).sort((a, b) => b.score - a.score).slice(0, topK);
40
+ return scored.map(({
41
+ id,
42
+ content,
43
+ metadata,
44
+ score,
45
+ embedding
46
+ }) => ({
47
+ id,
48
+ content,
49
+ metadata,
50
+ score,
51
+ embedding
52
+ }));
53
+ },
54
+ async delete(ids, namespace = "default") {
55
+ const list = store.get(namespace) || [];
56
+ const set = new Set(ids);
57
+ store.set(namespace, list.filter(d => !set.has(d.id)));
58
+ },
59
+ async clear(namespace = "default") {
60
+ store.set(namespace, []);
61
+ }
62
+ };
63
+ }