@firstlovecenter/ai-chat 0.1.0 → 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 +42 -9
- 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 +42 -9
- 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
|
@@ -37,11 +37,26 @@ async function runAgent(input) {
|
|
|
37
37
|
}
|
|
38
38
|
if (response.toolCalls.length === 0) {
|
|
39
39
|
if (presentPayload) break;
|
|
40
|
+
const text = (response.text ?? "").trim();
|
|
41
|
+
if (text) {
|
|
42
|
+
const topic = input.question.length > 80 ? input.question.slice(0, 77) + "..." : input.question;
|
|
43
|
+
presentPayload = {
|
|
44
|
+
blocks: [
|
|
45
|
+
{
|
|
46
|
+
kind: "paragraph_brief",
|
|
47
|
+
topic,
|
|
48
|
+
key_facts: [text]
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
raw_numbers: {}
|
|
52
|
+
};
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
40
55
|
return {
|
|
41
56
|
ok: false,
|
|
42
57
|
error: {
|
|
43
58
|
code: "AGENT_NO_PRESENT",
|
|
44
|
-
message: "The agent
|
|
59
|
+
message: "The agent produced no response. Try rephrasing."
|
|
45
60
|
},
|
|
46
61
|
transcript
|
|
47
62
|
};
|
|
@@ -129,11 +144,19 @@ var ClaudeToolProvider = class {
|
|
|
129
144
|
patchVertexBuildRequestSync(this.client);
|
|
130
145
|
}
|
|
131
146
|
async runTurn(input) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
+
});
|
|
137
160
|
const messages = toAnthropicMessages(input.messages);
|
|
138
161
|
const response = await this.client.messages.create({
|
|
139
162
|
model: this.modelId,
|
|
@@ -1376,6 +1399,16 @@ function configureAiChat(opts) {
|
|
|
1376
1399
|
];
|
|
1377
1400
|
const getProvider = (id) => toolProviders2.find((p) => p.id === id) ?? getToolProvider(id);
|
|
1378
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;
|
|
1379
1412
|
const runAgentBound = async ({
|
|
1380
1413
|
question,
|
|
1381
1414
|
ctx,
|
|
@@ -1398,11 +1431,11 @@ function configureAiChat(opts) {
|
|
|
1398
1431
|
modelIds: opts.vertex.modelIds,
|
|
1399
1432
|
location: location ?? settings.gcpLocation
|
|
1400
1433
|
});
|
|
1401
|
-
const systemBlocks = await
|
|
1434
|
+
const systemBlocks = await tools.buildSystemBlocks(ctx);
|
|
1402
1435
|
const input = {
|
|
1403
1436
|
question,
|
|
1404
1437
|
ctx,
|
|
1405
|
-
tools:
|
|
1438
|
+
tools: tools.tools,
|
|
1406
1439
|
systemBlocks,
|
|
1407
1440
|
provider,
|
|
1408
1441
|
maxToolTurns,
|
|
@@ -1418,7 +1451,7 @@ function configureAiChat(opts) {
|
|
|
1418
1451
|
persistence: opts.persistence,
|
|
1419
1452
|
auth: opts.auth,
|
|
1420
1453
|
scope: opts.scope,
|
|
1421
|
-
tools
|
|
1454
|
+
tools,
|
|
1422
1455
|
vertex: opts.vertex,
|
|
1423
1456
|
logger: opts.logger,
|
|
1424
1457
|
resolveNarratorId: opts.resolveNarratorId,
|