@columenlabs/lumen-ai 0.1.145 → 0.1.146
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 +1 -0
- package/dist/assistant/engine.d.ts +17 -0
- package/dist/assistant/engine.js +62 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -102,6 +102,7 @@ app.all('/api/assistant/*', (c) => routes(c.req.raw));
|
|
|
102
102
|
- **Web.** `fetch` is a plain GET from the Worker (10 s, 2 MB, never the app's own hosts or private addresses). ScrapeQL's own tools join over its MCP API once an admin pastes a Cloudflare Access service token (`PUT /web/scrapeql`). A Worker on ScrapeQL's own Cloudflare account passes its service binding instead, `scrapeql: { url: 'https://scrapeql/mcp', fetch: env.SCRAPEQL.fetch.bind(env.SCRAPEQL) }`: no token is stored, the tools are always on and Settings shows the link with a Test.
|
|
103
103
|
- **Hand off and advice.** The `escalate` tool hands one task to a higher tier (the person sets next, any or only when asked); Anthropic's advisor tool lets a higher tier send a plan, at most two an answer.
|
|
104
104
|
- **Chats** stream as server-sent events (`POST /chat`); closing the request stops the answer and keeps what was said. Past 150k tokens, or on `/compact`, the history sent to the model becomes a summary. Run `assistant.purge()` once a day to drop chats 30 days after their last message.
|
|
105
|
+
- **Background work** an app runs for a person (a score, a summary) is `assistant.complete(caller, { task, system, prompt, maxTokens })`: one answer on the task's tier (`tasks`, so the person picks its model in Settings ▸ Models), no chat and no tools, billed to their payer and counted in their spend and allowance like a chat turn. It throws `AssistantError` `not_connected`, `app_cap` or `own_cap` when nobody can pay, so the caller waits and tries later.
|
|
105
106
|
|
|
106
107
|
### Commands, `@` and `#`
|
|
107
108
|
|
|
@@ -241,6 +241,23 @@ export declare class Assistant {
|
|
|
241
241
|
private loopClaude;
|
|
242
242
|
/** Swaps the history for a short summary. False with nothing to compact. */
|
|
243
243
|
private compact;
|
|
244
|
+
/**
|
|
245
|
+
* One prompt answered on a task's tier as the person: no chat, no tools. It is for background
|
|
246
|
+
* work an app runs for someone (a job's fit score, a summary), billed to their payer and capped
|
|
247
|
+
* like a chat turn, so it shows in their spend and allowance. Throws `AssistantError` with code
|
|
248
|
+
* `not_connected`, `app_cap` or `own_cap` when nobody can pay, or `upstream` when the model fails.
|
|
249
|
+
*/
|
|
250
|
+
complete(caller: Caller, o: {
|
|
251
|
+
task: string;
|
|
252
|
+
system: string;
|
|
253
|
+
prompt: string;
|
|
254
|
+
maxTokens?: number;
|
|
255
|
+
signal?: AbortSignal;
|
|
256
|
+
}): Promise<{
|
|
257
|
+
text: string;
|
|
258
|
+
usd: number;
|
|
259
|
+
model: string;
|
|
260
|
+
}>;
|
|
244
261
|
/** How the person's cards ended since the last answer, once each. */
|
|
245
262
|
private cardNews;
|
|
246
263
|
/** Starts a person's ChatGPT plan sign-in: the address to open in a new tab. */
|
package/dist/assistant/engine.js
CHANGED
|
@@ -1745,6 +1745,68 @@ export class Assistant {
|
|
|
1745
1745
|
emit('compacted', {});
|
|
1746
1746
|
return true;
|
|
1747
1747
|
}
|
|
1748
|
+
// ---------------------------------------------------------------- one-shot work
|
|
1749
|
+
/**
|
|
1750
|
+
* One prompt answered on a task's tier as the person: no chat, no tools. It is for background
|
|
1751
|
+
* work an app runs for someone (a job's fit score, a summary), billed to their payer and capped
|
|
1752
|
+
* like a chat turn, so it shows in their spend and allowance. Throws `AssistantError` with code
|
|
1753
|
+
* `not_connected`, `app_cap` or `own_cap` when nobody can pay, or `upstream` when the model fails.
|
|
1754
|
+
*/
|
|
1755
|
+
async complete(caller, o) {
|
|
1756
|
+
const s = await this.session(caller, await this.mainTier(caller, o.task));
|
|
1757
|
+
const { paying, wait } = await this.state.paying(caller.id, s.model.provider);
|
|
1758
|
+
if (!paying) {
|
|
1759
|
+
const code = wait ?? 'not_connected';
|
|
1760
|
+
throw new AssistantError(waitText(code, s.model.provider, !!this.o.allowance), code, 402);
|
|
1761
|
+
}
|
|
1762
|
+
const clients = this.clients(paying);
|
|
1763
|
+
const max = Math.min(o.maxTokens ?? 1000, 8000);
|
|
1764
|
+
let text = '';
|
|
1765
|
+
try {
|
|
1766
|
+
if (s.model.provider === 'openai' && clients.openai) {
|
|
1767
|
+
const res = await clients.openai.responses.create({
|
|
1768
|
+
model: s.model.id,
|
|
1769
|
+
instructions: o.system,
|
|
1770
|
+
input: o.prompt,
|
|
1771
|
+
store: false,
|
|
1772
|
+
max_output_tokens: max,
|
|
1773
|
+
}, o.signal ? { signal: o.signal } : undefined);
|
|
1774
|
+
const u = res.usage;
|
|
1775
|
+
if (u)
|
|
1776
|
+
s.meter.add({
|
|
1777
|
+
input_tokens: u.input_tokens - (u.input_tokens_details?.cached_tokens ?? 0),
|
|
1778
|
+
output_tokens: u.output_tokens,
|
|
1779
|
+
cache_read_input_tokens: u.input_tokens_details?.cached_tokens ?? 0,
|
|
1780
|
+
}, !!clients.openaiPlan);
|
|
1781
|
+
text = res.output_text ?? '';
|
|
1782
|
+
}
|
|
1783
|
+
else if (clients.anthropic) {
|
|
1784
|
+
const msg = await clients.anthropic.messages.create({
|
|
1785
|
+
model: s.model.id,
|
|
1786
|
+
max_tokens: max,
|
|
1787
|
+
system: o.system,
|
|
1788
|
+
messages: [{ role: 'user', content: o.prompt }],
|
|
1789
|
+
...effortParams(s.effort),
|
|
1790
|
+
}, o.signal ? { signal: o.signal } : undefined);
|
|
1791
|
+
s.meter.add(msg.usage);
|
|
1792
|
+
text = msg.content
|
|
1793
|
+
.filter((b) => b.type === 'text')
|
|
1794
|
+
.map((b) => b.text)
|
|
1795
|
+
.join('');
|
|
1796
|
+
}
|
|
1797
|
+
else
|
|
1798
|
+
throw new AssistantError(waitText('not_connected', s.model.provider), 'not_connected', 402);
|
|
1799
|
+
}
|
|
1800
|
+
catch (e) {
|
|
1801
|
+
if (e instanceof AssistantError)
|
|
1802
|
+
throw e;
|
|
1803
|
+
throw new AssistantError(`${s.model.label} did not answer: ${String(e?.message ?? e).slice(0, 200)}`, 'upstream', 502);
|
|
1804
|
+
}
|
|
1805
|
+
finally {
|
|
1806
|
+
await this.state.addSpend(caller.id, paying.payer, [s.meter.part()]);
|
|
1807
|
+
}
|
|
1808
|
+
return { text, usd: s.meter.usd, model: s.model.id };
|
|
1809
|
+
}
|
|
1748
1810
|
/** How the person's cards ended since the last answer, once each. */
|
|
1749
1811
|
cardNews(doc) {
|
|
1750
1812
|
const told = new Set(doc.told ?? []);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@columenlabs/lumen-ai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.146",
|
|
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",
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@anthropic-ai/sdk": "0.133.0",
|
|
44
|
-
"@columenlabs/lumen-ops": "0.1.
|
|
45
|
-
"@columenlabs/lumen-ui-types": "0.1.
|
|
44
|
+
"@columenlabs/lumen-ops": "0.1.146",
|
|
45
|
+
"@columenlabs/lumen-ui-types": "0.1.146",
|
|
46
46
|
"openai": "7.32.0",
|
|
47
47
|
"zod": "^4.3.6"
|
|
48
48
|
},
|