@economic/agents 0.0.1-alpha.18 → 0.0.1-alpha.19
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/README.md +48 -8
- package/dist/index.d.mts +39 -6
- package/dist/index.mjs +40 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ npm install @economic/agents ai @cloudflare/ai-chat
|
|
|
13
13
|
`@economic/agents` provides:
|
|
14
14
|
|
|
15
15
|
- **`AIChatAgent`** — an abstract Cloudflare Durable Object base class. Implement `onChatMessage`, call `this.buildLLMParams()`, and pass the result to `streamText` from the AI SDK.
|
|
16
|
+
- **`guard`** — optional TypeScript 5+ method decorator for `onChatMessage`. Runs your function with `options.body`; return a `Response` to short-circuit (e.g. auth), or nothing to continue.
|
|
16
17
|
- **`buildLLMParams`** — the standalone version of the above, for use outside of `AIChatAgent` or in custom agent implementations.
|
|
17
18
|
|
|
18
19
|
Skills and compaction are AI SDK concerns — they control what goes to the LLM. The CF layer is responsible for WebSockets, Durable Objects, and message persistence. These are kept separate.
|
|
@@ -121,6 +122,43 @@ Protected method on `AIChatAgent`. Wraps the standalone `buildLLMParams` functio
|
|
|
121
122
|
|
|
122
123
|
Config is everything accepted by the standalone `buildLLMParams` except `messages`, `activeSkills`, and `fastModel`.
|
|
123
124
|
|
|
125
|
+
### `guard`
|
|
126
|
+
|
|
127
|
+
Method decorator (TypeScript 5+ stage-3) for handlers shaped like `onChatMessage(onFinish, options?)`. Before your method runs, it calls your `GuardFn` with `options?.body` (the same custom body the client sends via `useAgentChat` / `body` on the chat request).
|
|
128
|
+
|
|
129
|
+
- Return **`undefined` / nothing** — the decorated method runs as usual.
|
|
130
|
+
- Return a **`Response`** — that response is returned immediately; `onChatMessage` is not called.
|
|
131
|
+
|
|
132
|
+
All policy (tokens, tiers, rate limits) lives in the guard function; the decorator only forwards `body` and branches on whether a `Response` was returned.
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
import { streamText } from "ai";
|
|
136
|
+
import { openai } from "@ai-sdk/openai";
|
|
137
|
+
import { AIChatAgent, guard, type GuardFn } from "@economic/agents";
|
|
138
|
+
|
|
139
|
+
const requireToken: GuardFn = async (body) => {
|
|
140
|
+
const token = body?.token;
|
|
141
|
+
if (typeof token !== "string" || !(await isValidToken(token))) {
|
|
142
|
+
return new Response("Unauthorized", { status: 401 });
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export class ChatAgent extends AIChatAgent<Env> {
|
|
147
|
+
protected fastModel = openai("gpt-4o-mini");
|
|
148
|
+
|
|
149
|
+
@guard(requireToken)
|
|
150
|
+
async onChatMessage(onFinish, options) {
|
|
151
|
+
const params = await this.buildLLMParams({
|
|
152
|
+
options,
|
|
153
|
+
onFinish,
|
|
154
|
+
model: openai("gpt-4o"),
|
|
155
|
+
system: "You are a helpful assistant.",
|
|
156
|
+
});
|
|
157
|
+
return streamText(params).toUIMessageStreamResponse();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
124
162
|
### `fastModel` property
|
|
125
163
|
|
|
126
164
|
Override `fastModel` on your subclass to enable automatic compaction and future background conversation summarization:
|
|
@@ -503,17 +541,19 @@ If `userId` is not set on the request body, the upsert is skipped and a `console
|
|
|
503
541
|
|
|
504
542
|
### Functions
|
|
505
543
|
|
|
506
|
-
| Export | Signature | Description
|
|
507
|
-
| ---------------- | -------------------------------------- |
|
|
508
|
-
| `
|
|
544
|
+
| Export | Signature | Description |
|
|
545
|
+
| ---------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------ |
|
|
546
|
+
| `guard` | `(guardFn: GuardFn)` | Method decorator: runs `guardFn` with `options.body`; a returned `Response` short-circuits the method. |
|
|
547
|
+
| `buildLLMParams` | `async (config) => Promise<LLMParams>` | Builds the full parameter object for `streamText` or `generateText`. |
|
|
509
548
|
|
|
510
549
|
### Types
|
|
511
550
|
|
|
512
|
-
| Export | Description
|
|
513
|
-
| ---------------------- |
|
|
514
|
-
| `
|
|
515
|
-
| `
|
|
516
|
-
| `
|
|
551
|
+
| Export | Description |
|
|
552
|
+
| ---------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
553
|
+
| `GuardFn` | `(body) => Response \| void \| Promise<...>`. Receives chat request `body`; return `Response` to block the turn. |
|
|
554
|
+
| `Skill` | A named group of tools with optional guidance. |
|
|
555
|
+
| `AgentContext<TBody>` | Request body type merged with `log`. Use as the type of `experimental_context`. |
|
|
556
|
+
| `BuildLLMParamsConfig` | Config type for the standalone `buildLLMParams` function. |
|
|
517
557
|
|
|
518
558
|
---
|
|
519
559
|
|
package/dist/index.d.mts
CHANGED
|
@@ -172,13 +172,46 @@ declare abstract class AIChatAgent<Env extends Cloudflare.Env = Cloudflare.Env>
|
|
|
172
172
|
}): Promise<void>;
|
|
173
173
|
}
|
|
174
174
|
//#endregion
|
|
175
|
-
//#region src/
|
|
175
|
+
//#region src/decorators/guard.d.ts
|
|
176
176
|
/**
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
177
|
+
* Function run before a guarded handler (e.g. `onChatMessage`). Receives the
|
|
178
|
+
* custom request body from chat options (`options.body`, same shape as
|
|
179
|
+
* `OnChatMessageOptions` from `@cloudflare/ai-chat`).
|
|
180
|
+
*
|
|
181
|
+
* Return a {@link Response} to short-circuit and skip the decorated method.
|
|
182
|
+
* Return nothing (or `undefined`) to allow the method to run.
|
|
183
|
+
*
|
|
184
|
+
* Typical uses include auth, rate limits, or feature flags — all logic lives here;
|
|
185
|
+
* the `guard` decorator only forwards `body` and handles the return shape.
|
|
186
|
+
*/
|
|
187
|
+
type GuardFn = (body: Record<string, unknown> | undefined) => Response | void | Promise<Response | void>;
|
|
188
|
+
/**
|
|
189
|
+
* Method decorator (TypeScript 5+ stage-3) that runs `guardFn` with the second
|
|
190
|
+
* argument's `body` (the chat request body). If `guardFn` returns a
|
|
191
|
+
* {@link Response}, that value is returned and the original method is not called.
|
|
192
|
+
*
|
|
193
|
+
* Intended for `onChatMessage(onFinish, options?)` on subclasses of
|
|
194
|
+
* `AIChatAgent`; `options` is read as `{ body?: Record<string, unknown> }`.
|
|
195
|
+
*
|
|
196
|
+
* @param guardFn - Called with `options?.body` before the method body.
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```ts
|
|
200
|
+
* const requireToken: GuardFn = async (body) => {
|
|
201
|
+
* if (!await isValidToken(body?.token)) {
|
|
202
|
+
* return new Response("Unauthorized", { status: 401 });
|
|
203
|
+
* }
|
|
204
|
+
* };
|
|
205
|
+
*
|
|
206
|
+
* class MyAgent extends AIChatAgent<Env> {
|
|
207
|
+
* @guard(requireToken)
|
|
208
|
+
* async onChatMessage(onFinish, options) {
|
|
209
|
+
* // ...
|
|
210
|
+
* }
|
|
211
|
+
* }
|
|
212
|
+
* ```
|
|
180
213
|
*/
|
|
181
|
-
declare
|
|
214
|
+
declare function guard(guardFn: GuardFn): (target: Function, _context: ClassMethodDecoratorContext) => (this: unknown, ...args: unknown[]) => Promise<unknown>;
|
|
182
215
|
//#endregion
|
|
183
216
|
//#region src/types.d.ts
|
|
184
217
|
/**
|
|
@@ -196,4 +229,4 @@ type AgentContext<TBody = Record<string, unknown>> = TBody & {
|
|
|
196
229
|
log: (message: string, payload?: Record<string, unknown>) => void | Promise<void>;
|
|
197
230
|
};
|
|
198
231
|
//#endregion
|
|
199
|
-
export { AIChatAgent, type AgentContext, type BuildLLMParamsConfig,
|
|
232
|
+
export { AIChatAgent, type AgentContext, type BuildLLMParamsConfig, type GuardFn, type Skill, buildLLMParams, guard };
|
package/dist/index.mjs
CHANGED
|
@@ -174,14 +174,6 @@ function filterEphemeralMessages(messages) {
|
|
|
174
174
|
}];
|
|
175
175
|
});
|
|
176
176
|
}
|
|
177
|
-
//#endregion
|
|
178
|
-
//#region src/features/compaction/index.ts
|
|
179
|
-
/**
|
|
180
|
-
* Number of recent messages to keep verbatim when compaction runs.
|
|
181
|
-
* Older messages beyond this count are summarised into a single system message.
|
|
182
|
-
* Used as the default when `maxMessagesBeforeCompaction` is not provided to `buildLLMParams`.
|
|
183
|
-
*/
|
|
184
|
-
const DEFAULT_MAX_MESSAGES_BEFORE_COMPACTION = 15;
|
|
185
177
|
const TOOL_RESULT_PREVIEW_CHARS = 200;
|
|
186
178
|
const SUMMARY_MAX_TOKENS = 4e3;
|
|
187
179
|
/**
|
|
@@ -3819,7 +3811,7 @@ var AIChatAgent = class extends AIChatAgent$1 {
|
|
|
3819
3811
|
const db = this.env.AGENT_DB;
|
|
3820
3812
|
if (!db) return null;
|
|
3821
3813
|
if (!this._userId) {
|
|
3822
|
-
console.error("[AIChatAgent]
|
|
3814
|
+
console.error("[AIChatAgent] Logging & conversation tracking skipped: userId not set on request body");
|
|
3823
3815
|
return null;
|
|
3824
3816
|
}
|
|
3825
3817
|
return {
|
|
@@ -3957,4 +3949,42 @@ var AIChatAgent = class extends AIChatAgent$1 {
|
|
|
3957
3949
|
}
|
|
3958
3950
|
};
|
|
3959
3951
|
//#endregion
|
|
3960
|
-
|
|
3952
|
+
//#region src/decorators/guard.ts
|
|
3953
|
+
/**
|
|
3954
|
+
* Method decorator (TypeScript 5+ stage-3) that runs `guardFn` with the second
|
|
3955
|
+
* argument's `body` (the chat request body). If `guardFn` returns a
|
|
3956
|
+
* {@link Response}, that value is returned and the original method is not called.
|
|
3957
|
+
*
|
|
3958
|
+
* Intended for `onChatMessage(onFinish, options?)` on subclasses of
|
|
3959
|
+
* `AIChatAgent`; `options` is read as `{ body?: Record<string, unknown> }`.
|
|
3960
|
+
*
|
|
3961
|
+
* @param guardFn - Called with `options?.body` before the method body.
|
|
3962
|
+
*
|
|
3963
|
+
* @example
|
|
3964
|
+
* ```ts
|
|
3965
|
+
* const requireToken: GuardFn = async (body) => {
|
|
3966
|
+
* if (!await isValidToken(body?.token)) {
|
|
3967
|
+
* return new Response("Unauthorized", { status: 401 });
|
|
3968
|
+
* }
|
|
3969
|
+
* };
|
|
3970
|
+
*
|
|
3971
|
+
* class MyAgent extends AIChatAgent<Env> {
|
|
3972
|
+
* @guard(requireToken)
|
|
3973
|
+
* async onChatMessage(onFinish, options) {
|
|
3974
|
+
* // ...
|
|
3975
|
+
* }
|
|
3976
|
+
* }
|
|
3977
|
+
* ```
|
|
3978
|
+
*/
|
|
3979
|
+
function guard(guardFn) {
|
|
3980
|
+
return function(target, _context) {
|
|
3981
|
+
return async function(...args) {
|
|
3982
|
+
const options = args[1];
|
|
3983
|
+
const result = await guardFn(options?.body);
|
|
3984
|
+
if (result instanceof Response) return result;
|
|
3985
|
+
return target.apply(this, args);
|
|
3986
|
+
};
|
|
3987
|
+
};
|
|
3988
|
+
}
|
|
3989
|
+
//#endregion
|
|
3990
|
+
export { AIChatAgent, buildLLMParams, guard };
|