@absolutejs/ai 0.6.0 → 0.6.2
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/CHANGELOG.md +13 -0
- package/README.md +15 -0
- package/changelog.json +42 -0
- package/dist/ai/index.js +107 -4
- package/dist/ai/index.js.map +10 -8
- package/dist/ai/models/index.js +12 -1
- package/dist/ai/models/index.js.map +3 -3
- package/dist/ai/providers/anthropic.js +16 -3
- package/dist/ai/providers/anthropic.js.map +4 -4
- package/dist/ai/providers/gemini.js +16 -3
- package/dist/ai/providers/gemini.js.map +4 -4
- package/dist/ai/providers/openai.js +26 -3
- package/dist/ai/providers/openai.js.map +5 -5
- package/dist/ai/providers/openaiCompatible.js +26 -3
- package/dist/ai/providers/openaiCompatible.js.map +5 -5
- package/dist/ai/providers/openaiResponses.js +26 -3
- package/dist/ai/providers/openaiResponses.js.map +5 -5
- package/dist/ai/providers/openrouter.js +26 -3
- package/dist/ai/providers/openrouter.js.map +5 -5
- package/dist/ai/ui/index.js +75 -1
- package/dist/ai/ui/index.js.map +7 -5
- package/dist/src/ai/streamAIWithTools.d.ts +2 -0
- package/dist/src/ai/ui/backgroundWork.d.ts +28 -0
- package/dist/src/ai/ui/catalog.d.ts +2 -0
- package/dist/src/ai/ui/formSubmission.d.ts +11 -0
- package/dist/src/ai/ui/index.d.ts +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,19 @@ This file is generated by `absolute-changelog` from the entries in
|
|
|
6
6
|
`changelog/`. Edit an entry, not this file — and add new ones under
|
|
7
7
|
`changelog/unreleased/`.
|
|
8
8
|
|
|
9
|
+
## 0.6.2 — 2026-10-22
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Add stopAfterToolTurn to complete sibling tool actions before a UI handoff and retain error recovery without an unnecessary model turn** (`streamAIWithTools`, `StreamAIWithToolsOptions`)
|
|
14
|
+
- **Support current Sonnet 5.5, Haiku 5.5, GPT-6.1 Sol and GPT-6 Luna reasoning and input capacity** (`MODEL_REASONING`, `openaiResponses`, `anthropic`)
|
|
15
|
+
|
|
16
|
+
## 0.6.1 — 2026-10-20
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **Add typed forms and shared task next actions for top-up continuation** (`FormField`, `formCard`, `parseFormSpec`, `prepareFormSubmission`, `FormSubmission`, `BackgroundWorkCard`, `backgroundWorkNextAction`)
|
|
21
|
+
|
|
9
22
|
## 0.6.0 — 2026-10-20
|
|
10
23
|
|
|
11
24
|
### Added
|
package/README.md
CHANGED
|
@@ -575,3 +575,18 @@ can prevent `done`, and no adapter can reconstruct usage the provider never sent
|
|
|
575
575
|
`onUsage` still reports only complete usage, once per instrumented stream. Keep
|
|
576
576
|
credit reservations separate from charges and reconcile unknown outcomes in the
|
|
577
577
|
host application. The package does not invent token counts or decide billing policy.
|
|
578
|
+
|
|
579
|
+
### Typed forms and background work
|
|
580
|
+
|
|
581
|
+
Form fields support `valueType` (`string`, `number`, `integer`, `boolean`). For a
|
|
582
|
+
numeric dropdown use `type: "select", options: ["3", "10"], valueType: "integer"`.
|
|
583
|
+
All hosts should call `prepareFormSubmission(form, values)` before invoking the
|
|
584
|
+
bound tool: it returns typed input or field errors without discarding answers.
|
|
585
|
+
The host must still validate the complete tool schema and apply authorization.
|
|
586
|
+
Legacy dropdowns retain string values; migrate numeric fields explicitly.
|
|
587
|
+
|
|
588
|
+
`BackgroundWorkCard` and `backgroundWorkNextAction` provide framework-neutral
|
|
589
|
+
progress/CTA semantics. A funding pause offers Add credits; only confirmed
|
|
590
|
+
funding changes it to Continue research. Completed work offers View results;
|
|
591
|
+
uncertain work offers Check status, never replay. The host persists the work ID,
|
|
592
|
+
polls or subscribes to actual progress, and binds actions to authenticated routes.
|
package/changelog.json
CHANGED
|
@@ -2,6 +2,48 @@
|
|
|
2
2
|
"contract": 1,
|
|
3
3
|
"name": "@absolutejs/ai",
|
|
4
4
|
"releases": [
|
|
5
|
+
{
|
|
6
|
+
"version": "0.6.2",
|
|
7
|
+
"date": "2026-10-22",
|
|
8
|
+
"changes": [
|
|
9
|
+
{
|
|
10
|
+
"kind": "added",
|
|
11
|
+
"summary": "Add stopAfterToolTurn to complete sibling tool actions before a UI handoff and retain error recovery without an unnecessary model turn",
|
|
12
|
+
"symbols": [
|
|
13
|
+
"streamAIWithTools",
|
|
14
|
+
"StreamAIWithToolsOptions"
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"kind": "added",
|
|
19
|
+
"summary": "Support current Sonnet 5.5, Haiku 5.5, GPT-6.1 Sol and GPT-6 Luna reasoning and input capacity",
|
|
20
|
+
"symbols": [
|
|
21
|
+
"MODEL_REASONING",
|
|
22
|
+
"openaiResponses",
|
|
23
|
+
"anthropic"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"changes": [
|
|
30
|
+
{
|
|
31
|
+
"kind": "added",
|
|
32
|
+
"summary": "Add typed forms and shared task next actions for top-up continuation",
|
|
33
|
+
"symbols": [
|
|
34
|
+
"FormField",
|
|
35
|
+
"formCard",
|
|
36
|
+
"parseFormSpec",
|
|
37
|
+
"prepareFormSubmission",
|
|
38
|
+
"FormSubmission",
|
|
39
|
+
"BackgroundWorkCard",
|
|
40
|
+
"backgroundWorkNextAction"
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
"date": "2026-10-20",
|
|
45
|
+
"version": "0.6.1"
|
|
46
|
+
},
|
|
5
47
|
{
|
|
6
48
|
"changes": [
|
|
7
49
|
{
|
package/dist/ai/index.js
CHANGED
|
@@ -2009,6 +2009,7 @@ var streamAIWithTools = async function* (options) {
|
|
|
2009
2009
|
maxTurns = DEFAULT_STREAM_TOOL_MAX_TURNS,
|
|
2010
2010
|
provider,
|
|
2011
2011
|
stopAfterTools,
|
|
2012
|
+
stopAfterToolTurn,
|
|
2012
2013
|
validateInput,
|
|
2013
2014
|
contextPolicy,
|
|
2014
2015
|
toolChoice,
|
|
@@ -2139,6 +2140,8 @@ var streamAIWithTools = async function* (options) {
|
|
|
2139
2140
|
messages.push({ content: blocks, role: "assistant" });
|
|
2140
2141
|
const resultBlocks = [];
|
|
2141
2142
|
let terminalToolSucceeded = false;
|
|
2143
|
+
let toolFailed = false;
|
|
2144
|
+
let handoffToolSucceeded = false;
|
|
2142
2145
|
for (const call of pending) {
|
|
2143
2146
|
yield {
|
|
2144
2147
|
id: call.id,
|
|
@@ -2148,6 +2151,7 @@ var streamAIWithTools = async function* (options) {
|
|
|
2148
2151
|
};
|
|
2149
2152
|
const startedAt = Date.now();
|
|
2150
2153
|
const { ok, output } = await executeToolHandler(tools, call);
|
|
2154
|
+
toolFailed ||= !ok;
|
|
2151
2155
|
const result = toolOutputText(output);
|
|
2152
2156
|
executedKeys.add(toolCallKey(call));
|
|
2153
2157
|
yield {
|
|
@@ -2164,8 +2168,10 @@ var streamAIWithTools = async function* (options) {
|
|
|
2164
2168
|
terminalToolSucceeded = true;
|
|
2165
2169
|
break;
|
|
2166
2170
|
}
|
|
2171
|
+
if (ok && stopAfterToolTurn?.includes(call.name))
|
|
2172
|
+
handoffToolSucceeded = true;
|
|
2167
2173
|
}
|
|
2168
|
-
if (terminalToolSucceeded)
|
|
2174
|
+
if (terminalToolSucceeded || handoffToolSucceeded && !toolFailed)
|
|
2169
2175
|
break;
|
|
2170
2176
|
messages.push({ content: resultBlocks, role: "user" });
|
|
2171
2177
|
if (turn >= Math.max(1, maxTurns)) {
|
|
@@ -2751,6 +2757,11 @@ var parseFormField = (raw) => {
|
|
|
2751
2757
|
if (!name || !label || !type)
|
|
2752
2758
|
return null;
|
|
2753
2759
|
const field = { label, name, type };
|
|
2760
|
+
if (raw.valueType !== undefined) {
|
|
2761
|
+
if (!["string", "number", "integer", "boolean"].includes(String(raw.valueType)))
|
|
2762
|
+
return null;
|
|
2763
|
+
field.valueType = raw.valueType;
|
|
2764
|
+
}
|
|
2754
2765
|
const placeholder = cleanString(raw.placeholder, LABEL_MAX_CHARS);
|
|
2755
2766
|
if (placeholder)
|
|
2756
2767
|
field.placeholder = placeholder;
|
|
@@ -3122,6 +3133,11 @@ var formCard = {
|
|
|
3122
3133
|
items: { type: "string" },
|
|
3123
3134
|
type: "array"
|
|
3124
3135
|
},
|
|
3136
|
+
valueType: {
|
|
3137
|
+
enum: ["string", "number", "integer", "boolean"],
|
|
3138
|
+
type: "string",
|
|
3139
|
+
description: "Submitted scalar type, especially integer for numeric select options"
|
|
3140
|
+
},
|
|
3125
3141
|
placeholder: { type: "string" },
|
|
3126
3142
|
required: { type: "boolean" },
|
|
3127
3143
|
type: { enum: [...FORM_FIELD_TYPES], type: "string" },
|
|
@@ -3628,6 +3644,68 @@ var renderChartSvg = (spec, options = {}) => {
|
|
|
3628
3644
|
const body = spec.type === "donut" ? donutSvg(spec, frame) : cartesianSvg(spec, frame);
|
|
3629
3645
|
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}" role="img" aria-label="${escapeXml(spec.title)}" font-family="${FONT}"><rect width="${width}" height="${height}" fill="${theme.surface}" rx="12"/>${spec.type === "donut" ? headerSvg({ ...spec, series: [] }, frame) : headerSvg(spec, frame)}${body}</svg>`;
|
|
3630
3646
|
};
|
|
3647
|
+
// src/ai/ui/formSubmission.ts
|
|
3648
|
+
var prepareFormSubmission = (form, values) => {
|
|
3649
|
+
const input = { ...form.submit.input };
|
|
3650
|
+
const errors = {};
|
|
3651
|
+
for (const field of form.fields) {
|
|
3652
|
+
if (["__proto__", "constructor", "prototype"].includes(field.name)) {
|
|
3653
|
+
errors.form = "Invalid field name";
|
|
3654
|
+
continue;
|
|
3655
|
+
}
|
|
3656
|
+
const raw = values[field.name];
|
|
3657
|
+
if (raw === undefined || raw === null || typeof raw === "string" && !raw.trim()) {
|
|
3658
|
+
if (field.required)
|
|
3659
|
+
errors[field.name] = "This field is required";
|
|
3660
|
+
continue;
|
|
3661
|
+
}
|
|
3662
|
+
if (field.type === "select" && !field.options?.includes(String(raw))) {
|
|
3663
|
+
errors[field.name] = "Choose one of the available options";
|
|
3664
|
+
continue;
|
|
3665
|
+
}
|
|
3666
|
+
const type = field.valueType ?? (field.type === "number" ? "number" : field.type === "checkbox" ? "boolean" : "string");
|
|
3667
|
+
let value = raw;
|
|
3668
|
+
if (type === "number" || type === "integer") {
|
|
3669
|
+
value = typeof raw === "number" ? raw : typeof raw === "string" && /^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/iu.test(raw.trim()) ? Number(raw) : NaN;
|
|
3670
|
+
if (typeof value !== "number" || !Number.isFinite(value) || type === "integer" && !Number.isSafeInteger(value)) {
|
|
3671
|
+
errors[field.name] = type === "integer" ? "Enter a whole number" : "Enter a number";
|
|
3672
|
+
continue;
|
|
3673
|
+
}
|
|
3674
|
+
} else if (type === "boolean") {
|
|
3675
|
+
if (![true, false, "true", "false"].includes(raw)) {
|
|
3676
|
+
errors[field.name] = "Choose yes or no";
|
|
3677
|
+
continue;
|
|
3678
|
+
}
|
|
3679
|
+
value = raw === true || raw === "true";
|
|
3680
|
+
if (field.type === "checkbox" && field.required && !value) {
|
|
3681
|
+
errors[field.name] = "This field is required";
|
|
3682
|
+
continue;
|
|
3683
|
+
}
|
|
3684
|
+
} else {
|
|
3685
|
+
if (typeof raw !== "string") {
|
|
3686
|
+
errors[field.name] = "Enter text";
|
|
3687
|
+
continue;
|
|
3688
|
+
}
|
|
3689
|
+
value = field.type === "password" ? raw : raw.trim();
|
|
3690
|
+
}
|
|
3691
|
+
input[field.name] = value;
|
|
3692
|
+
}
|
|
3693
|
+
return Object.keys(errors).length ? { ok: false, errors } : { ok: true, input };
|
|
3694
|
+
};
|
|
3695
|
+
// src/ai/ui/backgroundWork.ts
|
|
3696
|
+
var backgroundWorkNextAction = (work) => {
|
|
3697
|
+
if (!work.workId || !Number.isSafeInteger(work.totalSteps) || work.totalSteps < 1 || !Number.isSafeInteger(work.completedSteps) || work.completedSteps < 0 || work.completedSteps > work.totalSteps)
|
|
3698
|
+
throw new Error("Invalid background work progress");
|
|
3699
|
+
if (work.status === "completed")
|
|
3700
|
+
return { kind: "view_results", label: "View results" };
|
|
3701
|
+
if (work.status === "paused" && work.pauseReason === "funding")
|
|
3702
|
+
return work.fundingConfirmed ? { kind: "continue", label: "Continue research" } : { kind: "top_up", label: "Add credits" };
|
|
3703
|
+
if (work.status === "unknown")
|
|
3704
|
+
return { kind: "check_status", label: "Check status" };
|
|
3705
|
+
if (work.hasResults)
|
|
3706
|
+
return { kind: "view_results", label: "View saved results" };
|
|
3707
|
+
return { kind: "check_status", label: "Check status" };
|
|
3708
|
+
};
|
|
3631
3709
|
// src/ai/conversationManager.ts
|
|
3632
3710
|
var NOT_FOUND2 = -1;
|
|
3633
3711
|
var TITLE_MAX_LENGTH2 = 80;
|
|
@@ -4603,6 +4681,16 @@ var LIMITS = {
|
|
|
4603
4681
|
contextWindowTokens: 1050000,
|
|
4604
4682
|
maxOutputTokens: 128000
|
|
4605
4683
|
},
|
|
4684
|
+
"gpt-6.1-sol": {
|
|
4685
|
+
maxInputTokens: 922000,
|
|
4686
|
+
contextWindowTokens: 1050000,
|
|
4687
|
+
maxOutputTokens: 128000
|
|
4688
|
+
},
|
|
4689
|
+
"gpt-6-luna": {
|
|
4690
|
+
maxInputTokens: 922000,
|
|
4691
|
+
contextWindowTokens: 1050000,
|
|
4692
|
+
maxOutputTokens: 128000
|
|
4693
|
+
},
|
|
4606
4694
|
"gpt-6-astra": {
|
|
4607
4695
|
maxInputTokens: 922000,
|
|
4608
4696
|
contextWindowTokens: 1050000,
|
|
@@ -4923,6 +5011,17 @@ var mistral = profile("effort", ["none", "high"], "Minimal thinking or full reas
|
|
|
4923
5011
|
var qwen = profile("budget", ["none", "low", "medium", "high"], haiku.description, "https://help.aliyun.com/en/model-studio/deep-thinking");
|
|
4924
5012
|
var MODEL_REASONING = {
|
|
4925
5013
|
"anthropic:claude-opus-5-5": claude,
|
|
5014
|
+
"anthropic:claude-sonnet-5-5": claude,
|
|
5015
|
+
"anthropic:claude-haiku-5-5": claude,
|
|
5016
|
+
"openai:gpt-6.1-sol": {
|
|
5017
|
+
...gpt,
|
|
5018
|
+
efforts: ["low", "medium", "high", "xhigh", "max"],
|
|
5019
|
+
source: "https://developers.openai.com/api/docs/models/gpt-6.1-sol"
|
|
5020
|
+
},
|
|
5021
|
+
"openai:gpt-6-luna": {
|
|
5022
|
+
...gpt,
|
|
5023
|
+
source: "https://developers.openai.com/api/docs/models/gpt-6-luna"
|
|
5024
|
+
},
|
|
4926
5025
|
"anthropic:claude-fable-5-1": claude,
|
|
4927
5026
|
"anthropic:claude-opus-5": claude,
|
|
4928
5027
|
"anthropic:claude-sonnet-5": claude,
|
|
@@ -4973,7 +5072,8 @@ var ANTHROPIC_EFFORT = [
|
|
|
4973
5072
|
/fable-5/,
|
|
4974
5073
|
/mythos-5/,
|
|
4975
5074
|
/opus-5/,
|
|
4976
|
-
/sonnet-5
|
|
5075
|
+
/sonnet-5/,
|
|
5076
|
+
/haiku-5/
|
|
4977
5077
|
];
|
|
4978
5078
|
var ANTHROPIC_ADAPTIVE_ONLY = [];
|
|
4979
5079
|
var ANTHROPIC_LEGACY_THINKING = [
|
|
@@ -4991,7 +5091,8 @@ var ANTHROPIC_NO_SAMPLING = [
|
|
|
4991
5091
|
/fable-5/,
|
|
4992
5092
|
/mythos-5/,
|
|
4993
5093
|
/opus-5/,
|
|
4994
|
-
/sonnet-5
|
|
5094
|
+
/sonnet-5/,
|
|
5095
|
+
/haiku-5/
|
|
4995
5096
|
];
|
|
4996
5097
|
var ANTHROPIC_MAX_EFFORT = [
|
|
4997
5098
|
/opus-4-[678]/,
|
|
@@ -9160,6 +9261,7 @@ export {
|
|
|
9160
9261
|
aiChat,
|
|
9161
9262
|
alibaba,
|
|
9162
9263
|
anthropic,
|
|
9264
|
+
backgroundWorkNextAction,
|
|
9163
9265
|
chartCard,
|
|
9164
9266
|
choiceCard,
|
|
9165
9267
|
configureProviderResilience,
|
|
@@ -9223,6 +9325,7 @@ export {
|
|
|
9223
9325
|
planCard,
|
|
9224
9326
|
prepareAITextInput,
|
|
9225
9327
|
prepareAITextInputStep,
|
|
9328
|
+
prepareFormSubmission,
|
|
9226
9329
|
providerStatusPage,
|
|
9227
9330
|
remoteProvider,
|
|
9228
9331
|
renderChartSvg,
|
|
@@ -9243,5 +9346,5 @@ export {
|
|
|
9243
9346
|
xai
|
|
9244
9347
|
};
|
|
9245
9348
|
|
|
9246
|
-
//# debugId=
|
|
9349
|
+
//# debugId=577984AE55403A4664756E2164756E21
|
|
9247
9350
|
//# sourceMappingURL=index.js.map
|