sonamu 0.10.6 → 0.10.8
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/api/base-frame.d.ts +2 -0
- package/dist/api/base-frame.d.ts.map +1 -1
- package/dist/api/base-frame.js +8 -1
- package/dist/api/decorators.js +1 -1
- package/dist/api/sonamu.js +1 -1
- package/dist/bin/cli.js +10 -253
- package/dist/database/base-model.js +2 -2
- package/dist/entity/entity-manager.js +1 -1
- package/dist/index.js +3 -3
- package/dist/migration/code-generation.js +2 -2
- package/dist/migration/migrator.js +1 -1
- package/dist/migration/slack-confirm.js +1 -1
- package/dist/naite/naite-reporter.js +1 -1
- package/dist/syncer/checksum.js +1 -1
- package/dist/syncer/code-generator.js +1 -1
- package/dist/syncer/entity-operations.js +1 -1
- package/dist/syncer/syncer-actions.js +1 -1
- package/dist/syncer/syncer.js +1 -1
- package/dist/ui-web/assets/{index-D0MHYbxl.js → index-CJf8uJYf.js} +47 -41
- package/dist/ui-web/assets/index-GMMIVGja.css +1 -0
- package/dist/ui-web/index.html +2 -2
- package/dist/utils/formatter.js +1 -1
- package/package.json +3 -3
- package/src/api/base-frame.ts +14 -1
- package/src/bin/cli.ts +12 -375
- package/src/migration/code-generation.ts +1 -1
- package/dist/ui-web/assets/index-Dx_JX4aQ.css +0 -1
- package/src/skills/AGENTS.md +0 -108
- package/src/skills/sonamu/SKILL.md +0 -75
- package/src/skills/sonamu-ai-agents/SKILL.md +0 -205
- package/src/skills/sonamu-api/SKILL.md +0 -480
- package/src/skills/sonamu-auth/SKILL.md +0 -327
- package/src/skills/sonamu-auth/references/plugins.md +0 -310
- package/src/skills/sonamu-auth/references/user-id-migration-followups.md +0 -192
- package/src/skills/sonamu-auth/references/user-id-migration.md +0 -455
- package/src/skills/sonamu-config/SKILL.md +0 -203
- package/src/skills/sonamu-config/references/database.md +0 -452
- package/src/skills/sonamu-config/references/environments.md +0 -178
- package/src/skills/sonamu-config/references/server-options.md +0 -400
- package/src/skills/sonamu-entity/SKILL.md +0 -180
- package/src/skills/sonamu-entity/references/creation-workflow.md +0 -581
- package/src/skills/sonamu-entity/references/design-guides.md +0 -243
- package/src/skills/sonamu-entity/references/field-types.md +0 -170
- package/src/skills/sonamu-entity/references/relations-detail.md +0 -245
- package/src/skills/sonamu-entity/references/relations.md +0 -463
- package/src/skills/sonamu-entity/references/subset.md +0 -156
- package/src/skills/sonamu-fixture/SKILL.md +0 -180
- package/src/skills/sonamu-fixture/references/cli-usage.md +0 -439
- package/src/skills/sonamu-fixture/references/cone.md +0 -298
- package/src/skills/sonamu-frontend/SKILL.md +0 -142
- package/src/skills/sonamu-frontend/references/components.md +0 -323
- package/src/skills/sonamu-frontend/references/examples.md +0 -64
- package/src/skills/sonamu-frontend/references/hooks.md +0 -273
- package/src/skills/sonamu-frontend/references/runtime.md +0 -165
- package/src/skills/sonamu-frontend/references/scaffolding.md +0 -439
- package/src/skills/sonamu-i18n/SKILL.md +0 -287
- package/src/skills/sonamu-migration/SKILL.md +0 -316
- package/src/skills/sonamu-naite/SKILL.md +0 -266
- package/src/skills/sonamu-query/SKILL.md +0 -48
- package/src/skills/sonamu-query/references/model-patterns.md +0 -390
- package/src/skills/sonamu-query/references/model.md +0 -366
- package/src/skills/sonamu-query/references/puri.md +0 -413
- package/src/skills/sonamu-query/references/search.md +0 -238
- package/src/skills/sonamu-query/references/upsert.md +0 -324
- package/src/skills/sonamu-tasks/SKILL.md +0 -236
- package/src/skills/sonamu-testing/SKILL.md +0 -251
- package/src/skills/sonamu-testing/references/devrunner.md +0 -405
- package/src/skills/sonamu-testing/references/helpers.md +0 -185
- package/src/skills/sonamu-testing/references/patterns.md +0 -263
- package/src/skills/sonamu-testing/references/pitfalls.md +0 -588
- package/src/skills/sonamu-testing/references/quick-start.md +0 -285
- package/src/skills/sonamu-testing/references/type-safety.md +0 -172
- package/src/skills/sonamu-testing/references/writing-plan.md +0 -375
- package/src/skills/sonamu-vector/SKILL.md +0 -222
|
@@ -1,375 +0,0 @@
|
|
|
1
|
-
# Test Writing Plan and Rollout Strategy
|
|
2
|
-
|
|
3
|
-
## Test Writing Plan
|
|
4
|
-
|
|
5
|
-
### Planning Based on Entity Design Prompt
|
|
6
|
-
|
|
7
|
-
After entity design is complete (confirming migration + scaffolding succeed), group tests according to **the business processes and data flows specified at the time of entity design**.
|
|
8
|
-
|
|
9
|
-
**CRITICAL:** Group tests by **business flow units**, not by simple alphabetical order or individual entities.
|
|
10
|
-
|
|
11
|
-
### Step 1: Re-examine the Entity Design Prompt
|
|
12
|
-
|
|
13
|
-
Extract the following from the prompt written at the time of the design request:
|
|
14
|
-
|
|
15
|
-
- Business process flow
|
|
16
|
-
- Relationships between entities (relations)
|
|
17
|
-
- Data creation order
|
|
18
|
-
- Key usage scenarios
|
|
19
|
-
|
|
20
|
-
### Step 2: Group by Business Process
|
|
21
|
-
|
|
22
|
-
Group entities by **business flow units**, not simple priority.
|
|
23
|
-
|
|
24
|
-
**Customer consultation system example:**
|
|
25
|
-
|
|
26
|
-
```
|
|
27
|
-
Group 1: Core Infrastructure
|
|
28
|
-
Organization (related agency)
|
|
29
|
-
└─ User
|
|
30
|
-
└─ LoginHistory
|
|
31
|
-
|
|
32
|
-
Business flow: register agency → create user → login
|
|
33
|
-
Test order: Organization → User → LoginHistory
|
|
34
|
-
|
|
35
|
-
Group 2: Damage Type Management
|
|
36
|
-
DamageType (self-referencing)
|
|
37
|
-
└─ CounterMeasure
|
|
38
|
-
|
|
39
|
-
Business flow: build damage type hierarchy → write countermeasures for each type
|
|
40
|
-
Test order: DamageType → CounterMeasure
|
|
41
|
-
|
|
42
|
-
Group 3: Consultation Process (core business)
|
|
43
|
-
User (applicant) + User (counselor) + DamageType
|
|
44
|
-
└─ Consultation
|
|
45
|
-
├─ ConsultationChannelLog
|
|
46
|
-
└─ ConsultationHistory
|
|
47
|
-
|
|
48
|
-
Business flow:
|
|
49
|
-
1. Applicant submits consultation request
|
|
50
|
-
2. Assign counselor
|
|
51
|
-
3. Classify damage type
|
|
52
|
-
4. Communication by channel (online/phone/SMS/KakaoTalk)
|
|
53
|
-
5. Record status change history
|
|
54
|
-
|
|
55
|
-
Test order: Consultation → ConsultationChannelLog → ConsultationHistory
|
|
56
|
-
|
|
57
|
-
Group 4: Content Management (independent)
|
|
58
|
-
FAQ
|
|
59
|
-
Banner
|
|
60
|
-
Material
|
|
61
|
-
Notice
|
|
62
|
-
|
|
63
|
-
Business flow: independent CRUD for each
|
|
64
|
-
Test order: any order (can be written in parallel)
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### Step 3: Work Order per Group
|
|
68
|
-
|
|
69
|
-
**For each group:**
|
|
70
|
-
|
|
71
|
-
1. **Modify types.ts** - handle nullable fields for all entities in the group at once
|
|
72
|
-
2. **Extend test-helpers.ts** - write helper functions for entities in the group together
|
|
73
|
-
3. **Write test files** - write in dependency order within the group
|
|
74
|
-
4. **Business Logic tests** - implement real business scenarios (the key!)
|
|
75
|
-
5. **Verify tests pass** - proceed to next group
|
|
76
|
-
|
|
77
|
-
**test-helpers.ts example (considering dependency chains):**
|
|
78
|
-
|
|
79
|
-
```typescript
|
|
80
|
-
// Write helpers considering dependency chains
|
|
81
|
-
export async function createTestUserWithDeps() {
|
|
82
|
-
const organizationId = await createTestOrganization();
|
|
83
|
-
const userId = await createTestUser(organizationId);
|
|
84
|
-
return { organizationId, userId };
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export async function createTestConsultationWithDeps() {
|
|
88
|
-
const { userId: applicantId } = await createTestUserWithDeps({
|
|
89
|
-
role: "applicant",
|
|
90
|
-
});
|
|
91
|
-
const { userId: counselorId } = await createTestUserWithDeps({
|
|
92
|
-
role: "counselor",
|
|
93
|
-
});
|
|
94
|
-
const damageTypeId = await createTestDamageType(null);
|
|
95
|
-
const consultationId = await createTestConsultation(applicantId, counselorId, damageTypeId);
|
|
96
|
-
return { applicantId, counselorId, damageTypeId, consultationId };
|
|
97
|
-
}
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
### Step 4: Business Logic Tests (the key!)
|
|
101
|
-
|
|
102
|
-
**IMPORTANT:** The E. Business Logic section is the most important.
|
|
103
|
-
|
|
104
|
-
In this section:
|
|
105
|
-
|
|
106
|
-
- Implement **real business scenarios** specified in the entity design prompt
|
|
107
|
-
- Test **interactions** between entities
|
|
108
|
-
- Validate **data flows**
|
|
109
|
-
|
|
110
|
-
This is what differentiates it from simple CRUD tests, and it's **the core that validates design intent**.
|
|
111
|
-
|
|
112
|
-
**Business Logic test example (consultation process):**
|
|
113
|
-
|
|
114
|
-
```typescript
|
|
115
|
-
describe("E. Business Logic", () => {
|
|
116
|
-
test("full process from consultation submission to completion", async () => {
|
|
117
|
-
// 1. submit consultation + create dependencies
|
|
118
|
-
const { consultationId, counselorId } = await createTestConsultationWithDeps();
|
|
119
|
-
// 2. record channel logs (online submission, phone consultation)
|
|
120
|
-
await createTestConsultationChannelLog(consultationId, {
|
|
121
|
-
channel: "online",
|
|
122
|
-
});
|
|
123
|
-
await createTestConsultationChannelLog(consultationId, {
|
|
124
|
-
channel: "phone",
|
|
125
|
-
});
|
|
126
|
-
// 3. record status history
|
|
127
|
-
await createTestConsultationHistory(consultationId, counselorId, {
|
|
128
|
-
status: "consulting",
|
|
129
|
-
});
|
|
130
|
-
// 4. complete consultation
|
|
131
|
-
await ConsultationModel.save([{ id: consultationId, status: "completed" }]);
|
|
132
|
-
// 5. verify: status, 2 channel logs, history
|
|
133
|
-
const c = await ConsultationModel.findById("A", consultationId);
|
|
134
|
-
expect(c.status).toBe("completed");
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
### Notes
|
|
140
|
-
|
|
141
|
-
**DO:**
|
|
142
|
-
|
|
143
|
-
- Always reference the entity design prompt
|
|
144
|
-
- Group by business process flow
|
|
145
|
-
- Test order that considers dependency order
|
|
146
|
-
- Business Logic tests based on real usage scenarios
|
|
147
|
-
- Clearly implement dependency chains in test-helpers
|
|
148
|
-
|
|
149
|
-
**DON'T:**
|
|
150
|
-
|
|
151
|
-
- Write tests in simple alphabetical order
|
|
152
|
-
- Only test entities individually (missing integration perspective)
|
|
153
|
-
- Set priorities unrelated to business flow
|
|
154
|
-
- Write tests that ignore the intent of the entity design
|
|
155
|
-
|
|
156
|
-
### Checklist per Group
|
|
157
|
-
|
|
158
|
-
When test writing for a process group is complete:
|
|
159
|
-
|
|
160
|
-
- [ ] Nullable field handling in types.ts completed for all entities in the group
|
|
161
|
-
- [ ] test-helpers written reflecting dependency chains within the group
|
|
162
|
-
- [ ] Module test file written for each entity in the group
|
|
163
|
-
- [ ] **Key business scenarios included in Business Logic tests**
|
|
164
|
-
- [ ] All tests pass confirmed (`pnpm sonamu test`)
|
|
165
|
-
- [ ] Proceed to next group
|
|
166
|
-
|
|
167
|
-
## Tasks to Do Immediately After Entity Creation
|
|
168
|
-
|
|
169
|
-
### Handling nullable Fields in types.ts (Required)
|
|
170
|
-
|
|
171
|
-
After creating an entity and generating types.ts with `sonamu generate`, immediately handle nullable fields **before writing tests**.
|
|
172
|
-
|
|
173
|
-
#### Work Order
|
|
174
|
-
|
|
175
|
-
1. Run `sonamu generate`
|
|
176
|
-
2. Check the generated `*.types.ts` file
|
|
177
|
-
3. Apply partial + extend + nullish handling for nullable fields
|
|
178
|
-
4. Start writing tests
|
|
179
|
-
|
|
180
|
-
#### Fields to Process
|
|
181
|
-
|
|
182
|
-
- All fields with `nullable: true`
|
|
183
|
-
- Fields with `dbDefault` (`.optional().default(value)`)
|
|
184
|
-
- FK relation fields that are nullable
|
|
185
|
-
|
|
186
|
-
#### Practical Example
|
|
187
|
-
|
|
188
|
-
**STEP 1: File generated after running sonamu generate**
|
|
189
|
-
|
|
190
|
-
```typescript
|
|
191
|
-
// faq.types.ts (auto-generated)
|
|
192
|
-
import type { z } from "zod"; // WRONG: type import
|
|
193
|
-
import { FAQBaseListParams, FAQBaseSchema } from "../sonamu.generated";
|
|
194
|
-
|
|
195
|
-
export const FAQListParams = FAQBaseListParams;
|
|
196
|
-
export type FAQListParams = z.infer<typeof FAQListParams>;
|
|
197
|
-
|
|
198
|
-
export const FAQSaveParams = FAQBaseSchema.partial({
|
|
199
|
-
id: true,
|
|
200
|
-
created_at: true,
|
|
201
|
-
updated_at: true,
|
|
202
|
-
});
|
|
203
|
-
export type FAQSaveParams = z.infer<typeof FAQSaveParams>;
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
**STEP 2: Immediate fix (nullable fields + Zod import handling)**
|
|
207
|
-
|
|
208
|
-
```typescript
|
|
209
|
-
// faq.types.ts (fix complete)
|
|
210
|
-
import { z } from "zod"; // CORRECT: change to regular import
|
|
211
|
-
import { FAQBaseListParams, FAQBaseSchema } from "../sonamu.generated";
|
|
212
|
-
|
|
213
|
-
export const FAQListParams = FAQBaseListParams;
|
|
214
|
-
export type FAQListParams = z.infer<typeof FAQListParams>;
|
|
215
|
-
|
|
216
|
-
export const FAQSaveParams = FAQBaseSchema.partial({
|
|
217
|
-
id: true,
|
|
218
|
-
created_at: true,
|
|
219
|
-
updated_at: true,
|
|
220
|
-
// add nullable fields
|
|
221
|
-
category: true,
|
|
222
|
-
order_num: true,
|
|
223
|
-
}).extend({
|
|
224
|
-
// redefine nullable fields as nullish
|
|
225
|
-
category: z.string().nullish(), // string | null | undefined
|
|
226
|
-
order_num: z.number().nullish(), // number | null | undefined
|
|
227
|
-
updated_at: z.date().nullish(), // date | null | undefined
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
export type FAQSaveParams = z.infer<typeof FAQSaveParams>;
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
#### Why Is This Necessary?
|
|
234
|
-
|
|
235
|
-
**Problem:** Zod's `nullable()` gives `T | null` but it's still required.
|
|
236
|
-
|
|
237
|
-
```typescript
|
|
238
|
-
// entity.json
|
|
239
|
-
{ "name": "category", "type": "string", "nullable": true }
|
|
240
|
-
|
|
241
|
-
// Generated BaseSchema
|
|
242
|
-
z.object({
|
|
243
|
-
category: z.string().nullable(), // string | null (required!)
|
|
244
|
-
})
|
|
245
|
-
|
|
246
|
-
// applying partial only
|
|
247
|
-
.partial({ category: true }) // category?: string | null
|
|
248
|
-
|
|
249
|
-
// WRONG: undefined cannot be assigned to string | null
|
|
250
|
-
const [id] = await FAQModel.save([{
|
|
251
|
-
question: "Question",
|
|
252
|
-
answer: "Answer",
|
|
253
|
-
// omitting category causes type error!
|
|
254
|
-
}]);
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
**Solution:** Combination of `partial()` + `extend()` + `nullish()`
|
|
258
|
-
|
|
259
|
-
```typescript
|
|
260
|
-
// CORRECT: proper handling
|
|
261
|
-
FAQBaseSchema.partial({ category: true }).extend({
|
|
262
|
-
category: z.string().nullish(),
|
|
263
|
-
}); // string | null | undefined
|
|
264
|
-
|
|
265
|
-
// Can freely omit in tests
|
|
266
|
-
const [id] = await FAQModel.save([
|
|
267
|
-
{
|
|
268
|
-
question: "Question",
|
|
269
|
-
answer: "Answer",
|
|
270
|
-
// category can be omitted!
|
|
271
|
-
},
|
|
272
|
-
]);
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
#### Application Criteria
|
|
276
|
-
|
|
277
|
-
| Field type | Handling |
|
|
278
|
-
| -------------------------------- | ------------------------------- |
|
|
279
|
-
| `id`, `created_at`, `updated_at` | Always partial (auto-generated) |
|
|
280
|
-
| Fields with `dbDefault` | `.optional().default(value)` |
|
|
281
|
-
| Fields with `nullable: true` | partial + extend + `.nullish()` |
|
|
282
|
-
| Required fields | Excluded from partial |
|
|
283
|
-
|
|
284
|
-
#### Checklist
|
|
285
|
-
|
|
286
|
-
- [ ] Change `import type { z }` to `import { z }`
|
|
287
|
-
- [ ] Add nullable fields to partial
|
|
288
|
-
- [ ] Redefine as nullish via extend
|
|
289
|
-
- [ ] Use `.optional().default()` for dbDefault fields
|
|
290
|
-
- [ ] Confirm required fields are excluded from partial
|
|
291
|
-
|
|
292
|
-
**Detailed type safety guide:** See "TypeScript Type Safety" and "Type Safety Notes" sections below
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
## Large-Scale Project Strategy (10 or more entities)
|
|
296
|
-
|
|
297
|
-
**CRITICAL: Do not work on all entities at once if a project has 10 or more entities.**
|
|
298
|
-
|
|
299
|
-
### Problems
|
|
300
|
-
|
|
301
|
-
- Working on 55 entities at once causes context confusion
|
|
302
|
-
- Serious risk of errors such as modifying the wrong file or deleting required content
|
|
303
|
-
- Cannot track relationships, lose direction while writing tests
|
|
304
|
-
|
|
305
|
-
### Solution: Batch Work Units
|
|
306
|
-
|
|
307
|
-
**Rule: Group related entities together and work in batches of 5–10**
|
|
308
|
-
|
|
309
|
-
```
|
|
310
|
-
Batch 1: User, Institution, Role related (5 entities)
|
|
311
|
-
→ Tests complete → Commit
|
|
312
|
-
|
|
313
|
-
Batch 2: Survey, Question, Response related (7 entities)
|
|
314
|
-
→ Tests complete → Commit
|
|
315
|
-
|
|
316
|
-
Batch 3: Report, Statistics related (6 entities)
|
|
317
|
-
→ Tests complete → Commit
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
### Batch Grouping Criteria
|
|
321
|
-
|
|
322
|
-
**Grouping by domain (recommended):**
|
|
323
|
-
|
|
324
|
-
```
|
|
325
|
-
Auth/Permissions: User, Role, Permission, Session
|
|
326
|
-
Surveys: Survey, Question, Choice, Response
|
|
327
|
-
Reports: Report, Chart, Export
|
|
328
|
-
Administration: Institution, Department, Settings
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
**Grouping by dependencies:**
|
|
332
|
-
|
|
333
|
-
```
|
|
334
|
-
1st: Independent entities (User, Institution, etc.)
|
|
335
|
-
2nd: Entities depending on 1st (Survey → Institution)
|
|
336
|
-
3rd: Entities depending on 2nd (Question → Survey)
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
### Batch Work Process
|
|
340
|
-
|
|
341
|
-
**For each batch:**
|
|
342
|
-
|
|
343
|
-
1. List entities in the batch explicitly
|
|
344
|
-
2. Write test helpers (createTest...)
|
|
345
|
-
3. Complete tests for all entities
|
|
346
|
-
4. Confirm all tests pass
|
|
347
|
-
5. **Git commit, then proceed to next batch**
|
|
348
|
-
|
|
349
|
-
**Between-batch checklist:**
|
|
350
|
-
|
|
351
|
-
- [ ] All tests in current batch pass
|
|
352
|
-
- [ ] Previous batch tests still pass (prevent regression)
|
|
353
|
-
- [ ] Commit complete (establish rollback point)
|
|
354
|
-
|
|
355
|
-
### Declare Before Starting Work
|
|
356
|
-
|
|
357
|
-
**IMPORTANT: Declare explicitly before starting each batch**
|
|
358
|
-
|
|
359
|
-
```
|
|
360
|
-
"Starting batch 1: User, Institution, Role entities (5)
|
|
361
|
-
- User: write user.model.test.ts
|
|
362
|
-
- Institution: write institution.model.test.ts
|
|
363
|
-
- Role: write role.model.test.ts
|
|
364
|
-
Only work on files to be modified, do not touch other files
|
|
365
|
-
Shall we proceed?"
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
### Warning Signs
|
|
369
|
-
|
|
370
|
-
**Stop work immediately** if any of the following occur:
|
|
371
|
-
|
|
372
|
-
- Attempting to modify entities outside the batch scope
|
|
373
|
-
- Asking the same question repeatedly
|
|
374
|
-
- Confusing entity relationships
|
|
375
|
-
- Trying to re-modify files already completed
|
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: sonamu-vector
|
|
3
|
-
description: Implements semantic and hybrid search with pgvector. Use when generating embeddings, chunking documents, or combining vector similarity with full-text search. Covers the Voyage AI and OpenAI embedding providers, chunking strategies, and hybrid ranking.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Vector Search Guide
|
|
7
|
-
|
|
8
|
-
Sonamu supports pgvector-based vector search. It integrates both Voyage AI and OpenAI embedding providers, and also supports hybrid search (Vector + Full-Text Search).
|
|
9
|
-
|
|
10
|
-
**Source code:** `modules/sonamu/src/vector/`
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## Structure
|
|
15
|
-
|
|
16
|
-
| File | Role |
|
|
17
|
-
| -------------- | --------------------------------------------------------------------------------- |
|
|
18
|
-
| `types.ts` | Full type definitions (EmbeddingProvider, VectorSearchResult, VectorConfig, etc.) |
|
|
19
|
-
| `config.ts` | Default configuration values + `createVectorConfig()` helper |
|
|
20
|
-
| `embedding.ts` | Embedding client (Voyage AI and OpenAI integration) |
|
|
21
|
-
| `chunking.ts` | Text chunking (splitting long documents) |
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## Embedding Providers
|
|
26
|
-
|
|
27
|
-
| Provider | Model | Dimensions | maxTokens | batchSize | Package |
|
|
28
|
-
| -------- | ------------------------ | ---------- | --------- | --------- | ---------------- |
|
|
29
|
-
| `voyage` | `voyage-3` | 1024 | 32000 | 128 | `voyageai` |
|
|
30
|
-
| `openai` | `text-embedding-3-small` | 1536 | 8191 | 100 | `@ai-sdk/openai` |
|
|
31
|
-
|
|
32
|
-
### API Key Configuration
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
# Environment variables
|
|
36
|
-
export VOYAGE_API_KEY=pa-...
|
|
37
|
-
export OPENAI_API_KEY=sk-...
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
Or in `sonamu.config.ts`:
|
|
41
|
-
|
|
42
|
-
```typescript
|
|
43
|
-
export default defineConfig({
|
|
44
|
-
secret: {
|
|
45
|
-
voyage_api_key: "pa-...",
|
|
46
|
-
openai_api_key: "sk-...",
|
|
47
|
-
},
|
|
48
|
-
});
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Key priority: `Sonamu.secrets.voyage_api_key` → `process.env.VOYAGE_API_KEY`
|
|
52
|
-
|
|
53
|
-
---
|
|
54
|
-
|
|
55
|
-
## Embedding Usage
|
|
56
|
-
|
|
57
|
-
```typescript
|
|
58
|
-
import { Embedding } from "sonamu/vector";
|
|
59
|
-
|
|
60
|
-
// Single text
|
|
61
|
-
const result = await Embedding.embedOne("text to search", "voyage", "query");
|
|
62
|
-
// result: { embedding: number[], model: "voyage-3", tokenCount: 15 }
|
|
63
|
-
|
|
64
|
-
// Multiple texts (auto-splits when exceeding batchSize)
|
|
65
|
-
const results = await Embedding.embed(
|
|
66
|
-
["text1", "text2", ...],
|
|
67
|
-
"voyage",
|
|
68
|
-
"document", // inputType: "document" | "query"
|
|
69
|
-
(processed, total) => console.log(`${processed}/${total}`), // progress callback
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
// Check number of dimensions
|
|
73
|
-
Embedding.getDimensions("voyage"); // 1024
|
|
74
|
-
Embedding.getDimensions("openai"); // 1536
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### Voyage AI inputType (Asymmetric Embedding)
|
|
78
|
-
|
|
79
|
-
| inputType | Use case |
|
|
80
|
-
| ------------ | --------------------------------------- |
|
|
81
|
-
| `"document"` | When embedding documents to store in DB |
|
|
82
|
-
| `"query"` | When embedding search queries |
|
|
83
|
-
|
|
84
|
-
**CRITICAL: Use `"document"` when storing and `"query"` when searching for asymmetric embedding to work correctly.**
|
|
85
|
-
|
|
86
|
-
---
|
|
87
|
-
|
|
88
|
-
## Chunking Usage
|
|
89
|
-
|
|
90
|
-
Splits long documents into appropriately-sized pieces.
|
|
91
|
-
|
|
92
|
-
```typescript
|
|
93
|
-
import { Chunking } from "sonamu/vector";
|
|
94
|
-
|
|
95
|
-
const chunker = new Chunking({
|
|
96
|
-
chunkSize: 500, // Maximum chunk size (character count)
|
|
97
|
-
chunkOverlap: 50, // Overlap between chunks
|
|
98
|
-
minChunkSize: 50, // Minimum chunk size
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
// Check if chunking is needed
|
|
102
|
-
chunker.needsChunking("short text"); // false
|
|
103
|
-
|
|
104
|
-
// Split into chunks
|
|
105
|
-
const chunks = chunker.chunk(longText);
|
|
106
|
-
// chunks: [{ index: 0, text: "...", startOffset: 0, endOffset: 500 }, ...]
|
|
107
|
-
|
|
108
|
-
// Estimate number of chunks
|
|
109
|
-
chunker.estimateChunkCount(longText); // 5
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
### Chunking Default Settings
|
|
113
|
-
|
|
114
|
-
| Option | Default | Description |
|
|
115
|
-
| --------------- | --------------------------------- | -------------------------------------------------------- |
|
|
116
|
-
| `chunkSize` | 500 | Maximum chunk size (character count) |
|
|
117
|
-
| `chunkOverlap` | 50 | Overlap between chunks |
|
|
118
|
-
| `minChunkSize` | 50 | Minimum chunk size |
|
|
119
|
-
| `skipThreshold` | 200 | Passes through without chunking if at or below this size |
|
|
120
|
-
| `separators` | `["\n\n", "\n", "。", ". ", ...]` | Split delimiters (in priority order) |
|
|
121
|
-
|
|
122
|
-
---
|
|
123
|
-
|
|
124
|
-
## Search Configuration
|
|
125
|
-
|
|
126
|
-
```typescript
|
|
127
|
-
import { createVectorConfig } from "sonamu/vector";
|
|
128
|
-
|
|
129
|
-
const config = createVectorConfig({
|
|
130
|
-
search: {
|
|
131
|
-
defaultLimit: 10,
|
|
132
|
-
similarityThreshold: 0.5, // Results below this value are excluded
|
|
133
|
-
vectorWeight: 0.7, // Vector weight in hybrid search
|
|
134
|
-
ftsWeight: 0.3, // FTS weight in hybrid search
|
|
135
|
-
},
|
|
136
|
-
pgvector: {
|
|
137
|
-
iterativeScan: true, // Use pgvector iterative scan
|
|
138
|
-
efSearch: 100, // HNSW index search accuracy
|
|
139
|
-
},
|
|
140
|
-
});
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
---
|
|
144
|
-
|
|
145
|
-
## Type Definitions
|
|
146
|
-
|
|
147
|
-
### VectorSearchResult
|
|
148
|
-
|
|
149
|
-
```typescript
|
|
150
|
-
interface VectorSearchResult<T = Record<string, unknown>> {
|
|
151
|
-
id: number | string;
|
|
152
|
-
similarity: number;
|
|
153
|
-
data: T;
|
|
154
|
-
}
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
### HybridSearchResult
|
|
158
|
-
|
|
159
|
-
```typescript
|
|
160
|
-
interface HybridSearchResult<T> extends VectorSearchResult<T> {
|
|
161
|
-
vectorScore?: number;
|
|
162
|
-
ftsScore?: number;
|
|
163
|
-
}
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
### VectorSearchOptions
|
|
167
|
-
|
|
168
|
-
```typescript
|
|
169
|
-
interface VectorSearchOptions {
|
|
170
|
-
embeddingColumn?: string; // Embedding column name (default: "embedding")
|
|
171
|
-
limit?: number;
|
|
172
|
-
threshold?: number; // Similarity threshold
|
|
173
|
-
where?: string; // SQL WHERE condition
|
|
174
|
-
}
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
### HybridSearchOptions
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
interface HybridSearchOptions extends VectorSearchOptions {
|
|
181
|
-
vectorWeight?: number; // Vector search weight
|
|
182
|
-
ftsWeight?: number; // FTS weight
|
|
183
|
-
ftsColumn?: string; // Target column name for FTS
|
|
184
|
-
}
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
---
|
|
188
|
-
|
|
189
|
-
## pgvector DB Setup
|
|
190
|
-
|
|
191
|
-
### Install Extension
|
|
192
|
-
|
|
193
|
-
```sql
|
|
194
|
-
CREATE EXTENSION IF NOT EXISTS vector;
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
### Add Embedding Column
|
|
198
|
-
|
|
199
|
-
```sql
|
|
200
|
-
-- Voyage AI (1024 dimensions)
|
|
201
|
-
ALTER TABLE documents ADD COLUMN embedding vector(1024);
|
|
202
|
-
|
|
203
|
-
-- OpenAI (1536 dimensions)
|
|
204
|
-
ALTER TABLE documents ADD COLUMN embedding vector(1536);
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
### HNSW Index
|
|
208
|
-
|
|
209
|
-
```sql
|
|
210
|
-
-- Cosine similarity-based index
|
|
211
|
-
CREATE INDEX ON documents
|
|
212
|
-
USING hnsw (embedding vector_cosine_ops)
|
|
213
|
-
WITH (m = 16, ef_construction = 64);
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
---
|
|
217
|
-
|
|
218
|
-
## References
|
|
219
|
-
|
|
220
|
-
- **Source code**: `modules/sonamu/src/vector/`
|
|
221
|
-
- **pgvector official**: https://github.com/pgvector/pgvector
|
|
222
|
-
- **Voyage AI**: https://docs.voyageai.com/
|