@columenlabs/lumen-ai 0.1.201 → 0.1.202

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.
@@ -16,7 +16,7 @@ import { Think, } from '@cloudflare/think';
16
16
  import { createCompactFunction } from 'agents/sessions';
17
17
  import { generateText } from 'ai';
18
18
  import { CONNECTOR_GROUP, checkConnectorUrl, connectorGroup, connectorId, connectorSource, } from './connectors.js';
19
- import { aiModel, planFallback, turnSettings } from './models.js';
19
+ import { aiModel, cachedTurn, planFallback, turnSettings } from './models.js';
20
20
  import { screenMapOf, screenNews, screenOutcomesOf, screenRules, screenTool, } from './screen.js';
21
21
  import { LAZY_AT, TOOL_SEARCH, lazyTools } from './search.js';
22
22
  import { chunkEvents, itemsOf } from './stream.js';
@@ -522,6 +522,7 @@ export class LumenAgent extends Think {
522
522
  this.#turn.lazy = lazy;
523
523
  }
524
524
  const turn = this.#turn;
525
+ const cached = cachedTurn(system, withLead(ctx.messages, `${this.#news()}${this.#screenNews()}${plan.lead ?? ''}`));
525
526
  return {
526
527
  model: this.#model(plan, () => {
527
528
  const fb = plan.fallback;
@@ -531,8 +532,9 @@ export class LumenAgent extends Think {
531
532
  turn.main = new UsageMeter(fb.model, fb.label, false);
532
533
  turn.meters.push(turn.main);
533
534
  }),
534
- instructions: system,
535
- messages: withLead(ctx.messages, `${this.#news()}${this.#screenNews()}${plan.lead ?? ''}`),
535
+ // Think types instructions as a string; the SDK it hands them to takes a system message too.
536
+ instructions: cached.instructions,
537
+ messages: cached.messages,
536
538
  tools,
537
539
  ...(lazy ? { activeTools: lazy.active() } : {}),
538
540
  ...(plan.first ? { toolChoice: { type: 'tool', toolName: plan.first } } : {}),
@@ -1,5 +1,5 @@
1
1
  import type { AssistantEffort } from '@columenlabs/lumen-ui-types/assistant';
2
- import type { JSONValue, LanguageModel } from 'ai';
2
+ import type { JSONValue, LanguageModel, ModelMessage, SystemModelMessage } from 'ai';
3
3
  import type { ModelDef } from '../assistant/models.js';
4
4
  /** The key a model runs on. `plan`: an OpenAI access token from a ChatGPT plan sign-in. */
5
5
  export interface ModelKey {
@@ -24,6 +24,14 @@ export interface AiModelOptions {
24
24
  export declare function planFetch(base?: typeof fetch, name?: string): typeof fetch;
25
25
  /** The AI SDK model a tier runs: its provider's client on the payer's key. */
26
26
  export declare function aiModel(model: ModelDef, key: ModelKey, o?: AiModelOptions): LanguageModel;
27
+ /**
28
+ * A turn's instructions and messages with Claude's two lasting cache breakpoints: the instructions
29
+ * as a system message that carries one, and the message before the newest person's message.
30
+ */
31
+ export declare function cachedTurn(instructions: string, messages: ModelMessage[]): {
32
+ instructions: SystemModelMessage;
33
+ messages: ModelMessage[];
34
+ };
27
35
  /** A tier's effort and output cap as a turn's settings, with Claude's prompt cache on. */
28
36
  export declare function turnSettings(model: ModelDef, o: {
29
37
  effort: AssistantEffort;
@@ -88,12 +88,40 @@ export function aiModel(model, key, o = {}) {
88
88
  return createOpenAI({ apiKey: key.apiKey, ...(f ? { fetch: f } : {}) }).responses(model.id);
89
89
  }
90
90
  /**
91
- * Claude's prompt cache: one breakpoint on the request, which Claude moves to the newest block on
92
- * each call, so a turn's later steps and the next turn read the tools, instructions and chat so
93
- * far from cache (a tenth of the input price) instead of paying for them again. Sent whatever the
94
- * tier's provider, so a plan's fallback to Claude caches too; other providers ignore it.
91
+ * Claude's prompt cache. The request's own breakpoint follows the newest block, so a turn's later
92
+ * steps read the steps before them. It alone never helps the next turn: the bearings put before
93
+ * the newest message are gone from it by then, so that prefix never comes again. `cachedTurn`
94
+ * adds two breakpoints that do: after the instructions (tools and instructions) and on the last
95
+ * message before the newest question (the chat so far). Sent whatever the tier's provider, so a
96
+ * plan's fallback to Claude caches too; other providers ignore it.
95
97
  */
96
98
  const CLAUDE_CACHE = { cacheControl: { type: 'ephemeral' } };
99
+ const CACHED = { anthropic: CLAUDE_CACHE };
100
+ /**
101
+ * A turn's instructions and messages with Claude's two lasting cache breakpoints: the instructions
102
+ * as a system message that carries one, and the message before the newest person's message.
103
+ */
104
+ export function cachedTurn(instructions, messages) {
105
+ let i = messages.length - 1;
106
+ while (i >= 0 && messages[i]?.role !== 'user')
107
+ i -= 1;
108
+ const before = i - 1;
109
+ const marked = before >= 0
110
+ ? messages.map((m, n) => n === before
111
+ ? {
112
+ ...m,
113
+ providerOptions: {
114
+ ...m.providerOptions,
115
+ anthropic: { ...m.providerOptions?.anthropic, ...CLAUDE_CACHE },
116
+ },
117
+ }
118
+ : m)
119
+ : messages;
120
+ return {
121
+ instructions: { role: 'system', content: instructions, providerOptions: CACHED },
122
+ messages: marked,
123
+ };
124
+ }
97
125
  /** A tier's effort and output cap as a turn's settings, with Claude's prompt cache on. */
98
126
  export function turnSettings(model, o) {
99
127
  const out = o.plan || !o.out ? {} : { maxOutputTokens: o.out };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@columenlabs/lumen-ai",
3
- "version": "0.1.201",
3
+ "version": "0.1.202",
4
4
  "description": "Lumen's assistant and agent parts: a chat assistant's server (Claude tiers, tools over MCP, the web, hand off, spend caps), serving an MCP server from a Worker, read-only tool sets and metered tool calls.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -52,8 +52,8 @@
52
52
  "@ai-sdk/openai": "4.0.108",
53
53
  "@anthropic-ai/sdk": "0.133.0",
54
54
  "@cloudflare/think": "0.23.0",
55
- "@columenlabs/lumen-ops": "0.1.201",
56
- "@columenlabs/lumen-ui-types": "0.1.201",
55
+ "@columenlabs/lumen-ops": "0.1.202",
56
+ "@columenlabs/lumen-ui-types": "0.1.202",
57
57
  "agents": "0.32.0",
58
58
  "ai": "7.0.162",
59
59
  "just-bash": "3.6.0",