@economic/agents 0.0.1-beta.2 → 0.0.1-beta.4
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 +27 -0
- package/dist/index.d.mts +3 -2
- package/dist/index.mjs +86 -32
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -339,6 +339,33 @@ export const datetimeSkill: Skill = {
|
|
|
339
339
|
|
|
340
340
|
---
|
|
341
341
|
|
|
342
|
+
## Surfacing source URLs from tools
|
|
343
|
+
|
|
344
|
+
Any tool can surface source URLs into the message stream by including a `sources` array in its return value. `buildLLMParams` automatically detects this and emits `source-url` stream parts that the playground's Sources block renders.
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
execute: async ({ query }) => {
|
|
348
|
+
const data = await fetchResults(query);
|
|
349
|
+
return {
|
|
350
|
+
results: data.results, // LLM receives full content
|
|
351
|
+
sources: data.results.map(r => ({ url: r.url, title: r.title })), // rendered as source links
|
|
352
|
+
};
|
|
353
|
+
},
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
The `sources` array is picked up by a built-in `experimental_transform` inside `buildLLMParams` — no agent changes, no writer passed to the tool, no additional wiring. The LLM continues to receive the full result object including `sources`. The transform fires on every `tool-result` stream part and emits a `source` part for each entry.
|
|
357
|
+
|
|
358
|
+
Each source entry shape:
|
|
359
|
+
|
|
360
|
+
| Field | Type | Required | Description |
|
|
361
|
+
| ------- | -------- | -------- | ----------------------- |
|
|
362
|
+
| `url` | `string` | Yes | The URL to link to. |
|
|
363
|
+
| `title` | `string` | No | Display name in the UI. |
|
|
364
|
+
|
|
365
|
+
The playground's `UIMessageRenderer` collects all `source-url` parts from a message and displays them in a collapsible **Sources** block above the response text.
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
342
369
|
## Compaction
|
|
343
370
|
|
|
344
371
|
When `fastModel` is set on the agent class, compaction runs automatically before each turn:
|
package/dist/index.d.mts
CHANGED
|
@@ -16,8 +16,9 @@ interface Skill {
|
|
|
16
16
|
description: string;
|
|
17
17
|
/**
|
|
18
18
|
* Guidance text for this skill — e.g. rate limits, preferred patterns,
|
|
19
|
-
* when to use each tool.
|
|
20
|
-
*
|
|
19
|
+
* when to use each tool. Injected into the `## Tools` section of the
|
|
20
|
+
* system prompt via `buildSystemPrompt` in `llm.ts` whenever this skill
|
|
21
|
+
* is loaded.
|
|
21
22
|
*/
|
|
22
23
|
guidance?: string;
|
|
23
24
|
tools: ToolSet;
|
package/dist/index.mjs
CHANGED
|
@@ -40,12 +40,7 @@ function buildActivateSkillDescription(skills) {
|
|
|
40
40
|
].join("\n");
|
|
41
41
|
}
|
|
42
42
|
function buildAvailableSkillList(skills) {
|
|
43
|
-
return
|
|
44
|
-
"**Loading More Tools:**",
|
|
45
|
-
"Use `activate_skill` to load these categories (BE PROACTIVE on requesting tools based on the user's request AND you DON'T need to mention that you are loading more tools):",
|
|
46
|
-
"",
|
|
47
|
-
skills.map((s) => `**${s.name}**: ${s.description}`).join("\n")
|
|
48
|
-
].join("\n");
|
|
43
|
+
return skills.map((s) => `**${s.name}**: ${s.description}`).join("\n");
|
|
49
44
|
}
|
|
50
45
|
const LIST_CAPABILITIES_DESCRIPTION = "List all tools currently available to you, which skills are loaded, and which can still be loaded. Call this when the user asks about your capabilities or what you can do.";
|
|
51
46
|
/**
|
|
@@ -95,20 +90,7 @@ function createSkills(config) {
|
|
|
95
90
|
return [`**${skill.name}**\n${skill.guidance}`];
|
|
96
91
|
});
|
|
97
92
|
if (sections.length === 0) return "";
|
|
98
|
-
return
|
|
99
|
-
"**Loaded skill instructions**",
|
|
100
|
-
"The following skills are currently active. Apply their instructions when using the corresponding tools.",
|
|
101
|
-
"",
|
|
102
|
-
sections.join("\n\n")
|
|
103
|
-
].join("\n");
|
|
104
|
-
}
|
|
105
|
-
function getSystem() {
|
|
106
|
-
const guidance = getLoadedGuidance();
|
|
107
|
-
return [
|
|
108
|
-
config.systemPrompt,
|
|
109
|
-
availableSkillList,
|
|
110
|
-
guidance
|
|
111
|
-
].filter(Boolean).join("\n\n");
|
|
93
|
+
return sections.join("\n\n");
|
|
112
94
|
}
|
|
113
95
|
allTools[ACTIVATE_SKILL] = tool({
|
|
114
96
|
description: buildActivateSkillDescription(skills),
|
|
@@ -155,17 +137,14 @@ function createSkills(config) {
|
|
|
155
137
|
}
|
|
156
138
|
});
|
|
157
139
|
const prepareStep = async () => {
|
|
158
|
-
return {
|
|
159
|
-
activeTools: getActiveToolNames(),
|
|
160
|
-
...config.systemPrompt !== void 0 && { system: getSystem() }
|
|
161
|
-
};
|
|
140
|
+
return { activeTools: getActiveToolNames() };
|
|
162
141
|
};
|
|
163
142
|
return {
|
|
164
143
|
tools: allTools,
|
|
165
144
|
activeTools: getActiveToolNames(),
|
|
166
145
|
prepareStep,
|
|
146
|
+
availableSkillList,
|
|
167
147
|
getLoadedGuidance,
|
|
168
|
-
getSystem,
|
|
169
148
|
getLoadedSkills() {
|
|
170
149
|
return [...loadedSkills];
|
|
171
150
|
}
|
|
@@ -304,6 +283,80 @@ async function compactIfNeeded(messages, model, tailSize) {
|
|
|
304
283
|
//#endregion
|
|
305
284
|
//#region src/server/llm.ts
|
|
306
285
|
/**
|
|
286
|
+
* Composes the full system prompt from its three parts: the consumer's base
|
|
287
|
+
* string, the static skill roster, and the dynamic loaded-skill guidance.
|
|
288
|
+
*
|
|
289
|
+
* The full shape, at a glance:
|
|
290
|
+
*
|
|
291
|
+
* {base}
|
|
292
|
+
*
|
|
293
|
+
* ## Tools
|
|
294
|
+
*
|
|
295
|
+
* Use `activate_skill` to load these skills (BE PROACTIVE on requesting
|
|
296
|
+
* tools based on the user's request AND you DON'T need to mention that you
|
|
297
|
+
* are loading more tools):
|
|
298
|
+
*
|
|
299
|
+
* **{name}**: {description}
|
|
300
|
+
* ...
|
|
301
|
+
*
|
|
302
|
+
* **Loaded skill instructions**
|
|
303
|
+
* The following skills are currently active. Apply their instructions when
|
|
304
|
+
* using the corresponding tools.
|
|
305
|
+
*
|
|
306
|
+
* **{name}**
|
|
307
|
+
* {guidance body}
|
|
308
|
+
*/
|
|
309
|
+
function buildSystemPrompt(basePrompt, availableSkillList, loadedGuidance) {
|
|
310
|
+
let prompt = `${basePrompt}
|
|
311
|
+
|
|
312
|
+
## Tools
|
|
313
|
+
|
|
314
|
+
### Loading More Tools:
|
|
315
|
+
Use \`activate_skill\` to load these skills (BE PROACTIVE on requesting tools based on the user's request AND you DON'T need to mention that you are loading more tools):
|
|
316
|
+
|
|
317
|
+
${availableSkillList}`;
|
|
318
|
+
if (loadedGuidance) prompt += `
|
|
319
|
+
|
|
320
|
+
### Loaded skill instructions:
|
|
321
|
+
The following skills are currently active. Apply their instructions when using the corresponding tools.
|
|
322
|
+
|
|
323
|
+
${loadedGuidance}`;
|
|
324
|
+
return prompt.trim();
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Convention: tools can include a `sources` array in their return value to surface
|
|
328
|
+
* source URLs in the message stream. This transform detects it automatically.
|
|
329
|
+
*
|
|
330
|
+
* ```typescript
|
|
331
|
+
* // In any tool's execute function:
|
|
332
|
+
* return { myContent: "...", sources: [{ url: "https://...", title: "Page title" }] };
|
|
333
|
+
* ```
|
|
334
|
+
*/
|
|
335
|
+
function buildSourcesTransform(additional) {
|
|
336
|
+
const sourcesTransform = () => new TransformStream({ transform(chunk, controller) {
|
|
337
|
+
controller.enqueue(chunk);
|
|
338
|
+
if (chunk.type !== "tool-result") return;
|
|
339
|
+
const output = chunk.output;
|
|
340
|
+
if (!output || typeof output !== "object" || Array.isArray(output)) return;
|
|
341
|
+
const sources = output.sources;
|
|
342
|
+
if (!Array.isArray(sources)) return;
|
|
343
|
+
for (const source of sources) {
|
|
344
|
+
if (!source || typeof source !== "object") continue;
|
|
345
|
+
const s = source;
|
|
346
|
+
if (typeof s.url !== "string") continue;
|
|
347
|
+
controller.enqueue({
|
|
348
|
+
type: "source",
|
|
349
|
+
sourceType: "url",
|
|
350
|
+
id: crypto.randomUUID(),
|
|
351
|
+
url: s.url,
|
|
352
|
+
...typeof s.title === "string" ? { title: s.title } : {}
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
} });
|
|
356
|
+
if (!additional) return sourcesTransform;
|
|
357
|
+
return [sourcesTransform, ...Array.isArray(additional) ? additional : [additional]];
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
307
360
|
* Builds the parameter object for a Vercel AI SDK `streamText` or `generateText` call.
|
|
308
361
|
*
|
|
309
362
|
* Handles message conversion, optional compaction, skill wiring (`activate_skill`,
|
|
@@ -318,33 +371,34 @@ async function compactIfNeeded(messages, model, tailSize) {
|
|
|
318
371
|
* ```
|
|
319
372
|
*/
|
|
320
373
|
async function buildLLMParams(config) {
|
|
321
|
-
const { options, messages, activeSkills = [], skills, fastModel, maxMessagesBeforeCompaction, ...rest } = config;
|
|
374
|
+
const { options, messages, activeSkills = [], skills, fastModel, maxMessagesBeforeCompaction, experimental_transform, ...rest } = config;
|
|
322
375
|
const rawMessages = await convertToModelMessages(messages);
|
|
323
376
|
const processedMessages = fastModel && maxMessagesBeforeCompaction !== void 0 ? await compactIfNeeded(rawMessages, fastModel, maxMessagesBeforeCompaction) : rawMessages;
|
|
377
|
+
const composedTransform = buildSourcesTransform(experimental_transform);
|
|
324
378
|
const baseParams = {
|
|
325
379
|
...rest,
|
|
380
|
+
experimental_transform: composedTransform,
|
|
326
381
|
messages: processedMessages,
|
|
327
382
|
experimental_context: options?.body,
|
|
328
383
|
abortSignal: options?.abortSignal,
|
|
329
384
|
stopWhen: rest.stopWhen ?? stepCountIs(20)
|
|
330
385
|
};
|
|
331
386
|
if (!skills?.length) return baseParams;
|
|
387
|
+
const base = typeof rest.system === "string" ? rest.system : void 0;
|
|
332
388
|
const skillsCtx = createSkills({
|
|
333
389
|
tools: rest.tools ?? {},
|
|
334
390
|
skills,
|
|
335
|
-
initialLoadedSkills: activeSkills
|
|
336
|
-
systemPrompt: typeof rest.system === "string" ? rest.system : void 0
|
|
391
|
+
initialLoadedSkills: activeSkills
|
|
337
392
|
});
|
|
338
393
|
const prepareStep = async (stepOptions) => {
|
|
339
|
-
const skillsResult = await skillsCtx.prepareStep(stepOptions) ?? {};
|
|
340
394
|
return {
|
|
341
|
-
activeTools:
|
|
342
|
-
system:
|
|
395
|
+
activeTools: (await skillsCtx.prepareStep(stepOptions) ?? {}).activeTools ?? [],
|
|
396
|
+
system: buildSystemPrompt(base, skillsCtx.availableSkillList, skillsCtx.getLoadedGuidance())
|
|
343
397
|
};
|
|
344
398
|
};
|
|
345
399
|
return {
|
|
346
400
|
...baseParams,
|
|
347
|
-
system: skillsCtx.
|
|
401
|
+
system: buildSystemPrompt(base, skillsCtx.availableSkillList, skillsCtx.getLoadedGuidance()),
|
|
348
402
|
tools: skillsCtx.tools,
|
|
349
403
|
activeTools: skillsCtx.activeTools,
|
|
350
404
|
prepareStep
|