gencow 0.1.169 → 0.1.171
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/lib/backup-command.mjs +88 -30
- package/lib/component-readme.mjs +82 -10
- package/lib/readme-codegen.mjs +11 -3
- package/package.json +1 -1
- package/server/index.js +302 -181
- package/server/index.js.map +4 -4
- package/templateFeature/agent/agent.ts +70 -71
- package/templateFeature/agent/manifest.json +4 -4
- package/templateFeature/ai/ai-provider.ts +142 -15
- package/templateFeature/ai/manifest.json +2 -0
- package/templateFeature/guardrails/guardrails.ts +141 -118
- package/templateFeature/guardrails/manifest.json +7 -5
- package/templateFeature/memory/manifest.json +7 -2
- package/templateFeature/memory/memory.ts +185 -155
- package/templateFeature/rag/manifest.json +4 -1
- package/templateFeature/rag/rag.ts +311 -267
- package/templateFeature/reranker/manifest.json +10 -5
- package/templateFeature/reranker/reranker.ts +201 -175
- package/templateFeature/tools/manifest.json +6 -5
package/lib/backup-command.mjs
CHANGED
|
@@ -20,7 +20,7 @@ export function resolveBackupAppId(restArgs, gencowJson = null) {
|
|
|
20
20
|
export function resolveBackupFilePath(restArgs) {
|
|
21
21
|
for (let i = 0; i < restArgs.length; i++) {
|
|
22
22
|
const arg = restArgs[i];
|
|
23
|
-
if (arg === "--app" || arg === "-a") {
|
|
23
|
+
if (arg === "--app" || arg === "-a" || arg === "--limit" || arg === "--cursor") {
|
|
24
24
|
i++;
|
|
25
25
|
continue;
|
|
26
26
|
}
|
|
@@ -45,10 +45,34 @@ export function formatBackupDate(value) {
|
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
export function parseBackupListOptions(restArgs) {
|
|
49
|
+
const options = { all: false, cursor: null, limit: 20 };
|
|
50
|
+
for (let i = 0; i < restArgs.length; i++) {
|
|
51
|
+
const arg = restArgs[i];
|
|
52
|
+
if (arg === "--all") {
|
|
53
|
+
options.all = true;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (arg === "--limit") {
|
|
57
|
+
const next = Number(restArgs[i + 1]);
|
|
58
|
+
if (Number.isFinite(next)) options.limit = Math.min(100, Math.max(1, Math.trunc(next)));
|
|
59
|
+
i++;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (arg === "--cursor") {
|
|
63
|
+
options.cursor = restArgs[i + 1] || null;
|
|
64
|
+
i++;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return options;
|
|
68
|
+
}
|
|
69
|
+
|
|
48
70
|
function renderBackupHelp(logImpl = log, errorImpl = error, subCmd = null) {
|
|
49
71
|
if (subCmd) errorImpl(`Unknown sub-command: ${subCmd}`);
|
|
50
72
|
logImpl(`\n ${BOLD}Usage:${RESET}`);
|
|
51
|
-
logImpl(` gencow backup list List
|
|
73
|
+
logImpl(` gencow backup list List recent backups`);
|
|
74
|
+
logImpl(` gencow backup list --all List all backups by following cursors`);
|
|
75
|
+
logImpl(` gencow backup list --limit 50 List a page with up to 50 backups`);
|
|
52
76
|
logImpl(` gencow backup create [note] Create a manual backup`);
|
|
53
77
|
logImpl(` gencow backup restore <id> Restore from backup`);
|
|
54
78
|
logImpl(` gencow backup restore-file <path> Restore from a downloaded dump file`);
|
|
@@ -56,18 +80,23 @@ function renderBackupHelp(logImpl = log, errorImpl = error, subCmd = null) {
|
|
|
56
80
|
logImpl(` gencow backup download <id> Download backup file (Pro+)\n`);
|
|
57
81
|
}
|
|
58
82
|
|
|
59
|
-
|
|
83
|
+
export function buildRestoreConfirmPrompt(sourceLabel, { includeStorageRisk = false } = {}) {
|
|
84
|
+
const storageRisk = includeStorageRisk
|
|
85
|
+
? `\n ${YELLOW}⚠ 백업 파일 복원은 DB만 교체하며 storage object/blob는 함께 이동하지 않습니다.${RESET}` +
|
|
86
|
+
`\n ${DIM}기본 용도는 같은 환경 복원(prod→prod, dev→dev)입니다. 다른 환경의 dump를 복원하면 이미지/파일 참조가 깨질 수 있습니다.${RESET}`
|
|
87
|
+
: "";
|
|
88
|
+
return `\n ${YELLOW}⚠ 현재 데이터베이스가 ${sourceLabel}로 교체됩니다.${RESET}${storageRisk}\n 계속하시겠습니까? (y/N): `;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function promptRestoreConfirm(sourceLabel, { createInterfaceImpl, includeStorageRisk = false }) {
|
|
60
92
|
const createInterface = createInterfaceImpl ?? (await import("readline")).createInterface;
|
|
61
93
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
62
94
|
return await new Promise((resolveAnswer) => {
|
|
63
|
-
rl.question(
|
|
64
|
-
|
|
65
|
-
(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
resolveAnswer(normalized === "y" || normalized === "yes");
|
|
69
|
-
},
|
|
70
|
-
);
|
|
95
|
+
rl.question(buildRestoreConfirmPrompt(sourceLabel, { includeStorageRisk }), (answer) => {
|
|
96
|
+
rl.close();
|
|
97
|
+
const normalized = answer.trim().toLowerCase();
|
|
98
|
+
resolveAnswer(normalized === "y" || normalized === "yes");
|
|
99
|
+
});
|
|
71
100
|
});
|
|
72
101
|
}
|
|
73
102
|
|
|
@@ -108,19 +137,39 @@ export function createBackupCommand({
|
|
|
108
137
|
switch (subCmd) {
|
|
109
138
|
case "list":
|
|
110
139
|
case "ls": {
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
140
|
+
const options = parseBackupListOptions(restArgs);
|
|
141
|
+
const backups = [];
|
|
142
|
+
let data = null;
|
|
143
|
+
let cursor = options.cursor;
|
|
144
|
+
for (let page = 0; page < 1000; page++) {
|
|
145
|
+
const response = await rpcQueryImpl(creds, "backup.list", {
|
|
146
|
+
appName: appId,
|
|
147
|
+
limit: options.limit,
|
|
148
|
+
cursor,
|
|
149
|
+
});
|
|
150
|
+
if (!response.ok) {
|
|
151
|
+
errorImpl(
|
|
152
|
+
`Failed to fetch backup list: ${(await response.json().catch(() => ({}))).error || response.statusText}`,
|
|
153
|
+
);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
data = await response.json();
|
|
157
|
+
backups.push(...(data.backups || []));
|
|
158
|
+
cursor = data.pageInfo?.nextCursor || null;
|
|
159
|
+
if (options.all && cursor && page === 999) {
|
|
160
|
+
errorImpl(
|
|
161
|
+
"Backup list is too large to fetch with --all. Resume with --cursor from the previous page.",
|
|
162
|
+
);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (!options.all || !cursor) break;
|
|
117
166
|
}
|
|
118
|
-
const
|
|
119
|
-
const
|
|
120
|
-
const
|
|
167
|
+
const limits = data?.limits || {};
|
|
168
|
+
const summary = data?.summary || {};
|
|
169
|
+
const pageInfo = data?.pageInfo || {};
|
|
121
170
|
|
|
122
171
|
logImpl(
|
|
123
|
-
`\n${BOLD}${CYAN}Database Backups${RESET} — ${appId} ${DIM}(Plan: ${data
|
|
172
|
+
`\n${BOLD}${CYAN}Database Backups${RESET} — ${appId} ${DIM}(Plan: ${data?.plan || "free"})${RESET}\n`,
|
|
124
173
|
);
|
|
125
174
|
|
|
126
175
|
if (backups.length === 0) {
|
|
@@ -136,6 +185,16 @@ export function createBackupCommand({
|
|
|
136
185
|
}
|
|
137
186
|
}
|
|
138
187
|
|
|
188
|
+
const totalCount = summary.totalCount ?? backups.length;
|
|
189
|
+
const pendingCount = summary.pendingCount ?? 0;
|
|
190
|
+
const shownLabel = options.all
|
|
191
|
+
? `${backups.length}/${totalCount}`
|
|
192
|
+
: `${backups.length} of ${totalCount}`;
|
|
193
|
+
logImpl(`\n ${DIM}Showing: ${shownLabel} | Pending: ${pendingCount}${RESET}`);
|
|
194
|
+
if (!options.all && pageInfo.nextCursor) {
|
|
195
|
+
logImpl(` ${DIM}Next page: gencow backup list --cursor ${pageInfo.nextCursor}${RESET}`);
|
|
196
|
+
logImpl(` ${DIM}Full history: gencow backup list --all${RESET}`);
|
|
197
|
+
}
|
|
139
198
|
logImpl(
|
|
140
199
|
`\n ${DIM}Limits: ${limits.maxManualBackups || "?"} manual | ${limits.retentionDays || "?"} days | Auto: ${limits.autoBackupEnabled ? "ON" : "OFF"}${RESET}\n`,
|
|
141
200
|
);
|
|
@@ -198,7 +257,10 @@ export function createBackupCommand({
|
|
|
198
257
|
return;
|
|
199
258
|
}
|
|
200
259
|
|
|
201
|
-
const confirmed = await promptRestoreConfirm(`파일 ${filePath}`, {
|
|
260
|
+
const confirmed = await promptRestoreConfirm(`파일 ${filePath}`, {
|
|
261
|
+
createInterfaceImpl,
|
|
262
|
+
includeStorageRisk: true,
|
|
263
|
+
});
|
|
202
264
|
if (!confirmed) {
|
|
203
265
|
infoImpl("Restore cancelled.");
|
|
204
266
|
return;
|
|
@@ -209,14 +271,10 @@ export function createBackupCommand({
|
|
|
209
271
|
const buffer = await readFileAsync(filePath);
|
|
210
272
|
const form = new FormData();
|
|
211
273
|
form.append("file", new Blob([buffer]), filePath.split(/[\\/]/).pop() || "backup.dump");
|
|
212
|
-
const response = await platformFetchImpl(
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
method: "POST",
|
|
217
|
-
body: form,
|
|
218
|
-
},
|
|
219
|
-
);
|
|
274
|
+
const response = await platformFetchImpl(creds, `/platform/apps/${appId}/backups/restore-file`, {
|
|
275
|
+
method: "POST",
|
|
276
|
+
body: form,
|
|
277
|
+
});
|
|
220
278
|
if (!response.ok) {
|
|
221
279
|
errorImpl(
|
|
222
280
|
`Restore failed: ${(await response.json?.().catch(() => ({}))).error || response.statusText}`,
|
package/lib/component-readme.mjs
CHANGED
|
@@ -48,9 +48,18 @@ export function LoginPage() {
|
|
|
48
48
|
| \`ai.estimateTokens()\` | \`function\` | Lightweight token estimate |
|
|
49
49
|
| \`ai.trimMessages()\` | \`function\` | Trim messages to a token budget |`,
|
|
50
50
|
usage: `\`\`\`typescript
|
|
51
|
-
import {
|
|
51
|
+
import { generateText, rerank } from "ai";
|
|
52
|
+
import { ai, createGencowAI } from "@/gencow/ai";
|
|
52
53
|
|
|
53
|
-
|
|
54
|
+
const gencow = createGencowAI();
|
|
55
|
+
|
|
56
|
+
// Pure AI SDK text generation
|
|
57
|
+
const sdkReply = await generateText({
|
|
58
|
+
model: gencow.languageModel("gpt-5.4-mini"),
|
|
59
|
+
messages: [{ role: "user", content: "Hello" }],
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Legacy facade text generation
|
|
54
63
|
const reply = await ai.chat({
|
|
55
64
|
system: "You are a helpful assistant.",
|
|
56
65
|
messages: [{ role: "user", content: "Hello" }],
|
|
@@ -77,6 +86,14 @@ const ranked = await ai.rerank({
|
|
|
77
86
|
topK: 5,
|
|
78
87
|
});
|
|
79
88
|
|
|
89
|
+
// Pure AI SDK rerank path
|
|
90
|
+
const sdkRanked = await rerank({
|
|
91
|
+
model: gencow.rerankingModel("Cohere-rerank-v4.0-fast"),
|
|
92
|
+
documents: candidates.map((candidate) => candidate.text),
|
|
93
|
+
query: "refund policy",
|
|
94
|
+
topN: 5,
|
|
95
|
+
});
|
|
96
|
+
|
|
80
97
|
// Image generation
|
|
81
98
|
const image = await ai.image.generate({
|
|
82
99
|
prompt: "A clean app icon for a todo product",
|
|
@@ -91,7 +108,9 @@ const receipt = await ai.vision.extractText({
|
|
|
91
108
|
});
|
|
92
109
|
\`\`\``,
|
|
93
110
|
vibePrompt: `AI Engine API:
|
|
94
|
-
import { ai } from "@/gencow/ai";
|
|
111
|
+
import { ai, createGencowAI } from "@/gencow/ai";
|
|
112
|
+
createGencowAI().languageModel("gpt-5.4-mini") // pure AI SDK model
|
|
113
|
+
createGencowAI().rerankingModel("Cohere-rerank-v4.0-fast") // pure rerank model
|
|
95
114
|
ai.chat({ messages }) // text generation
|
|
96
115
|
ai.stream({ messages }) // streaming
|
|
97
116
|
ai.embed("text") // embedding
|
|
@@ -108,6 +127,7 @@ const receipt = await ai.vision.extractText({
|
|
|
108
127
|
title: "🤖 Workflow Agent",
|
|
109
128
|
table: `| Function | Type | Description |
|
|
110
129
|
| :--- | :--- | :--- |
|
|
130
|
+
| \`createWorkflowAgent()\` | \`factory\` | Compose the starter with an injected AI SDK language model |
|
|
111
131
|
| \`runAgent\` | \`workflow\` | Durable multi-step agent entrypoint |
|
|
112
132
|
| \`wf.step()\` | \`runtime\` | Memoized checkpoint step |
|
|
113
133
|
| \`wf.parallel()\` | \`runtime\` | Branch fan-out with partial progress retention |
|
|
@@ -115,8 +135,15 @@ const receipt = await ai.vision.extractText({
|
|
|
115
135
|
| \`workflows.signal()\` | \`mutation\` | Wake waiting workflow with payload |`,
|
|
116
136
|
usage: `\`\`\`typescript
|
|
117
137
|
import { api } from "@/gencow/api";
|
|
138
|
+
import { createGencowAI } from "@/gencow/ai";
|
|
139
|
+
import { createWorkflowAgent } from "@/gencow/agent";
|
|
118
140
|
import { useMutation, useWorkflow } from "@gencow/react";
|
|
119
141
|
|
|
142
|
+
const gencow = createGencowAI();
|
|
143
|
+
export const customRunAgent = createWorkflowAgent({
|
|
144
|
+
model: gencow.languageModel("gpt-5.4-mini"),
|
|
145
|
+
});
|
|
146
|
+
|
|
120
147
|
const { mutate: start } = useMutation(api.agents.run);
|
|
121
148
|
const { mutate: signal } = useMutation(api.workflows.signal);
|
|
122
149
|
|
|
@@ -131,6 +158,7 @@ await signal({
|
|
|
131
158
|
\`\`\``,
|
|
132
159
|
vibePrompt: `Workflow Agent Starter:
|
|
133
160
|
workflow("agents.run", { args, handler }) // durable start entrypoint
|
|
161
|
+
createWorkflowAgent({ model }) // pure AI SDK composition
|
|
134
162
|
wf.step("plan", fn) // memoized checkpoint
|
|
135
163
|
wf.parallel([taskA, taskB]) // branch fan-out
|
|
136
164
|
wf.waitForEvent("approval") // human approval checkpoint
|
|
@@ -141,6 +169,7 @@ await signal({
|
|
|
141
169
|
title: "🔍 RAG Engine",
|
|
142
170
|
table: `| Function | Type | Description |
|
|
143
171
|
| :--- | :--- | :--- |
|
|
172
|
+
| \`createRag()\` | \`factory\` | Compose RAG with injected embedding and answer models |
|
|
144
173
|
| \`rag.ingest()\` | \`function\` | Document chunking plus embedding storage |
|
|
145
174
|
| \`rag.retrieve()\` | \`function\` | Relevant document retrieval; currently vector search, hybrid-ready |
|
|
146
175
|
| \`rag.search()\` | \`function\` | \`rag.retrieve()\` compatibility alias |
|
|
@@ -150,7 +179,14 @@ await signal({
|
|
|
150
179
|
| \`rag.extractTopics()\` | \`function\` | Topic extraction grounded answer |
|
|
151
180
|
| \`rag.delete()\` | \`function\` | Delete documents by source |`,
|
|
152
181
|
usage: `\`\`\`typescript
|
|
153
|
-
import {
|
|
182
|
+
import { createGencowAI } from "@/gencow/ai";
|
|
183
|
+
import { createRag, rag } from "@/gencow/rag";
|
|
184
|
+
|
|
185
|
+
const gencow = createGencowAI();
|
|
186
|
+
const customRag = createRag({
|
|
187
|
+
embeddingModel: gencow.embeddingModel("text-embedding-3-small"),
|
|
188
|
+
answerModel: gencow.languageModel("gpt-5.4-mini"),
|
|
189
|
+
});
|
|
154
190
|
|
|
155
191
|
// Legacy local RAG path: ingest documents into rag_documents.
|
|
156
192
|
await rag.ingest(ctx, "manual.pdf", documentText, { strategy: "markdown" });
|
|
@@ -175,6 +211,7 @@ await rag.delete(ctx, "old-manual.pdf");
|
|
|
175
211
|
\`\`\``,
|
|
176
212
|
vibePrompt: `RAG Engine API:
|
|
177
213
|
import { rag } from "@/gencow/rag";
|
|
214
|
+
createRag({ embeddingModel, answerModel }) // pure AI SDK composition
|
|
178
215
|
rag.ingest(ctx, source, text) // document ingest with smart chunking
|
|
179
216
|
rag.retrieve(ctx, query, { filter }) // relevant document retrieval
|
|
180
217
|
rag.search(ctx, query, { filter }) // compatibility alias
|
|
@@ -237,6 +274,8 @@ const hybrid = await hybridSearchRecords(ctx, "searchable_docs", "refund", {
|
|
|
237
274
|
| \`defineTools()\` | \`function\` | Define AI tools with ctx injection |`,
|
|
238
275
|
usage: `\`\`\`typescript
|
|
239
276
|
import { defineTools } from "@/gencow/tools";
|
|
277
|
+
import { generateText } from "ai";
|
|
278
|
+
import { createGencowAI } from "@/gencow/ai";
|
|
240
279
|
|
|
241
280
|
const tools = defineTools(ctx, {
|
|
242
281
|
getOrderStatus: {
|
|
@@ -250,21 +289,35 @@ const tools = defineTools(ctx, {
|
|
|
250
289
|
});
|
|
251
290
|
|
|
252
291
|
const reply = await ai.chat({ messages, tools });
|
|
292
|
+
const sdkReply = await generateText({
|
|
293
|
+
model: createGencowAI().languageModel("gpt-5.4-mini"),
|
|
294
|
+
messages,
|
|
295
|
+
tools,
|
|
296
|
+
});
|
|
253
297
|
\`\`\``,
|
|
254
298
|
vibePrompt: `Tool Calling API:
|
|
255
299
|
import { defineTools } from "@/gencow/tools";
|
|
256
300
|
const tools = defineTools(ctx, { ... }) // define AI tools
|
|
301
|
+
generateText({ model, messages, tools }) // pure AI SDK tool call
|
|
257
302
|
ai.chat({ messages, tools }) // respond with tool calls`,
|
|
258
303
|
},
|
|
259
304
|
"memory.ts": {
|
|
260
305
|
title: "🧠 Agent Memory",
|
|
261
306
|
table: `| Function | Type | Description |
|
|
262
307
|
| :--- | :--- | :--- |
|
|
308
|
+
| \`createMemory()\` | \`factory\` | Compose memory with injected extraction and embedding models |
|
|
263
309
|
| \`memory.extract()\` | \`function\` | Automatically extract facts from conversations |
|
|
264
310
|
| \`memory.search()\` | \`function\` | Semantic search over related memories |
|
|
265
311
|
| \`memory.buildContext()\` | \`function\` | Compose memory context for agents |`,
|
|
266
312
|
usage: `\`\`\`typescript
|
|
267
|
-
import {
|
|
313
|
+
import { createGencowAI } from "@/gencow/ai";
|
|
314
|
+
import { createMemory, memory } from "@/gencow/memory";
|
|
315
|
+
|
|
316
|
+
const gencow = createGencowAI();
|
|
317
|
+
const customMemory = createMemory({
|
|
318
|
+
extractionModel: gencow.languageModel("gpt-5.4-mini"),
|
|
319
|
+
embeddingModel: gencow.embeddingModel("text-embedding-3-small"),
|
|
320
|
+
});
|
|
268
321
|
|
|
269
322
|
const memCtx = await memory.buildContext(ctx, userId, sessionId, query);
|
|
270
323
|
const reply = await ai.chat({
|
|
@@ -274,6 +327,7 @@ const reply = await ai.chat({
|
|
|
274
327
|
\`\`\``,
|
|
275
328
|
vibePrompt: `Agent Memory API:
|
|
276
329
|
import { memory } from "@/gencow/memory";
|
|
330
|
+
createMemory({ extractionModel, embeddingModel }) // pure AI SDK composition
|
|
277
331
|
memory.buildContext(ctx, userId, sessionId, query) // memory context
|
|
278
332
|
memory.extract(ctx, userId, conversation) // fact extraction`,
|
|
279
333
|
},
|
|
@@ -293,14 +347,23 @@ const reply = await ai.chat({
|
|
|
293
347
|
title: "🎯 Reranker",
|
|
294
348
|
table: `| Function | Type | Description |
|
|
295
349
|
| :--- | :--- | :--- |
|
|
296
|
-
| \`
|
|
350
|
+
| \`createReranker()\` | \`factory\` | Compose reranking with an injected AI SDK reranking model |
|
|
351
|
+
| \`reranker.rerank()\` | \`function\` | AI SDK rerank primary path with explicit LLM compatibility fallback |
|
|
297
352
|
| \`reranker.searchAndRerank()\` | \`function\` | Integrated RAG search plus rerank |
|
|
298
353
|
| \`reranker.answerGrounded()\` | \`function\` | Phase 3 grounded answer helper |`,
|
|
299
354
|
usage: `\`\`\`typescript
|
|
300
|
-
import {
|
|
355
|
+
import { createGencowAI } from "@/gencow/ai";
|
|
356
|
+
import { createReranker, reranker } from "@/gencow/reranker";
|
|
301
357
|
import { rag } from "@/gencow/rag";
|
|
302
358
|
|
|
303
|
-
|
|
359
|
+
const gencow = createGencowAI();
|
|
360
|
+
const customReranker = createReranker({
|
|
361
|
+
rerankingModel: gencow.rerankingModel("Cohere-rerank-v4.0-fast"),
|
|
362
|
+
fallbackModel: gencow.languageModel("gpt-5.4-mini"),
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// Rerank vector search results with the proxy-backed AI SDK rerank path.
|
|
366
|
+
// In local direct mode, the generated starter can use the fallback model path.
|
|
304
367
|
const results = await rag.retrieve(ctx, query, { limit: 20 });
|
|
305
368
|
const reranked = await reranker.rerank(query, results, { topK: 5 });
|
|
306
369
|
|
|
@@ -312,7 +375,8 @@ const grounded = await reranker.answerGrounded(ctx, query, { corpus: "default" }
|
|
|
312
375
|
\`\`\``,
|
|
313
376
|
vibePrompt: `Reranker API:
|
|
314
377
|
import { reranker } from "@/gencow/reranker";
|
|
315
|
-
|
|
378
|
+
createReranker({ rerankingModel, fallbackModel }) // pure AI SDK + compat fallback
|
|
379
|
+
reranker.rerank(query, docs, { topK: 5 }) // SDK rerank primary, compat fallback secondary
|
|
316
380
|
reranker.searchAndRerank(ctx, rag, query) // integrated pipeline
|
|
317
381
|
reranker.answerGrounded(ctx, query, opts) // grounded answer`,
|
|
318
382
|
},
|
|
@@ -320,11 +384,18 @@ const grounded = await reranker.answerGrounded(ctx, query, { corpus: "default" }
|
|
|
320
384
|
title: "🛡️ Guardrails",
|
|
321
385
|
table: `| Function | Type | Description |
|
|
322
386
|
| :--- | :--- | :--- |
|
|
387
|
+
| \`createGuardrails()\` | \`factory\` | Compose guardrails with an injected AI SDK language model |
|
|
323
388
|
| \`guardrails.validateInput()\` | \`function\` | Input validation for PII, topics, and length |
|
|
324
389
|
| \`guardrails.validateOutput()\` | \`function\` | Output validation for length, patterns, and custom checks |
|
|
325
390
|
| \`guardrails.wrap()\` | \`function\` | Wrap input, AI call, and output validation in one helper |`,
|
|
326
391
|
usage: `\`\`\`typescript
|
|
327
|
-
import {
|
|
392
|
+
import { createGencowAI } from "@/gencow/ai";
|
|
393
|
+
import { createGuardrails, guardrails } from "@/gencow/guardrails";
|
|
394
|
+
|
|
395
|
+
const gencow = createGencowAI();
|
|
396
|
+
const customGuardrails = createGuardrails({
|
|
397
|
+
model: gencow.languageModel("gpt-5.4-mini"),
|
|
398
|
+
});
|
|
328
399
|
|
|
329
400
|
// Mask PII and block disallowed topics.
|
|
330
401
|
const safe = await guardrails.validateInput(userMsg, {
|
|
@@ -343,6 +414,7 @@ const result = await guardrails.wrap(
|
|
|
343
414
|
\`\`\``,
|
|
344
415
|
vibePrompt: `Guardrails API:
|
|
345
416
|
import { guardrails } from "@/gencow/guardrails";
|
|
417
|
+
createGuardrails({ model }) // pure AI SDK composition
|
|
346
418
|
guardrails.validateInput(text, { maskPII: true }) // PII masking
|
|
347
419
|
guardrails.validateInput(text, { blockTopics }) // topic blocking
|
|
348
420
|
guardrails.wrap(fn, input, inputOpts, outputOpts) // one-call wrapper`,
|
package/lib/readme-codegen.mjs
CHANGED
|
@@ -291,11 +291,13 @@ export function buildAiPrompt(apiObj, namespaces) {
|
|
|
291
291
|
md += `- Prefer SHA-256 with node:crypto or Web Crypto API instead of custom 32-bit hashes.\n`;
|
|
292
292
|
md += ` const hash = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text));\n\n`;
|
|
293
293
|
md += `AI rules:\n`;
|
|
294
|
-
md += `- New AI SDK-native code can use createGencowAI() from "./ai" with generateText(), embed(), and generateImage().\n`;
|
|
294
|
+
md += `- New AI SDK-native code can use createGencowAI() from "./ai" with generateText(), embed(), rerank(), and generateImage().\n`;
|
|
295
295
|
md += `- Existing generated components can use ai.chat(), ai.rerank(), ai.vision.extractText(), ai.image.generate(), and ai.generateObject() through the Gencow facade.\n`;
|
|
296
296
|
md += `- Do not import @ai-sdk/openai/createOpenAI directly, call provider APIs directly, or create wrapper files such as openai-direct.ts.\n`;
|
|
297
297
|
md += `- Do not log image base64, prompts, provider tokens, or raw private document snippets.\n`;
|
|
298
|
-
md += `- Use ai.rerank() only after retrieval authorization; do not pass private metadata as provider payload.\n`;
|
|
298
|
+
md += `- Use rerank({ model: createGencowAI().rerankingModel(...) }) or ai.rerank() only after retrieval authorization; do not pass private metadata as provider payload.\n`;
|
|
299
|
+
md += `- createGencowAI().rerankingModel(...) is proxy-only today. Local direct mode with only OPENAI_API_KEY must fail fast for reranking.\n`;
|
|
300
|
+
md += `- The generated reranker starter may use an explicit fallbackModel compatibility path, but strict providerPreference such as azure_cohere must still fail closed on auth/config errors.\n`;
|
|
299
301
|
md += `- Recommended chat models: gpt-5.4-mini for most apps, gpt-5.4 for Pro/Scale highest quality, gpt-5.4-nano for high-volume extraction.\n`;
|
|
300
302
|
md += `- Use ai.generateObject() with a Zod schema for structured output. Do not use ai.chat() + JSON.parse().\n`;
|
|
301
303
|
md += `- Install AI with gencow add AI, RAG with gencow add RAG, Memory with gencow add Memory, and durable agents with gencow add Agent.\n`;
|
|
@@ -308,7 +310,7 @@ export function buildAiUsageSection() {
|
|
|
308
310
|
md += `Gencow supports an Azure-first active chat catalog: gpt-5.4 on Pro/Scale, gpt-5.4-mini by default, gpt-5.4-nano for high volume, plus compatibility models gpt-5-mini and gpt-5-nano. Deprecated requests such as \`gpt-5.5\` and \`gpt-4o-mini\` are automatically substituted to the closest supported model before provider execution. Image and embedding models are also controlled by the platform \`model_pricing\` catalog.\n`;
|
|
309
311
|
md += `Local development uses \`OPENAI_API_KEY\`; cloud deployments automatically use the Gencow AI proxy and service credits.\n\n`;
|
|
310
312
|
md += `\`\`\`typescript\n`;
|
|
311
|
-
md += `import { generateImage, generateText } from "ai";\n`;
|
|
313
|
+
md += `import { generateImage, generateText, rerank } from "ai";\n`;
|
|
312
314
|
md += `import { createGencowAI, ai } from "./ai";\n\n`;
|
|
313
315
|
md += `const gencow = createGencowAI();\n\n`;
|
|
314
316
|
md += `const result = await generateText({\n`;
|
|
@@ -320,6 +322,12 @@ export function buildAiUsageSection() {
|
|
|
320
322
|
md += ` model: gencow.imageModel("gpt-image-2"),\n`;
|
|
321
323
|
md += ` prompt: "A clean app icon for a project management product",\n`;
|
|
322
324
|
md += `});\n\n`;
|
|
325
|
+
md += `const ranked = await rerank({\n`;
|
|
326
|
+
md += ` model: gencow.rerankingModel("Cohere-rerank-v4.0-fast"),\n`;
|
|
327
|
+
md += ` documents: ["Refunds are available within 7 days.", "Shipping takes 2 days."],\n`;
|
|
328
|
+
md += ` query: "refund policy",\n`;
|
|
329
|
+
md += ` topN: 1,\n`;
|
|
330
|
+
md += `});\n\n`;
|
|
323
331
|
md += `const icon = await ai.image.generate({\n`;
|
|
324
332
|
md += ` prompt: "A clean app icon for a project management product",\n`;
|
|
325
333
|
md += ` model: "gpt-image-2",\n`;
|