@bitbaum/ai-kit 1.4.0 → 1.4.1
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/dist/capability/classify.d.ts +4 -3
- package/dist/capability/classify.js +30 -9
- package/dist/complete.d.ts +30 -2
- package/dist/index.d.ts +1 -1
- package/package.json +4 -4
- package/src/capability/classify.ts +30 -9
- package/src/complete.ts +24 -2
- package/src/index.ts +1 -0
|
@@ -23,9 +23,10 @@ import type { Classification } from "./types.js";
|
|
|
23
23
|
/**
|
|
24
24
|
* Does this error body explicitly say the model cannot do tools?
|
|
25
25
|
*
|
|
26
|
-
* Conservative on purpose — see the header.
|
|
27
|
-
*
|
|
28
|
-
*
|
|
26
|
+
* Conservative on purpose — see the header. Both failures are real and neither
|
|
27
|
+
* is free: a false positive downgrades a working model for a reason nobody can
|
|
28
|
+
* see, and a false negative leaves the caller repeating a request it will
|
|
29
|
+
* never learn from. Match on "names tools AND negates support", nothing looser.
|
|
29
30
|
*/
|
|
30
31
|
export declare function saysToolsUnsupported(body: string): boolean;
|
|
31
32
|
export type ToolAttempt = {
|
|
@@ -4,29 +4,50 @@
|
|
|
4
4
|
* Every entry names tools or functions explicitly. Deliberately absent:
|
|
5
5
|
* "invalid request", "bad parameter", "unsupported" on its own — each of those
|
|
6
6
|
* appears in vendor 400s for a dozen unrelated reasons, and a match on one of
|
|
7
|
-
* them would silently disable a working model.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* them would silently disable a working model.
|
|
8
|
+
*
|
|
9
|
+
* But "when in doubt, record nothing" is only cheap for a caller that can
|
|
10
|
+
* retry freely, and the real caller cannot. A caller that sends definitions
|
|
11
|
+
* ALSO drops whatever prose fallback it has, because definitions are supposed
|
|
12
|
+
* to replace it — so a refusal this list fails to recognise costs a whole turn
|
|
13
|
+
* in which the model can neither call a tool nor be told how to act without
|
|
14
|
+
* one, and it costs that turn EVERY turn, forever, because nothing is ever
|
|
15
|
+
* learned. A missed phrasing is not one wasted request; it is a permanently
|
|
16
|
+
* mute model.
|
|
17
|
+
*
|
|
18
|
+
* So the bar is: does the vendor NAME tools or functions, and NEGATE support?
|
|
19
|
+
* Both halves, explicitly. Everything meeting that bar belongs here, including
|
|
20
|
+
* the boring grammatical variants — plural, `are`, `cannot use` — which is
|
|
21
|
+
* where the first real gap was found (`tools are not supported by this model`
|
|
22
|
+
* matched nothing at all).
|
|
10
23
|
*/
|
|
11
24
|
const TOOLS_UNSUPPORTED_PATTERNS = [
|
|
12
|
-
/tool[\s_-]?(use|call|calls|calling)\s+(is\s+)?(not|un)[\s_-]?support/i,
|
|
25
|
+
/tool[\s_-]?(use|call|calls|calling)\s+(is\s+|are\s+)?(not|un)[\s_-]?support/i,
|
|
13
26
|
/does\s+not\s+support\s+tool/i,
|
|
14
27
|
/doesn'?t\s+support\s+tool/i,
|
|
15
28
|
/no\s+support\s+for\s+tool/i,
|
|
16
|
-
/function[\s_-]?call(ing)?\s+(is\s+)?(not|un)[\s_-]?support/i,
|
|
29
|
+
/function[\s_-]?call(ing)?\s+(is\s+|are\s+)?(not|un)[\s_-]?support/i,
|
|
17
30
|
/does\s+not\s+support\s+function/i,
|
|
18
31
|
/doesn'?t\s+support\s+function/i,
|
|
19
32
|
/model\s+.{0,60}?\s+does\s+not\s+support\s+(the\s+)?(`?tools`?|`?functions`?)/i,
|
|
20
33
|
/unsupported\s+parameter:?\s*'?"?tools?"?'?/i,
|
|
21
34
|
/unknown\s+(field|parameter):?\s*'?"?tools?"?'?/i,
|
|
22
|
-
|
|
35
|
+
// "tools are not supported", "tool is not valid", "functions are unsupported".
|
|
36
|
+
// The singular-and-`is` form of this was already here; the plural-and-`are`
|
|
37
|
+
// form is the one vendors actually write, and it matched nothing.
|
|
38
|
+
/`?(tools?|functions?)`?\s+(is|are)\s+(not\s+(a\s+)?(valid|supported|allowed|available)|unsupported)/i,
|
|
39
|
+
// "this model cannot use tools" / "can't call functions".
|
|
40
|
+
/(cannot|can'?t|is\s+unable\s+to)\s+(use|call|handle|execute)\s+(`?tools?`?|`?functions?`?)/i,
|
|
41
|
+
// "no tool support", "without function support".
|
|
42
|
+
/no\s+(`?tools?`?|`?functions?`?)\s+support/i,
|
|
23
43
|
];
|
|
24
44
|
/**
|
|
25
45
|
* Does this error body explicitly say the model cannot do tools?
|
|
26
46
|
*
|
|
27
|
-
* Conservative on purpose — see the header.
|
|
28
|
-
*
|
|
29
|
-
*
|
|
47
|
+
* Conservative on purpose — see the header. Both failures are real and neither
|
|
48
|
+
* is free: a false positive downgrades a working model for a reason nobody can
|
|
49
|
+
* see, and a false negative leaves the caller repeating a request it will
|
|
50
|
+
* never learn from. Match on "names tools AND negates support", nothing looser.
|
|
30
51
|
*/
|
|
31
52
|
export function saysToolsUnsupported(body) {
|
|
32
53
|
if (!body)
|
package/dist/complete.d.ts
CHANGED
|
@@ -59,10 +59,38 @@ import { type Env, type Link, type Provider } from "./chain.js";
|
|
|
59
59
|
import type { HealthTracker } from "./health.js";
|
|
60
60
|
import { type RateLimitKind } from "./limits.js";
|
|
61
61
|
import { type QuotaReading } from "./meter.js";
|
|
62
|
-
/**
|
|
62
|
+
/**
|
|
63
|
+
* A piece of a message, for the models that accept more than text.
|
|
64
|
+
*
|
|
65
|
+
* The same shape every provider here already speaks, because it is OpenAI's —
|
|
66
|
+
* `image_url.url` takes a `data:` URL or an `https:` one.
|
|
67
|
+
*/
|
|
68
|
+
export type ContentPart = {
|
|
69
|
+
type: "text";
|
|
70
|
+
text: string;
|
|
71
|
+
} | {
|
|
72
|
+
type: "image_url";
|
|
73
|
+
image_url: {
|
|
74
|
+
url: string;
|
|
75
|
+
detail?: "auto" | "low" | "high";
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* One message in the OpenAI chat-completions shape every provider here speaks.
|
|
80
|
+
*
|
|
81
|
+
* `content` accepts parts as well as a string because the implementation
|
|
82
|
+
* always carried them: `callLink` forwards `messages` into the request body
|
|
83
|
+
* untouched, so a multimodal message has worked at runtime since the first
|
|
84
|
+
* release while the type insisted it could not. A consumer that needed to send
|
|
85
|
+
* a screenshot therefore had to cast around our own type — and a cast written
|
|
86
|
+
* to work around a library is a thing the next consumer copies.
|
|
87
|
+
*
|
|
88
|
+
* Backward compatible by construction: every existing caller passes a string,
|
|
89
|
+
* and a string is still a `ChatMessage["content"]`.
|
|
90
|
+
*/
|
|
63
91
|
export interface ChatMessage {
|
|
64
92
|
role: "system" | "user" | "assistant" | "tool";
|
|
65
|
-
content: string;
|
|
93
|
+
content: string | ContentPart[];
|
|
66
94
|
/** Present on `role: "tool"` replies; passed through untouched. */
|
|
67
95
|
tool_call_id?: string;
|
|
68
96
|
name?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
export { type Provider, type Env, type Link, type CostVerdict, providerModels, withEnvPrefix, freeChain, modelCost, modelCostAt, paidModelsIn, dayCapacityTokens, usableChain, chainFrom, } from "./chain.js";
|
|
53
53
|
export { type CatalogVerdict, type CheckCatalogOptions, checkCatalog, hasRot, deadProviders, catalogReport, } from "./catalog.js";
|
|
54
54
|
export { type ChainAttemptFailure, type TryChainOptions, ChainExhaustedError, tryChain, } from "./attempt.js";
|
|
55
|
-
export { type ChatMessage, type ToolCall, type CompleteOptions, type CompleteResult, LinkFailure, complete, linkId, } from "./complete.js";
|
|
55
|
+
export { type ChatMessage, type ContentPart, type ToolCall, type CompleteOptions, type CompleteResult, LinkFailure, complete, linkId, } from "./complete.js";
|
|
56
56
|
export { type HealthStatus, type Health, type HealthTrackerOptions, type HealthTracker, createHealthTracker, } from "./health.js";
|
|
57
57
|
export { type LivenessResult, type LivenessOptions, type LivenessProbe, type AiHealthHandlerOptions, createLivenessProbe, createAiHealthHandler, } from "./liveness.js";
|
|
58
58
|
export { type RateLimitKind, classifyRateLimit, retryAfterSeconds, humanizeWait, rateLimitMessage, } from "./limits.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitbaum/ai-kit",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Mao Nakamoto",
|
|
6
6
|
"homepage": "https://github.com/bitbaum/ai-kit#readme",
|
|
@@ -90,12 +90,12 @@
|
|
|
90
90
|
},
|
|
91
91
|
"devDependencies": {
|
|
92
92
|
"@eslint/js": "^10.0.1",
|
|
93
|
-
"@types/node": "^26.
|
|
94
|
-
"eslint": "^10.
|
|
93
|
+
"@types/node": "^26.5.0",
|
|
94
|
+
"eslint": "^10.10.0",
|
|
95
95
|
"globals": "^17.12.0",
|
|
96
96
|
"prettier": "3.9.6",
|
|
97
97
|
"typescript": "^6.0.3",
|
|
98
|
-
"typescript-eslint": "^8.
|
|
98
|
+
"typescript-eslint": "^8.70.0"
|
|
99
99
|
},
|
|
100
100
|
"dependencies": {
|
|
101
101
|
"ai-forms": "^0.1.2"
|
|
@@ -27,30 +27,51 @@ import type { Classification } from "./types.js";
|
|
|
27
27
|
* Every entry names tools or functions explicitly. Deliberately absent:
|
|
28
28
|
* "invalid request", "bad parameter", "unsupported" on its own — each of those
|
|
29
29
|
* appears in vendor 400s for a dozen unrelated reasons, and a match on one of
|
|
30
|
-
* them would silently disable a working model.
|
|
31
|
-
*
|
|
32
|
-
*
|
|
30
|
+
* them would silently disable a working model.
|
|
31
|
+
*
|
|
32
|
+
* But "when in doubt, record nothing" is only cheap for a caller that can
|
|
33
|
+
* retry freely, and the real caller cannot. A caller that sends definitions
|
|
34
|
+
* ALSO drops whatever prose fallback it has, because definitions are supposed
|
|
35
|
+
* to replace it — so a refusal this list fails to recognise costs a whole turn
|
|
36
|
+
* in which the model can neither call a tool nor be told how to act without
|
|
37
|
+
* one, and it costs that turn EVERY turn, forever, because nothing is ever
|
|
38
|
+
* learned. A missed phrasing is not one wasted request; it is a permanently
|
|
39
|
+
* mute model.
|
|
40
|
+
*
|
|
41
|
+
* So the bar is: does the vendor NAME tools or functions, and NEGATE support?
|
|
42
|
+
* Both halves, explicitly. Everything meeting that bar belongs here, including
|
|
43
|
+
* the boring grammatical variants — plural, `are`, `cannot use` — which is
|
|
44
|
+
* where the first real gap was found (`tools are not supported by this model`
|
|
45
|
+
* matched nothing at all).
|
|
33
46
|
*/
|
|
34
47
|
const TOOLS_UNSUPPORTED_PATTERNS: RegExp[] = [
|
|
35
|
-
/tool[\s_-]?(use|call|calls|calling)\s+(is\s+)?(not|un)[\s_-]?support/i,
|
|
48
|
+
/tool[\s_-]?(use|call|calls|calling)\s+(is\s+|are\s+)?(not|un)[\s_-]?support/i,
|
|
36
49
|
/does\s+not\s+support\s+tool/i,
|
|
37
50
|
/doesn'?t\s+support\s+tool/i,
|
|
38
51
|
/no\s+support\s+for\s+tool/i,
|
|
39
|
-
/function[\s_-]?call(ing)?\s+(is\s+)?(not|un)[\s_-]?support/i,
|
|
52
|
+
/function[\s_-]?call(ing)?\s+(is\s+|are\s+)?(not|un)[\s_-]?support/i,
|
|
40
53
|
/does\s+not\s+support\s+function/i,
|
|
41
54
|
/doesn'?t\s+support\s+function/i,
|
|
42
55
|
/model\s+.{0,60}?\s+does\s+not\s+support\s+(the\s+)?(`?tools`?|`?functions`?)/i,
|
|
43
56
|
/unsupported\s+parameter:?\s*'?"?tools?"?'?/i,
|
|
44
57
|
/unknown\s+(field|parameter):?\s*'?"?tools?"?'?/i,
|
|
45
|
-
|
|
58
|
+
// "tools are not supported", "tool is not valid", "functions are unsupported".
|
|
59
|
+
// The singular-and-`is` form of this was already here; the plural-and-`are`
|
|
60
|
+
// form is the one vendors actually write, and it matched nothing.
|
|
61
|
+
/`?(tools?|functions?)`?\s+(is|are)\s+(not\s+(a\s+)?(valid|supported|allowed|available)|unsupported)/i,
|
|
62
|
+
// "this model cannot use tools" / "can't call functions".
|
|
63
|
+
/(cannot|can'?t|is\s+unable\s+to)\s+(use|call|handle|execute)\s+(`?tools?`?|`?functions?`?)/i,
|
|
64
|
+
// "no tool support", "without function support".
|
|
65
|
+
/no\s+(`?tools?`?|`?functions?`?)\s+support/i,
|
|
46
66
|
];
|
|
47
67
|
|
|
48
68
|
/**
|
|
49
69
|
* Does this error body explicitly say the model cannot do tools?
|
|
50
70
|
*
|
|
51
|
-
* Conservative on purpose — see the header.
|
|
52
|
-
*
|
|
53
|
-
*
|
|
71
|
+
* Conservative on purpose — see the header. Both failures are real and neither
|
|
72
|
+
* is free: a false positive downgrades a working model for a reason nobody can
|
|
73
|
+
* see, and a false negative leaves the caller repeating a request it will
|
|
74
|
+
* never learn from. Match on "names tools AND negates support", nothing looser.
|
|
54
75
|
*/
|
|
55
76
|
export function saysToolsUnsupported(body: string): boolean {
|
|
56
77
|
if (!body) return false;
|
package/src/complete.ts
CHANGED
|
@@ -63,10 +63,32 @@ import { classifyRateLimit, retryAfterSeconds, type RateLimitKind } from "./limi
|
|
|
63
63
|
import { readQuota, readingFromRefusal, type QuotaReading } from "./meter.js";
|
|
64
64
|
import { parseTextToolCalls, stripToolCallLines, toolNamesFrom } from "./tool-protocol.js";
|
|
65
65
|
|
|
66
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* A piece of a message, for the models that accept more than text.
|
|
68
|
+
*
|
|
69
|
+
* The same shape every provider here already speaks, because it is OpenAI's —
|
|
70
|
+
* `image_url.url` takes a `data:` URL or an `https:` one.
|
|
71
|
+
*/
|
|
72
|
+
export type ContentPart =
|
|
73
|
+
| { type: "text"; text: string }
|
|
74
|
+
| { type: "image_url"; image_url: { url: string; detail?: "auto" | "low" | "high" } };
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* One message in the OpenAI chat-completions shape every provider here speaks.
|
|
78
|
+
*
|
|
79
|
+
* `content` accepts parts as well as a string because the implementation
|
|
80
|
+
* always carried them: `callLink` forwards `messages` into the request body
|
|
81
|
+
* untouched, so a multimodal message has worked at runtime since the first
|
|
82
|
+
* release while the type insisted it could not. A consumer that needed to send
|
|
83
|
+
* a screenshot therefore had to cast around our own type — and a cast written
|
|
84
|
+
* to work around a library is a thing the next consumer copies.
|
|
85
|
+
*
|
|
86
|
+
* Backward compatible by construction: every existing caller passes a string,
|
|
87
|
+
* and a string is still a `ChatMessage["content"]`.
|
|
88
|
+
*/
|
|
67
89
|
export interface ChatMessage {
|
|
68
90
|
role: "system" | "user" | "assistant" | "tool";
|
|
69
|
-
content: string;
|
|
91
|
+
content: string | ContentPart[];
|
|
70
92
|
/** Present on `role: "tool"` replies; passed through untouched. */
|
|
71
93
|
tool_call_id?: string;
|
|
72
94
|
name?: string;
|