connectbase-client 3.39.2 → 3.41.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/CHANGELOG.md CHANGED
@@ -3,6 +3,35 @@
3
3
  본 SDK 의 모든 주요 변경사항을 [Keep a Changelog](https://keepachangelog.com/ko/1.1.0/) 형식으로 기록합니다.
4
4
  버전은 [Semantic Versioning](https://semver.org/lang/ko/) 을 따릅니다.
5
5
 
6
+ ## [3.41.0] - 2026-07-02
7
+
8
+ ### Added — 웹 배포 파일당 크기 한도 사전 검증 (fail-fast)
9
+
10
+ `connectbase deploy` 가 파일을 수집한 직후, 서버로 업로드하기 **전에** 각 파일이 웹 스토리지
11
+ 파일당 크기 한도를 넘는지 검사한다(플랫폼 이슈 019f221b). 기존에는 전량 수집·업로드를 마친
12
+ 뒤에야 서버에서 원시 DB 오류(`Error 8025 (HY000): entry too large`)로 실패해 원인 파악이
13
+ 어려웠다.
14
+
15
+ - 초과 파일이 있으면 **어떤 파일이 얼마나 큰지**(바이너리는 원본→base64 크기까지) 나열하고,
16
+ 파일 스토리지(`cb.storage`) 사용을 안내한 뒤 업로드 없이 즉시 종료한다.
17
+ - 검사 기준: 파일 내용은 DB(TiDB Text 컬럼)에 저장되며 단일 엔트리 한도 6 MiB 를 넘을 수 없다.
18
+ 바이너리는 base64(약 1.33x)로 저장되므로 원본 기준 실질 상한은 약 4.5 MB.
19
+ - 텍스트 파일은 UTF-8 바이트 기준으로 정확히 계산(멀티바이트 문자 포함).
20
+
21
+ ## [3.40.0] - 2026-06-28
22
+
23
+ ### Added — KB 하이브리드(시맨틱) 검색 지원
24
+
25
+ 지식베이스 검색에 BM25 키워드 + 벡터 의미 검색을 RRF(Reciprocal Rank Fusion)로 융합하는
26
+ 하이브리드 검색을 추가했다. 콘솔 > **AI** 에서 임베딩을 켠 앱(gemini / openai BYOK)에서
27
+ 동작하며, 오타·동의어·의역에 강하다.
28
+
29
+ - **\`KnowledgeSearchRequest.hybrid?: boolean\`** — \`cb.knowledge.search()\` 에 hybrid 옵션 추가.
30
+ 미설정(기본)이면 임베딩을 켠 앱에서 자동 적용, \`false\` 면 키워드 전용 검색을 강제.
31
+ - **\`AIChatRequest.hybrid?: boolean\`** — \`cb.ai.chat()\` / \`cb.ai.chatStream()\` 의 RAG 검색에도
32
+ 동일 옵션 적용. 미설정이면 임베딩을 켠 앱에서 자동 적용된다.
33
+ - 임베딩 미설정 앱에서는 무시되며 기존 BM25 검색으로 동작한다(하위호환, 회귀 없음).
34
+
6
35
  ## [3.39.2] - 2026-06-26
7
36
 
8
37
  ### Performance — 터널 프록시 응답 지연 제거 (TCP_NODELAY / keep-alive)
package/dist/cli.js CHANGED
@@ -215,6 +215,7 @@ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
215
215
  ".zip",
216
216
  ".wasm"
217
217
  ]);
218
+ var WEB_FILE_MAX_STORED_BYTES = 6 * 1024 * 1024 - 64 * 1024;
218
219
  function sha256Hex(value) {
219
220
  return crypto.createHash("sha256").update(value, "utf8").digest("hex");
220
221
  }
@@ -281,6 +282,29 @@ function collectFiles(dir, baseDir = dir) {
281
282
  }
282
283
  return files;
283
284
  }
285
+ function formatSize(bytes) {
286
+ if (bytes >= 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
287
+ return `${(bytes / 1024).toFixed(1)} KB`;
288
+ }
289
+ function validateFileSizes(files) {
290
+ const oversized = files.map((f) => {
291
+ const storedBytes = Buffer.byteLength(f.content, "utf8");
292
+ const originalBytes = f.isBinary ? Math.round(f.content.length * 3 / 4) : storedBytes;
293
+ return { path: f.path, isBinary: f.isBinary, storedBytes, originalBytes };
294
+ }).filter((f) => f.storedBytes > WEB_FILE_MAX_STORED_BYTES);
295
+ if (oversized.length === 0) return;
296
+ error(`\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uD30C\uC77C\uB2F9 \uD06C\uAE30 \uD55C\uB3C4(\uC57D ${formatSize(WEB_FILE_MAX_STORED_BYTES)})\uB97C \uCD08\uACFC\uD55C \uD30C\uC77C\uC774 \uC788\uC5B4 \uBC30\uD3EC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4:`);
297
+ for (const f of oversized) {
298
+ const detail = f.isBinary ? `${formatSize(f.originalBytes)} \u2192 base64 ${formatSize(f.storedBytes)}` : formatSize(f.storedBytes);
299
+ error(` \u2022 ${f.path} (${detail})`);
300
+ }
301
+ log("");
302
+ warn("\uC815\uC801 \uD638\uC2A4\uD305 \uD30C\uC77C\uC740 DB \uB2E8\uC77C \uC5D4\uD2B8\uB9AC(6 MiB)\uC5D0 \uC800\uC7A5\uB418\uBA70, \uBC14\uC774\uB108\uB9AC\uB294 base64(\uC57D 1.33x)\uB85C");
303
+ warn("\uBD80\uD480\uC5B4 \uC6D0\uBCF8 \uAE30\uC900 \uC57D 4.5 MB \uAC00 \uC2E4\uC9C8 \uC0C1\uD55C\uC785\uB2C8\uB2E4.");
304
+ warn(`\uB300\uC6A9\uB7C9 \uC5D0\uC14B(3D \uBAA8\uB378\xB7\uB3D9\uC601\uC0C1\xB7\uD070 \uD3F0\uD2B8 \uB4F1)\uC740 \uD30C\uC77C \uC2A4\uD1A0\uB9AC\uC9C0(${colors.cyan}cb.storage${colors.reset}, presigned URL)\uC5D0`);
305
+ warn("\uC62C\uB9AC\uACE0 URL \uB85C \uCC38\uC870\uD558\uC138\uC694. \uD30C\uC77C \uC2A4\uD1A0\uB9AC\uC9C0\uB294 \uD30C\uC77C\uB2F9 \uACE0\uC815 \uC0C1\uD55C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
306
+ process.exit(1);
307
+ }
284
308
  var DEFAULT_REQUEST_TIMEOUT_MS = 6e4;
285
309
  async function makeRequest(url, method, headers, body, reqOpts = {}) {
286
310
  return new Promise((resolve2, reject) => {
@@ -378,6 +402,7 @@ async function deploy(directory, config, isDev = false, deployOpts = {}) {
378
402
  error("\uBC30\uD3EC\uD560 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4");
379
403
  process.exit(1);
380
404
  }
405
+ validateFileSizes(files);
381
406
  const totalSize = files.reduce((sum, f) => sum + f.content.length, 0);
382
407
  const sizeKB = (totalSize / 1024).toFixed(1);
383
408
  log(`${colors.green}\u2713${colors.reset} ${files.length}\uAC1C \uD30C\uC77C \uBC1C\uACAC (${sizeKB} KB)`);
package/dist/index.d.mts CHANGED
@@ -2402,14 +2402,15 @@ declare class StorageAPI {
2402
2402
  * 파일 목록 조회
2403
2403
  *
2404
2404
  * @param storageId - 파일 스토리지 ID
2405
- * @param parentId - 부모 폴더 ID (미지정루트 폴더)
2405
+ * @param parentId - 부모 폴더 ID. 지정해당 폴더의 **직계 자식만** 반환한다.
2406
+ * 미지정 시 스토리지 **전체 파일 트리**를 flat 배열로 반환한다.
2406
2407
  *
2407
2408
  * @example
2408
2409
  * ```ts
2409
- * // 루트 폴더의 파일 목록
2410
+ * // 스토리지 전체 파일 목록 (flat 트리)
2410
2411
  * const files = await cb.storage.getFiles('storage-id')
2411
2412
  *
2412
- * // 특정 폴더의 하위 파일 목록
2413
+ * // 특정 폴더의 직계 자식만 (대용량 버킷에서 전체 트리 전송 회피)
2413
2414
  * const subFiles = await cb.storage.getFiles('storage-id', 'folder-id')
2414
2415
  * ```
2415
2416
  */
@@ -7370,6 +7371,12 @@ interface KnowledgeSearchRequest {
7370
7371
  top_k?: number;
7371
7372
  /** Agentic Search 활성화 — AI 가 쿼리를 자동 생성하여 다중 검색 수행 */
7372
7373
  agentic?: boolean;
7374
+ /**
7375
+ * Hybrid 검색 — BM25 키워드 + 벡터 시맨틱 검색을 RRF 로 융합.
7376
+ * 미설정(기본): 콘솔 > AI 에서 embedding 을 켠 앱은 자동 적용.
7377
+ * `false`: 키워드 전용 검색 강제. embedding 미설정 앱에서는 무시(BM25 only).
7378
+ */
7379
+ hybrid?: boolean;
7373
7380
  /**
7374
7381
  * metadata 기반 필터 (옵셔널). 키 형식 `metadata.<path>` 또는 raw key.
7375
7382
  * 값에 `AUTH_MEMBER_ID_TOKEN` 사용 시 서버가 인증된 AppMember ID 로 치환.
@@ -7655,6 +7662,11 @@ interface AIChatRequest {
7655
7662
  knowledgeBaseId?: string;
7656
7663
  topK?: number;
7657
7664
  agentic?: boolean;
7665
+ /**
7666
+ * KB 검색 hybrid(BM25 + 벡터 시맨틱, RRF 융합) 제어. 미설정(기본)이면 콘솔 > AI 에서
7667
+ * embedding 을 켠 앱은 자동 적용, `false` 면 키워드 전용 검색 강제.
7668
+ */
7669
+ hybrid?: boolean;
7658
7670
  toolGroupId?: string;
7659
7671
  /**
7660
7672
  * 0 보다 크면, 서버측 도구 그룹 실행(`toolGroupId` 사용 시)에서 각 도구 결과를 모델에
package/dist/index.d.ts CHANGED
@@ -2402,14 +2402,15 @@ declare class StorageAPI {
2402
2402
  * 파일 목록 조회
2403
2403
  *
2404
2404
  * @param storageId - 파일 스토리지 ID
2405
- * @param parentId - 부모 폴더 ID (미지정루트 폴더)
2405
+ * @param parentId - 부모 폴더 ID. 지정해당 폴더의 **직계 자식만** 반환한다.
2406
+ * 미지정 시 스토리지 **전체 파일 트리**를 flat 배열로 반환한다.
2406
2407
  *
2407
2408
  * @example
2408
2409
  * ```ts
2409
- * // 루트 폴더의 파일 목록
2410
+ * // 스토리지 전체 파일 목록 (flat 트리)
2410
2411
  * const files = await cb.storage.getFiles('storage-id')
2411
2412
  *
2412
- * // 특정 폴더의 하위 파일 목록
2413
+ * // 특정 폴더의 직계 자식만 (대용량 버킷에서 전체 트리 전송 회피)
2413
2414
  * const subFiles = await cb.storage.getFiles('storage-id', 'folder-id')
2414
2415
  * ```
2415
2416
  */
@@ -7370,6 +7371,12 @@ interface KnowledgeSearchRequest {
7370
7371
  top_k?: number;
7371
7372
  /** Agentic Search 활성화 — AI 가 쿼리를 자동 생성하여 다중 검색 수행 */
7372
7373
  agentic?: boolean;
7374
+ /**
7375
+ * Hybrid 검색 — BM25 키워드 + 벡터 시맨틱 검색을 RRF 로 융합.
7376
+ * 미설정(기본): 콘솔 > AI 에서 embedding 을 켠 앱은 자동 적용.
7377
+ * `false`: 키워드 전용 검색 강제. embedding 미설정 앱에서는 무시(BM25 only).
7378
+ */
7379
+ hybrid?: boolean;
7373
7380
  /**
7374
7381
  * metadata 기반 필터 (옵셔널). 키 형식 `metadata.<path>` 또는 raw key.
7375
7382
  * 값에 `AUTH_MEMBER_ID_TOKEN` 사용 시 서버가 인증된 AppMember ID 로 치환.
@@ -7655,6 +7662,11 @@ interface AIChatRequest {
7655
7662
  knowledgeBaseId?: string;
7656
7663
  topK?: number;
7657
7664
  agentic?: boolean;
7665
+ /**
7666
+ * KB 검색 hybrid(BM25 + 벡터 시맨틱, RRF 융합) 제어. 미설정(기본)이면 콘솔 > AI 에서
7667
+ * embedding 을 켠 앱은 자동 적용, `false` 면 키워드 전용 검색 강제.
7668
+ */
7669
+ hybrid?: boolean;
7658
7670
  toolGroupId?: string;
7659
7671
  /**
7660
7672
  * 0 보다 크면, 서버측 도구 그룹 실행(`toolGroupId` 사용 시)에서 각 도구 결과를 모델에
package/dist/index.js CHANGED
@@ -2279,14 +2279,15 @@ var StorageAPI = class {
2279
2279
  * 파일 목록 조회
2280
2280
  *
2281
2281
  * @param storageId - 파일 스토리지 ID
2282
- * @param parentId - 부모 폴더 ID (미지정루트 폴더)
2282
+ * @param parentId - 부모 폴더 ID. 지정해당 폴더의 **직계 자식만** 반환한다.
2283
+ * 미지정 시 스토리지 **전체 파일 트리**를 flat 배열로 반환한다.
2283
2284
  *
2284
2285
  * @example
2285
2286
  * ```ts
2286
- * // 루트 폴더의 파일 목록
2287
+ * // 스토리지 전체 파일 목록 (flat 트리)
2287
2288
  * const files = await cb.storage.getFiles('storage-id')
2288
2289
  *
2289
- * // 특정 폴더의 하위 파일 목록
2290
+ * // 특정 폴더의 직계 자식만 (대용량 버킷에서 전체 트리 전송 회피)
2290
2291
  * const subFiles = await cb.storage.getFiles('storage-id', 'folder-id')
2291
2292
  * ```
2292
2293
  */
package/dist/index.mjs CHANGED
@@ -2234,14 +2234,15 @@ var StorageAPI = class {
2234
2234
  * 파일 목록 조회
2235
2235
  *
2236
2236
  * @param storageId - 파일 스토리지 ID
2237
- * @param parentId - 부모 폴더 ID (미지정루트 폴더)
2237
+ * @param parentId - 부모 폴더 ID. 지정해당 폴더의 **직계 자식만** 반환한다.
2238
+ * 미지정 시 스토리지 **전체 파일 트리**를 flat 배열로 반환한다.
2238
2239
  *
2239
2240
  * @example
2240
2241
  * ```ts
2241
- * // 루트 폴더의 파일 목록
2242
+ * // 스토리지 전체 파일 목록 (flat 트리)
2242
2243
  * const files = await cb.storage.getFiles('storage-id')
2243
2244
  *
2244
- * // 특정 폴더의 하위 파일 목록
2245
+ * // 특정 폴더의 직계 자식만 (대용량 버킷에서 전체 트리 전송 회피)
2245
2246
  * const subFiles = await cb.storage.getFiles('storage-id', 'folder-id')
2246
2247
  * ```
2247
2248
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "connectbase-client",
3
- "version": "3.39.2",
3
+ "version": "3.41.0",
4
4
  "description": "Connect Base JavaScript/TypeScript SDK for browser and Node.js",
5
5
  "repository": {
6
6
  "type": "git",