@keo-ai/axiom 0.2.4 → 0.2.7

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/README.md CHANGED
@@ -926,16 +926,16 @@ const multi = await EmbeddingSearch.query('query', {
926
926
 
927
927
  ### 生成 Embedding
928
928
 
929
- 如果你只需要把文本转成向量,直接用 `embed`:
929
+ 如果你只需要把文本转成向量,直接用 `EmbeddingSearch.embed`:
930
930
 
931
931
  ```ts
932
- import { embed } from '@keo-ai/axiom';
932
+ import { EmbeddingSearch } from '@keo-ai/axiom';
933
933
 
934
- const vector = await embed('衣服质量怎么样');
934
+ const vector = await EmbeddingSearch.embed('衣服质量怎么样');
935
935
  // vector: number[],默认 1024 维
936
936
 
937
937
  // 指定维度(1 ~ 1024)
938
- const vector256 = await embed('衣服质量怎么样', 256);
938
+ const vector256 = await EmbeddingSearch.embed('衣服质量怎么样', 256);
939
939
  ```
940
940
 
941
941
  | 参数 | 类型 | 默认值 | 说明 |
@@ -943,6 +943,28 @@ const vector256 = await embed('衣服质量怎么样', 256);
943
943
  | `text` | `string` | — | 要转成向量的文本(必填) |
944
944
  | `dimensions` | `number` | `1024` | 输出维度(1 ~ 1024) |
945
945
 
946
+ ### 批量生成 Embedding
947
+
948
+ 需要一次性把多篇文本转成向量时,使用 `EmbeddingSearch.batchEmbed`:
949
+
950
+ ```ts
951
+ import { EmbeddingSearch } from '@keo-ai/axiom';
952
+
953
+ const texts = ['衣服质量怎么样', '物流速度快吗', '售后服务如何'];
954
+ const vectors = await EmbeddingSearch.batchEmbed(texts);
955
+ // vectors: number[][],顺序与输入文本一致
956
+
957
+ // 指定维度
958
+ const vectors256 = await EmbeddingSearch.batchEmbed(texts, 256);
959
+ ```
960
+
961
+ | 参数 | 类型 | 默认值 | 说明 |
962
+ |---|---|---|---|
963
+ | `texts` | `string[]` | — | 要转成向量的文本数组(必填) |
964
+ | `dimensions` | `number` | `1024` | 输出维度(1 ~ 1024) |
965
+
966
+ > ⚠️ `EmbeddingSearch.batchEmbed` 单次最多支持 **25 条**文本(DashScope 兼容接口限制)。超过此数量请先自行分批。返回向量的顺序与输入顺序一致。
967
+
946
968
  ### 纯向量检索(已有向量)
947
969
 
948
970
  `EmbeddingSearch.query` 会自动把文本转成向量并做 rerank。如果你**已经有了向量**(比如自己生成的 embedding),只想做最原始的 pgvector 近邻搜索,用 `EmbeddingSearch.vectorSearch`:
@@ -1 +1,6 @@
1
1
  export declare function embed(text: string, dimensions?: number): Promise<number[]>;
2
+ /**
3
+ * 批量获取文本向量。
4
+ * 阿里云 DashScope 的兼容接口支持 input 为数组,但单次有数量上限。
5
+ */
6
+ export declare function embedBatch(texts: string[], dimensions?: number): Promise<number[][]>;
@@ -10,29 +10,32 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.embed = embed;
13
+ exports.embedBatch = embedBatch;
13
14
  const EMBEDDING_URL = 'https://dashscope.aliyuncs.com/compatible-mode/v1/embeddings';
14
15
  const EMBEDDING_MODEL = 'text-embedding-v4';
15
- function embed(text_1) {
16
- return __awaiter(this, arguments, void 0, function* (text, dimensions = 1024) {
17
- var _a;
18
- const apiKey = process.env.BAILIAN_API_KEY;
19
- if (!apiKey) {
20
- throw new Error('BAILIAN_API_KEY environment variable is not set');
21
- }
22
- const body = {
23
- model: EMBEDDING_MODEL,
24
- input: text,
25
- dimensions,
26
- };
16
+ const BATCH_SIZE_LIMIT = 10; // DashScope 兼容接口的单次上限
17
+ function getApiKey() {
18
+ const apiKey = process.env.BAILIAN_API_KEY;
19
+ if (!apiKey) {
20
+ throw new Error('BAILIAN_API_KEY environment variable is not set');
21
+ }
22
+ return apiKey;
23
+ }
24
+ function fetchEmbeddings(input, dimensions) {
25
+ return __awaiter(this, void 0, void 0, function* () {
27
26
  let response;
28
27
  try {
29
28
  response = yield fetch(EMBEDDING_URL, {
30
29
  method: 'POST',
31
30
  headers: {
32
31
  'Content-Type': 'application/json',
33
- Authorization: `Bearer ${apiKey}`,
32
+ Authorization: `Bearer ${getApiKey()}`,
34
33
  },
35
- body: JSON.stringify(body),
34
+ body: JSON.stringify({
35
+ model: EMBEDDING_MODEL,
36
+ input,
37
+ dimensions,
38
+ }),
36
39
  });
37
40
  }
38
41
  catch (cause) {
@@ -42,7 +45,13 @@ function embed(text_1) {
42
45
  const text = yield response.text();
43
46
  throw new Error(`[embedding] HTTP ${response.status}: ${text}`);
44
47
  }
45
- const data = (yield response.json());
48
+ return (yield response.json());
49
+ });
50
+ }
51
+ function embed(text_1) {
52
+ return __awaiter(this, arguments, void 0, function* (text, dimensions = 1024) {
53
+ var _a;
54
+ const data = yield fetchEmbeddings(text, dimensions);
46
55
  const vector = (_a = data.data[0]) === null || _a === void 0 ? void 0 : _a.embedding;
47
56
  if (!vector) {
48
57
  throw new Error('[embedding] No embedding in response');
@@ -50,3 +59,27 @@ function embed(text_1) {
50
59
  return vector;
51
60
  });
52
61
  }
62
+ /**
63
+ * 批量获取文本向量。
64
+ * 阿里云 DashScope 的兼容接口支持 input 为数组,但单次有数量上限。
65
+ */
66
+ function embedBatch(texts_1) {
67
+ return __awaiter(this, arguments, void 0, function* (texts, dimensions = 1024) {
68
+ var _a;
69
+ if (texts.length === 0) {
70
+ return [];
71
+ }
72
+ if (texts.length > BATCH_SIZE_LIMIT) {
73
+ throw new Error(`[embedding] Batch size ${texts.length} exceeds limit ${BATCH_SIZE_LIMIT}. Split before calling.`);
74
+ }
75
+ const data = yield fetchEmbeddings(texts, dimensions);
76
+ // 服务返回的数据可能不是按请求顺序的,用 index 重新排序
77
+ const sorted = data.data.slice().sort((a, b) => a.index - b.index);
78
+ for (let i = 0; i < texts.length; i++) {
79
+ if (((_a = sorted[i]) === null || _a === void 0 ? void 0 : _a.index) !== i || !sorted[i].embedding) {
80
+ throw new Error(`[embedding] Missing or malformed embedding at index ${i}`);
81
+ }
82
+ }
83
+ return sorted.map((item) => item.embedding);
84
+ });
85
+ }
@@ -22,6 +22,29 @@ export declare class EmbeddingSearch {
22
22
  static vectorSearch(options: Omit<import('./pgvector').PgVectorSearchOptions, 'pool'> & {
23
23
  pool: Pool;
24
24
  }): Promise<SearchResult[]>;
25
+ /**
26
+ * 单条文本转成向量。
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * const vector = await EmbeddingSearch.embed('衣服质量怎么样');
31
+ * ```
32
+ */
33
+ static embed(text: string, dimensions?: number): Promise<number[]>;
34
+ /**
35
+ * 批量获取文本向量。
36
+ *
37
+ * 调用百炼 embedding 接口,一次最多 10 条文本。
38
+ * 返回向量的顺序与输入顺序一致。
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * const vectors = await EmbeddingSearch.batchEmbed([
43
+ * '衣服质量怎么样',
44
+ * '物流速度快吗',
45
+ * ]);
46
+ * ```
47
+ */
48
+ static batchEmbed(texts: string[], dimensions?: number): Promise<number[][]>;
25
49
  }
26
50
  export type { EmbeddingSearchConfig, SearchResult } from './types';
27
- export { embed } from './embed';
@@ -20,7 +20,7 @@ var __rest = (this && this.__rest) || function (s, e) {
20
20
  return t;
21
21
  };
22
22
  Object.defineProperty(exports, "__esModule", { value: true });
23
- exports.embed = exports.EmbeddingSearch = void 0;
23
+ exports.EmbeddingSearch = void 0;
24
24
  const pgvector_1 = require("./pgvector");
25
25
  const embed_1 = require("./embed");
26
26
  const enums_1 = require("./enums");
@@ -163,7 +163,37 @@ class EmbeddingSearch {
163
163
  return (0, pgvector_1.vectorSearch)(options);
164
164
  });
165
165
  }
166
+ /**
167
+ * 单条文本转成向量。
168
+ *
169
+ * @example
170
+ * ```ts
171
+ * const vector = await EmbeddingSearch.embed('衣服质量怎么样');
172
+ * ```
173
+ */
174
+ static embed(text, dimensions) {
175
+ return __awaiter(this, void 0, void 0, function* () {
176
+ return (0, embed_1.embed)(text, dimensions);
177
+ });
178
+ }
179
+ /**
180
+ * 批量获取文本向量。
181
+ *
182
+ * 调用百炼 embedding 接口,一次最多 10 条文本。
183
+ * 返回向量的顺序与输入顺序一致。
184
+ *
185
+ * @example
186
+ * ```ts
187
+ * const vectors = await EmbeddingSearch.batchEmbed([
188
+ * '衣服质量怎么样',
189
+ * '物流速度快吗',
190
+ * ]);
191
+ * ```
192
+ */
193
+ static batchEmbed(texts, dimensions) {
194
+ return __awaiter(this, void 0, void 0, function* () {
195
+ return (0, embed_1.embedBatch)(texts, dimensions);
196
+ });
197
+ }
166
198
  }
167
199
  exports.EmbeddingSearch = EmbeddingSearch;
168
- var embed_2 = require("./embed");
169
- Object.defineProperty(exports, "embed", { enumerable: true, get: function () { return embed_2.embed; } });
@@ -129,7 +129,7 @@ function createLLMCaller(options) {
129
129
  'Content-Type': 'application/json',
130
130
  Authorization: `Bearer ${options.apiKey}`,
131
131
  },
132
- body: JSON.stringify(body),
132
+ body: (0, llm_provider_1.serializeChatRequest)(body),
133
133
  }));
134
134
  }
135
135
  catch (cause) {
package/dist/index.d.ts CHANGED
@@ -30,8 +30,16 @@ export { BailianProvider } from './predict';
30
30
  export { MODEL_REGISTRY } from './llm_provider';
31
31
  export type { Model } from './llm_provider';
32
32
  export type { PredictConfig, PredictWithMessagesConfig } from './predict';
33
- /** 向量检索(Embedding + pgvector + Rerank) */
34
- export { EmbeddingSearch, embed } from './embedding_search';
33
+ /**
34
+ * 向量检索静态入口类。
35
+ *
36
+ * 提供 embedding 生成、批量 embedding 生成、pgvector 向量检索与 rerank 的完整链路:
37
+ * - `EmbeddingSearch.query()` — 文本 → embedding → pgvector 检索 → 可选 rerank
38
+ * - `EmbeddingSearch.vectorSearch()` — 已有向量 → pgvector 近邻检索
39
+ * - `EmbeddingSearch.embed()` — 单条文本转向量
40
+ * - `EmbeddingSearch.batchEmbed()` — 批量文本转向量(单次最多 10 条)
41
+ */
42
+ export { EmbeddingSearch } from './embedding_search';
35
43
  export type { EmbeddingSearchConfig, SearchResult } from './embedding_search';
36
44
  /** Function Call Loop */
37
45
  export * as FunctionCallLoop from './function_call_loop';
package/dist/index.js CHANGED
@@ -53,7 +53,7 @@ var __importStar = (this && this.__importStar) || (function () {
53
53
  };
54
54
  })();
55
55
  Object.defineProperty(exports, "__esModule", { value: true });
56
- exports.FunctionCallLoop = exports.embed = exports.EmbeddingSearch = exports.MODEL_REGISTRY = exports.BailianProvider = exports.Predictor = exports.LLM = void 0;
56
+ exports.FunctionCallLoop = exports.EmbeddingSearch = exports.MODEL_REGISTRY = exports.BailianProvider = exports.Predictor = exports.LLM = void 0;
57
57
  /** LLM 静态入口类,封装 Provider 连接、故障转移和返回解析 */
58
58
  var predict_1 = require("./predict");
59
59
  Object.defineProperty(exports, "LLM", { enumerable: true, get: function () { return predict_1.LLM; } });
@@ -66,9 +66,16 @@ Object.defineProperty(exports, "BailianProvider", { enumerable: true, get: funct
66
66
  /** 模型注册表与枚举,定义模型与 Provider 的映射关系 */
67
67
  var llm_provider_1 = require("./llm_provider");
68
68
  Object.defineProperty(exports, "MODEL_REGISTRY", { enumerable: true, get: function () { return llm_provider_1.MODEL_REGISTRY; } });
69
- /** 向量检索(Embedding + pgvector + Rerank) */
69
+ /**
70
+ * 向量检索静态入口类。
71
+ *
72
+ * 提供 embedding 生成、批量 embedding 生成、pgvector 向量检索与 rerank 的完整链路:
73
+ * - `EmbeddingSearch.query()` — 文本 → embedding → pgvector 检索 → 可选 rerank
74
+ * - `EmbeddingSearch.vectorSearch()` — 已有向量 → pgvector 近邻检索
75
+ * - `EmbeddingSearch.embed()` — 单条文本转向量
76
+ * - `EmbeddingSearch.batchEmbed()` — 批量文本转向量(单次最多 10 条)
77
+ */
70
78
  var embedding_search_1 = require("./embedding_search");
71
79
  Object.defineProperty(exports, "EmbeddingSearch", { enumerable: true, get: function () { return embedding_search_1.EmbeddingSearch; } });
72
- Object.defineProperty(exports, "embed", { enumerable: true, get: function () { return embedding_search_1.embed; } });
73
80
  /** Function Call Loop */
74
81
  exports.FunctionCallLoop = __importStar(require("./function_call_loop"));
@@ -65,11 +65,9 @@ class BailianProvider {
65
65
  }
66
66
  if (body.model === 'kimi-k2.6') {
67
67
  const { reasoning_effort } = body, rest = __rest(body, ["reasoning_effort"]);
68
- // Kimi 默认开启思考;只有 low 显式禁用,medium/high 走默认(不传 thinking 字段)
69
- if (reasoning_effort === 'low') {
70
- return Object.assign(Object.assign({}, rest), { extra_body: Object.assign(Object.assign({}, ((_d = rest.extra_body) !== null && _d !== void 0 ? _d : {})), { thinking: { type: 'disabled' } }) });
71
- }
72
- return rest;
68
+ // 百炼兼容模式下 Kimi 默认不输出 reasoning_content,
69
+ // 需要显式传 thinking.type:low 禁用,medium/high 显式开启
70
+ return Object.assign(Object.assign({}, rest), { extra_body: Object.assign(Object.assign({}, ((_d = rest.extra_body) !== null && _d !== void 0 ? _d : {})), { thinking: { type: reasoning_effort === 'low' ? 'disabled' : 'enabled' } }) });
73
71
  }
74
72
  // deepseek-v4-pro / deepseek-v4-flash 原生支持 reasoning_effort,直接传递
75
73
  }
@@ -121,7 +119,7 @@ class BailianProvider {
121
119
  'Content-Type': 'application/json',
122
120
  Authorization: `Bearer ${this.config.apiKey}`,
123
121
  },
124
- body: JSON.stringify(body),
122
+ body: (0, index_1.serializeChatRequest)(body),
125
123
  }));
126
124
  }
127
125
  catch (cause) {
@@ -69,6 +69,12 @@ export interface OpenAIChatResponse {
69
69
  };
70
70
  model?: string;
71
71
  }
72
+ /**
73
+ * 序列化 chat completion 请求体。
74
+ * `extra_body` 是 OpenAI SDK 的客户端概念,在 HTTP 协议层面不存在,
75
+ * 必须将其内容展平到请求体顶层,否则服务端会忽略这些扩展参数。
76
+ */
77
+ export declare function serializeChatRequest(body: OpenAIChatRequest): string;
72
78
  /**
73
79
  * 发送 OpenAI 兼容的 chat completions 请求。
74
80
  * 只做 HTTP 层:构造请求、fetch、错误处理、基础解析。
@@ -12,9 +12,30 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
12
12
  step((generator = generator.apply(thisArg, _arguments || [])).next());
13
13
  });
14
14
  };
15
+ var __rest = (this && this.__rest) || function (s, e) {
16
+ var t = {};
17
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
18
+ t[p] = s[p];
19
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
20
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
21
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
22
+ t[p[i]] = s[p[i]];
23
+ }
24
+ return t;
25
+ };
15
26
  Object.defineProperty(exports, "__esModule", { value: true });
16
27
  exports.BailianProvider = exports.MODEL_REGISTRY = void 0;
28
+ exports.serializeChatRequest = serializeChatRequest;
17
29
  exports.callChatCompletions = callChatCompletions;
30
+ /**
31
+ * 序列化 chat completion 请求体。
32
+ * `extra_body` 是 OpenAI SDK 的客户端概念,在 HTTP 协议层面不存在,
33
+ * 必须将其内容展平到请求体顶层,否则服务端会忽略这些扩展参数。
34
+ */
35
+ function serializeChatRequest(body) {
36
+ const { extra_body } = body, rest = __rest(body, ["extra_body"]);
37
+ return JSON.stringify(Object.assign(Object.assign({}, rest), (extra_body !== null && extra_body !== void 0 ? extra_body : {})));
38
+ }
18
39
  /**
19
40
  * 发送 OpenAI 兼容的 chat completions 请求。
20
41
  * 只做 HTTP 层:构造请求、fetch、错误处理、基础解析。
@@ -32,7 +53,7 @@ function callChatCompletions(baseUrl_1, apiKey_1, body_1) {
32
53
  'Content-Type': 'application/json',
33
54
  Authorization: `Bearer ${apiKey}`,
34
55
  },
35
- body: JSON.stringify(body),
56
+ body: serializeChatRequest(body),
36
57
  });
37
58
  }
38
59
  catch (cause) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keo-ai/axiom",
3
- "version": "0.2.4",
3
+ "version": "0.2.7",
4
4
  "description": "基于 LLM 的预测与推理库,支持多 Provider 切换",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",