@firstlovecenter/ai-chat 0.1.1 → 0.2.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/server/index.cjs +26 -8
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +30 -0
- package/dist/server/index.d.ts +30 -0
- package/dist/server/index.js +26 -8
- package/dist/server/index.js.map +1 -1
- package/package.json +12 -10
package/dist/server/index.d.cts
CHANGED
|
@@ -453,6 +453,36 @@ type ConfigureAiChatOpts<S = unknown> = {
|
|
|
453
453
|
* - `onSessionEnd` — cleanup (always-runs, never throws out).
|
|
454
454
|
*/
|
|
455
455
|
hooks?: AgentCustomHooks$1<S>;
|
|
456
|
+
/**
|
|
457
|
+
* Persona / role given to the AI as the very first cached system block
|
|
458
|
+
* on every turn. Use this to give the assistant a consistent voice and
|
|
459
|
+
* domain perspective across all conversations in your app.
|
|
460
|
+
*
|
|
461
|
+
* Accepts:
|
|
462
|
+
* - a `string` for a static org-wide role, or
|
|
463
|
+
* - a `(ctx) => string | Promise<string>` function for per-request
|
|
464
|
+
* variation (different role per scope, per locale, per A/B cohort).
|
|
465
|
+
*
|
|
466
|
+
* Token economics: the role text is sent on every turn (Vertex APIs
|
|
467
|
+
* are stateless), but it is marked `cached: true`. On Claude that
|
|
468
|
+
* becomes an ephemeral `cache_control` marker — first turn pays ~1.25×
|
|
469
|
+
* for the cache write; subsequent turns within ~5 min pay ~0.1× for
|
|
470
|
+
* cache reads. On Gemini the hint is informational; Vertex auto-caches
|
|
471
|
+
* stable prefixes regardless. For a typical 200–500 token role, the
|
|
472
|
+
* per-turn marginal cost after the first is negligible. The package
|
|
473
|
+
* caps total `cache_control` markers at Anthropic's 4-per-request
|
|
474
|
+
* limit; if the host already marks 4 blocks cached, the extras
|
|
475
|
+
* (including this one if last) silently drop the cache hint rather
|
|
476
|
+
* than reject the request.
|
|
477
|
+
*
|
|
478
|
+
* Example (static):
|
|
479
|
+
* rolePrompt: `You are the head pastor of First Love Center, a
|
|
480
|
+
* multinational church operating across multiple countries. Frame
|
|
481
|
+
* answers from the perspective of advancing the gospel through
|
|
482
|
+
* sustainable growth, financial discipline, compliance, and church
|
|
483
|
+
* planting. Be direct, pastoral, and action-oriented.`
|
|
484
|
+
*/
|
|
485
|
+
rolePrompt?: string | ((ctx: ToolContext<S>) => string | Promise<string>);
|
|
456
486
|
};
|
|
457
487
|
type AiChatRuntime<S = unknown> = {
|
|
458
488
|
/**
|
package/dist/server/index.d.ts
CHANGED
|
@@ -453,6 +453,36 @@ type ConfigureAiChatOpts<S = unknown> = {
|
|
|
453
453
|
* - `onSessionEnd` — cleanup (always-runs, never throws out).
|
|
454
454
|
*/
|
|
455
455
|
hooks?: AgentCustomHooks$1<S>;
|
|
456
|
+
/**
|
|
457
|
+
* Persona / role given to the AI as the very first cached system block
|
|
458
|
+
* on every turn. Use this to give the assistant a consistent voice and
|
|
459
|
+
* domain perspective across all conversations in your app.
|
|
460
|
+
*
|
|
461
|
+
* Accepts:
|
|
462
|
+
* - a `string` for a static org-wide role, or
|
|
463
|
+
* - a `(ctx) => string | Promise<string>` function for per-request
|
|
464
|
+
* variation (different role per scope, per locale, per A/B cohort).
|
|
465
|
+
*
|
|
466
|
+
* Token economics: the role text is sent on every turn (Vertex APIs
|
|
467
|
+
* are stateless), but it is marked `cached: true`. On Claude that
|
|
468
|
+
* becomes an ephemeral `cache_control` marker — first turn pays ~1.25×
|
|
469
|
+
* for the cache write; subsequent turns within ~5 min pay ~0.1× for
|
|
470
|
+
* cache reads. On Gemini the hint is informational; Vertex auto-caches
|
|
471
|
+
* stable prefixes regardless. For a typical 200–500 token role, the
|
|
472
|
+
* per-turn marginal cost after the first is negligible. The package
|
|
473
|
+
* caps total `cache_control` markers at Anthropic's 4-per-request
|
|
474
|
+
* limit; if the host already marks 4 blocks cached, the extras
|
|
475
|
+
* (including this one if last) silently drop the cache hint rather
|
|
476
|
+
* than reject the request.
|
|
477
|
+
*
|
|
478
|
+
* Example (static):
|
|
479
|
+
* rolePrompt: `You are the head pastor of First Love Center, a
|
|
480
|
+
* multinational church operating across multiple countries. Frame
|
|
481
|
+
* answers from the perspective of advancing the gospel through
|
|
482
|
+
* sustainable growth, financial discipline, compliance, and church
|
|
483
|
+
* planting. Be direct, pastoral, and action-oriented.`
|
|
484
|
+
*/
|
|
485
|
+
rolePrompt?: string | ((ctx: ToolContext<S>) => string | Promise<string>);
|
|
456
486
|
};
|
|
457
487
|
type AiChatRuntime<S = unknown> = {
|
|
458
488
|
/**
|
package/dist/server/index.js
CHANGED
|
@@ -144,11 +144,19 @@ var ClaudeToolProvider = class {
|
|
|
144
144
|
patchVertexBuildRequestSync(this.client);
|
|
145
145
|
}
|
|
146
146
|
async runTurn(input) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
147
|
+
let cacheMarkersUsed = 0;
|
|
148
|
+
const MAX_CACHE_MARKERS = 4;
|
|
149
|
+
const system = input.system.map((b) => {
|
|
150
|
+
if (b.cached && cacheMarkersUsed < MAX_CACHE_MARKERS) {
|
|
151
|
+
cacheMarkersUsed++;
|
|
152
|
+
return {
|
|
153
|
+
type: "text",
|
|
154
|
+
text: b.text,
|
|
155
|
+
cache_control: { type: "ephemeral" }
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return { type: "text", text: b.text };
|
|
159
|
+
});
|
|
152
160
|
const messages = toAnthropicMessages(input.messages);
|
|
153
161
|
const response = await this.client.messages.create({
|
|
154
162
|
model: this.modelId,
|
|
@@ -1391,6 +1399,16 @@ function configureAiChat(opts) {
|
|
|
1391
1399
|
];
|
|
1392
1400
|
const getProvider = (id) => toolProviders2.find((p) => p.id === id) ?? getToolProvider(id);
|
|
1393
1401
|
const chatInterfaces = opts.chatInterfaces ?? BUILTIN_CHAT_INTERFACE_IDS.map((id) => ({ id }));
|
|
1402
|
+
const tools = opts.rolePrompt ? {
|
|
1403
|
+
tools: opts.tools.tools,
|
|
1404
|
+
async buildSystemBlocks(ctx) {
|
|
1405
|
+
const inner = await opts.tools.buildSystemBlocks(ctx);
|
|
1406
|
+
const rolePrompt = opts.rolePrompt;
|
|
1407
|
+
const role = typeof rolePrompt === "function" ? await rolePrompt(ctx) : rolePrompt;
|
|
1408
|
+
if (!role || !role.trim()) return inner;
|
|
1409
|
+
return [{ text: role, cached: true }, ...inner];
|
|
1410
|
+
}
|
|
1411
|
+
} : opts.tools;
|
|
1394
1412
|
const runAgentBound = async ({
|
|
1395
1413
|
question,
|
|
1396
1414
|
ctx,
|
|
@@ -1413,11 +1431,11 @@ function configureAiChat(opts) {
|
|
|
1413
1431
|
modelIds: opts.vertex.modelIds,
|
|
1414
1432
|
location: location ?? settings.gcpLocation
|
|
1415
1433
|
});
|
|
1416
|
-
const systemBlocks = await
|
|
1434
|
+
const systemBlocks = await tools.buildSystemBlocks(ctx);
|
|
1417
1435
|
const input = {
|
|
1418
1436
|
question,
|
|
1419
1437
|
ctx,
|
|
1420
|
-
tools:
|
|
1438
|
+
tools: tools.tools,
|
|
1421
1439
|
systemBlocks,
|
|
1422
1440
|
provider,
|
|
1423
1441
|
maxToolTurns,
|
|
@@ -1433,7 +1451,7 @@ function configureAiChat(opts) {
|
|
|
1433
1451
|
persistence: opts.persistence,
|
|
1434
1452
|
auth: opts.auth,
|
|
1435
1453
|
scope: opts.scope,
|
|
1436
|
-
tools
|
|
1454
|
+
tools,
|
|
1437
1455
|
vertex: opts.vertex,
|
|
1438
1456
|
logger: opts.logger,
|
|
1439
1457
|
resolveNarratorId: opts.resolveNarratorId,
|