@fgv/ts-extras 5.1.0-2 → 5.1.0-20
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/index.browser.js.map +1 -0
- package/dist/index.js.map +1 -0
- package/dist/packlets/ai-assist/apiClient.js +792 -57
- package/dist/packlets/ai-assist/apiClient.js.map +1 -0
- package/dist/packlets/ai-assist/chatRequestBuilders.js +180 -0
- package/dist/packlets/ai-assist/chatRequestBuilders.js.map +1 -0
- package/dist/packlets/ai-assist/converters.js.map +1 -0
- package/dist/packlets/ai-assist/index.js +4 -3
- package/dist/packlets/ai-assist/index.js.map +1 -0
- package/dist/packlets/ai-assist/model.js +20 -3
- package/dist/packlets/ai-assist/model.js.map +1 -0
- package/dist/packlets/ai-assist/registry.js +111 -10
- package/dist/packlets/ai-assist/registry.js.map +1 -0
- package/dist/packlets/ai-assist/sseParser.js +122 -0
- package/dist/packlets/ai-assist/sseParser.js.map +1 -0
- package/dist/packlets/ai-assist/streamingAdapters/anthropic.js +192 -0
- package/dist/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -0
- package/dist/packlets/ai-assist/streamingAdapters/common.js +77 -0
- package/dist/packlets/ai-assist/streamingAdapters/common.js.map +1 -0
- package/dist/packlets/ai-assist/streamingAdapters/gemini.js +160 -0
- package/dist/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -0
- package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js +149 -0
- package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -0
- package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js +163 -0
- package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -0
- package/dist/packlets/ai-assist/streamingAdapters/proxy.js +157 -0
- package/dist/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -0
- package/dist/packlets/ai-assist/streamingClient.js +88 -0
- package/dist/packlets/ai-assist/streamingClient.js.map +1 -0
- package/dist/packlets/ai-assist/toolFormats.js.map +1 -0
- package/dist/packlets/conversion/converters.js +34 -1
- package/dist/packlets/conversion/converters.js.map +1 -0
- package/dist/packlets/conversion/index.js.map +1 -0
- package/dist/packlets/crypto-utils/constants.js.map +1 -0
- package/dist/packlets/crypto-utils/converters.js.map +1 -0
- package/dist/packlets/crypto-utils/directEncryptionProvider.js.map +1 -0
- package/dist/packlets/crypto-utils/encryptedFile.js.map +1 -0
- package/dist/packlets/crypto-utils/index.browser.js +2 -0
- package/dist/packlets/crypto-utils/index.browser.js.map +1 -0
- package/dist/packlets/crypto-utils/index.js +2 -0
- package/dist/packlets/crypto-utils/index.js.map +1 -0
- package/dist/packlets/crypto-utils/keyPairAlgorithmParams.js +57 -0
- package/dist/packlets/crypto-utils/keyPairAlgorithmParams.js.map +1 -0
- package/dist/packlets/crypto-utils/keystore/converters.js +101 -9
- package/dist/packlets/crypto-utils/keystore/converters.js.map +1 -0
- package/dist/packlets/crypto-utils/keystore/index.js +1 -0
- package/dist/packlets/crypto-utils/keystore/index.js.map +1 -0
- package/dist/packlets/crypto-utils/keystore/keyStore.js +431 -118
- package/dist/packlets/crypto-utils/keystore/keyStore.js.map +1 -0
- package/dist/packlets/crypto-utils/keystore/model.js +22 -1
- package/dist/packlets/crypto-utils/keystore/model.js.map +1 -0
- package/dist/packlets/crypto-utils/keystore/privateKeyStorage.js +21 -0
- package/dist/packlets/crypto-utils/keystore/privateKeyStorage.js.map +1 -0
- package/dist/packlets/crypto-utils/model.js +9 -0
- package/dist/packlets/crypto-utils/model.js.map +1 -0
- package/dist/packlets/crypto-utils/nodeCryptoProvider.js +152 -1
- package/dist/packlets/crypto-utils/nodeCryptoProvider.js.map +1 -0
- package/dist/packlets/csv/csvFileHelpers.js.map +1 -0
- package/dist/packlets/csv/csvHelpers.js.map +1 -0
- package/dist/packlets/csv/index.browser.js.map +1 -0
- package/dist/packlets/csv/index.js.map +1 -0
- package/dist/packlets/experimental/extendedArray.js.map +1 -0
- package/dist/packlets/experimental/formatter.js.map +1 -0
- package/dist/packlets/experimental/index.js.map +1 -0
- package/dist/packlets/experimental/rangeOf.js.map +1 -0
- package/dist/packlets/hash/index.browser.js.map +1 -0
- package/dist/packlets/hash/index.js.map +1 -0
- package/dist/packlets/hash/index.node.js.map +1 -0
- package/dist/packlets/hash/md5Normalizer.browser.js.map +1 -0
- package/dist/packlets/hash/md5Normalizer.js.map +1 -0
- package/dist/packlets/mustache/index.js.map +1 -0
- package/dist/packlets/mustache/interfaces.js.map +1 -0
- package/dist/packlets/mustache/mustacheTemplate.js.map +1 -0
- package/dist/packlets/record-jar/index.browser.js.map +1 -0
- package/dist/packlets/record-jar/index.js.map +1 -0
- package/dist/packlets/record-jar/recordJarFileHelpers.js.map +1 -0
- package/dist/packlets/record-jar/recordJarHelpers.js.map +1 -0
- package/dist/packlets/yaml/converters.js.map +1 -0
- package/dist/packlets/yaml/index.js +1 -0
- package/dist/packlets/yaml/index.js.map +1 -0
- package/dist/packlets/yaml/serializers.js +48 -0
- package/dist/packlets/yaml/serializers.js.map +1 -0
- package/dist/packlets/zip-file-tree/index.js.map +1 -0
- package/dist/packlets/zip-file-tree/zipFileTreeAccessors.js +2 -2
- package/dist/packlets/zip-file-tree/zipFileTreeAccessors.js.map +1 -0
- package/dist/packlets/zip-file-tree/zipFileTreeWriter.js.map +1 -0
- package/dist/ts-extras.d.ts +1442 -45
- package/dist/tsdoc-metadata.json +1 -1
- package/lib/index.browser.d.ts +2 -1
- package/lib/index.browser.d.ts.map +1 -0
- package/lib/index.browser.js +3 -1
- package/lib/index.browser.js.map +1 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js.map +1 -0
- package/lib/packlets/ai-assist/apiClient.d.ts +111 -1
- package/lib/packlets/ai-assist/apiClient.d.ts.map +1 -0
- package/lib/packlets/ai-assist/apiClient.js +795 -56
- package/lib/packlets/ai-assist/apiClient.js.map +1 -0
- package/lib/packlets/ai-assist/chatRequestBuilders.d.ts +89 -0
- package/lib/packlets/ai-assist/chatRequestBuilders.d.ts.map +1 -0
- package/lib/packlets/ai-assist/chatRequestBuilders.js +189 -0
- package/lib/packlets/ai-assist/chatRequestBuilders.js.map +1 -0
- package/lib/packlets/ai-assist/converters.d.ts.map +1 -0
- package/lib/packlets/ai-assist/converters.js.map +1 -0
- package/lib/packlets/ai-assist/index.d.ts +4 -3
- package/lib/packlets/ai-assist/index.d.ts.map +1 -0
- package/lib/packlets/ai-assist/index.js +12 -1
- package/lib/packlets/ai-assist/index.js.map +1 -0
- package/lib/packlets/ai-assist/model.d.ts +332 -2
- package/lib/packlets/ai-assist/model.d.ts.map +1 -0
- package/lib/packlets/ai-assist/model.js +21 -3
- package/lib/packlets/ai-assist/model.js.map +1 -0
- package/lib/packlets/ai-assist/registry.d.ts +34 -1
- package/lib/packlets/ai-assist/registry.d.ts.map +1 -0
- package/lib/packlets/ai-assist/registry.js +114 -11
- package/lib/packlets/ai-assist/registry.js.map +1 -0
- package/lib/packlets/ai-assist/sseParser.d.ts +45 -0
- package/lib/packlets/ai-assist/sseParser.d.ts.map +1 -0
- package/lib/packlets/ai-assist/sseParser.js +127 -0
- package/lib/packlets/ai-assist/sseParser.js.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts +18 -0
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.js +195 -0
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/common.d.ts +71 -0
- package/lib/packlets/ai-assist/streamingAdapters/common.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/common.js +81 -0
- package/lib/packlets/ai-assist/streamingAdapters/common.js.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts +19 -0
- package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/gemini.js +163 -0
- package/lib/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts +18 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js +152 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts +19 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js +166 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/proxy.d.ts +34 -0
- package/lib/packlets/ai-assist/streamingAdapters/proxy.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/proxy.js +160 -0
- package/lib/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -0
- package/lib/packlets/ai-assist/streamingClient.d.ts +33 -0
- package/lib/packlets/ai-assist/streamingClient.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamingClient.js +93 -0
- package/lib/packlets/ai-assist/streamingClient.js.map +1 -0
- package/lib/packlets/ai-assist/toolFormats.d.ts.map +1 -0
- package/lib/packlets/ai-assist/toolFormats.js.map +1 -0
- package/lib/packlets/conversion/converters.d.ts +8 -1
- package/lib/packlets/conversion/converters.d.ts.map +1 -0
- package/lib/packlets/conversion/converters.js +35 -2
- package/lib/packlets/conversion/converters.js.map +1 -0
- package/lib/packlets/conversion/index.d.ts.map +1 -0
- package/lib/packlets/conversion/index.js.map +1 -0
- package/lib/packlets/crypto-utils/constants.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/constants.js.map +1 -0
- package/lib/packlets/crypto-utils/converters.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/converters.js.map +1 -0
- package/lib/packlets/crypto-utils/directEncryptionProvider.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/directEncryptionProvider.js.map +1 -0
- package/lib/packlets/crypto-utils/encryptedFile.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/encryptedFile.js.map +1 -0
- package/lib/packlets/crypto-utils/index.browser.d.ts +1 -0
- package/lib/packlets/crypto-utils/index.browser.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/index.browser.js +4 -1
- package/lib/packlets/crypto-utils/index.browser.js.map +1 -0
- package/lib/packlets/crypto-utils/index.d.ts +1 -0
- package/lib/packlets/crypto-utils/index.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/index.js +4 -1
- package/lib/packlets/crypto-utils/index.js.map +1 -0
- package/lib/packlets/crypto-utils/keyPairAlgorithmParams.d.ts +39 -0
- package/lib/packlets/crypto-utils/keyPairAlgorithmParams.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keyPairAlgorithmParams.js +60 -0
- package/lib/packlets/crypto-utils/keyPairAlgorithmParams.js.map +1 -0
- package/lib/packlets/crypto-utils/keystore/converters.d.ts +68 -6
- package/lib/packlets/crypto-utils/keystore/converters.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keystore/converters.js +100 -8
- package/lib/packlets/crypto-utils/keystore/converters.js.map +1 -0
- package/lib/packlets/crypto-utils/keystore/index.d.ts +1 -0
- package/lib/packlets/crypto-utils/keystore/index.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keystore/index.js +1 -0
- package/lib/packlets/crypto-utils/keystore/index.js.map +1 -0
- package/lib/packlets/crypto-utils/keystore/keyStore.d.ts +125 -12
- package/lib/packlets/crypto-utils/keystore/keyStore.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keystore/keyStore.js +431 -118
- package/lib/packlets/crypto-utils/keystore/keyStore.js.map +1 -0
- package/lib/packlets/crypto-utils/keystore/model.d.ts +248 -17
- package/lib/packlets/crypto-utils/keystore/model.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keystore/model.js +24 -2
- package/lib/packlets/crypto-utils/keystore/model.js.map +1 -0
- package/lib/packlets/crypto-utils/keystore/privateKeyStorage.d.ts +50 -0
- package/lib/packlets/crypto-utils/keystore/privateKeyStorage.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keystore/privateKeyStorage.js +22 -0
- package/lib/packlets/crypto-utils/keystore/privateKeyStorage.js.map +1 -0
- package/lib/packlets/crypto-utils/model.d.ts +140 -0
- package/lib/packlets/crypto-utils/model.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/model.js +10 -1
- package/lib/packlets/crypto-utils/model.js.map +1 -0
- package/lib/packlets/crypto-utils/nodeCryptoProvider.d.ts +51 -1
- package/lib/packlets/crypto-utils/nodeCryptoProvider.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/nodeCryptoProvider.js +151 -0
- package/lib/packlets/crypto-utils/nodeCryptoProvider.js.map +1 -0
- package/lib/packlets/csv/csvFileHelpers.d.ts.map +1 -0
- package/lib/packlets/csv/csvFileHelpers.js.map +1 -0
- package/lib/packlets/csv/csvHelpers.d.ts.map +1 -0
- package/lib/packlets/csv/csvHelpers.js.map +1 -0
- package/lib/packlets/csv/index.browser.d.ts.map +1 -0
- package/lib/packlets/csv/index.browser.js.map +1 -0
- package/lib/packlets/csv/index.d.ts.map +1 -0
- package/lib/packlets/csv/index.js.map +1 -0
- package/lib/packlets/experimental/extendedArray.d.ts.map +1 -0
- package/lib/packlets/experimental/extendedArray.js.map +1 -0
- package/lib/packlets/experimental/formatter.d.ts.map +1 -0
- package/lib/packlets/experimental/formatter.js.map +1 -0
- package/lib/packlets/experimental/index.d.ts.map +1 -0
- package/lib/packlets/experimental/index.js.map +1 -0
- package/lib/packlets/experimental/rangeOf.d.ts.map +1 -0
- package/lib/packlets/experimental/rangeOf.js.map +1 -0
- package/lib/packlets/hash/index.browser.d.ts.map +1 -0
- package/lib/packlets/hash/index.browser.js.map +1 -0
- package/lib/packlets/hash/index.d.ts.map +1 -0
- package/lib/packlets/hash/index.js.map +1 -0
- package/lib/packlets/hash/index.node.d.ts.map +1 -0
- package/lib/packlets/hash/index.node.js.map +1 -0
- package/lib/packlets/hash/md5Normalizer.browser.d.ts.map +1 -0
- package/lib/packlets/hash/md5Normalizer.browser.js.map +1 -0
- package/lib/packlets/hash/md5Normalizer.d.ts.map +1 -0
- package/lib/packlets/hash/md5Normalizer.js.map +1 -0
- package/lib/packlets/mustache/index.d.ts.map +1 -0
- package/lib/packlets/mustache/index.js.map +1 -0
- package/lib/packlets/mustache/interfaces.d.ts.map +1 -0
- package/lib/packlets/mustache/interfaces.js.map +1 -0
- package/lib/packlets/mustache/mustacheTemplate.d.ts.map +1 -0
- package/lib/packlets/mustache/mustacheTemplate.js.map +1 -0
- package/lib/packlets/record-jar/index.browser.d.ts.map +1 -0
- package/lib/packlets/record-jar/index.browser.js.map +1 -0
- package/lib/packlets/record-jar/index.d.ts.map +1 -0
- package/lib/packlets/record-jar/index.js.map +1 -0
- package/lib/packlets/record-jar/recordJarFileHelpers.d.ts.map +1 -0
- package/lib/packlets/record-jar/recordJarFileHelpers.js.map +1 -0
- package/lib/packlets/record-jar/recordJarHelpers.d.ts.map +1 -0
- package/lib/packlets/record-jar/recordJarHelpers.js.map +1 -0
- package/lib/packlets/yaml/converters.d.ts.map +1 -0
- package/lib/packlets/yaml/converters.js.map +1 -0
- package/lib/packlets/yaml/index.d.ts +1 -0
- package/lib/packlets/yaml/index.d.ts.map +1 -0
- package/lib/packlets/yaml/index.js +1 -0
- package/lib/packlets/yaml/index.js.map +1 -0
- package/lib/packlets/yaml/serializers.d.ts +45 -0
- package/lib/packlets/yaml/serializers.d.ts.map +1 -0
- package/lib/packlets/yaml/serializers.js +84 -0
- package/lib/packlets/yaml/serializers.js.map +1 -0
- package/lib/packlets/zip-file-tree/index.d.ts.map +1 -0
- package/lib/packlets/zip-file-tree/index.js.map +1 -0
- package/lib/packlets/zip-file-tree/zipFileTreeAccessors.d.ts +2 -2
- package/lib/packlets/zip-file-tree/zipFileTreeAccessors.d.ts.map +1 -0
- package/lib/packlets/zip-file-tree/zipFileTreeAccessors.js +2 -2
- package/lib/packlets/zip-file-tree/zipFileTreeAccessors.js.map +1 -0
- package/lib/packlets/zip-file-tree/zipFileTreeWriter.d.ts.map +1 -0
- package/lib/packlets/zip-file-tree/zipFileTreeWriter.js.map +1 -0
- package/package.json +24 -23
|
@@ -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,17 @@ 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: 'gemini-2.5-flash-image' },
|
|
63
67
|
supportedTools: ['web_search'],
|
|
64
|
-
corsRestricted: false
|
|
68
|
+
corsRestricted: false,
|
|
69
|
+
streamingCorsRestricted: false,
|
|
70
|
+
acceptsImageInput: true,
|
|
71
|
+
imageGeneration: [
|
|
72
|
+
// imagen-* models are predict-only and do not accept reference images;
|
|
73
|
+
// everything else uses chat-style :generateContent with refs.
|
|
74
|
+
{ modelPrefix: 'imagen-', format: 'gemini-imagen' },
|
|
75
|
+
{ modelPrefix: '', format: 'gemini-image-out', acceptsImageReferenceInput: true }
|
|
76
|
+
]
|
|
65
77
|
},
|
|
66
78
|
{
|
|
67
79
|
id: 'groq',
|
|
@@ -72,7 +84,9 @@ const BUILTIN_PROVIDERS = [
|
|
|
72
84
|
baseUrl: 'https://api.groq.com/openai/v1',
|
|
73
85
|
defaultModel: 'llama-3.3-70b-versatile',
|
|
74
86
|
supportedTools: [],
|
|
75
|
-
corsRestricted: false
|
|
87
|
+
corsRestricted: false,
|
|
88
|
+
streamingCorsRestricted: false,
|
|
89
|
+
acceptsImageInput: false
|
|
76
90
|
},
|
|
77
91
|
{
|
|
78
92
|
id: 'mistral',
|
|
@@ -83,7 +97,9 @@ const BUILTIN_PROVIDERS = [
|
|
|
83
97
|
baseUrl: 'https://api.mistral.ai/v1',
|
|
84
98
|
defaultModel: 'mistral-large-latest',
|
|
85
99
|
supportedTools: [],
|
|
86
|
-
corsRestricted: false
|
|
100
|
+
corsRestricted: false,
|
|
101
|
+
streamingCorsRestricted: false,
|
|
102
|
+
acceptsImageInput: false
|
|
87
103
|
},
|
|
88
104
|
{
|
|
89
105
|
id: 'openai',
|
|
@@ -92,9 +108,19 @@ const BUILTIN_PROVIDERS = [
|
|
|
92
108
|
needsSecret: true,
|
|
93
109
|
apiFormat: 'openai',
|
|
94
110
|
baseUrl: 'https://api.openai.com/v1',
|
|
95
|
-
defaultModel: 'gpt-4o',
|
|
111
|
+
defaultModel: { base: 'gpt-4o', image: 'dall-e-3' },
|
|
96
112
|
supportedTools: ['web_search'],
|
|
97
|
-
corsRestricted: false
|
|
113
|
+
corsRestricted: false,
|
|
114
|
+
streamingCorsRestricted: false,
|
|
115
|
+
acceptsImageInput: true,
|
|
116
|
+
imageGeneration: [
|
|
117
|
+
// gpt-image-1 supports /images/edits with reference images. dall-e-3
|
|
118
|
+
// (the default image model) does not, so the catch-all rule omits
|
|
119
|
+
// acceptsImageReferenceInput; callers selecting dall-e-3 with refs hit
|
|
120
|
+
// the up-front rejection rather than a provider 400.
|
|
121
|
+
{ modelPrefix: 'gpt-image-', format: 'openai-images', acceptsImageReferenceInput: true },
|
|
122
|
+
{ modelPrefix: '', format: 'openai-images' }
|
|
123
|
+
]
|
|
98
124
|
},
|
|
99
125
|
{
|
|
100
126
|
id: 'xai-grok',
|
|
@@ -103,9 +129,16 @@ const BUILTIN_PROVIDERS = [
|
|
|
103
129
|
needsSecret: true,
|
|
104
130
|
apiFormat: 'openai',
|
|
105
131
|
baseUrl: 'https://api.x.ai/v1',
|
|
106
|
-
defaultModel: {
|
|
132
|
+
defaultModel: {
|
|
133
|
+
base: 'grok-4-1-fast',
|
|
134
|
+
tools: 'grok-4-1-fast-reasoning',
|
|
135
|
+
image: 'grok-2-image-1212'
|
|
136
|
+
},
|
|
107
137
|
supportedTools: ['web_search'],
|
|
108
|
-
corsRestricted: true
|
|
138
|
+
corsRestricted: true,
|
|
139
|
+
streamingCorsRestricted: true,
|
|
140
|
+
acceptsImageInput: true,
|
|
141
|
+
imageGeneration: [{ modelPrefix: '', format: 'xai-images' }]
|
|
109
142
|
}
|
|
110
143
|
];
|
|
111
144
|
/**
|
|
@@ -142,4 +175,72 @@ export function getProviderDescriptor(id) {
|
|
|
142
175
|
}
|
|
143
176
|
return succeed(descriptor);
|
|
144
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* Whether a provider declares any image-generation capability at all.
|
|
180
|
+
*
|
|
181
|
+
* @param descriptor - The provider descriptor
|
|
182
|
+
* @returns `true` when {@link IAiProviderDescriptor.imageGeneration} has at
|
|
183
|
+
* least one entry; `false` otherwise.
|
|
184
|
+
* @public
|
|
185
|
+
*/
|
|
186
|
+
export function supportsImageGeneration(descriptor) {
|
|
187
|
+
var _a, _b;
|
|
188
|
+
return ((_b = (_a = descriptor.imageGeneration) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Resolve the image-generation capability that applies to a given model id
|
|
192
|
+
* for a provider. Returns the entry from
|
|
193
|
+
* {@link IAiProviderDescriptor.imageGeneration} whose `modelPrefix` is the
|
|
194
|
+
* longest prefix of `modelId`. Ties are broken by first-encountered, so rule
|
|
195
|
+
* order does not matter for correctness — only for tie-breaking among rules
|
|
196
|
+
* with identical-length prefixes (an unusual case).
|
|
197
|
+
*
|
|
198
|
+
* @param descriptor - The provider descriptor
|
|
199
|
+
* @param modelId - The resolved image model id
|
|
200
|
+
* @returns The matching capability, or `undefined` when no rule matches or
|
|
201
|
+
* the provider declares no image-generation capabilities.
|
|
202
|
+
* @public
|
|
203
|
+
*/
|
|
204
|
+
export function resolveImageCapability(descriptor, modelId) {
|
|
205
|
+
var _a;
|
|
206
|
+
return ((_a = descriptor.imageGeneration) !== null && _a !== void 0 ? _a : [])
|
|
207
|
+
.filter((cap) => modelId.startsWith(cap.modelPrefix))
|
|
208
|
+
.reduce((best, cap) => (best && best.modelPrefix.length >= cap.modelPrefix.length ? best : cap), undefined);
|
|
209
|
+
}
|
|
210
|
+
// ============================================================================
|
|
211
|
+
// Default model capability config
|
|
212
|
+
// ============================================================================
|
|
213
|
+
/**
|
|
214
|
+
* Default capability config used by `callProviderListModels` when callers
|
|
215
|
+
* don't supply their own. Patterns are intentionally narrow — false
|
|
216
|
+
* positives are worse than missing a model. Caller can override per call
|
|
217
|
+
* via {@link IProviderListModelsParams.capabilityConfig}.
|
|
218
|
+
*
|
|
219
|
+
* @public
|
|
220
|
+
*/
|
|
221
|
+
export const DEFAULT_MODEL_CAPABILITY_CONFIG = {
|
|
222
|
+
perProvider: {
|
|
223
|
+
openai: [
|
|
224
|
+
{ idPattern: /^dall-e/, capabilities: ['image-generation'] },
|
|
225
|
+
{ idPattern: /^gpt-image/, capabilities: ['image-generation'] },
|
|
226
|
+
{ idPattern: /^gpt-4/, capabilities: ['chat', 'tools', 'vision'] },
|
|
227
|
+
{ idPattern: /^gpt-3\.5/, capabilities: ['chat'] },
|
|
228
|
+
{ idPattern: /^o\d/, capabilities: ['chat', 'tools'] }
|
|
229
|
+
],
|
|
230
|
+
'xai-grok': [
|
|
231
|
+
{ idPattern: /-image/, capabilities: ['image-generation'] },
|
|
232
|
+
{ idPattern: /^grok-4/, capabilities: ['chat', 'tools', 'vision'] },
|
|
233
|
+
{ idPattern: /^grok-3/, capabilities: ['chat', 'tools'] },
|
|
234
|
+
{ idPattern: /^grok-2/, capabilities: ['chat', 'vision'] }
|
|
235
|
+
],
|
|
236
|
+
'google-gemini': [
|
|
237
|
+
{ idPattern: /^imagen/, capabilities: ['image-generation'] },
|
|
238
|
+
{ idPattern: /^gemini-.*-image/, capabilities: ['image-generation'] },
|
|
239
|
+
{ idPattern: /^gemini-/, capabilities: ['chat', 'tools', 'vision'] }
|
|
240
|
+
],
|
|
241
|
+
anthropic: [{ idPattern: /^claude-/, capabilities: ['chat', 'tools', 'vision'] }],
|
|
242
|
+
groq: [{ idPattern: /./, capabilities: ['chat'] }],
|
|
243
|
+
mistral: [{ idPattern: /./, capabilities: ['chat'] }]
|
|
244
|
+
}
|
|
245
|
+
};
|
|
145
246
|
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/packlets/ai-assist/registry.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Centralized provider registry — single source of truth for all AI provider metadata.\n * @packageDocumentation\n */\n\nimport { fail, Result, succeed } from '@fgv/ts-utils';\n\nimport {\n type AiProviderId,\n type IAiImageModelCapability,\n type IAiModelCapabilityConfig,\n type IAiProviderDescriptor\n} from './model';\n\n// ============================================================================\n// Built-in providers\n// ============================================================================\n\n/**\n * All known AI provider descriptors. Copy-paste first, then alphabetical.\n * @internal\n */\nconst BUILTIN_PROVIDERS: ReadonlyArray<IAiProviderDescriptor> = [\n {\n id: 'copy-paste',\n label: 'Copy / Paste',\n buttonLabel: 'AI Assist | Copy',\n needsSecret: false,\n apiFormat: 'openai',\n baseUrl: '',\n defaultModel: '',\n supportedTools: [],\n corsRestricted: false,\n streamingCorsRestricted: false,\n acceptsImageInput: false\n },\n {\n id: 'anthropic',\n label: 'Anthropic Claude',\n buttonLabel: 'AI Assist | Claude',\n needsSecret: true,\n apiFormat: 'anthropic',\n baseUrl: 'https://api.anthropic.com/v1',\n defaultModel: 'claude-sonnet-4-5-20250929',\n supportedTools: ['web_search'],\n corsRestricted: false,\n streamingCorsRestricted: false,\n acceptsImageInput: true\n },\n {\n id: 'google-gemini',\n label: 'Google Gemini',\n buttonLabel: 'AI Assist | Gemini',\n needsSecret: true,\n apiFormat: 'gemini',\n baseUrl: 'https://generativelanguage.googleapis.com/v1beta',\n defaultModel: { base: 'gemini-2.5-flash', image: 'gemini-2.5-flash-image' },\n supportedTools: ['web_search'],\n corsRestricted: false,\n streamingCorsRestricted: false,\n acceptsImageInput: true,\n imageGeneration: [\n // imagen-* models are predict-only and do not accept reference images;\n // everything else uses chat-style :generateContent with refs.\n { modelPrefix: 'imagen-', format: 'gemini-imagen' },\n { modelPrefix: '', format: 'gemini-image-out', acceptsImageReferenceInput: true }\n ]\n },\n {\n id: 'groq',\n label: 'Groq',\n buttonLabel: 'AI Assist | Groq',\n needsSecret: true,\n apiFormat: 'openai',\n baseUrl: 'https://api.groq.com/openai/v1',\n defaultModel: 'llama-3.3-70b-versatile',\n supportedTools: [],\n corsRestricted: false,\n streamingCorsRestricted: false,\n acceptsImageInput: false\n },\n {\n id: 'mistral',\n label: 'Mistral',\n buttonLabel: 'AI Assist | Mistral',\n needsSecret: true,\n apiFormat: 'openai',\n baseUrl: 'https://api.mistral.ai/v1',\n defaultModel: 'mistral-large-latest',\n supportedTools: [],\n corsRestricted: false,\n streamingCorsRestricted: false,\n acceptsImageInput: false\n },\n {\n id: 'openai',\n label: 'OpenAI',\n buttonLabel: 'AI Assist | OpenAI',\n needsSecret: true,\n apiFormat: 'openai',\n baseUrl: 'https://api.openai.com/v1',\n defaultModel: { base: 'gpt-4o', image: 'dall-e-3' },\n supportedTools: ['web_search'],\n corsRestricted: false,\n streamingCorsRestricted: false,\n acceptsImageInput: true,\n imageGeneration: [\n // gpt-image-1 supports /images/edits with reference images. dall-e-3\n // (the default image model) does not, so the catch-all rule omits\n // acceptsImageReferenceInput; callers selecting dall-e-3 with refs hit\n // the up-front rejection rather than a provider 400.\n { modelPrefix: 'gpt-image-', format: 'openai-images', acceptsImageReferenceInput: true },\n { modelPrefix: '', format: 'openai-images' }\n ]\n },\n {\n id: 'xai-grok',\n label: 'xAI Grok',\n buttonLabel: 'AI Assist | Grok',\n needsSecret: true,\n apiFormat: 'openai',\n baseUrl: 'https://api.x.ai/v1',\n defaultModel: {\n base: 'grok-4-1-fast',\n tools: 'grok-4-1-fast-reasoning',\n image: 'grok-2-image-1212'\n },\n supportedTools: ['web_search'],\n corsRestricted: true,\n streamingCorsRestricted: true,\n acceptsImageInput: true,\n imageGeneration: [{ modelPrefix: '', format: 'xai-images' }]\n }\n];\n\n/**\n * Index for O(1) lookup by id.\n * @internal\n */\nconst PROVIDER_BY_ID: ReadonlyMap<string, IAiProviderDescriptor> = new Map(\n BUILTIN_PROVIDERS.map((d) => [d.id, d])\n);\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * All valid provider ID values, in the same order as the registry.\n * @public\n */\nexport const allProviderIds: ReadonlyArray<AiProviderId> = BUILTIN_PROVIDERS.map((d) => d.id);\n\n/**\n * Get all known provider descriptors. Copy-paste first, then alphabetical.\n * @returns All built-in provider descriptors\n * @public\n */\nexport function getProviderDescriptors(): ReadonlyArray<IAiProviderDescriptor> {\n return BUILTIN_PROVIDERS;\n}\n\n/**\n * Get a provider descriptor by id.\n * @param id - The provider identifier\n * @returns The descriptor, or a failure if the provider is unknown\n * @public\n */\nexport function getProviderDescriptor(id: string): Result<IAiProviderDescriptor> {\n const descriptor = PROVIDER_BY_ID.get(id);\n if (!descriptor) {\n return fail(`unknown AI provider: ${id}`);\n }\n return succeed(descriptor);\n}\n\n/**\n * Whether a provider declares any image-generation capability at all.\n *\n * @param descriptor - The provider descriptor\n * @returns `true` when {@link IAiProviderDescriptor.imageGeneration} has at\n * least one entry; `false` otherwise.\n * @public\n */\nexport function supportsImageGeneration(descriptor: IAiProviderDescriptor): boolean {\n return (descriptor.imageGeneration?.length ?? 0) > 0;\n}\n\n/**\n * Resolve the image-generation capability that applies to a given model id\n * for a provider. Returns the entry from\n * {@link IAiProviderDescriptor.imageGeneration} whose `modelPrefix` is the\n * longest prefix of `modelId`. Ties are broken by first-encountered, so rule\n * order does not matter for correctness — only for tie-breaking among rules\n * with identical-length prefixes (an unusual case).\n *\n * @param descriptor - The provider descriptor\n * @param modelId - The resolved image model id\n * @returns The matching capability, or `undefined` when no rule matches or\n * the provider declares no image-generation capabilities.\n * @public\n */\nexport function resolveImageCapability(\n descriptor: IAiProviderDescriptor,\n modelId: string\n): IAiImageModelCapability | undefined {\n return (descriptor.imageGeneration ?? [])\n .filter((cap) => modelId.startsWith(cap.modelPrefix))\n .reduce<IAiImageModelCapability | undefined>(\n (best, cap) => (best && best.modelPrefix.length >= cap.modelPrefix.length ? best : cap),\n undefined\n );\n}\n\n// ============================================================================\n// Default model capability config\n// ============================================================================\n\n/**\n * Default capability config used by `callProviderListModels` when callers\n * don't supply their own. Patterns are intentionally narrow — false\n * positives are worse than missing a model. Caller can override per call\n * via {@link IProviderListModelsParams.capabilityConfig}.\n *\n * @public\n */\nexport const DEFAULT_MODEL_CAPABILITY_CONFIG: IAiModelCapabilityConfig = {\n perProvider: {\n openai: [\n { idPattern: /^dall-e/, capabilities: ['image-generation'] },\n { idPattern: /^gpt-image/, capabilities: ['image-generation'] },\n { idPattern: /^gpt-4/, capabilities: ['chat', 'tools', 'vision'] },\n { idPattern: /^gpt-3\\.5/, capabilities: ['chat'] },\n { idPattern: /^o\\d/, capabilities: ['chat', 'tools'] }\n ],\n 'xai-grok': [\n { idPattern: /-image/, capabilities: ['image-generation'] },\n { idPattern: /^grok-4/, capabilities: ['chat', 'tools', 'vision'] },\n { idPattern: /^grok-3/, capabilities: ['chat', 'tools'] },\n { idPattern: /^grok-2/, capabilities: ['chat', 'vision'] }\n ],\n 'google-gemini': [\n { idPattern: /^imagen/, capabilities: ['image-generation'] },\n { idPattern: /^gemini-.*-image/, capabilities: ['image-generation'] },\n { idPattern: /^gemini-/, capabilities: ['chat', 'tools', 'vision'] }\n ],\n anthropic: [{ idPattern: /^claude-/, capabilities: ['chat', 'tools', 'vision'] }],\n groq: [{ idPattern: /./, capabilities: ['chat'] }],\n mistral: [{ idPattern: /./, capabilities: ['chat'] }]\n }\n};\n"]}
|
|
@@ -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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sseParser.js","sourceRoot":"","sources":["../../../src/packlets/ai-assist/sseParser.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,4EAA4E;AAC5E,wEAAwE;AACxE,2DAA2D;AAC3D,EAAE;AACF,iFAAiF;AACjF,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,8EAA8E;AAC9E,yEAAyE;AACzE,gFAAgF;AAChF,gFAAgF;AAChF,YAAY;;;;;;;;;;;;;;AAwBZ;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,IAAI,KAAyB,CAAC;IAC9B,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9B,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC/B,CAAC;aAAM,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACpC,+FAA+F;YAC/F,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC5B,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;IACD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AACtG,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAiB,aAAa,CAAC,IAAgC;;;QACnE,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAChC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;QAClC,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,CAAC;YACH,IAAI,SAAS,GAAG,IAAI,CAAC;YACrB,OAAO,SAAS,EAAE,CAAC;gBACjB,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,cAAM,MAAM,CAAC,IAAI,EAAE,CAAA,CAAC;gBAC5C,IAAI,IAAI,EAAE,CAAC;oBACT,SAAS,GAAG,KAAK,CAAC;oBAClB,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBACtB,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;wBAC1D,IAAI,IAAI,EAAE,CAAC;4BACT,oBAAM,IAAI,CAAA,CAAC;wBACb,CAAC;oBACH,CAAC;oBACD,MAAM;gBACR,CAAC;gBACD,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;gBAClD,qEAAqE;gBACrE,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;gBACjD,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;gBACvC,gFAAgF;gBAChF,MAAM,GAAG,MAAA,KAAK,CAAC,GAAG,EAAE,mCAAI,EAAE,CAAC;gBAC3B,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;oBAC1B,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;oBACnC,IAAI,KAAK,EAAE,CAAC;wBACV,oBAAM,KAAK,CAAA,CAAC;oBACd,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,MAAM,CAAC,WAAW,EAAE,CAAC;QACvB,CAAC;IACH,CAAC;CAAA;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,WAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Server-Sent Events (SSE) parser primitives shared by the streaming format\n * adapters in `streamingClient.ts`. Implements the subset of the SSE wire\n * format that LLM providers actually emit: optional `event:` line, one or\n * more `data:` lines per message, messages separated by a blank line.\n *\n * @packageDocumentation\n */\n\n/**\n * One parsed SSE message (the SSE spec calls each blank-line-terminated\n * record an \"event\").\n *\n * @internal\n */\nexport interface ISseEvent {\n /** The `event:` field, if present (some providers omit it). */\n readonly event?: string;\n /** Concatenated contents of the message's `data:` lines. */\n readonly data: string;\n}\n\n/**\n * Parses a single SSE message (the text between blank-line separators) into\n * an {@link ISseEvent}. Returns undefined for messages with no `data:` lines\n * (comments, heartbeats).\n *\n * @internal\n */\nexport function parseSseEvent(chunk: string): ISseEvent | undefined {\n let event: string | undefined;\n const dataLines: string[] = [];\n for (const line of chunk.split('\\n')) {\n if (line.startsWith('event:')) {\n event = line.slice(6).trim();\n } else if (line.startsWith('data:')) {\n // Per the SSE spec the value starts after the colon, with one optional leading space stripped.\n const value = line.slice(5);\n dataLines.push(value.startsWith(' ') ? value.slice(1) : value);\n }\n }\n if (dataLines.length === 0) {\n return undefined;\n }\n return event !== undefined ? { event, data: dataLines.join('\\n') } : { data: dataLines.join('\\n') };\n}\n\n/**\n * Reads an SSE response body and yields parsed events. Buffers across read()\n * boundaries so a message split mid-chunk still parses cleanly. Terminates\n * when the stream closes (normal EOF or aborted fetch).\n *\n * @internal\n */\nexport async function* readSseEvents(body: ReadableStream<Uint8Array>): AsyncGenerator<ISseEvent> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n try {\n let streaming = true;\n while (streaming) {\n const { value, done } = await reader.read();\n if (done) {\n streaming = false;\n if (buffer.length > 0) {\n const tail = parseSseEvent(buffer.replace(/\\r\\n/g, '\\n'));\n if (tail) {\n yield tail;\n }\n }\n break;\n }\n buffer += decoder.decode(value, { stream: true });\n // SSE messages are separated by a blank line; some servers use \\r\\n.\n const normalized = buffer.replace(/\\r\\n/g, '\\n');\n const parts = normalized.split('\\n\\n');\n // Last element is the partial chunk (no terminating blank line yet); buffer it.\n buffer = parts.pop() ?? '';\n for (const chunk of parts) {\n const event = parseSseEvent(chunk);\n if (event) {\n yield event;\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n}\n\n/**\n * Parses the `data` payload of an SSE event as JSON. Returns undefined for\n * the OpenAI `[DONE]` sentinel and for any payload that fails to parse —\n * adapters treat both as \"skip this event.\"\n *\n * @internal\n */\nexport function parseSseEventJson(data: string): unknown | undefined {\n if (data === '[DONE]') {\n return undefined;\n }\n try {\n return JSON.parse(data);\n } catch {\n return undefined;\n }\n}\n"]}
|
|
@@ -0,0 +1,192 @@
|
|
|
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 __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
22
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
23
|
+
var m = o[Symbol.asyncIterator], i;
|
|
24
|
+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
25
|
+
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
26
|
+
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
27
|
+
};
|
|
28
|
+
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
|
|
29
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
30
|
+
var g = generator.apply(thisArg, _arguments || []), i, q = [];
|
|
31
|
+
return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
|
|
32
|
+
function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }
|
|
33
|
+
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]); } }
|
|
34
|
+
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
|
|
35
|
+
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
|
|
36
|
+
function fulfill(value) { resume("next", value); }
|
|
37
|
+
function reject(value) { resume("throw", value); }
|
|
38
|
+
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Streaming adapter for the Anthropic Messages API. Surfaces server tool
|
|
42
|
+
* use (e.g. `web_search`) as unified `tool-event` markers based on the
|
|
43
|
+
* `content_block_start` / `content_block_stop` lifecycle.
|
|
44
|
+
*
|
|
45
|
+
* @packageDocumentation
|
|
46
|
+
*/
|
|
47
|
+
import { succeed, Validators } from '@fgv/ts-utils';
|
|
48
|
+
import { buildAnthropicMessages } from '../chatRequestBuilders';
|
|
49
|
+
import { parseSseEventJson, readSseEvents } from '../sseParser';
|
|
50
|
+
import { toAnthropicTools } from '../toolFormats';
|
|
51
|
+
import { openSseConnection, validateEventPayload } from './common';
|
|
52
|
+
const anthropicContentBlockInner = Validators.object({
|
|
53
|
+
type: Validators.string.optional(),
|
|
54
|
+
name: Validators.string.optional()
|
|
55
|
+
}, { options: { optionalFields: ['type', 'name'] } });
|
|
56
|
+
const anthropicContentBlockStartPayload = Validators.object({
|
|
57
|
+
content_block: anthropicContentBlockInner
|
|
58
|
+
});
|
|
59
|
+
const anthropicContentBlockDeltaInner = Validators.object({
|
|
60
|
+
type: Validators.string.optional(),
|
|
61
|
+
text: Validators.string.optional()
|
|
62
|
+
}, { options: { optionalFields: ['type', 'text'] } });
|
|
63
|
+
const anthropicContentBlockDeltaPayload = Validators.object({
|
|
64
|
+
delta: anthropicContentBlockDeltaInner
|
|
65
|
+
});
|
|
66
|
+
const anthropicMessageDeltaInner = Validators.object({ stop_reason: Validators.string.optional() }, { options: { optionalFields: ['stop_reason'] } });
|
|
67
|
+
const anthropicMessageDeltaPayload = Validators.object({
|
|
68
|
+
delta: anthropicMessageDeltaInner
|
|
69
|
+
});
|
|
70
|
+
const anthropicErrorInner = Validators.object({ message: Validators.string.optional() }, { options: { optionalFields: ['message'] } });
|
|
71
|
+
const anthropicErrorPayload = Validators.object({ error: anthropicErrorInner.optional() }, { options: { optionalFields: ['error'] } });
|
|
72
|
+
// ============================================================================
|
|
73
|
+
// Stream translator
|
|
74
|
+
// ============================================================================
|
|
75
|
+
/**
|
|
76
|
+
* Translates an Anthropic Messages API SSE stream into unified events.
|
|
77
|
+
*
|
|
78
|
+
* @internal
|
|
79
|
+
*/
|
|
80
|
+
function translateAnthropicStream(response) {
|
|
81
|
+
return __asyncGenerator(this, arguments, function* translateAnthropicStream_1() {
|
|
82
|
+
var _a, e_1, _b, _c;
|
|
83
|
+
var _d, _e;
|
|
84
|
+
let fullText = '';
|
|
85
|
+
let truncated = false;
|
|
86
|
+
let stopped = false;
|
|
87
|
+
try {
|
|
88
|
+
/* c8 ignore next - body is non-null at this point per openSseConnection */
|
|
89
|
+
if (!response.body)
|
|
90
|
+
return yield __await(void 0);
|
|
91
|
+
try {
|
|
92
|
+
for (var _f = true, _g = __asyncValues(readSseEvents(response.body)), _h; _h = yield __await(_g.next()), _a = _h.done, !_a; _f = true) {
|
|
93
|
+
_c = _h.value;
|
|
94
|
+
_f = false;
|
|
95
|
+
const message = _c;
|
|
96
|
+
const eventName = message.event;
|
|
97
|
+
if (eventName === 'content_block_start') {
|
|
98
|
+
const payload = validateEventPayload(parseSseEventJson(message.data), anthropicContentBlockStartPayload);
|
|
99
|
+
const block = payload === null || payload === void 0 ? void 0 : payload.content_block;
|
|
100
|
+
if ((block === null || block === void 0 ? void 0 : block.type) === 'server_tool_use' && block.name === 'web_search') {
|
|
101
|
+
yield yield __await({ type: 'tool-event', toolType: 'web_search', phase: 'started' });
|
|
102
|
+
}
|
|
103
|
+
else if ((block === null || block === void 0 ? void 0 : block.type) === 'web_search_tool_result') {
|
|
104
|
+
yield yield __await({ type: 'tool-event', toolType: 'web_search', phase: 'completed' });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else if (eventName === 'content_block_delta') {
|
|
108
|
+
const payload = validateEventPayload(parseSseEventJson(message.data), anthropicContentBlockDeltaPayload);
|
|
109
|
+
if ((payload === null || payload === void 0 ? void 0 : payload.delta.type) === 'text_delta' && typeof payload.delta.text === 'string') {
|
|
110
|
+
const delta = payload.delta.text;
|
|
111
|
+
if (delta.length > 0) {
|
|
112
|
+
fullText += delta;
|
|
113
|
+
yield yield __await({ type: 'text-delta', delta });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else if (eventName === 'message_delta') {
|
|
118
|
+
const payload = validateEventPayload(parseSseEventJson(message.data), anthropicMessageDeltaPayload);
|
|
119
|
+
if ((payload === null || payload === void 0 ? void 0 : payload.delta.stop_reason) === 'max_tokens') {
|
|
120
|
+
truncated = true;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else if (eventName === 'message_stop') {
|
|
124
|
+
stopped = true;
|
|
125
|
+
}
|
|
126
|
+
else if (eventName === 'error') {
|
|
127
|
+
const payload = validateEventPayload(parseSseEventJson(message.data), anthropicErrorPayload);
|
|
128
|
+
yield yield __await({
|
|
129
|
+
type: 'error',
|
|
130
|
+
message: (_e = (_d = payload === null || payload === void 0 ? void 0 : payload.error) === null || _d === void 0 ? void 0 : _d.message) !== null && _e !== void 0 ? _e : 'Anthropic stream returned an error event'
|
|
131
|
+
});
|
|
132
|
+
return yield __await(void 0);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
137
|
+
finally {
|
|
138
|
+
try {
|
|
139
|
+
if (!_f && !_a && (_b = _g.return)) yield __await(_b.call(_g));
|
|
140
|
+
}
|
|
141
|
+
finally { if (e_1) throw e_1.error; }
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
yield yield __await({ type: 'error', message: err instanceof Error ? err.message : String(err) });
|
|
146
|
+
return yield __await(void 0);
|
|
147
|
+
}
|
|
148
|
+
if (stopped) {
|
|
149
|
+
yield yield __await({ type: 'done', truncated, fullText });
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
yield yield __await({ type: 'error', message: 'Anthropic stream ended without a message_stop event' });
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
// ============================================================================
|
|
157
|
+
// Per-format request caller
|
|
158
|
+
// ============================================================================
|
|
159
|
+
/**
|
|
160
|
+
* Issues a streaming Anthropic Messages request and returns the
|
|
161
|
+
* unified-event iterable on success.
|
|
162
|
+
*
|
|
163
|
+
* @internal
|
|
164
|
+
*/
|
|
165
|
+
export async function callAnthropicStream(config, prompt, messagesBefore, temperature, tools, logger, signal) {
|
|
166
|
+
const url = `${config.baseUrl}/messages`;
|
|
167
|
+
const messages = buildAnthropicMessages(prompt, { head: messagesBefore });
|
|
168
|
+
const body = {
|
|
169
|
+
model: config.model,
|
|
170
|
+
system: prompt.system,
|
|
171
|
+
messages,
|
|
172
|
+
max_tokens: 4096,
|
|
173
|
+
temperature,
|
|
174
|
+
stream: true
|
|
175
|
+
};
|
|
176
|
+
if (tools && tools.length > 0) {
|
|
177
|
+
body.tools = toAnthropicTools(tools);
|
|
178
|
+
}
|
|
179
|
+
const headers = {
|
|
180
|
+
'x-api-key': config.apiKey,
|
|
181
|
+
'anthropic-version': '2023-06-01',
|
|
182
|
+
'anthropic-dangerous-direct-browser-access': 'true'
|
|
183
|
+
};
|
|
184
|
+
/* c8 ignore next 3 - optional logger diagnostic output */
|
|
185
|
+
if (logger) {
|
|
186
|
+
const toolTypes = tools && tools.length > 0 ? tools.map((t) => t.type).join(',') : 'none';
|
|
187
|
+
logger.info(`Anthropic streaming: model=${config.model}, tools=${toolTypes}`);
|
|
188
|
+
}
|
|
189
|
+
const conn = await openSseConnection(url, headers, body, logger, signal);
|
|
190
|
+
return conn.onSuccess((response) => succeed(translateAnthropicStream(response)));
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=anthropic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../../../src/packlets/ai-assist/streamingAdapters/anthropic.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Streaming adapter for the Anthropic Messages API. Surfaces server tool\n * use (e.g. `web_search`) as unified `tool-event` markers based on the\n * `content_block_start` / `content_block_stop` lifecycle.\n *\n * @packageDocumentation\n */\n\nimport { type Logging, Result, succeed, type Validator, Validators } from '@fgv/ts-utils';\n\nimport { buildAnthropicMessages } from '../chatRequestBuilders';\nimport { AiPrompt, type AiServerToolConfig, type IAiStreamEvent, type IChatMessage } from '../model';\nimport { parseSseEventJson, readSseEvents } from '../sseParser';\nimport { toAnthropicTools } from '../toolFormats';\nimport { IStreamApiConfig, openSseConnection, validateEventPayload } from './common';\n\n// ============================================================================\n// Event payload shapes\n// ============================================================================\n\n/**\n * Payload of a `content_block_start` SSE event. The `type` discriminator\n * tells us whether the block is text, a server-tool invocation, or a\n * tool result.\n *\n * @internal\n */\ninterface IAnthropicContentBlockStartPayload {\n readonly content_block: { readonly type?: string; readonly name?: string };\n}\n\n/**\n * Payload of a `content_block_delta` SSE event. The inner `delta.type`\n * discriminator is `text_delta` for the text we care about; other values\n * (e.g. `input_json_delta` for tool args) are ignored.\n *\n * @internal\n */\ninterface IAnthropicContentBlockDeltaPayload {\n readonly delta: { readonly type?: string; readonly text?: string };\n}\n\n/**\n * Payload of a `message_delta` SSE event carrying the final stop reason.\n *\n * @internal\n */\ninterface IAnthropicMessageDeltaPayload {\n readonly delta: { readonly stop_reason?: string };\n}\n\n/**\n * Payload of an `error` SSE event.\n *\n * @internal\n */\ninterface IAnthropicErrorPayload {\n readonly error?: { readonly message?: string };\n}\n\nconst anthropicContentBlockInner: Validator<{ type?: string; name?: string }> = Validators.object<{\n type?: string;\n name?: string;\n}>(\n {\n type: Validators.string.optional(),\n name: Validators.string.optional()\n },\n { options: { optionalFields: ['type', 'name'] } }\n);\n\nconst anthropicContentBlockStartPayload: Validator<IAnthropicContentBlockStartPayload> =\n Validators.object<IAnthropicContentBlockStartPayload>({\n content_block: anthropicContentBlockInner\n });\n\nconst anthropicContentBlockDeltaInner: Validator<{ type?: string; text?: string }> = Validators.object<{\n type?: string;\n text?: string;\n}>(\n {\n type: Validators.string.optional(),\n text: Validators.string.optional()\n },\n { options: { optionalFields: ['type', 'text'] } }\n);\n\nconst anthropicContentBlockDeltaPayload: Validator<IAnthropicContentBlockDeltaPayload> =\n Validators.object<IAnthropicContentBlockDeltaPayload>({\n delta: anthropicContentBlockDeltaInner\n });\n\nconst anthropicMessageDeltaInner: Validator<{ stop_reason?: string }> = Validators.object<{\n stop_reason?: string;\n}>({ stop_reason: Validators.string.optional() }, { options: { optionalFields: ['stop_reason'] } });\n\nconst anthropicMessageDeltaPayload: Validator<IAnthropicMessageDeltaPayload> =\n Validators.object<IAnthropicMessageDeltaPayload>({\n delta: anthropicMessageDeltaInner\n });\n\nconst anthropicErrorInner: Validator<{ message?: string }> = Validators.object<{ message?: string }>(\n { message: Validators.string.optional() },\n { options: { optionalFields: ['message'] } }\n);\n\nconst anthropicErrorPayload: Validator<IAnthropicErrorPayload> = Validators.object<IAnthropicErrorPayload>(\n { error: anthropicErrorInner.optional() },\n { options: { optionalFields: ['error'] } }\n);\n\n// ============================================================================\n// Stream translator\n// ============================================================================\n\n/**\n * Translates an Anthropic Messages API SSE stream into unified events.\n *\n * @internal\n */\nasync function* translateAnthropicStream(response: Response): AsyncGenerator<IAiStreamEvent> {\n let fullText = '';\n let truncated = false;\n let stopped = false;\n\n try {\n /* c8 ignore next - body is non-null at this point per openSseConnection */\n if (!response.body) return;\n for await (const message of readSseEvents(response.body)) {\n const eventName = message.event;\n if (eventName === 'content_block_start') {\n const payload = validateEventPayload(\n parseSseEventJson(message.data),\n anthropicContentBlockStartPayload\n );\n const block = payload?.content_block;\n if (block?.type === 'server_tool_use' && block.name === 'web_search') {\n yield { type: 'tool-event', toolType: 'web_search', phase: 'started' };\n } else if (block?.type === 'web_search_tool_result') {\n yield { type: 'tool-event', toolType: 'web_search', phase: 'completed' };\n }\n } else if (eventName === 'content_block_delta') {\n const payload = validateEventPayload(\n parseSseEventJson(message.data),\n anthropicContentBlockDeltaPayload\n );\n if (payload?.delta.type === 'text_delta' && typeof payload.delta.text === 'string') {\n const delta = payload.delta.text;\n if (delta.length > 0) {\n fullText += delta;\n yield { type: 'text-delta', delta };\n }\n }\n } else if (eventName === 'message_delta') {\n const payload = validateEventPayload(parseSseEventJson(message.data), anthropicMessageDeltaPayload);\n if (payload?.delta.stop_reason === 'max_tokens') {\n truncated = true;\n }\n } else if (eventName === 'message_stop') {\n stopped = true;\n } else if (eventName === 'error') {\n const payload = validateEventPayload(parseSseEventJson(message.data), anthropicErrorPayload);\n yield {\n type: 'error',\n message: payload?.error?.message ?? 'Anthropic stream returned an error event'\n };\n return;\n }\n }\n } catch (err: unknown) {\n yield { type: 'error', message: err instanceof Error ? err.message : String(err) };\n return;\n }\n\n if (stopped) {\n yield { type: 'done', truncated, fullText };\n } else {\n yield { type: 'error', message: 'Anthropic stream ended without a message_stop event' };\n }\n}\n\n// ============================================================================\n// Per-format request caller\n// ============================================================================\n\n/**\n * Issues a streaming Anthropic Messages request and returns the\n * unified-event iterable on success.\n *\n * @internal\n */\nexport async function callAnthropicStream(\n config: IStreamApiConfig,\n prompt: AiPrompt,\n messagesBefore: ReadonlyArray<IChatMessage> | undefined,\n temperature: number,\n tools: ReadonlyArray<AiServerToolConfig> | undefined,\n logger?: Logging.ILogger,\n signal?: AbortSignal\n): Promise<Result<AsyncIterable<IAiStreamEvent>>> {\n const url = `${config.baseUrl}/messages`;\n const messages = buildAnthropicMessages(prompt, { head: messagesBefore });\n const body: Record<string, unknown> = {\n model: config.model,\n system: prompt.system,\n messages,\n max_tokens: 4096,\n temperature,\n stream: true\n };\n if (tools && tools.length > 0) {\n body.tools = toAnthropicTools(tools);\n }\n const headers: Record<string, string> = {\n 'x-api-key': config.apiKey,\n 'anthropic-version': '2023-06-01',\n 'anthropic-dangerous-direct-browser-access': 'true'\n };\n /* c8 ignore next 3 - optional logger diagnostic output */\n if (logger) {\n const toolTypes = tools && tools.length > 0 ? tools.map((t) => t.type).join(',') : 'none';\n logger.info(`Anthropic streaming: model=${config.model}, tools=${toolTypes}`);\n }\n const conn = await openSseConnection(url, headers, body, logger, signal);\n return conn.onSuccess((response) => succeed(translateAnthropicStream(response)));\n}\n"]}
|