@allen-wei/model-hub-core 0.0.0-stage → 0.1.0
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/chain.d.ts +151 -0
- package/dist/chain.d.ts.map +1 -0
- package/dist/chain.js +62 -0
- package/dist/chain.js.map +1 -0
- package/dist/client-id.d.ts +7 -0
- package/dist/client-id.d.ts.map +1 -0
- package/dist/client-id.js +55 -0
- package/dist/client-id.js.map +1 -0
- package/dist/context.d.ts +26 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +53 -0
- package/dist/context.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/llm.d.ts +70 -0
- package/dist/llm.d.ts.map +1 -0
- package/dist/llm.js +439 -0
- package/dist/llm.js.map +1 -0
- package/dist/models.d.ts +157 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +257 -0
- package/dist/models.js.map +1 -0
- package/dist/prefs.d.ts +11 -0
- package/dist/prefs.d.ts.map +1 -0
- package/dist/prefs.js +38 -0
- package/dist/prefs.js.map +1 -0
- package/dist/sessions.d.ts +22 -0
- package/dist/sessions.d.ts.map +1 -0
- package/dist/sessions.js +109 -0
- package/dist/sessions.js.map +1 -0
- package/dist/store.d.ts +90 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +410 -0
- package/dist/store.js.map +1 -0
- package/dist/task-detect.d.ts +11 -0
- package/dist/task-detect.d.ts.map +1 -0
- package/dist/task-detect.js +49 -0
- package/dist/task-detect.js.map +1 -0
- package/dist/types.d.ts +82 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/package.json +49 -4
- package/README.md +0 -3
package/dist/chain.d.ts
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { type ModelAvailability, type ModelOptions } from './models.js';
|
|
2
|
+
import type { IChatTurn, ModelKey, Msg, TaskType } from './types.js';
|
|
3
|
+
/** 会话标题:首条用户消息前 30 字,超出加省略号。 */
|
|
4
|
+
export declare function titleFor(question: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* 主问答链路(failover 顺序,按模型能力排):
|
|
7
|
+
* 付费主力 GLM-4.7-Flash → 免费长上下文 Xing4.0-29B → 工具型轻量 GLM-Z1-9B → 兜底 Qwen2.5-7B(32K,靠逐候选截断保护)。
|
|
8
|
+
*/
|
|
9
|
+
export declare const main_failover_chain: readonly ModelKey[];
|
|
10
|
+
/** 专项路由:按任务类型主动使用专用模型(数学/逻辑 → 推理模型;翻译 → 翻译模型)。 */
|
|
11
|
+
export declare const specialty_routes: ReadonlyArray<{
|
|
12
|
+
task: TaskType;
|
|
13
|
+
model: ModelKey;
|
|
14
|
+
}>;
|
|
15
|
+
export declare function specialty_route_for(task: TaskType): ModelKey | null;
|
|
16
|
+
/** 本轮可用的模型:服务端已配置 key,且前端标记为可对话(视觉/OCR 模型待功能接入)。 */
|
|
17
|
+
export declare function canUseModel(key: ModelKey, availability: ModelAvailability, options?: ModelOptions): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* 构建尝试链(按能力排序):
|
|
20
|
+
* 1. 专项任务先试专用模型(数学/逻辑 → DeepSeek-R1-8B,翻译 → Hunyuan-MT-7B);
|
|
21
|
+
* 2. 其次是用户当前所选模型(不越过用户选择强行改答案);
|
|
22
|
+
* 3. 最后按 `main_failover_chain` 依次兜底,已试过的自动去重。
|
|
23
|
+
* 不可用(未配 key / 暂未开放对话)的模型直接跳过;`autoFallback` 为 false 时只保留当前模型。
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildChain(selected: ModelKey, question: string, availability: ModelAvailability, autoFallback?: boolean, options?: ModelOptions): ModelKey[];
|
|
26
|
+
/** 已完成轮次 → 多轮上下文(跳过正在流式/失败的轮次与重试轮本身)。 */
|
|
27
|
+
export declare function getCompletedHistory(turns: IChatTurn[], retryId?: string): Msg[];
|
|
28
|
+
export declare const SYSTEM_MESSAGE: Msg;
|
|
29
|
+
/** 便捷函数:从 ModelKey 取模型展示信息(供 UI 层使用,与 store 解耦)。 */
|
|
30
|
+
export declare function describeModel(key: ModelKey, options?: ModelOptions): {
|
|
31
|
+
readonly key: "zhipu";
|
|
32
|
+
readonly provider: "zhipu";
|
|
33
|
+
readonly label: "GLM-4.7-Flash";
|
|
34
|
+
readonly providerLabel: "智谱";
|
|
35
|
+
readonly model: "glm-4.7-flash";
|
|
36
|
+
readonly baseURL: "https://open.bigmodel.cn/api/paas/v4";
|
|
37
|
+
readonly keyEnv: "ZHIPU_API_KEY";
|
|
38
|
+
readonly description: "商用旗舰 · 200K 上下文 · Tools 工具调用";
|
|
39
|
+
readonly contextWindow: 200000;
|
|
40
|
+
readonly chat_enabled: true;
|
|
41
|
+
readonly tags: readonly ["商用旗舰", "200K 上下文", "Tools 工具调用", "通用助手", "付费"];
|
|
42
|
+
readonly points: readonly ["智谱 GLM 商用旗舰系列 Flash 版本,综合能力与稳定性最高", "200K 长上下文,支持长文档与多轮对话", "原生支持 Tools 工具调用,函数调用成熟可靠", "付费模型,主问答链路主力,承担复杂问答与兜底"];
|
|
43
|
+
} | {
|
|
44
|
+
readonly key: "xing";
|
|
45
|
+
readonly provider: "siliconflow";
|
|
46
|
+
readonly label: "Xing4.0-29B";
|
|
47
|
+
readonly providerLabel: "硅基流动";
|
|
48
|
+
readonly model: "XingChenAGI/Xing4.0-29B";
|
|
49
|
+
readonly baseURL: "https://api.siliconflow.cn/v1";
|
|
50
|
+
readonly keyEnv: "SILICONFLOW_API_KEY";
|
|
51
|
+
readonly description: "29B MoE · 4B 激活 · 256K 上下文";
|
|
52
|
+
readonly contextWindow: 262144;
|
|
53
|
+
readonly chat_enabled: true;
|
|
54
|
+
readonly tags: readonly ["29B MoE", "4B 激活", "256K 上下文", "Tools", "Free"];
|
|
55
|
+
readonly points: readonly ["MoE 架构,总参数 29B、单次激活 4B,兼顾能力与响应速度", "原生 256K 上下文,外推可达 512K,适合长文本处理", "支持 Tools 工具调用,具备复杂推理与长程任务执行能力", "免费模型,作为第一降级与长文档 RAG 场景"];
|
|
56
|
+
} | {
|
|
57
|
+
readonly key: "glm_z1_9b";
|
|
58
|
+
readonly provider: "siliconflow";
|
|
59
|
+
readonly label: "GLM-Z1-9B-0414";
|
|
60
|
+
readonly providerLabel: "硅基流动";
|
|
61
|
+
readonly model: "THUDM/GLM-Z1-9B-0414";
|
|
62
|
+
readonly baseURL: "https://api.siliconflow.cn/v1";
|
|
63
|
+
readonly keyEnv: "SILICONFLOW_API_KEY";
|
|
64
|
+
readonly description: "9B 开源 · 128K 上下文 · 工具型降级";
|
|
65
|
+
readonly contextWindow: 131072;
|
|
66
|
+
readonly chat_enabled: true;
|
|
67
|
+
readonly tags: readonly ["9B 开源", "128K 上下文", "Tools", "RAG", "Free"];
|
|
68
|
+
readonly points: readonly ["GLM 系列 90 亿参数小模型,同规格开源模型中性能领先", "数学推理与通用任务表现出色,效率与效果平衡", "支持 Tools 工具调用、长文本处理与 RAG", "适合资源受限场景的轻量级部署,定位工具型降级"];
|
|
69
|
+
} | {
|
|
70
|
+
readonly key: "qwen";
|
|
71
|
+
readonly provider: "siliconflow";
|
|
72
|
+
readonly label: "Qwen2.5-7B-Instruct";
|
|
73
|
+
readonly providerLabel: "硅基流动";
|
|
74
|
+
readonly model: "Qwen/Qwen2.5-7B-Instruct";
|
|
75
|
+
readonly baseURL: "https://api.siliconflow.cn/v1";
|
|
76
|
+
readonly keyEnv: "SILICONFLOW_API_KEY";
|
|
77
|
+
readonly description: "7B 开源 · 32K 上下文 · 兜底";
|
|
78
|
+
readonly contextWindow: 32768;
|
|
79
|
+
readonly chat_enabled: true;
|
|
80
|
+
readonly tags: readonly ["7B 开源", "32K 上下文", "29+ 语言", "JSON 输出", "Free"];
|
|
81
|
+
readonly points: readonly ["阿里开源 7B 模型,编码与数学能力显著改进", "支持 29+ 种语言(含中英文),多语言任务表现均衡", "指令跟随与 JSON 结构化输出显著提升", "轻量可本地部署,成本低,适合通用助手与文案创作"];
|
|
82
|
+
} | {
|
|
83
|
+
readonly key: "deepseek_r1_8b";
|
|
84
|
+
readonly provider: "siliconflow";
|
|
85
|
+
readonly label: "DeepSeek-R1-0528-Qwen3-8B";
|
|
86
|
+
readonly providerLabel: "硅基流动";
|
|
87
|
+
readonly model: "deepseek-ai/DeepSeek-R1-0528-Qwen3-8B";
|
|
88
|
+
readonly baseURL: "https://api.siliconflow.cn/v1";
|
|
89
|
+
readonly keyEnv: "SILICONFLOW_API_KEY";
|
|
90
|
+
readonly description: "8B 推理模型 · 128K 上下文 · 数学/逻辑专项";
|
|
91
|
+
readonly contextWindow: 131072;
|
|
92
|
+
readonly chat_enabled: true;
|
|
93
|
+
readonly tags: readonly ["8B 开源", "128K 上下文", "推理模型", "数学推理", "Free"];
|
|
94
|
+
readonly points: readonly ["由 DeepSeek-R1-0528 蒸馏思维链到 Qwen3 8B 获得", "开源模型中达到 SOTA,AIME 2024 超越 Qwen3 8B 10%", "数学推理、编程与通用逻辑基准表现出色", "适合数学、编程、逻辑推理专项任务"];
|
|
95
|
+
} | {
|
|
96
|
+
readonly key: "qwen3_5_4b";
|
|
97
|
+
readonly provider: "siliconflow";
|
|
98
|
+
readonly label: "Qwen3.5-4B";
|
|
99
|
+
readonly providerLabel: "硅基流动";
|
|
100
|
+
readonly model: "Qwen/Qwen3.5-4B";
|
|
101
|
+
readonly baseURL: "https://api.siliconflow.cn/v1";
|
|
102
|
+
readonly keyEnv: "SILICONFLOW_API_KEY";
|
|
103
|
+
readonly description: "4B 多模态 · 256K 上下文 · 图片/视频理解";
|
|
104
|
+
readonly contextWindow: 262144;
|
|
105
|
+
readonly chat_enabled: false;
|
|
106
|
+
readonly disabled_hint: "图片 / 视频理解功能尚未接入,暂不可对话";
|
|
107
|
+
readonly tags: readonly ["4B 开源", "256K 上下文", "多模态", "Tools", "Free"];
|
|
108
|
+
readonly points: readonly ["Qwen3.5 系列最轻量 Dense 模型,原生支持文/图/视频理解", "原生 256K 上下文,可扩展至约 100 万 token", "默认思考模式,支持工具调用,覆盖 201 种语言", "同规模指标超越 GPT-5-Nano 与 Gemini-2.5-Flash-Lite"];
|
|
109
|
+
} | {
|
|
110
|
+
readonly key: "paddle_ocr_vl";
|
|
111
|
+
readonly provider: "siliconflow";
|
|
112
|
+
readonly label: "PaddleOCR-VL-1.5";
|
|
113
|
+
readonly providerLabel: "硅基流动";
|
|
114
|
+
readonly model: "PaddlePaddle/PaddleOCR-VL-1.5";
|
|
115
|
+
readonly baseURL: "https://api.siliconflow.cn/v1";
|
|
116
|
+
readonly keyEnv: "SILICONFLOW_API_KEY";
|
|
117
|
+
readonly description: "0.9B 视觉 OCR · 文档解析 · 印章识别";
|
|
118
|
+
readonly contextWindow: 32768;
|
|
119
|
+
readonly chat_enabled: false;
|
|
120
|
+
readonly disabled_hint: "需要图片输入,图片能力接入前暂不可对话";
|
|
121
|
+
readonly tags: readonly ["0.9B 开源", "视觉 OCR", "文档解析", "印章识别", "Free"];
|
|
122
|
+
readonly points: readonly ["OmniDocBench v1.5 精度 94.5%,保持 SOTA", "支持文档元素异形框定位,扫描/倾斜/屏幕拍摄表现稳定", "集成印章识别与文本检测识别,关键指标领先", "适合复杂版面文档解析与入库"];
|
|
123
|
+
} | {
|
|
124
|
+
readonly key: "deepseek_ocr";
|
|
125
|
+
readonly provider: "siliconflow";
|
|
126
|
+
readonly label: "DeepSeek-OCR";
|
|
127
|
+
readonly providerLabel: "硅基流动";
|
|
128
|
+
readonly model: "deepseek-ai/DeepSeek-OCR";
|
|
129
|
+
readonly baseURL: "https://api.siliconflow.cn/v1";
|
|
130
|
+
readonly keyEnv: "SILICONFLOW_API_KEY";
|
|
131
|
+
readonly description: "3B 视觉 OCR · 文档转 Markdown";
|
|
132
|
+
readonly contextWindow: 8192;
|
|
133
|
+
readonly chat_enabled: false;
|
|
134
|
+
readonly disabled_hint: "需要图片输入,图片能力接入前暂不可对话";
|
|
135
|
+
readonly tags: readonly ["3B 开源", "8K 上下文", "视觉 OCR", "Markdown", "限免"];
|
|
136
|
+
readonly points: readonly ["DeepSeek 推出的视觉语言模型,专注 OCR 与上下文光学压缩", "高效处理文档并转换为 Markdown 等结构化文本", "探索从图像中压缩上下文信息的边界", "适合文档结构化提取与 Markdown 转换"];
|
|
137
|
+
} | {
|
|
138
|
+
readonly key: "hunyuan_mt";
|
|
139
|
+
readonly provider: "siliconflow";
|
|
140
|
+
readonly label: "Hunyuan-MT-7B";
|
|
141
|
+
readonly providerLabel: "硅基流动";
|
|
142
|
+
readonly model: "tencent/Hunyuan-MT-7B";
|
|
143
|
+
readonly baseURL: "https://api.siliconflow.cn/v1";
|
|
144
|
+
readonly keyEnv: "SILICONFLOW_API_KEY";
|
|
145
|
+
readonly description: "7B 翻译模型 · 32K 上下文 · 33 语言";
|
|
146
|
+
readonly contextWindow: 32768;
|
|
147
|
+
readonly chat_enabled: true;
|
|
148
|
+
readonly tags: readonly ["7B 开源", "32K 上下文", "内容翻译", "33 语言", "限免"];
|
|
149
|
+
readonly points: readonly ["腾讯混元 70 亿参数轻量翻译模型", "支持 33 种语言及 5 种中国少数民族语言互译", "WMT25 参与 31 个类别获 30 个第一", "计算效率高、易部署,适合多语言翻译场景"];
|
|
150
|
+
};
|
|
151
|
+
//# sourceMappingURL=chain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chain.d.ts","sourceRoot":"","sources":["../src/chain.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,KAAK,iBAAiB,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAEjG,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAErE,gCAAgC;AAChC,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIjD;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,QAAQ,EAA2C,CAAC;AAE/F,kDAAkD;AAClD,eAAO,MAAM,gBAAgB,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,CAG/E,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,IAAI,CAEnE;AAED,oDAAoD;AACpD,wBAAgB,WAAW,CACzB,GAAG,EAAE,QAAQ,EACb,YAAY,EAAE,iBAAiB,EAC/B,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAET;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,iBAAiB,EAC/B,YAAY,UAAO,EACnB,OAAO,CAAC,EAAE,YAAY,GACrB,QAAQ,EAAE,CAWZ;AAED,yCAAyC;AACzC,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,GAAG,EAAE,CAM/E;AAED,eAAO,MAAM,cAAc,EAAE,GAAqD,CAAC;AAEnF,oDAAoD;AACpD,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAElE"}
|
package/dist/chain.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { getModel, isChatEnabled } from './models.js';
|
|
2
|
+
import { detectTaskType } from './task-detect.js';
|
|
3
|
+
/** 会话标题:首条用户消息前 30 字,超出加省略号。 */
|
|
4
|
+
export function titleFor(question) {
|
|
5
|
+
const trimmed = question.trim();
|
|
6
|
+
if (!trimmed)
|
|
7
|
+
return '新对话';
|
|
8
|
+
return trimmed.length > 30 ? `${trimmed.slice(0, 30)}…` : trimmed;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 主问答链路(failover 顺序,按模型能力排):
|
|
12
|
+
* 付费主力 GLM-4.7-Flash → 免费长上下文 Xing4.0-29B → 工具型轻量 GLM-Z1-9B → 兜底 Qwen2.5-7B(32K,靠逐候选截断保护)。
|
|
13
|
+
*/
|
|
14
|
+
export const main_failover_chain = ['zhipu', 'xing', 'glm_z1_9b', 'qwen'];
|
|
15
|
+
/** 专项路由:按任务类型主动使用专用模型(数学/逻辑 → 推理模型;翻译 → 翻译模型)。 */
|
|
16
|
+
export const specialty_routes = [
|
|
17
|
+
{ task: 'math', model: 'deepseek_r1_8b' },
|
|
18
|
+
{ task: 'translate', model: 'hunyuan_mt' },
|
|
19
|
+
];
|
|
20
|
+
export function specialty_route_for(task) {
|
|
21
|
+
return specialty_routes.find(route => route.task === task)?.model ?? null;
|
|
22
|
+
}
|
|
23
|
+
/** 本轮可用的模型:服务端已配置 key,且前端标记为可对话(视觉/OCR 模型待功能接入)。 */
|
|
24
|
+
export function canUseModel(key, availability, options) {
|
|
25
|
+
return Boolean(availability[key]) && isChatEnabled(key, options);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 构建尝试链(按能力排序):
|
|
29
|
+
* 1. 专项任务先试专用模型(数学/逻辑 → DeepSeek-R1-8B,翻译 → Hunyuan-MT-7B);
|
|
30
|
+
* 2. 其次是用户当前所选模型(不越过用户选择强行改答案);
|
|
31
|
+
* 3. 最后按 `main_failover_chain` 依次兜底,已试过的自动去重。
|
|
32
|
+
* 不可用(未配 key / 暂未开放对话)的模型直接跳过;`autoFallback` 为 false 时只保留当前模型。
|
|
33
|
+
*/
|
|
34
|
+
export function buildChain(selected, question, availability, autoFallback = true, options) {
|
|
35
|
+
if (!autoFallback)
|
|
36
|
+
return [selected];
|
|
37
|
+
const route = specialty_route_for(detectTaskType(question));
|
|
38
|
+
const order = [...(route ? [route] : []), selected, ...main_failover_chain];
|
|
39
|
+
const chain = [];
|
|
40
|
+
for (const key of order) {
|
|
41
|
+
if (chain.includes(key))
|
|
42
|
+
continue;
|
|
43
|
+
if (key !== selected && !canUseModel(key, availability, options))
|
|
44
|
+
continue;
|
|
45
|
+
chain.push(key);
|
|
46
|
+
}
|
|
47
|
+
return chain;
|
|
48
|
+
}
|
|
49
|
+
/** 已完成轮次 → 多轮上下文(跳过正在流式/失败的轮次与重试轮本身)。 */
|
|
50
|
+
export function getCompletedHistory(turns, retryId) {
|
|
51
|
+
return turns.filter(turn => turn.status === 'complete' && turn.id !== retryId)
|
|
52
|
+
.flatMap(turn => [
|
|
53
|
+
{ role: 'user', content: turn.question },
|
|
54
|
+
{ role: 'assistant', content: turn.answer },
|
|
55
|
+
]);
|
|
56
|
+
}
|
|
57
|
+
export const SYSTEM_MESSAGE = { role: 'system', content: '你是一个严谨的助手,用中文回答。' };
|
|
58
|
+
/** 便捷函数:从 ModelKey 取模型展示信息(供 UI 层使用,与 store 解耦)。 */
|
|
59
|
+
export function describeModel(key, options) {
|
|
60
|
+
return getModel(key, options);
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=chain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chain.js","sourceRoot":"","sources":["../src/chain.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** 读取(必要时生成并持久化)匿名客户端标识;localStorage 不可用时退化为内存态。 */
|
|
2
|
+
export declare function getClientId(): string;
|
|
3
|
+
/** 请求头:会话接口与对话接口用它区分不同浏览器。 */
|
|
4
|
+
export declare function clientIdHeaders(): Record<string, string>;
|
|
5
|
+
/** 仅供测试:清掉内存缓存,重新从 localStorage 读取。 */
|
|
6
|
+
export declare function resetClientIdCache(): void;
|
|
7
|
+
//# sourceMappingURL=client-id.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-id.d.ts","sourceRoot":"","sources":["../src/client-id.ts"],"names":[],"mappings":"AAyBA,oDAAoD;AACpD,wBAAgB,WAAW,IAAI,MAAM,CAgBpC;AAED,8BAA8B;AAC9B,wBAAgB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAExD;AAED,uCAAuC;AACvC,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 匿名客户端标识(不是账号):首次访问生成一个随机 id 存在 localStorage,
|
|
3
|
+
* 之后每次请求带 `X-Client-Id`,服务端据此把会话数据与限流配额按浏览器隔离。
|
|
4
|
+
* 同一浏览器同一域名下多标签页共用同一个 id;换设备/清站点数据会得到新的 id。
|
|
5
|
+
*
|
|
6
|
+
* 与服务端 `/api/sessions`、`/api/chat` 的约定一致(见 server/API.md)。
|
|
7
|
+
*/
|
|
8
|
+
const STORAGE_KEY = 'modelhub:client-id';
|
|
9
|
+
const VALID_ID = /^[A-Za-z0-9_-]{8,64}$/;
|
|
10
|
+
let memoryId = null;
|
|
11
|
+
/** 生成 UUID v4;非安全上下文(http 局域网)没有 randomUUID,用 getRandomValues 兜底。 */
|
|
12
|
+
function randomId() {
|
|
13
|
+
const cryptoApi = globalThis.crypto;
|
|
14
|
+
if (typeof cryptoApi?.randomUUID === 'function')
|
|
15
|
+
return cryptoApi.randomUUID();
|
|
16
|
+
const bytes = new Uint8Array(16);
|
|
17
|
+
if (typeof cryptoApi?.getRandomValues === 'function')
|
|
18
|
+
cryptoApi.getRandomValues(bytes);
|
|
19
|
+
else
|
|
20
|
+
for (let i = 0; i < bytes.length; i++)
|
|
21
|
+
bytes[i] = Math.floor(Math.random() * 256);
|
|
22
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
23
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
24
|
+
const hex = Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
25
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
26
|
+
}
|
|
27
|
+
/** 读取(必要时生成并持久化)匿名客户端标识;localStorage 不可用时退化为内存态。 */
|
|
28
|
+
export function getClientId() {
|
|
29
|
+
if (memoryId)
|
|
30
|
+
return memoryId;
|
|
31
|
+
try {
|
|
32
|
+
const stored = globalThis.localStorage.getItem(STORAGE_KEY);
|
|
33
|
+
if (stored && VALID_ID.test(stored)) {
|
|
34
|
+
memoryId = stored;
|
|
35
|
+
return stored;
|
|
36
|
+
}
|
|
37
|
+
const created = randomId();
|
|
38
|
+
globalThis.localStorage.setItem(STORAGE_KEY, created);
|
|
39
|
+
memoryId = created;
|
|
40
|
+
return created;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
memoryId = randomId();
|
|
44
|
+
return memoryId;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** 请求头:会话接口与对话接口用它区分不同浏览器。 */
|
|
48
|
+
export function clientIdHeaders() {
|
|
49
|
+
return { 'X-Client-Id': getClientId() };
|
|
50
|
+
}
|
|
51
|
+
/** 仅供测试:清掉内存缓存,重新从 localStorage 读取。 */
|
|
52
|
+
export function resetClientIdCache() {
|
|
53
|
+
memoryId = null;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=client-id.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-id.js","sourceRoot":"","sources":["../src/client-id.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,GAAG,oBAAoB,CAAC;AACzC,MAAM,QAAQ,GAAG,uBAAuB,CAAC;AAEzC,IAAI,QAAQ,GAAkB,IAAI,CAAC;AAEnC,qEAAqE;AACrE,SAAS,QAAQ;IACf,MAAM,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC;IACpC,IAAI,OAAO,SAAS,EAAE,UAAU,KAAK,UAAU;QAAE,OAAO,SAAS,CAAC,UAAU,EAAE,CAAC;IAC/E,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IACjC,IAAI,OAAO,SAAS,EAAE,eAAe,KAAK,UAAU;QAAE,SAAS,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;;QAClF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE;YAAE,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC;IACvF,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IACpC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IACpC,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACnF,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;AAC7G,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,WAAW;IACzB,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,UAAU,CAAC,YAAY,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAC5D,IAAI,MAAM,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YACpC,QAAQ,GAAG,MAAM,CAAC;YAClB,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,MAAM,OAAO,GAAG,QAAQ,EAAE,CAAC;QAC3B,UAAU,CAAC,YAAY,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACtD,QAAQ,GAAG,OAAO,CAAC;QACnB,OAAO,OAAO,CAAC;IACjB,CAAC;IAAC,MAAM,CAAC;QACP,QAAQ,GAAG,QAAQ,EAAE,CAAC;QACtB,OAAO,QAAQ,CAAC;IAClB,CAAC;AACH,CAAC;AAED,8BAA8B;AAC9B,MAAM,UAAU,eAAe;IAC7B,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,EAAE,CAAC;AAC1C,CAAC;AAED,uCAAuC;AACvC,MAAM,UAAU,kBAAkB;IAChC,QAAQ,GAAG,IAAI,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Msg } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* 粗略估算一段文本的 token 数:
|
|
4
|
+
* 中文/全角字符约 1 token,其余字符约 4 字符 1 token。
|
|
5
|
+
* 仅用于上下文占用占比与自动截断判断,不代表平台真实 tokenizer 精确值。
|
|
6
|
+
*/
|
|
7
|
+
export declare function estimateTokens(text: string): number;
|
|
8
|
+
/** 估算整组消息的 token 数(每条消息加固定开销近似)。 */
|
|
9
|
+
export declare function estimateMessagesTokens(messages: Msg[]): number;
|
|
10
|
+
/** 自动截断:保留 system 与最新一条 user,从最旧对话对开始丢弃,直到估算占用不超窗口(留 5% 安全余量)。 */
|
|
11
|
+
export declare function truncateMessages(messages: Msg[], limit: number): {
|
|
12
|
+
messages: Msg[];
|
|
13
|
+
droppedTurns: number;
|
|
14
|
+
};
|
|
15
|
+
export interface ContextUsage {
|
|
16
|
+
used: number;
|
|
17
|
+
limit: number;
|
|
18
|
+
ratio: number;
|
|
19
|
+
exceeds: boolean;
|
|
20
|
+
}
|
|
21
|
+
/** 计算「已完成历史 + 当前输入」的上下文占用情况(含 system prompt 与消息开销)。 */
|
|
22
|
+
export declare function buildContextUsage(history: {
|
|
23
|
+
question: string;
|
|
24
|
+
answer: string;
|
|
25
|
+
}[], draft: string, limit: number): ContextUsage;
|
|
26
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAEtC;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQnD;AAED,oCAAoC;AACpC,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,MAAM,CAE9D;AAED,kEAAkE;AAClE,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,GAAG,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAe1G;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,uDAAuD;AACvD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CAY7H"}
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 粗略估算一段文本的 token 数:
|
|
3
|
+
* 中文/全角字符约 1 token,其余字符约 4 字符 1 token。
|
|
4
|
+
* 仅用于上下文占用占比与自动截断判断,不代表平台真实 tokenizer 精确值。
|
|
5
|
+
*/
|
|
6
|
+
export function estimateTokens(text) {
|
|
7
|
+
if (!text)
|
|
8
|
+
return 0;
|
|
9
|
+
let cjk = 0;
|
|
10
|
+
for (const ch of text) {
|
|
11
|
+
const code = ch.codePointAt(0);
|
|
12
|
+
if ((code >= 0x4e00 && code <= 0x9fff) || (code >= 0x3000 && code <= 0x30ff) || (code >= 0xff00 && code <= 0xffef))
|
|
13
|
+
cjk++;
|
|
14
|
+
}
|
|
15
|
+
return Math.ceil(cjk + (text.length - cjk) / 4);
|
|
16
|
+
}
|
|
17
|
+
/** 估算整组消息的 token 数(每条消息加固定开销近似)。 */
|
|
18
|
+
export function estimateMessagesTokens(messages) {
|
|
19
|
+
return messages.reduce((sum, m) => sum + estimateTokens(m.content) + 4, 0);
|
|
20
|
+
}
|
|
21
|
+
/** 自动截断:保留 system 与最新一条 user,从最旧对话对开始丢弃,直到估算占用不超窗口(留 5% 安全余量)。 */
|
|
22
|
+
export function truncateMessages(messages, limit) {
|
|
23
|
+
const safeLimit = limit * 0.95;
|
|
24
|
+
if (estimateMessagesTokens(messages) <= safeLimit)
|
|
25
|
+
return { messages, droppedTurns: 0 };
|
|
26
|
+
const system = messages[0];
|
|
27
|
+
const lastUser = messages[messages.length - 1];
|
|
28
|
+
const head = messages.slice(1, -1);
|
|
29
|
+
let dropped = 0;
|
|
30
|
+
while (head.length >= 2 && estimateMessagesTokens([system, ...head, lastUser]) > safeLimit) {
|
|
31
|
+
head.splice(0, 2);
|
|
32
|
+
dropped++;
|
|
33
|
+
}
|
|
34
|
+
if (estimateMessagesTokens([system, ...head, lastUser]) > safeLimit) {
|
|
35
|
+
throw new Error(`当前内容已超过模型上下文上限(约 ${limit.toLocaleString()} tokens),请精简内容或开启新对话。`);
|
|
36
|
+
}
|
|
37
|
+
return { messages: [system, ...head, lastUser], droppedTurns: dropped };
|
|
38
|
+
}
|
|
39
|
+
/** 计算「已完成历史 + 当前输入」的上下文占用情况(含 system prompt 与消息开销)。 */
|
|
40
|
+
export function buildContextUsage(history, draft, limit) {
|
|
41
|
+
const messages = [
|
|
42
|
+
{ role: 'system', content: '你是一个严谨的助手,用中文回答。' },
|
|
43
|
+
...history.flatMap(turn => [
|
|
44
|
+
{ role: 'user', content: turn.question },
|
|
45
|
+
{ role: 'assistant', content: turn.answer },
|
|
46
|
+
]),
|
|
47
|
+
{ role: 'user', content: draft },
|
|
48
|
+
];
|
|
49
|
+
const used = estimateMessagesTokens(messages);
|
|
50
|
+
const ratio = limit > 0 ? Math.min(1, used / limit) : 1;
|
|
51
|
+
return { used, limit, ratio, exceeds: used >= limit };
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './types.js';
|
|
2
|
+
export * from './models.js';
|
|
3
|
+
export * from './context.js';
|
|
4
|
+
export * from './task-detect.js';
|
|
5
|
+
export * from './llm.js';
|
|
6
|
+
export * from './chain.js';
|
|
7
|
+
export * from './store.js';
|
|
8
|
+
export * from './client-id.js';
|
|
9
|
+
export * from './prefs.js';
|
|
10
|
+
export * from './sessions.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './types.js';
|
|
2
|
+
export * from './models.js';
|
|
3
|
+
export * from './context.js';
|
|
4
|
+
export * from './task-detect.js';
|
|
5
|
+
export * from './llm.js';
|
|
6
|
+
export * from './chain.js';
|
|
7
|
+
export * from './store.js';
|
|
8
|
+
export * from './client-id.js';
|
|
9
|
+
export * from './prefs.js';
|
|
10
|
+
export * from './sessions.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC"}
|
package/dist/llm.d.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { type ModelOptions } from './models.js';
|
|
2
|
+
import type { ChatRetryState, GetEnv, ModelKey, Msg, Provider } from './types.js';
|
|
3
|
+
export type { ChatRetryState };
|
|
4
|
+
export type ChatOptions = {
|
|
5
|
+
/** 模型标识(默认 zhipu)。 */
|
|
6
|
+
modelKey?: ModelKey;
|
|
7
|
+
/** 显式 API key(优先于 getEnv 读取)。 */
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
/** 显式平台 baseURL(优先于模型配置)。 */
|
|
10
|
+
baseURL?: string;
|
|
11
|
+
/** 显式模型 ID(优先于模型配置,用于运行时覆盖)。 */
|
|
12
|
+
model?: string;
|
|
13
|
+
/** 读取环境变量的注入函数(替代 import.meta.env / process.env)。 */
|
|
14
|
+
getEnv?: GetEnv;
|
|
15
|
+
/** 自定义模型白名单。 */
|
|
16
|
+
modelOptions?: ModelOptions;
|
|
17
|
+
/** 直连模式仅限本机:hostname 非回环地址时拒绝(默认 true)。非浏览器环境(hostname 为空)不校验,由使用方自行负责。 */
|
|
18
|
+
localOnly?: boolean;
|
|
19
|
+
/** 覆盖 hostname 判断(测试 / 服务端场景)。缺省取 globalThis.location?.hostname。 */
|
|
20
|
+
hostname?: string;
|
|
21
|
+
signal?: AbortSignal;
|
|
22
|
+
onRetryState?: (state: ChatRetryState | null) => void;
|
|
23
|
+
/** 模型思考过程增量回调(delta.reasoning_content / delta.reasoning);平台不返回时不会触发。 */
|
|
24
|
+
onReasoning?: (text: string) => void;
|
|
25
|
+
/** 限流类错误(1305/429)的自动重试次数;0 表示首次遇到即失败(用于上层自动切换模型)。默认与 RETRY_DELAYS_MS 长度一致。 */
|
|
26
|
+
rateLimitRetries?: number;
|
|
27
|
+
/**
|
|
28
|
+
* 服务端对话接口(如 `/api/chat`)。传入后改走「服务端代理」通道:
|
|
29
|
+
* key、模型 ID、上游地址全部由服务端持有,浏览器不发 key,也不需要 localOnly 保护。
|
|
30
|
+
* 不传则走直连模式(openai SDK 逐平台调用,仅适合本机开发或已注入 key 的宿主)。
|
|
31
|
+
*/
|
|
32
|
+
chatEndpoint?: string;
|
|
33
|
+
};
|
|
34
|
+
export declare const RETRY_DELAYS_MS: readonly [5000, 10000, 20000];
|
|
35
|
+
/** 流式响应静默超时:连续超过该时长未收到任何数据块(正文或思考)视为卡住/断流,中断并交由上层切换模型。 */
|
|
36
|
+
export declare const STREAM_IDLE_TIMEOUT_MS = 12000;
|
|
37
|
+
/** 流式静默超时错误:模型连接后长时间无数据,应触发自动切换(区别于普通网络错误)。 */
|
|
38
|
+
export declare class StreamIdleError extends Error {
|
|
39
|
+
constructor();
|
|
40
|
+
}
|
|
41
|
+
/** 后端不可达:服务未启动、端口/代理配置错误或网络断开。 */
|
|
42
|
+
export declare class BackendUnreachableError extends Error {
|
|
43
|
+
constructor();
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 服务端 / 平台返回的结构化错误。
|
|
47
|
+
* `status` 是**上游**真实状态码(如 429),不是 HTTP 层可能出现的 502;
|
|
48
|
+
* `error.code` 是平台业务码(如智谱 1305)。两者供 `getChatError` / `isRateLimitError` 判定。
|
|
49
|
+
*/
|
|
50
|
+
export type ChatServiceError = Error & {
|
|
51
|
+
status?: number;
|
|
52
|
+
error?: {
|
|
53
|
+
code?: string;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* 多模型流式对话调用(框架无关),支持两种通道:
|
|
58
|
+
* - **服务端通道**(推荐):传 `chatEndpoint`(如 `/api/chat`),key 与模型 ID 全部由服务端持有;
|
|
59
|
+
* - **直连通道**:不传 chatEndpoint,用 openai SDK 直连平台,key 通过 apiKey / getEnv 注入,
|
|
60
|
+
* 默认 localOnly=true 拒绝非回环地址,仅适合本机开发。
|
|
61
|
+
*/
|
|
62
|
+
export declare function chat(messages: Msg[], onChunk: (text: string) => void, { signal, onRetryState, onReasoning, modelKey, apiKey, baseURL, model, getEnv, modelOptions, localOnly, hostname, rateLimitRetries, chatEndpoint, }?: ChatOptions): Promise<void>;
|
|
63
|
+
export declare function getChatError(error: unknown, provider?: Provider): string;
|
|
64
|
+
/**
|
|
65
|
+
* 判断错误是否属于「限流/繁忙」类,可触发自动切换模型:
|
|
66
|
+
* 智谱业务码 1305(访问量过大)与 1302(请求频率限制)、硅基流动 HTTP 429。
|
|
67
|
+
* 其他错误(401 无效 key、404 模型不可用、套餐上限等)一律不切换,切换了也没用。
|
|
68
|
+
*/
|
|
69
|
+
export declare function isRateLimitError(error: unknown, provider?: Provider): boolean;
|
|
70
|
+
//# sourceMappingURL=llm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../src/llm.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2C,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAEzF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAElF,YAAY,EAAE,cAAc,EAAE,CAAC;AAE/B,MAAM,MAAM,WAAW,GAAG;IACxB,sBAAsB;IACtB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6BAA6B;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB;IAChB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,KAAK,IAAI,CAAC;IACtD,wEAAwE;IACxE,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,eAAO,MAAM,eAAe,+BAAmC,CAAC;AAEhE,0DAA0D;AAC1D,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAE7C,+CAA+C;AAC/C,qBAAa,eAAgB,SAAQ,KAAK;;CAKzC;AAED,kCAAkC;AAClC,qBAAa,uBAAwB,SAAQ,KAAK;;CAKjD;AAED;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAuPtF;;;;;GAKG;AACH,wBAAsB,IAAI,CACxB,QAAQ,EAAE,GAAG,EAAE,EACf,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,EAC/B,EACE,MAAM,EACN,YAAY,EACZ,WAAW,EACX,QAAkB,EAClB,MAAM,EACN,OAAO,EACP,KAAK,EACL,MAAM,EACN,YAAY,EACZ,SAAgB,EAChB,QAAQ,EACR,gBAAgB,EAChB,YAAY,GACb,GAAE,WAAgB,GAClB,OAAO,CAAC,IAAI,CAAC,CAoFf;AA6BD,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,GAAE,QAAkB,GAAG,MAAM,CA2BjF;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,GAAE,QAAkB,GAAG,OAAO,CAKtF"}
|