@fgv/ts-extras 5.1.0-16 → 5.1.0-18
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/index.browser.js +2 -1
- package/dist/packlets/ai-assist/apiClient.js +570 -58
- package/dist/packlets/ai-assist/chatRequestBuilders.js +180 -0
- package/dist/packlets/ai-assist/index.js +4 -3
- package/dist/packlets/ai-assist/model.js +20 -3
- package/dist/packlets/ai-assist/registry.js +66 -10
- package/dist/packlets/ai-assist/sseParser.js +122 -0
- package/dist/packlets/ai-assist/streamingAdapters/anthropic.js +192 -0
- package/dist/packlets/ai-assist/streamingAdapters/common.js +77 -0
- package/dist/packlets/ai-assist/streamingAdapters/gemini.js +160 -0
- package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js +149 -0
- package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js +163 -0
- package/dist/packlets/ai-assist/streamingAdapters/proxy.js +157 -0
- package/dist/packlets/ai-assist/streamingClient.js +88 -0
- package/dist/packlets/conversion/converters.js +1 -1
- package/dist/packlets/crypto-utils/index.browser.js +2 -0
- package/dist/packlets/crypto-utils/index.js +2 -0
- package/dist/packlets/crypto-utils/keyPairAlgorithmParams.js +47 -0
- package/dist/packlets/crypto-utils/keystore/converters.js +101 -9
- package/dist/packlets/crypto-utils/keystore/index.js +1 -0
- package/dist/packlets/crypto-utils/keystore/keyStore.js +271 -46
- package/dist/packlets/crypto-utils/keystore/model.js +22 -1
- package/dist/packlets/crypto-utils/keystore/privateKeyStorage.js +21 -0
- package/dist/packlets/crypto-utils/model.js +5 -0
- package/dist/packlets/crypto-utils/nodeCryptoProvider.js +44 -1
- package/dist/packlets/zip-file-tree/zipFileTreeAccessors.js +2 -2
- package/dist/test/unit/crypto/keystore/inMemoryPrivateKeyStorage.js +78 -0
- package/dist/ts-extras.d.ts +1089 -37
- package/lib/index.browser.d.ts +2 -1
- package/lib/index.browser.js +3 -1
- package/lib/packlets/ai-assist/apiClient.d.ts +103 -1
- package/lib/packlets/ai-assist/apiClient.js +574 -58
- package/lib/packlets/ai-assist/chatRequestBuilders.d.ts +89 -0
- package/lib/packlets/ai-assist/chatRequestBuilders.js +189 -0
- package/lib/packlets/ai-assist/index.d.ts +4 -3
- package/lib/packlets/ai-assist/index.js +10 -1
- package/lib/packlets/ai-assist/model.d.ts +271 -2
- package/lib/packlets/ai-assist/model.js +21 -3
- package/lib/packlets/ai-assist/registry.d.ts +10 -1
- package/lib/packlets/ai-assist/registry.js +67 -11
- package/lib/packlets/ai-assist/sseParser.d.ts +45 -0
- package/lib/packlets/ai-assist/sseParser.js +127 -0
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts +18 -0
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.js +195 -0
- package/lib/packlets/ai-assist/streamingAdapters/common.d.ts +71 -0
- package/lib/packlets/ai-assist/streamingAdapters/common.js +81 -0
- package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts +19 -0
- package/lib/packlets/ai-assist/streamingAdapters/gemini.js +163 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts +18 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js +152 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts +19 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js +166 -0
- package/lib/packlets/ai-assist/streamingAdapters/proxy.d.ts +34 -0
- package/lib/packlets/ai-assist/streamingAdapters/proxy.js +160 -0
- package/lib/packlets/ai-assist/streamingClient.d.ts +33 -0
- package/lib/packlets/ai-assist/streamingClient.js +93 -0
- package/lib/packlets/conversion/converters.d.ts +1 -1
- package/lib/packlets/conversion/converters.js +1 -1
- package/lib/packlets/crypto-utils/index.browser.d.ts +1 -0
- package/lib/packlets/crypto-utils/index.browser.js +4 -1
- package/lib/packlets/crypto-utils/index.d.ts +1 -0
- package/lib/packlets/crypto-utils/index.js +4 -1
- package/lib/packlets/crypto-utils/keyPairAlgorithmParams.d.ts +39 -0
- package/lib/packlets/crypto-utils/keyPairAlgorithmParams.js +50 -0
- package/lib/packlets/crypto-utils/keystore/converters.d.ts +68 -6
- package/lib/packlets/crypto-utils/keystore/converters.js +100 -8
- package/lib/packlets/crypto-utils/keystore/index.d.ts +1 -0
- package/lib/packlets/crypto-utils/keystore/index.js +1 -0
- package/lib/packlets/crypto-utils/keystore/keyStore.d.ts +77 -9
- package/lib/packlets/crypto-utils/keystore/keyStore.js +271 -46
- package/lib/packlets/crypto-utils/keystore/model.d.ts +238 -19
- package/lib/packlets/crypto-utils/keystore/model.js +24 -2
- package/lib/packlets/crypto-utils/keystore/privateKeyStorage.d.ts +50 -0
- package/lib/packlets/crypto-utils/keystore/privateKeyStorage.js +22 -0
- package/lib/packlets/crypto-utils/model.d.ts +38 -0
- package/lib/packlets/crypto-utils/model.js +6 -1
- package/lib/packlets/crypto-utils/nodeCryptoProvider.d.ts +26 -1
- package/lib/packlets/crypto-utils/nodeCryptoProvider.js +43 -0
- package/lib/packlets/zip-file-tree/zipFileTreeAccessors.d.ts +2 -2
- package/lib/packlets/zip-file-tree/zipFileTreeAccessors.js +2 -2
- package/package.json +7 -7
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// Copyright (c) 2026 Erik Fortune
|
|
2
|
+
//
|
|
3
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
// in the Software without restriction, including without limitation the rights
|
|
6
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
// furnished to do so, subject to the following conditions:
|
|
9
|
+
//
|
|
10
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
// copies or substantial portions of the Software.
|
|
12
|
+
//
|
|
13
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
// SOFTWARE.
|
|
20
|
+
/**
|
|
21
|
+
* Per-format chat request shape builders. Shared between the synchronous
|
|
22
|
+
* (`apiClient.ts`) and streaming (`streamingClient.ts`) paths so the wire
|
|
23
|
+
* shapes stay consistent.
|
|
24
|
+
*
|
|
25
|
+
* @packageDocumentation
|
|
26
|
+
*/
|
|
27
|
+
import { toDataUrl } from './model';
|
|
28
|
+
/**
|
|
29
|
+
* Builds the messages array from prompt + optional head/tail messages.
|
|
30
|
+
* The caller supplies the user content (string for text-only, parts array
|
|
31
|
+
* for vision prompts) since the parts shape differs by format.
|
|
32
|
+
*
|
|
33
|
+
* @internal
|
|
34
|
+
*/
|
|
35
|
+
export function buildMessages(systemPrompt, userContent, options) {
|
|
36
|
+
const messages = [
|
|
37
|
+
{ role: 'system', content: systemPrompt }
|
|
38
|
+
];
|
|
39
|
+
if (options === null || options === void 0 ? void 0 : options.head) {
|
|
40
|
+
for (const msg of options.head) {
|
|
41
|
+
messages.push({ role: msg.role, content: msg.content });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
messages.push({ role: 'user', content: userContent });
|
|
45
|
+
if (options === null || options === void 0 ? void 0 : options.tail) {
|
|
46
|
+
for (const msg of options.tail) {
|
|
47
|
+
messages.push({ role: msg.role, content: msg.content });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return messages;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Builds the user content for OpenAI Chat Completions when attachments are
|
|
54
|
+
* present. Returns a string when there are no attachments.
|
|
55
|
+
*
|
|
56
|
+
* @internal
|
|
57
|
+
*/
|
|
58
|
+
export function buildOpenAiChatUserContent(prompt) {
|
|
59
|
+
if (prompt.attachments.length === 0) {
|
|
60
|
+
return prompt.user;
|
|
61
|
+
}
|
|
62
|
+
return [
|
|
63
|
+
{ type: 'text', text: prompt.user },
|
|
64
|
+
...prompt.attachments.map((att) => ({
|
|
65
|
+
type: 'image_url',
|
|
66
|
+
image_url: Object.assign({ url: toDataUrl(att) }, (att.detail !== undefined ? { detail: att.detail } : {}))
|
|
67
|
+
}))
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Builds the user content for OpenAI / xAI Responses API when attachments
|
|
72
|
+
* are present. Responses API uses `input_text` / `input_image` part types,
|
|
73
|
+
* distinct from Chat Completions' `text` / `image_url`.
|
|
74
|
+
*
|
|
75
|
+
* @internal
|
|
76
|
+
*/
|
|
77
|
+
export function buildOpenAiResponsesUserContent(prompt) {
|
|
78
|
+
if (prompt.attachments.length === 0) {
|
|
79
|
+
return prompt.user;
|
|
80
|
+
}
|
|
81
|
+
return [
|
|
82
|
+
{ type: 'input_text', text: prompt.user },
|
|
83
|
+
...prompt.attachments.map((att) => (Object.assign({ type: 'input_image', image_url: toDataUrl(att) }, (att.detail !== undefined ? { detail: att.detail } : {}))))
|
|
84
|
+
];
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Builds the user-message content for Anthropic when attachments are present.
|
|
88
|
+
*
|
|
89
|
+
* @internal
|
|
90
|
+
*/
|
|
91
|
+
export function buildAnthropicUserContent(prompt) {
|
|
92
|
+
if (prompt.attachments.length === 0) {
|
|
93
|
+
return prompt.user;
|
|
94
|
+
}
|
|
95
|
+
return [
|
|
96
|
+
{ type: 'text', text: prompt.user },
|
|
97
|
+
...prompt.attachments.map((att) => ({
|
|
98
|
+
type: 'image',
|
|
99
|
+
source: {
|
|
100
|
+
type: 'base64',
|
|
101
|
+
media_type: att.mimeType,
|
|
102
|
+
data: att.base64
|
|
103
|
+
}
|
|
104
|
+
}))
|
|
105
|
+
];
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Builds the Gemini `parts` array for the user turn, including any image
|
|
109
|
+
* attachments as `inlineData` parts.
|
|
110
|
+
*
|
|
111
|
+
* @internal
|
|
112
|
+
*/
|
|
113
|
+
export function buildGeminiUserParts(prompt) {
|
|
114
|
+
const parts = [{ text: prompt.user }];
|
|
115
|
+
for (const att of prompt.attachments) {
|
|
116
|
+
parts.push({ inlineData: { mimeType: att.mimeType, data: att.base64 } });
|
|
117
|
+
}
|
|
118
|
+
return parts;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Builds the Anthropic messages array, weaving any `head` messages between
|
|
122
|
+
* implicit system + the prompt's user message and appending `tail` messages
|
|
123
|
+
* after. System messages are filtered out (Anthropic uses a top-level system
|
|
124
|
+
* field).
|
|
125
|
+
*
|
|
126
|
+
* @internal
|
|
127
|
+
*/
|
|
128
|
+
export function buildAnthropicMessages(prompt, options) {
|
|
129
|
+
const messages = [];
|
|
130
|
+
if (options === null || options === void 0 ? void 0 : options.head) {
|
|
131
|
+
for (const msg of options.head) {
|
|
132
|
+
if (msg.role !== 'system') {
|
|
133
|
+
messages.push({ role: msg.role, content: msg.content });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
messages.push({ role: 'user', content: buildAnthropicUserContent(prompt) });
|
|
138
|
+
if (options === null || options === void 0 ? void 0 : options.tail) {
|
|
139
|
+
for (const msg of options.tail) {
|
|
140
|
+
if (msg.role !== 'system') {
|
|
141
|
+
messages.push({ role: msg.role, content: msg.content });
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return messages;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Builds the Gemini `contents` array, weaving any `head` messages before the
|
|
149
|
+
* prompt's user parts and appending `tail` messages after. System messages
|
|
150
|
+
* are filtered out (Gemini uses a top-level systemInstruction field) and
|
|
151
|
+
* assistant roles are mapped to Gemini's `model` role.
|
|
152
|
+
*
|
|
153
|
+
* @internal
|
|
154
|
+
*/
|
|
155
|
+
export function buildGeminiContents(prompt, options) {
|
|
156
|
+
const contents = [];
|
|
157
|
+
if (options === null || options === void 0 ? void 0 : options.head) {
|
|
158
|
+
for (const msg of options.head) {
|
|
159
|
+
if (msg.role !== 'system') {
|
|
160
|
+
contents.push({
|
|
161
|
+
role: msg.role === 'assistant' ? 'model' : msg.role,
|
|
162
|
+
parts: [{ text: msg.content }]
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
contents.push({ role: 'user', parts: buildGeminiUserParts(prompt) });
|
|
168
|
+
if (options === null || options === void 0 ? void 0 : options.tail) {
|
|
169
|
+
for (const msg of options.tail) {
|
|
170
|
+
if (msg.role !== 'system') {
|
|
171
|
+
contents.push({
|
|
172
|
+
role: msg.role === 'assistant' ? 'model' : msg.role,
|
|
173
|
+
parts: [{ text: msg.content }]
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return contents;
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=chatRequestBuilders.js.map
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* AI assist packlet - provider registry, prompt class, settings, and API client.
|
|
3
3
|
* @packageDocumentation
|
|
4
4
|
*/
|
|
5
|
-
export { AiPrompt, DEFAULT_AI_ASSIST, allModelSpecKeys, MODEL_SPEC_BASE_KEY, resolveModel } from './model';
|
|
6
|
-
export { allProviderIds, getProviderDescriptors, getProviderDescriptor } from './registry';
|
|
7
|
-
export { callProviderCompletion, callProxiedCompletion } from './apiClient';
|
|
5
|
+
export { AiPrompt, DEFAULT_AI_ASSIST, allModelSpecKeys, MODEL_SPEC_BASE_KEY, resolveModel, toDataUrl } from './model';
|
|
6
|
+
export { allProviderIds, getProviderDescriptors, getProviderDescriptor, DEFAULT_MODEL_CAPABILITY_CONFIG } from './registry';
|
|
7
|
+
export { callProviderCompletion, callProxiedCompletion, callProviderImageGeneration, callProxiedImageGeneration, callProviderListModels, callProxiedListModels } from './apiClient';
|
|
8
|
+
export { callProviderCompletionStream, callProxiedCompletionStream } from './streamingClient';
|
|
8
9
|
export { aiProviderId, aiServerToolType, aiWebSearchToolConfig, aiServerToolConfig, aiToolEnablement, aiAssistProviderConfig, aiAssistSettings, modelSpecKey, modelSpec } from './converters';
|
|
9
10
|
export { resolveEffectiveTools } from './toolFormats';
|
|
10
11
|
//# sourceMappingURL=index.js.map
|
|
@@ -17,6 +17,15 @@
|
|
|
17
17
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
18
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
19
|
// SOFTWARE.
|
|
20
|
+
/**
|
|
21
|
+
* Formats an {@link IAiImageData} as a `data:` URL suitable for browser display.
|
|
22
|
+
* @param image - The image to format
|
|
23
|
+
* @returns A `data:<mime>;base64,<data>` URL string
|
|
24
|
+
* @public
|
|
25
|
+
*/
|
|
26
|
+
export function toDataUrl(image) {
|
|
27
|
+
return `data:${image.mimeType};base64,${image.base64}`;
|
|
28
|
+
}
|
|
20
29
|
// ============================================================================
|
|
21
30
|
// AiPrompt
|
|
22
31
|
// ============================================================================
|
|
@@ -26,13 +35,21 @@
|
|
|
26
35
|
* @public
|
|
27
36
|
*/
|
|
28
37
|
export class AiPrompt {
|
|
29
|
-
constructor(user, system) {
|
|
38
|
+
constructor(user, system, attachments) {
|
|
30
39
|
this.system = system;
|
|
31
40
|
this.user = user;
|
|
41
|
+
this.attachments = attachments !== null && attachments !== void 0 ? attachments : [];
|
|
32
42
|
}
|
|
33
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* Combined single-string version (user + system joined) for copy/paste.
|
|
45
|
+
* When attachments are present, includes a sentinel noting they aren't
|
|
46
|
+
* part of the copied text.
|
|
47
|
+
*/
|
|
34
48
|
get combined() {
|
|
35
|
-
|
|
49
|
+
const sentinel = this.attachments.length > 0
|
|
50
|
+
? `\n\n[${this.attachments.length} image attachment(s) — not included in copied text]`
|
|
51
|
+
: '';
|
|
52
|
+
return `${this.user}${sentinel}\n\n${this.system}`;
|
|
36
53
|
}
|
|
37
54
|
}
|
|
38
55
|
/**
|
|
@@ -39,7 +39,9 @@ const BUILTIN_PROVIDERS = [
|
|
|
39
39
|
baseUrl: '',
|
|
40
40
|
defaultModel: '',
|
|
41
41
|
supportedTools: [],
|
|
42
|
-
corsRestricted: false
|
|
42
|
+
corsRestricted: false,
|
|
43
|
+
streamingCorsRestricted: false,
|
|
44
|
+
acceptsImageInput: false
|
|
43
45
|
},
|
|
44
46
|
{
|
|
45
47
|
id: 'anthropic',
|
|
@@ -50,7 +52,9 @@ const BUILTIN_PROVIDERS = [
|
|
|
50
52
|
baseUrl: 'https://api.anthropic.com/v1',
|
|
51
53
|
defaultModel: 'claude-sonnet-4-5-20250929',
|
|
52
54
|
supportedTools: ['web_search'],
|
|
53
|
-
corsRestricted: false
|
|
55
|
+
corsRestricted: false,
|
|
56
|
+
streamingCorsRestricted: false,
|
|
57
|
+
acceptsImageInput: true
|
|
54
58
|
},
|
|
55
59
|
{
|
|
56
60
|
id: 'google-gemini',
|
|
@@ -59,9 +63,12 @@ const BUILTIN_PROVIDERS = [
|
|
|
59
63
|
needsSecret: true,
|
|
60
64
|
apiFormat: 'gemini',
|
|
61
65
|
baseUrl: 'https://generativelanguage.googleapis.com/v1beta',
|
|
62
|
-
defaultModel: 'gemini-2.5-flash',
|
|
66
|
+
defaultModel: { base: 'gemini-2.5-flash', image: 'imagen-3.0-generate-002' },
|
|
63
67
|
supportedTools: ['web_search'],
|
|
64
|
-
corsRestricted: false
|
|
68
|
+
corsRestricted: false,
|
|
69
|
+
streamingCorsRestricted: false,
|
|
70
|
+
acceptsImageInput: true,
|
|
71
|
+
imageApiFormat: 'gemini-imagen'
|
|
65
72
|
},
|
|
66
73
|
{
|
|
67
74
|
id: 'groq',
|
|
@@ -72,7 +79,9 @@ const BUILTIN_PROVIDERS = [
|
|
|
72
79
|
baseUrl: 'https://api.groq.com/openai/v1',
|
|
73
80
|
defaultModel: 'llama-3.3-70b-versatile',
|
|
74
81
|
supportedTools: [],
|
|
75
|
-
corsRestricted: false
|
|
82
|
+
corsRestricted: false,
|
|
83
|
+
streamingCorsRestricted: false,
|
|
84
|
+
acceptsImageInput: false
|
|
76
85
|
},
|
|
77
86
|
{
|
|
78
87
|
id: 'mistral',
|
|
@@ -83,7 +92,9 @@ const BUILTIN_PROVIDERS = [
|
|
|
83
92
|
baseUrl: 'https://api.mistral.ai/v1',
|
|
84
93
|
defaultModel: 'mistral-large-latest',
|
|
85
94
|
supportedTools: [],
|
|
86
|
-
corsRestricted: false
|
|
95
|
+
corsRestricted: false,
|
|
96
|
+
streamingCorsRestricted: false,
|
|
97
|
+
acceptsImageInput: false
|
|
87
98
|
},
|
|
88
99
|
{
|
|
89
100
|
id: 'openai',
|
|
@@ -92,9 +103,12 @@ const BUILTIN_PROVIDERS = [
|
|
|
92
103
|
needsSecret: true,
|
|
93
104
|
apiFormat: 'openai',
|
|
94
105
|
baseUrl: 'https://api.openai.com/v1',
|
|
95
|
-
defaultModel: 'gpt-4o',
|
|
106
|
+
defaultModel: { base: 'gpt-4o', image: 'dall-e-3' },
|
|
96
107
|
supportedTools: ['web_search'],
|
|
97
|
-
corsRestricted: false
|
|
108
|
+
corsRestricted: false,
|
|
109
|
+
streamingCorsRestricted: false,
|
|
110
|
+
acceptsImageInput: true,
|
|
111
|
+
imageApiFormat: 'openai-images'
|
|
98
112
|
},
|
|
99
113
|
{
|
|
100
114
|
id: 'xai-grok',
|
|
@@ -103,9 +117,16 @@ const BUILTIN_PROVIDERS = [
|
|
|
103
117
|
needsSecret: true,
|
|
104
118
|
apiFormat: 'openai',
|
|
105
119
|
baseUrl: 'https://api.x.ai/v1',
|
|
106
|
-
defaultModel: {
|
|
120
|
+
defaultModel: {
|
|
121
|
+
base: 'grok-4-1-fast',
|
|
122
|
+
tools: 'grok-4-1-fast-reasoning',
|
|
123
|
+
image: 'grok-2-image-1212'
|
|
124
|
+
},
|
|
107
125
|
supportedTools: ['web_search'],
|
|
108
|
-
corsRestricted: true
|
|
126
|
+
corsRestricted: true,
|
|
127
|
+
streamingCorsRestricted: true,
|
|
128
|
+
acceptsImageInput: true,
|
|
129
|
+
imageApiFormat: 'xai-images'
|
|
109
130
|
}
|
|
110
131
|
];
|
|
111
132
|
/**
|
|
@@ -142,4 +163,39 @@ export function getProviderDescriptor(id) {
|
|
|
142
163
|
}
|
|
143
164
|
return succeed(descriptor);
|
|
144
165
|
}
|
|
166
|
+
// ============================================================================
|
|
167
|
+
// Default model capability config
|
|
168
|
+
// ============================================================================
|
|
169
|
+
/**
|
|
170
|
+
* Default capability config used by `callProviderListModels` when callers
|
|
171
|
+
* don't supply their own. Patterns are intentionally narrow — false
|
|
172
|
+
* positives are worse than missing a model. Caller can override per call
|
|
173
|
+
* via {@link IProviderListModelsParams.capabilityConfig}.
|
|
174
|
+
*
|
|
175
|
+
* @public
|
|
176
|
+
*/
|
|
177
|
+
export const DEFAULT_MODEL_CAPABILITY_CONFIG = {
|
|
178
|
+
perProvider: {
|
|
179
|
+
openai: [
|
|
180
|
+
{ idPattern: /^dall-e/, capabilities: ['image-generation'] },
|
|
181
|
+
{ idPattern: /^gpt-image/, capabilities: ['image-generation'] },
|
|
182
|
+
{ idPattern: /^gpt-4/, capabilities: ['chat', 'tools', 'vision'] },
|
|
183
|
+
{ idPattern: /^gpt-3\.5/, capabilities: ['chat'] },
|
|
184
|
+
{ idPattern: /^o\d/, capabilities: ['chat', 'tools'] }
|
|
185
|
+
],
|
|
186
|
+
'xai-grok': [
|
|
187
|
+
{ idPattern: /-image/, capabilities: ['image-generation'] },
|
|
188
|
+
{ idPattern: /^grok-4/, capabilities: ['chat', 'tools', 'vision'] },
|
|
189
|
+
{ idPattern: /^grok-3/, capabilities: ['chat', 'tools'] },
|
|
190
|
+
{ idPattern: /^grok-2/, capabilities: ['chat', 'vision'] }
|
|
191
|
+
],
|
|
192
|
+
'google-gemini': [
|
|
193
|
+
{ idPattern: /^imagen/, capabilities: ['image-generation'] },
|
|
194
|
+
{ idPattern: /^gemini-/, capabilities: ['chat', 'tools', 'vision'] }
|
|
195
|
+
],
|
|
196
|
+
anthropic: [{ idPattern: /^claude-/, capabilities: ['chat', 'tools', 'vision'] }],
|
|
197
|
+
groq: [{ idPattern: /./, capabilities: ['chat'] }],
|
|
198
|
+
mistral: [{ idPattern: /./, capabilities: ['chat'] }]
|
|
199
|
+
}
|
|
200
|
+
};
|
|
145
201
|
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// Copyright (c) 2026 Erik Fortune
|
|
2
|
+
//
|
|
3
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
// in the Software without restriction, including without limitation the rights
|
|
6
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
// furnished to do so, subject to the following conditions:
|
|
9
|
+
//
|
|
10
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
// copies or substantial portions of the Software.
|
|
12
|
+
//
|
|
13
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
// SOFTWARE.
|
|
20
|
+
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
|
|
21
|
+
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
|
|
22
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
23
|
+
var g = generator.apply(thisArg, _arguments || []), i, q = [];
|
|
24
|
+
return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
|
|
25
|
+
function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }
|
|
26
|
+
function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }
|
|
27
|
+
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
|
|
28
|
+
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
|
|
29
|
+
function fulfill(value) { resume("next", value); }
|
|
30
|
+
function reject(value) { resume("throw", value); }
|
|
31
|
+
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Parses a single SSE message (the text between blank-line separators) into
|
|
35
|
+
* an {@link ISseEvent}. Returns undefined for messages with no `data:` lines
|
|
36
|
+
* (comments, heartbeats).
|
|
37
|
+
*
|
|
38
|
+
* @internal
|
|
39
|
+
*/
|
|
40
|
+
export function parseSseEvent(chunk) {
|
|
41
|
+
let event;
|
|
42
|
+
const dataLines = [];
|
|
43
|
+
for (const line of chunk.split('\n')) {
|
|
44
|
+
if (line.startsWith('event:')) {
|
|
45
|
+
event = line.slice(6).trim();
|
|
46
|
+
}
|
|
47
|
+
else if (line.startsWith('data:')) {
|
|
48
|
+
// Per the SSE spec the value starts after the colon, with one optional leading space stripped.
|
|
49
|
+
const value = line.slice(5);
|
|
50
|
+
dataLines.push(value.startsWith(' ') ? value.slice(1) : value);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (dataLines.length === 0) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
return event !== undefined ? { event, data: dataLines.join('\n') } : { data: dataLines.join('\n') };
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Reads an SSE response body and yields parsed events. Buffers across read()
|
|
60
|
+
* boundaries so a message split mid-chunk still parses cleanly. Terminates
|
|
61
|
+
* when the stream closes (normal EOF or aborted fetch).
|
|
62
|
+
*
|
|
63
|
+
* @internal
|
|
64
|
+
*/
|
|
65
|
+
export function readSseEvents(body) {
|
|
66
|
+
return __asyncGenerator(this, arguments, function* readSseEvents_1() {
|
|
67
|
+
var _a;
|
|
68
|
+
const reader = body.getReader();
|
|
69
|
+
const decoder = new TextDecoder();
|
|
70
|
+
let buffer = '';
|
|
71
|
+
try {
|
|
72
|
+
let streaming = true;
|
|
73
|
+
while (streaming) {
|
|
74
|
+
const { value, done } = yield __await(reader.read());
|
|
75
|
+
if (done) {
|
|
76
|
+
streaming = false;
|
|
77
|
+
if (buffer.length > 0) {
|
|
78
|
+
const tail = parseSseEvent(buffer.replace(/\r\n/g, '\n'));
|
|
79
|
+
if (tail) {
|
|
80
|
+
yield yield __await(tail);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
buffer += decoder.decode(value, { stream: true });
|
|
86
|
+
// SSE messages are separated by a blank line; some servers use \r\n.
|
|
87
|
+
const normalized = buffer.replace(/\r\n/g, '\n');
|
|
88
|
+
const parts = normalized.split('\n\n');
|
|
89
|
+
// Last element is the partial chunk (no terminating blank line yet); buffer it.
|
|
90
|
+
buffer = (_a = parts.pop()) !== null && _a !== void 0 ? _a : '';
|
|
91
|
+
for (const chunk of parts) {
|
|
92
|
+
const event = parseSseEvent(chunk);
|
|
93
|
+
if (event) {
|
|
94
|
+
yield yield __await(event);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
reader.releaseLock();
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Parses the `data` payload of an SSE event as JSON. Returns undefined for
|
|
106
|
+
* the OpenAI `[DONE]` sentinel and for any payload that fails to parse —
|
|
107
|
+
* adapters treat both as "skip this event."
|
|
108
|
+
*
|
|
109
|
+
* @internal
|
|
110
|
+
*/
|
|
111
|
+
export function parseSseEventJson(data) {
|
|
112
|
+
if (data === '[DONE]') {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
return JSON.parse(data);
|
|
117
|
+
}
|
|
118
|
+
catch (_a) {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=sseParser.js.map
|