@ai-sdk/typesafe-ai 0.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/LICENSE +13 -0
- package/README.md +58 -3
- package/dist/index.d.ts +23 -0
- package/dist/index.js +235 -0
- package/dist/index.js.map +1 -0
- package/docs/105-typesafe-ai.mdx +117 -0
- package/package.json +67 -11
- package/src/index.ts +7 -0
- package/src/typesafe-ai-evaluation-api.ts +49 -0
- package/src/typesafe-ai-evaluation-model.ts +183 -0
- package/src/typesafe-ai-provider.ts +67 -0
- package/src/version.ts +6 -0
- package/index.js +0 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# @ai-sdk/typesafe-ai
|
|
2
|
+
|
|
3
|
+
## 3.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 8c4aa22: Add the TypeSafe provider for `experimental_evaluate`, supporting native Choice, Score, and Boolean questions in one request, structured JSON rubrics, probability distributions, confidence metadata, and workflow serialization.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [91c2128]
|
|
12
|
+
- Updated dependencies [2cd80b3]
|
|
13
|
+
- Updated dependencies [d06bb2a]
|
|
14
|
+
- Updated dependencies [123d71f]
|
|
15
|
+
- Updated dependencies [2fa5e0e]
|
|
16
|
+
- @ai-sdk/provider-utils@5.0.42
|
|
17
|
+
- @ai-sdk/provider@4.0.16
|
package/LICENSE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright 2023 Vercel, Inc.
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|
package/README.md
CHANGED
|
@@ -1,5 +1,60 @@
|
|
|
1
|
-
#
|
|
1
|
+
# AI SDK TypeSafe Provider
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The **[TypeSafe provider](https://ai-sdk.dev/providers/ai-sdk-providers/typesafe-ai)**
|
|
4
|
+
for the [AI SDK](https://ai-sdk.dev/docs) evaluates Choice, Score, and Boolean
|
|
5
|
+
questions using TypeSafe's System One models.
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
pnpm add @ai-sdk/typesafe-ai ai
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Set `TYPESAFE_AI_API_KEY`, then pass an evaluation model instance:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { typeSafeAi } from '@ai-sdk/typesafe-ai';
|
|
17
|
+
import { experimental_evaluate } from 'ai';
|
|
18
|
+
|
|
19
|
+
const result = await experimental_evaluate({
|
|
20
|
+
model: typeSafeAi.evaluationModel('jev-latest'),
|
|
21
|
+
state: 'I was charged twice. Please refund the duplicate.',
|
|
22
|
+
questions: {
|
|
23
|
+
department: {
|
|
24
|
+
type: 'choice',
|
|
25
|
+
instructions: 'Which team should handle this?',
|
|
26
|
+
criteria: { billing: 'Charges and refunds', support: 'Other requests' },
|
|
27
|
+
},
|
|
28
|
+
requestsRefund: {
|
|
29
|
+
type: 'boolean',
|
|
30
|
+
instructions: 'Is the customer requesting money back?',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
console.log(result.answers);
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Evaluation is experimental. The package exports `typeSafeAi`, `createTypeSafeAi`,
|
|
39
|
+
`TypeSafeAiProviderSettings`, `TypeSafeAiProvider`,
|
|
40
|
+
`Experimental_TypeSafeAiEvaluationModelId`, and `VERSION`.
|
|
41
|
+
|
|
42
|
+
## Configuration
|
|
43
|
+
|
|
44
|
+
`createTypeSafeAi({ apiKey, baseURL, headers, fetch })` supports an explicit key,
|
|
45
|
+
custom headers, a custom fetch implementation, and a base URL (default:
|
|
46
|
+
`https://api.typesafe.ai/v1`). The default key comes from `TYPESAFE_AI_API_KEY`.
|
|
47
|
+
Language, embedding, and image model factories are unsupported.
|
|
48
|
+
|
|
49
|
+
All questions are sent in one request against shared state. Choice supports up to
|
|
50
|
+
255 options; Score supports 2–10 ordered levels; Boolean maps to TypeSafe's Noul
|
|
51
|
+
primitive. Instructions and descriptions accept structured JSON. No
|
|
52
|
+
provider-specific options are currently defined.
|
|
53
|
+
|
|
54
|
+
Native distributions and scores are preserved. TypeSafe returns rounded values;
|
|
55
|
+
`result.rounding` declares two decimal places so core can validate rounding error
|
|
56
|
+
without changing the numbers. TypeSafe confidence is separate from probability:
|
|
57
|
+
find it at `result.providerMetadata.typesafe.confidence[questionId]`.
|
|
58
|
+
|
|
59
|
+
See the [provider documentation](https://ai-sdk.dev/providers/ai-sdk-providers/typesafe-ai)
|
|
60
|
+
and [TypeSafe API reference](https://docs.typesafe.ai/api).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ProviderV4, Experimental_EvaluationModelV4 } from '@ai-sdk/provider';
|
|
2
|
+
import { FetchFunction } from '@ai-sdk/provider-utils';
|
|
3
|
+
|
|
4
|
+
type TypeSafeAiEvaluationModelId = 'jev-latest' | (string & {});
|
|
5
|
+
|
|
6
|
+
/** TypeSafe's experimental evaluation capability, isolated from ProviderV4. */
|
|
7
|
+
interface TypeSafeAiProvider extends ProviderV4 {
|
|
8
|
+
evaluationModel(modelId: TypeSafeAiEvaluationModelId): Experimental_EvaluationModelV4;
|
|
9
|
+
}
|
|
10
|
+
interface TypeSafeAiProviderSettings {
|
|
11
|
+
/** API key. Defaults to the TYPESAFE_AI_API_KEY environment variable. */
|
|
12
|
+
apiKey?: string;
|
|
13
|
+
/** API base URL. Defaults to https://api.typesafe.ai/v1. */
|
|
14
|
+
baseURL?: string;
|
|
15
|
+
headers?: Record<string, string>;
|
|
16
|
+
fetch?: FetchFunction;
|
|
17
|
+
}
|
|
18
|
+
declare function createTypeSafeAi(options?: TypeSafeAiProviderSettings): TypeSafeAiProvider;
|
|
19
|
+
declare const typeSafeAi: TypeSafeAiProvider;
|
|
20
|
+
|
|
21
|
+
declare const VERSION: string;
|
|
22
|
+
|
|
23
|
+
export { type TypeSafeAiEvaluationModelId as Experimental_TypeSafeAiEvaluationModelId, type TypeSafeAiProvider, type TypeSafeAiProviderSettings, VERSION, createTypeSafeAi, typeSafeAi };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
// src/typesafe-ai-provider.ts
|
|
2
|
+
import {
|
|
3
|
+
NoSuchModelError
|
|
4
|
+
} from "@ai-sdk/provider";
|
|
5
|
+
import {
|
|
6
|
+
loadApiKey as loadApiKey2,
|
|
7
|
+
withoutTrailingSlash,
|
|
8
|
+
withUserAgentSuffix as withUserAgentSuffix2
|
|
9
|
+
} from "@ai-sdk/provider-utils";
|
|
10
|
+
|
|
11
|
+
// src/typesafe-ai-evaluation-model.ts
|
|
12
|
+
import {
|
|
13
|
+
InvalidArgumentError
|
|
14
|
+
} from "@ai-sdk/provider";
|
|
15
|
+
import {
|
|
16
|
+
combineHeaders,
|
|
17
|
+
createJsonResponseHandler,
|
|
18
|
+
loadApiKey,
|
|
19
|
+
postJsonToApi,
|
|
20
|
+
resolve,
|
|
21
|
+
serializeModelOptions,
|
|
22
|
+
withUserAgentSuffix,
|
|
23
|
+
WORKFLOW_SERIALIZE,
|
|
24
|
+
WORKFLOW_DESERIALIZE
|
|
25
|
+
} from "@ai-sdk/provider-utils";
|
|
26
|
+
|
|
27
|
+
// src/typesafe-ai-evaluation-api.ts
|
|
28
|
+
import { createJsonErrorResponseHandler } from "@ai-sdk/provider-utils";
|
|
29
|
+
import * as z from "zod/v4";
|
|
30
|
+
var typesafeEvaluationResponseSchema = z.object({
|
|
31
|
+
model: z.string().nullish(),
|
|
32
|
+
answers: z.record(
|
|
33
|
+
z.string(),
|
|
34
|
+
z.discriminatedUnion("type", [
|
|
35
|
+
z.object({
|
|
36
|
+
type: z.literal("choice"),
|
|
37
|
+
choice: z.string(),
|
|
38
|
+
probabilities: z.record(z.string(), z.number()),
|
|
39
|
+
confidence: z.number().nullish()
|
|
40
|
+
}),
|
|
41
|
+
z.object({
|
|
42
|
+
type: z.literal("score"),
|
|
43
|
+
score: z.number(),
|
|
44
|
+
probabilities: z.record(z.string(), z.number()),
|
|
45
|
+
confidence: z.number().nullish()
|
|
46
|
+
}),
|
|
47
|
+
z.object({ type: z.literal("noul"), noul: z.number() })
|
|
48
|
+
])
|
|
49
|
+
),
|
|
50
|
+
usage: z.object({
|
|
51
|
+
input_tokens: z.number().nullish(),
|
|
52
|
+
output_tokens: z.number().nullish()
|
|
53
|
+
}).nullish()
|
|
54
|
+
});
|
|
55
|
+
var typesafeFailedResponseHandler = createJsonErrorResponseHandler({
|
|
56
|
+
errorSchema: z.object({
|
|
57
|
+
message: z.string().nullish(),
|
|
58
|
+
detail: z.unknown().nullish(),
|
|
59
|
+
error: z.union([z.string(), z.object({ message: z.string().nullish() })]).nullish()
|
|
60
|
+
}),
|
|
61
|
+
errorToMessage: (error) => {
|
|
62
|
+
var _a, _b, _c, _d;
|
|
63
|
+
return (_d = (_c = (_b = error.message) != null ? _b : typeof error.error === "string" ? error.error : (_a = error.error) == null ? void 0 : _a.message) != null ? _c : typeof error.detail === "string" ? error.detail : JSON.stringify(error.detail)) != null ? _d : "TypeSafe request failed";
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// src/version.ts
|
|
68
|
+
var VERSION = true ? "3.0.0" : "0.0.0-test";
|
|
69
|
+
|
|
70
|
+
// src/typesafe-ai-evaluation-model.ts
|
|
71
|
+
var EvaluationTypeSafeAiModel = class _EvaluationTypeSafeAiModel {
|
|
72
|
+
constructor(modelId, config) {
|
|
73
|
+
this.modelId = modelId;
|
|
74
|
+
this.config = config;
|
|
75
|
+
this.specificationVersion = "v4";
|
|
76
|
+
this.supportedQuestionTypes = ["choice", "score", "boolean"];
|
|
77
|
+
}
|
|
78
|
+
get provider() {
|
|
79
|
+
return this.config.provider;
|
|
80
|
+
}
|
|
81
|
+
static [WORKFLOW_SERIALIZE](model) {
|
|
82
|
+
return serializeModelOptions({
|
|
83
|
+
modelId: model.modelId,
|
|
84
|
+
config: model.config
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
static [WORKFLOW_DESERIALIZE](options) {
|
|
88
|
+
return new _EvaluationTypeSafeAiModel(options.modelId, options.config);
|
|
89
|
+
}
|
|
90
|
+
async doEvaluate({
|
|
91
|
+
state,
|
|
92
|
+
questions,
|
|
93
|
+
headers,
|
|
94
|
+
abortSignal,
|
|
95
|
+
providerOptions
|
|
96
|
+
}) {
|
|
97
|
+
var _a, _b, _c, _d, _e, _f;
|
|
98
|
+
for (const [id, question] of Object.entries(questions)) {
|
|
99
|
+
if (question.type === "choice" && Object.keys(question.criteria).length > 255) {
|
|
100
|
+
throw new InvalidArgumentError({
|
|
101
|
+
argument: `questions.${id}.criteria`,
|
|
102
|
+
message: "TypeSafe Choice questions support at most 255 options."
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
if (question.type === "score" && question.criteria.length > 10) {
|
|
106
|
+
throw new InvalidArgumentError({
|
|
107
|
+
argument: `questions.${id}.criteria`,
|
|
108
|
+
message: "TypeSafe Score questions support at most 10 levels."
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const warnings = Object.keys(
|
|
113
|
+
(_a = providerOptions == null ? void 0 : providerOptions.typesafe) != null ? _a : {}
|
|
114
|
+
).map((option) => ({
|
|
115
|
+
type: "unsupported",
|
|
116
|
+
feature: `providerOptions.typesafe.${option}`
|
|
117
|
+
}));
|
|
118
|
+
const modelHeaders = this.config.headers === void 0 ? withUserAgentSuffix(
|
|
119
|
+
{
|
|
120
|
+
Authorization: `Bearer ${loadApiKey({ apiKey: void 0, environmentVariableName: "TYPESAFE_AI_API_KEY", description: "TypeSafe" })}`
|
|
121
|
+
},
|
|
122
|
+
`ai-sdk/typesafe-ai/${VERSION}`
|
|
123
|
+
) : await resolve(this.config.headers);
|
|
124
|
+
const {
|
|
125
|
+
value: response,
|
|
126
|
+
rawValue,
|
|
127
|
+
responseHeaders
|
|
128
|
+
} = await postJsonToApi({
|
|
129
|
+
url: `${this.config.baseURL}/systemone`,
|
|
130
|
+
headers: combineHeaders(modelHeaders, headers),
|
|
131
|
+
body: {
|
|
132
|
+
model: this.modelId,
|
|
133
|
+
state,
|
|
134
|
+
questions: Object.fromEntries(
|
|
135
|
+
Object.entries(questions).map(([id, question]) => [
|
|
136
|
+
id,
|
|
137
|
+
question.type === "boolean" ? { ...question, type: "noul" } : question
|
|
138
|
+
])
|
|
139
|
+
)
|
|
140
|
+
},
|
|
141
|
+
abortSignal,
|
|
142
|
+
fetch: this.config.fetch,
|
|
143
|
+
failedResponseHandler: typesafeFailedResponseHandler,
|
|
144
|
+
successfulResponseHandler: createJsonResponseHandler(
|
|
145
|
+
typesafeEvaluationResponseSchema
|
|
146
|
+
)
|
|
147
|
+
});
|
|
148
|
+
const confidence = Object.fromEntries(
|
|
149
|
+
Object.entries(response.answers).flatMap(
|
|
150
|
+
([id, answer]) => answer.type !== "noul" && answer.confidence != null ? [[id, answer.confidence]] : []
|
|
151
|
+
)
|
|
152
|
+
);
|
|
153
|
+
return {
|
|
154
|
+
answers: Object.fromEntries(
|
|
155
|
+
Object.entries(response.answers).map(
|
|
156
|
+
([id, answer]) => {
|
|
157
|
+
switch (answer.type) {
|
|
158
|
+
case "noul":
|
|
159
|
+
return [id, { type: "boolean", probability: answer.noul }];
|
|
160
|
+
case "choice":
|
|
161
|
+
return [
|
|
162
|
+
id,
|
|
163
|
+
{
|
|
164
|
+
type: "choice",
|
|
165
|
+
choice: answer.choice,
|
|
166
|
+
probabilities: answer.probabilities
|
|
167
|
+
}
|
|
168
|
+
];
|
|
169
|
+
case "score":
|
|
170
|
+
return [
|
|
171
|
+
id,
|
|
172
|
+
{
|
|
173
|
+
type: "score",
|
|
174
|
+
score: answer.score,
|
|
175
|
+
probabilities: answer.probabilities
|
|
176
|
+
}
|
|
177
|
+
];
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
)
|
|
181
|
+
),
|
|
182
|
+
usage: {
|
|
183
|
+
inputTokens: (_c = (_b = response.usage) == null ? void 0 : _b.input_tokens) != null ? _c : void 0,
|
|
184
|
+
outputTokens: (_e = (_d = response.usage) == null ? void 0 : _d.output_tokens) != null ? _e : void 0
|
|
185
|
+
},
|
|
186
|
+
// The API rounds displayed probabilities and scores to two decimal places.
|
|
187
|
+
rounding: { probabilityDecimals: 2, scoreDecimals: 2 },
|
|
188
|
+
warnings,
|
|
189
|
+
providerMetadata: { typesafe: { confidence } },
|
|
190
|
+
response: {
|
|
191
|
+
modelId: (_f = response.model) != null ? _f : this.modelId,
|
|
192
|
+
headers: responseHeaders,
|
|
193
|
+
body: rawValue
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// src/typesafe-ai-provider.ts
|
|
200
|
+
function createTypeSafeAi(options = {}) {
|
|
201
|
+
var _a;
|
|
202
|
+
const baseURL = (_a = withoutTrailingSlash(options.baseURL)) != null ? _a : "https://api.typesafe.ai/v1";
|
|
203
|
+
const headers = () => withUserAgentSuffix2(
|
|
204
|
+
{
|
|
205
|
+
Authorization: `Bearer ${loadApiKey2({ apiKey: options.apiKey, environmentVariableName: "TYPESAFE_AI_API_KEY", description: "TypeSafe" })}`,
|
|
206
|
+
...options.headers
|
|
207
|
+
},
|
|
208
|
+
`ai-sdk/typesafe-ai/${VERSION}`
|
|
209
|
+
);
|
|
210
|
+
return {
|
|
211
|
+
specificationVersion: "v4",
|
|
212
|
+
evaluationModel: (modelId) => new EvaluationTypeSafeAiModel(modelId, {
|
|
213
|
+
provider: "typesafe.evaluation",
|
|
214
|
+
baseURL,
|
|
215
|
+
headers,
|
|
216
|
+
fetch: options.fetch
|
|
217
|
+
}),
|
|
218
|
+
languageModel: (modelId) => {
|
|
219
|
+
throw new NoSuchModelError({ modelId, modelType: "languageModel" });
|
|
220
|
+
},
|
|
221
|
+
embeddingModel: (modelId) => {
|
|
222
|
+
throw new NoSuchModelError({ modelId, modelType: "embeddingModel" });
|
|
223
|
+
},
|
|
224
|
+
imageModel: (modelId) => {
|
|
225
|
+
throw new NoSuchModelError({ modelId, modelType: "imageModel" });
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
var typeSafeAi = createTypeSafeAi();
|
|
230
|
+
export {
|
|
231
|
+
VERSION,
|
|
232
|
+
createTypeSafeAi,
|
|
233
|
+
typeSafeAi
|
|
234
|
+
};
|
|
235
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/typesafe-ai-provider.ts","../src/typesafe-ai-evaluation-model.ts","../src/typesafe-ai-evaluation-api.ts","../src/version.ts"],"sourcesContent":["import {\n NoSuchModelError,\n type Experimental_EvaluationModelV4 as EvaluationModelV4,\n type ProviderV4,\n} from '@ai-sdk/provider';\nimport {\n loadApiKey,\n withoutTrailingSlash,\n withUserAgentSuffix,\n type FetchFunction,\n} from '@ai-sdk/provider-utils';\nimport {\n EvaluationTypeSafeAiModel,\n type TypeSafeAiEvaluationModelId,\n} from './typesafe-ai-evaluation-model';\nimport { VERSION } from './version';\n\n/** TypeSafe's experimental evaluation capability, isolated from ProviderV4. */\nexport interface TypeSafeAiProvider extends ProviderV4 {\n evaluationModel(modelId: TypeSafeAiEvaluationModelId): EvaluationModelV4;\n}\n\nexport interface TypeSafeAiProviderSettings {\n /** API key. Defaults to the TYPESAFE_AI_API_KEY environment variable. */\n apiKey?: string;\n /** API base URL. Defaults to https://api.typesafe.ai/v1. */\n baseURL?: string;\n headers?: Record<string, string>;\n fetch?: FetchFunction;\n}\n\nexport function createTypeSafeAi(\n options: TypeSafeAiProviderSettings = {},\n): TypeSafeAiProvider {\n const baseURL =\n withoutTrailingSlash(options.baseURL) ?? 'https://api.typesafe.ai/v1';\n const headers = () =>\n withUserAgentSuffix(\n {\n Authorization: `Bearer ${loadApiKey({ apiKey: options.apiKey, environmentVariableName: 'TYPESAFE_AI_API_KEY', description: 'TypeSafe' })}`,\n ...options.headers,\n },\n `ai-sdk/typesafe-ai/${VERSION}`,\n );\n\n return {\n specificationVersion: 'v4',\n evaluationModel: modelId =>\n new EvaluationTypeSafeAiModel(modelId, {\n provider: 'typesafe.evaluation',\n baseURL,\n headers,\n fetch: options.fetch,\n }),\n languageModel: modelId => {\n throw new NoSuchModelError({ modelId, modelType: 'languageModel' });\n },\n embeddingModel: modelId => {\n throw new NoSuchModelError({ modelId, modelType: 'embeddingModel' });\n },\n imageModel: modelId => {\n throw new NoSuchModelError({ modelId, modelType: 'imageModel' });\n },\n };\n}\n\nexport const typeSafeAi = createTypeSafeAi();\n","import {\n InvalidArgumentError,\n type Experimental_EvaluationModelV4 as EvaluationModelV4,\n type Experimental_EvaluationModelV4Answer as EvaluationModelV4Answer,\n type SharedV4Warning,\n} from '@ai-sdk/provider';\nimport {\n combineHeaders,\n createJsonResponseHandler,\n loadApiKey,\n postJsonToApi,\n resolve,\n serializeModelOptions,\n withUserAgentSuffix,\n WORKFLOW_SERIALIZE,\n WORKFLOW_DESERIALIZE,\n type FetchFunction,\n type Resolvable,\n} from '@ai-sdk/provider-utils';\nimport {\n typesafeEvaluationResponseSchema,\n typesafeFailedResponseHandler,\n} from './typesafe-ai-evaluation-api';\nimport { VERSION } from './version';\n\nexport type TypeSafeAiEvaluationModelId = 'jev-latest' | (string & {});\n\ntype TypeSafeAiEvaluationModelConfig = {\n provider: string;\n baseURL: string;\n headers?: Resolvable<Record<string, string | undefined>>;\n fetch?: FetchFunction;\n};\n\nexport class EvaluationTypeSafeAiModel implements EvaluationModelV4 {\n readonly specificationVersion = 'v4';\n readonly supportedQuestionTypes = ['choice', 'score', 'boolean'] as const;\n\n constructor(\n readonly modelId: TypeSafeAiEvaluationModelId,\n private readonly config: TypeSafeAiEvaluationModelConfig,\n ) {}\n\n get provider() {\n return this.config.provider;\n }\n\n static [WORKFLOW_SERIALIZE](model: EvaluationTypeSafeAiModel) {\n return serializeModelOptions({\n modelId: model.modelId,\n config: model.config,\n });\n }\n\n static [WORKFLOW_DESERIALIZE](options: {\n modelId: TypeSafeAiEvaluationModelId;\n config: TypeSafeAiEvaluationModelConfig;\n }) {\n return new EvaluationTypeSafeAiModel(options.modelId, options.config);\n }\n\n async doEvaluate({\n state,\n questions,\n headers,\n abortSignal,\n providerOptions,\n }: Parameters<EvaluationModelV4['doEvaluate']>[0]): Promise<\n Awaited<ReturnType<EvaluationModelV4['doEvaluate']>>\n > {\n for (const [id, question] of Object.entries(questions)) {\n if (\n question.type === 'choice' &&\n Object.keys(question.criteria).length > 255\n ) {\n throw new InvalidArgumentError({\n argument: `questions.${id}.criteria`,\n message: 'TypeSafe Choice questions support at most 255 options.',\n });\n }\n if (question.type === 'score' && question.criteria.length > 10) {\n throw new InvalidArgumentError({\n argument: `questions.${id}.criteria`,\n message: 'TypeSafe Score questions support at most 10 levels.',\n });\n }\n }\n\n const warnings: SharedV4Warning[] = Object.keys(\n providerOptions?.typesafe ?? {},\n ).map(option => ({\n type: 'unsupported',\n feature: `providerOptions.typesafe.${option}`,\n }));\n const modelHeaders =\n this.config.headers === undefined\n ? withUserAgentSuffix(\n {\n Authorization: `Bearer ${loadApiKey({ apiKey: undefined, environmentVariableName: 'TYPESAFE_AI_API_KEY', description: 'TypeSafe' })}`,\n },\n `ai-sdk/typesafe-ai/${VERSION}`,\n )\n : await resolve(this.config.headers);\n const {\n value: response,\n rawValue,\n responseHeaders,\n } = await postJsonToApi({\n url: `${this.config.baseURL}/systemone`,\n headers: combineHeaders(modelHeaders, headers),\n body: {\n model: this.modelId,\n state,\n questions: Object.fromEntries(\n Object.entries(questions).map(([id, question]) => [\n id,\n question.type === 'boolean'\n ? { ...question, type: 'noul' }\n : question,\n ]),\n ),\n },\n abortSignal,\n fetch: this.config.fetch,\n failedResponseHandler: typesafeFailedResponseHandler,\n successfulResponseHandler: createJsonResponseHandler(\n typesafeEvaluationResponseSchema,\n ),\n });\n\n const confidence = Object.fromEntries(\n Object.entries(response.answers).flatMap(([id, answer]) =>\n answer.type !== 'noul' && answer.confidence != null\n ? [[id, answer.confidence]]\n : [],\n ),\n );\n\n return {\n answers: Object.fromEntries(\n Object.entries(response.answers).map(\n ([id, answer]): [string, EvaluationModelV4Answer] => {\n switch (answer.type) {\n case 'noul':\n return [id, { type: 'boolean', probability: answer.noul }];\n case 'choice':\n return [\n id,\n {\n type: 'choice',\n choice: answer.choice,\n probabilities: answer.probabilities,\n },\n ];\n case 'score':\n return [\n id,\n {\n type: 'score',\n score: answer.score,\n probabilities: answer.probabilities,\n },\n ];\n }\n },\n ),\n ),\n usage: {\n inputTokens: response.usage?.input_tokens ?? undefined,\n outputTokens: response.usage?.output_tokens ?? undefined,\n },\n // The API rounds displayed probabilities and scores to two decimal places.\n rounding: { probabilityDecimals: 2, scoreDecimals: 2 },\n warnings,\n providerMetadata: { typesafe: { confidence } },\n response: {\n modelId: response.model ?? this.modelId,\n headers: responseHeaders,\n body: rawValue,\n },\n };\n }\n}\n","import { createJsonErrorResponseHandler } from '@ai-sdk/provider-utils';\nimport * as z from 'zod/v4';\n\n// Verified against the official SDK at 66880ccded6cb642dc1809620c2b108c33730214.\n// https://github.com/typesafe-ai/typesafe-sdk-js/blob/66880ccded6cb642dc1809620c2b108c33730214/src/types.ts\nexport const typesafeEvaluationResponseSchema = z.object({\n model: z.string().nullish(),\n answers: z.record(\n z.string(),\n z.discriminatedUnion('type', [\n z.object({\n type: z.literal('choice'),\n choice: z.string(),\n probabilities: z.record(z.string(), z.number()),\n confidence: z.number().nullish(),\n }),\n z.object({\n type: z.literal('score'),\n score: z.number(),\n probabilities: z.record(z.string(), z.number()),\n confidence: z.number().nullish(),\n }),\n z.object({ type: z.literal('noul'), noul: z.number() }),\n ]),\n ),\n usage: z\n .object({\n input_tokens: z.number().nullish(),\n output_tokens: z.number().nullish(),\n })\n .nullish(),\n});\n\nexport const typesafeFailedResponseHandler = createJsonErrorResponseHandler({\n errorSchema: z.object({\n message: z.string().nullish(),\n detail: z.unknown().nullish(),\n error: z\n .union([z.string(), z.object({ message: z.string().nullish() })])\n .nullish(),\n }),\n errorToMessage: error =>\n error.message ??\n (typeof error.error === 'string' ? error.error : error.error?.message) ??\n (typeof error.detail === 'string'\n ? error.detail\n : JSON.stringify(error.detail)) ??\n 'TypeSafe request failed',\n});\n","// Version string of this package injected at build time.\ndeclare const __PACKAGE_VERSION__: string | undefined;\nexport const VERSION: string =\n typeof __PACKAGE_VERSION__ !== 'undefined'\n ? __PACKAGE_VERSION__\n : '0.0.0-test';\n"],"mappings":";AAAA;AAAA,EACE;AAAA,OAGK;AACP;AAAA,EACE,cAAAA;AAAA,EACA;AAAA,EACA,uBAAAC;AAAA,OAEK;;;ACVP;AAAA,EACE;AAAA,OAIK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;;;AClBP,SAAS,sCAAsC;AAC/C,YAAY,OAAO;AAIZ,IAAM,mCAAqC,SAAO;AAAA,EACvD,OAAS,SAAO,EAAE,QAAQ;AAAA,EAC1B,SAAW;AAAA,IACP,SAAO;AAAA,IACP,qBAAmB,QAAQ;AAAA,MACzB,SAAO;AAAA,QACP,MAAQ,UAAQ,QAAQ;AAAA,QACxB,QAAU,SAAO;AAAA,QACjB,eAAiB,SAAS,SAAO,GAAK,SAAO,CAAC;AAAA,QAC9C,YAAc,SAAO,EAAE,QAAQ;AAAA,MACjC,CAAC;AAAA,MACC,SAAO;AAAA,QACP,MAAQ,UAAQ,OAAO;AAAA,QACvB,OAAS,SAAO;AAAA,QAChB,eAAiB,SAAS,SAAO,GAAK,SAAO,CAAC;AAAA,QAC9C,YAAc,SAAO,EAAE,QAAQ;AAAA,MACjC,CAAC;AAAA,MACC,SAAO,EAAE,MAAQ,UAAQ,MAAM,GAAG,MAAQ,SAAO,EAAE,CAAC;AAAA,IACxD,CAAC;AAAA,EACH;AAAA,EACA,OACG,SAAO;AAAA,IACN,cAAgB,SAAO,EAAE,QAAQ;AAAA,IACjC,eAAiB,SAAO,EAAE,QAAQ;AAAA,EACpC,CAAC,EACA,QAAQ;AACb,CAAC;AAEM,IAAM,gCAAgC,+BAA+B;AAAA,EAC1E,aAAe,SAAO;AAAA,IACpB,SAAW,SAAO,EAAE,QAAQ;AAAA,IAC5B,QAAU,UAAQ,EAAE,QAAQ;AAAA,IAC5B,OACG,QAAM,CAAG,SAAO,GAAK,SAAO,EAAE,SAAW,SAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAC/D,QAAQ;AAAA,EACb,CAAC;AAAA,EACD,gBAAgB,WAAM;AAzCxB;AA0CI,mCAAM,YAAN,YACC,OAAO,MAAM,UAAU,WAAW,MAAM,SAAQ,WAAM,UAAN,mBAAa,YAD9D,YAEC,OAAO,MAAM,WAAW,WACrB,MAAM,SACN,KAAK,UAAU,MAAM,MAAM,MAJ/B,YAKA;AAAA;AACJ,CAAC;;;AC9CM,IAAM,UACX,OACI,UACA;;;AF6BC,IAAM,4BAAN,MAAM,2BAAuD;AAAA,EAIlE,YACW,SACQ,QACjB;AAFS;AACQ;AALnB,SAAS,uBAAuB;AAChC,SAAS,yBAAyB,CAAC,UAAU,SAAS,SAAS;AAAA,EAK5D;AAAA,EAEH,IAAI,WAAW;AACb,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEA,QAAQ,kBAAkB,EAAE,OAAkC;AAC5D,WAAO,sBAAsB;AAAA,MAC3B,SAAS,MAAM;AAAA,MACf,QAAQ,MAAM;AAAA,IAChB,CAAC;AAAA,EACH;AAAA,EAEA,QAAQ,oBAAoB,EAAE,SAG3B;AACD,WAAO,IAAI,2BAA0B,QAAQ,SAAS,QAAQ,MAAM;AAAA,EACtE;AAAA,EAEA,MAAM,WAAW;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAEE;AArEJ;AAsEI,eAAW,CAAC,IAAI,QAAQ,KAAK,OAAO,QAAQ,SAAS,GAAG;AACtD,UACE,SAAS,SAAS,YAClB,OAAO,KAAK,SAAS,QAAQ,EAAE,SAAS,KACxC;AACA,cAAM,IAAI,qBAAqB;AAAA,UAC7B,UAAU,aAAa,EAAE;AAAA,UACzB,SAAS;AAAA,QACX,CAAC;AAAA,MACH;AACA,UAAI,SAAS,SAAS,WAAW,SAAS,SAAS,SAAS,IAAI;AAC9D,cAAM,IAAI,qBAAqB;AAAA,UAC7B,UAAU,aAAa,EAAE;AAAA,UACzB,SAAS;AAAA,QACX,CAAC;AAAA,MACH;AAAA,IACF;AAEA,UAAM,WAA8B,OAAO;AAAA,OACzC,wDAAiB,aAAjB,YAA6B,CAAC;AAAA,IAChC,EAAE,IAAI,aAAW;AAAA,MACf,MAAM;AAAA,MACN,SAAS,4BAA4B,MAAM;AAAA,IAC7C,EAAE;AACF,UAAM,eACJ,KAAK,OAAO,YAAY,SACpB;AAAA,MACE;AAAA,QACE,eAAe,UAAU,WAAW,EAAE,QAAQ,QAAW,yBAAyB,uBAAuB,aAAa,WAAW,CAAC,CAAC;AAAA,MACrI;AAAA,MACA,sBAAsB,OAAO;AAAA,IAC/B,IACA,MAAM,QAAQ,KAAK,OAAO,OAAO;AACvC,UAAM;AAAA,MACJ,OAAO;AAAA,MACP;AAAA,MACA;AAAA,IACF,IAAI,MAAM,cAAc;AAAA,MACtB,KAAK,GAAG,KAAK,OAAO,OAAO;AAAA,MAC3B,SAAS,eAAe,cAAc,OAAO;AAAA,MAC7C,MAAM;AAAA,QACJ,OAAO,KAAK;AAAA,QACZ;AAAA,QACA,WAAW,OAAO;AAAA,UAChB,OAAO,QAAQ,SAAS,EAAE,IAAI,CAAC,CAAC,IAAI,QAAQ,MAAM;AAAA,YAChD;AAAA,YACA,SAAS,SAAS,YACd,EAAE,GAAG,UAAU,MAAM,OAAO,IAC5B;AAAA,UACN,CAAC;AAAA,QACH;AAAA,MACF;AAAA,MACA;AAAA,MACA,OAAO,KAAK,OAAO;AAAA,MACnB,uBAAuB;AAAA,MACvB,2BAA2B;AAAA,QACzB;AAAA,MACF;AAAA,IACF,CAAC;AAED,UAAM,aAAa,OAAO;AAAA,MACxB,OAAO,QAAQ,SAAS,OAAO,EAAE;AAAA,QAAQ,CAAC,CAAC,IAAI,MAAM,MACnD,OAAO,SAAS,UAAU,OAAO,cAAc,OAC3C,CAAC,CAAC,IAAI,OAAO,UAAU,CAAC,IACxB,CAAC;AAAA,MACP;AAAA,IACF;AAEA,WAAO;AAAA,MACL,SAAS,OAAO;AAAA,QACd,OAAO,QAAQ,SAAS,OAAO,EAAE;AAAA,UAC/B,CAAC,CAAC,IAAI,MAAM,MAAyC;AACnD,oBAAQ,OAAO,MAAM;AAAA,cACnB,KAAK;AACH,uBAAO,CAAC,IAAI,EAAE,MAAM,WAAW,aAAa,OAAO,KAAK,CAAC;AAAA,cAC3D,KAAK;AACH,uBAAO;AAAA,kBACL;AAAA,kBACA;AAAA,oBACE,MAAM;AAAA,oBACN,QAAQ,OAAO;AAAA,oBACf,eAAe,OAAO;AAAA,kBACxB;AAAA,gBACF;AAAA,cACF,KAAK;AACH,uBAAO;AAAA,kBACL;AAAA,kBACA;AAAA,oBACE,MAAM;AAAA,oBACN,OAAO,OAAO;AAAA,oBACd,eAAe,OAAO;AAAA,kBACxB;AAAA,gBACF;AAAA,YACJ;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA,OAAO;AAAA,QACL,cAAa,oBAAS,UAAT,mBAAgB,iBAAhB,YAAgC;AAAA,QAC7C,eAAc,oBAAS,UAAT,mBAAgB,kBAAhB,YAAiC;AAAA,MACjD;AAAA;AAAA,MAEA,UAAU,EAAE,qBAAqB,GAAG,eAAe,EAAE;AAAA,MACrD;AAAA,MACA,kBAAkB,EAAE,UAAU,EAAE,WAAW,EAAE;AAAA,MAC7C,UAAU;AAAA,QACR,UAAS,cAAS,UAAT,YAAkB,KAAK;AAAA,QAChC,SAAS;AAAA,QACT,MAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;;;ADvJO,SAAS,iBACd,UAAsC,CAAC,GACnB;AAjCtB;AAkCE,QAAM,WACJ,0BAAqB,QAAQ,OAAO,MAApC,YAAyC;AAC3C,QAAM,UAAU,MACdC;AAAA,IACE;AAAA,MACE,eAAe,UAAUC,YAAW,EAAE,QAAQ,QAAQ,QAAQ,yBAAyB,uBAAuB,aAAa,WAAW,CAAC,CAAC;AAAA,MACxI,GAAG,QAAQ;AAAA,IACb;AAAA,IACA,sBAAsB,OAAO;AAAA,EAC/B;AAEF,SAAO;AAAA,IACL,sBAAsB;AAAA,IACtB,iBAAiB,aACf,IAAI,0BAA0B,SAAS;AAAA,MACrC,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA,OAAO,QAAQ;AAAA,IACjB,CAAC;AAAA,IACH,eAAe,aAAW;AACxB,YAAM,IAAI,iBAAiB,EAAE,SAAS,WAAW,gBAAgB,CAAC;AAAA,IACpE;AAAA,IACA,gBAAgB,aAAW;AACzB,YAAM,IAAI,iBAAiB,EAAE,SAAS,WAAW,iBAAiB,CAAC;AAAA,IACrE;AAAA,IACA,YAAY,aAAW;AACrB,YAAM,IAAI,iBAAiB,EAAE,SAAS,WAAW,aAAa,CAAC;AAAA,IACjE;AAAA,EACF;AACF;AAEO,IAAM,aAAa,iBAAiB;","names":["loadApiKey","withUserAgentSuffix","withUserAgentSuffix","loadApiKey"]}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: TypeSafe
|
|
3
|
+
description: Use TypeSafe System One models for typed evaluations with the AI SDK.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TypeSafe Provider
|
|
7
|
+
|
|
8
|
+
The [TypeSafe](https://typesafe.ai) provider supports the experimental evaluation
|
|
9
|
+
API with native Choice, Score, and Boolean answers. It sends all questions in one
|
|
10
|
+
request against the same state.
|
|
11
|
+
|
|
12
|
+
## Setup
|
|
13
|
+
|
|
14
|
+
Install the provider:
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
pnpm add @ai-sdk/typesafe-ai
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Set the `TYPESAFE_AI_API_KEY` environment variable. Import the default provider or
|
|
21
|
+
configure an instance:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { createTypeSafeAi, typeSafeAi } from '@ai-sdk/typesafe-ai';
|
|
25
|
+
|
|
26
|
+
const customTypeSafe = createTypeSafeAi({
|
|
27
|
+
apiKey: 'your-api-key',
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`createTypeSafeAi` accepts:
|
|
32
|
+
|
|
33
|
+
| Setting | Description |
|
|
34
|
+
| --------- | ------------------------------------------- |
|
|
35
|
+
| `apiKey` | API key; defaults to `TYPESAFE_AI_API_KEY`. |
|
|
36
|
+
| `baseURL` | Defaults to `https://api.typesafe.ai/v1`. |
|
|
37
|
+
| `headers` | Additional request headers. |
|
|
38
|
+
| `fetch` | Custom fetch implementation. |
|
|
39
|
+
|
|
40
|
+
## Evaluation
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { typeSafeAi } from '@ai-sdk/typesafe-ai';
|
|
44
|
+
import { experimental_evaluate } from 'ai';
|
|
45
|
+
|
|
46
|
+
const result = await experimental_evaluate({
|
|
47
|
+
model: typeSafeAi.evaluationModel('jev-latest'),
|
|
48
|
+
state: {
|
|
49
|
+
message: 'I was charged twice. Please refund the duplicate.',
|
|
50
|
+
},
|
|
51
|
+
questions: {
|
|
52
|
+
department: {
|
|
53
|
+
type: 'choice',
|
|
54
|
+
instructions: 'Which team should handle this?',
|
|
55
|
+
criteria: {
|
|
56
|
+
billing: { includes: ['Charges', 'Invoices', 'Refunds'] },
|
|
57
|
+
technical: ['Bugs', 'Outages'],
|
|
58
|
+
other: null,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
severity: {
|
|
62
|
+
type: 'score',
|
|
63
|
+
instructions: 'How severe is the issue?',
|
|
64
|
+
criteria: ['Cosmetic', 'Workaround exists', 'Blocking; no workaround'],
|
|
65
|
+
},
|
|
66
|
+
requestsRefund: {
|
|
67
|
+
type: 'boolean',
|
|
68
|
+
instructions: 'Is the customer requesting money back?',
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
console.log(result.answers.department.choice);
|
|
74
|
+
console.log(result.answers.severity.score);
|
|
75
|
+
console.log(result.answers.requestsRefund.probability);
|
|
76
|
+
console.log(result.usage);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Use `jev-latest` or another TypeSafe model ID. The response includes the actual
|
|
80
|
+
model ID, such as the resolved version behind the alias. String model resolution
|
|
81
|
+
and registry integration are not supported yet; pass a model instance.
|
|
82
|
+
|
|
83
|
+
| Question | Native TypeSafe primitive | Limits and semantics |
|
|
84
|
+
| --------- | ------------------------- | -------------------------------------------------------------------- |
|
|
85
|
+
| `choice` | Choice | 1–255 options; selected option and full probabilities. |
|
|
86
|
+
| `score` | Score | 2–10 ordered rubric levels; fractional score and full probabilities. |
|
|
87
|
+
| `boolean` | Noul | Required model-estimated probability of true. |
|
|
88
|
+
|
|
89
|
+
State, instructions, and descriptions accept JSON objects and arrays in addition
|
|
90
|
+
to strings. Descriptions may be `null`. Boolean true/false criteria are optional.
|
|
91
|
+
The SDK uses the neutral name `boolean` and maps it to TypeSafe's `noul` field.
|
|
92
|
+
|
|
93
|
+
### Rounded values and confidence
|
|
94
|
+
|
|
95
|
+
TypeSafe returns scores and probabilities rounded to two decimal places.
|
|
96
|
+
`result.rounding` reports this precision so the SDK can account for rounding when
|
|
97
|
+
checking distribution sums and weighted means. The returned values are preserved
|
|
98
|
+
unchanged; rounding means probabilities may not add up to exactly one.
|
|
99
|
+
|
|
100
|
+
Confidence is a separate TypeSafe statistic, available under
|
|
101
|
+
`result.providerMetadata.typesafe.confidence[questionId]` for Choice and Score
|
|
102
|
+
answers. Boolean probability always means P(true), not confidence in either
|
|
103
|
+
outcome. Choose decision thresholds in application code.
|
|
104
|
+
|
|
105
|
+
### Errors and retries
|
|
106
|
+
|
|
107
|
+
Provider failures use `APICallError`, including validation and authentication
|
|
108
|
+
errors. Core retries transient failures such as `429` and `529` according to
|
|
109
|
+
`maxRetries` (default: 2). `abortSignal`, request headers, and custom fetch are
|
|
110
|
+
forwarded. There is no provider-side retry loop.
|
|
111
|
+
|
|
112
|
+
No provider-specific options are currently defined. Unknown entries under
|
|
113
|
+
`providerOptions.typesafe` produce unsupported-option warnings. Language,
|
|
114
|
+
embedding, and image model factories throw `NoSuchModelError`.
|
|
115
|
+
|
|
116
|
+
Evaluation models support workflow serialization. A custom fetch function is not
|
|
117
|
+
serialized; restored models use the default fetch implementation.
|
package/package.json
CHANGED
|
@@ -1,22 +1,78 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/typesafe-ai",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "AI SDK integration for Typesafe AI",
|
|
5
|
-
"license": "Apache-2.0",
|
|
3
|
+
"version": "3.0.0",
|
|
6
4
|
"type": "module",
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
11
9
|
"files": [
|
|
12
|
-
"
|
|
10
|
+
"dist/**/*",
|
|
11
|
+
"docs/**/*",
|
|
12
|
+
"src",
|
|
13
|
+
"!src/**/*.test.ts",
|
|
14
|
+
"!src/**/*.test-d.ts",
|
|
15
|
+
"!src/**/__snapshots__",
|
|
16
|
+
"!src/**/__fixtures__",
|
|
17
|
+
"CHANGELOG.md",
|
|
13
18
|
"README.md"
|
|
14
19
|
],
|
|
20
|
+
"directories": {
|
|
21
|
+
"doc": "./docs"
|
|
22
|
+
},
|
|
23
|
+
"exports": {
|
|
24
|
+
"./package.json": "./package.json",
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"import": "./dist/index.js",
|
|
28
|
+
"default": "./dist/index.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@ai-sdk/provider": "4.0.16",
|
|
33
|
+
"@ai-sdk/provider-utils": "5.0.42"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@ai-sdk/test-server": "2.0.1",
|
|
37
|
+
"@types/node": "22.19.19",
|
|
38
|
+
"@vercel/ai-tsconfig": "0.0.0",
|
|
39
|
+
"tsup": "^8.5.1",
|
|
40
|
+
"typescript": "5.8.3",
|
|
41
|
+
"zod": "3.25.76",
|
|
42
|
+
"@edge-runtime/vm": "^5.0.0"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"zod": "^3.25.76 || ^4.1.8"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=22"
|
|
49
|
+
},
|
|
15
50
|
"publishConfig": {
|
|
16
|
-
"access": "public"
|
|
51
|
+
"access": "public",
|
|
52
|
+
"provenance": true
|
|
17
53
|
},
|
|
54
|
+
"homepage": "https://ai-sdk.dev/docs",
|
|
18
55
|
"repository": {
|
|
19
56
|
"type": "git",
|
|
20
|
-
"url": "
|
|
57
|
+
"url": "https://github.com/vercel/ai",
|
|
58
|
+
"directory": "packages/typesafe-ai"
|
|
59
|
+
},
|
|
60
|
+
"bugs": {
|
|
61
|
+
"url": "https://github.com/vercel/ai/issues"
|
|
62
|
+
},
|
|
63
|
+
"keywords": [
|
|
64
|
+
"ai"
|
|
65
|
+
],
|
|
66
|
+
"source": "./src/index.ts",
|
|
67
|
+
"scripts": {
|
|
68
|
+
"build": "pnpm clean && tsup --tsconfig tsconfig.build.json",
|
|
69
|
+
"build:watch": "pnpm clean && tsup --watch",
|
|
70
|
+
"clean": "del-cli dist docs *.tsbuildinfo",
|
|
71
|
+
"type-check": "tsc --build",
|
|
72
|
+
"test": "pnpm test:node && pnpm test:edge",
|
|
73
|
+
"test:update": "pnpm test:node -u",
|
|
74
|
+
"test:watch": "vitest --config vitest.node.config.js",
|
|
75
|
+
"test:edge": "vitest --config vitest.edge.config.js --run",
|
|
76
|
+
"test:node": "vitest --config vitest.node.config.js --run"
|
|
21
77
|
}
|
|
22
|
-
}
|
|
78
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { createTypeSafeAi, typeSafeAi } from './typesafe-ai-provider';
|
|
2
|
+
export type {
|
|
3
|
+
TypeSafeAiProvider,
|
|
4
|
+
TypeSafeAiProviderSettings,
|
|
5
|
+
} from './typesafe-ai-provider';
|
|
6
|
+
export type { TypeSafeAiEvaluationModelId as Experimental_TypeSafeAiEvaluationModelId } from './typesafe-ai-evaluation-model';
|
|
7
|
+
export { VERSION } from './version';
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createJsonErrorResponseHandler } from '@ai-sdk/provider-utils';
|
|
2
|
+
import * as z from 'zod/v4';
|
|
3
|
+
|
|
4
|
+
// Verified against the official SDK at 66880ccded6cb642dc1809620c2b108c33730214.
|
|
5
|
+
// https://github.com/typesafe-ai/typesafe-sdk-js/blob/66880ccded6cb642dc1809620c2b108c33730214/src/types.ts
|
|
6
|
+
export const typesafeEvaluationResponseSchema = z.object({
|
|
7
|
+
model: z.string().nullish(),
|
|
8
|
+
answers: z.record(
|
|
9
|
+
z.string(),
|
|
10
|
+
z.discriminatedUnion('type', [
|
|
11
|
+
z.object({
|
|
12
|
+
type: z.literal('choice'),
|
|
13
|
+
choice: z.string(),
|
|
14
|
+
probabilities: z.record(z.string(), z.number()),
|
|
15
|
+
confidence: z.number().nullish(),
|
|
16
|
+
}),
|
|
17
|
+
z.object({
|
|
18
|
+
type: z.literal('score'),
|
|
19
|
+
score: z.number(),
|
|
20
|
+
probabilities: z.record(z.string(), z.number()),
|
|
21
|
+
confidence: z.number().nullish(),
|
|
22
|
+
}),
|
|
23
|
+
z.object({ type: z.literal('noul'), noul: z.number() }),
|
|
24
|
+
]),
|
|
25
|
+
),
|
|
26
|
+
usage: z
|
|
27
|
+
.object({
|
|
28
|
+
input_tokens: z.number().nullish(),
|
|
29
|
+
output_tokens: z.number().nullish(),
|
|
30
|
+
})
|
|
31
|
+
.nullish(),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
export const typesafeFailedResponseHandler = createJsonErrorResponseHandler({
|
|
35
|
+
errorSchema: z.object({
|
|
36
|
+
message: z.string().nullish(),
|
|
37
|
+
detail: z.unknown().nullish(),
|
|
38
|
+
error: z
|
|
39
|
+
.union([z.string(), z.object({ message: z.string().nullish() })])
|
|
40
|
+
.nullish(),
|
|
41
|
+
}),
|
|
42
|
+
errorToMessage: error =>
|
|
43
|
+
error.message ??
|
|
44
|
+
(typeof error.error === 'string' ? error.error : error.error?.message) ??
|
|
45
|
+
(typeof error.detail === 'string'
|
|
46
|
+
? error.detail
|
|
47
|
+
: JSON.stringify(error.detail)) ??
|
|
48
|
+
'TypeSafe request failed',
|
|
49
|
+
});
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import {
|
|
2
|
+
InvalidArgumentError,
|
|
3
|
+
type Experimental_EvaluationModelV4 as EvaluationModelV4,
|
|
4
|
+
type Experimental_EvaluationModelV4Answer as EvaluationModelV4Answer,
|
|
5
|
+
type SharedV4Warning,
|
|
6
|
+
} from '@ai-sdk/provider';
|
|
7
|
+
import {
|
|
8
|
+
combineHeaders,
|
|
9
|
+
createJsonResponseHandler,
|
|
10
|
+
loadApiKey,
|
|
11
|
+
postJsonToApi,
|
|
12
|
+
resolve,
|
|
13
|
+
serializeModelOptions,
|
|
14
|
+
withUserAgentSuffix,
|
|
15
|
+
WORKFLOW_SERIALIZE,
|
|
16
|
+
WORKFLOW_DESERIALIZE,
|
|
17
|
+
type FetchFunction,
|
|
18
|
+
type Resolvable,
|
|
19
|
+
} from '@ai-sdk/provider-utils';
|
|
20
|
+
import {
|
|
21
|
+
typesafeEvaluationResponseSchema,
|
|
22
|
+
typesafeFailedResponseHandler,
|
|
23
|
+
} from './typesafe-ai-evaluation-api';
|
|
24
|
+
import { VERSION } from './version';
|
|
25
|
+
|
|
26
|
+
export type TypeSafeAiEvaluationModelId = 'jev-latest' | (string & {});
|
|
27
|
+
|
|
28
|
+
type TypeSafeAiEvaluationModelConfig = {
|
|
29
|
+
provider: string;
|
|
30
|
+
baseURL: string;
|
|
31
|
+
headers?: Resolvable<Record<string, string | undefined>>;
|
|
32
|
+
fetch?: FetchFunction;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export class EvaluationTypeSafeAiModel implements EvaluationModelV4 {
|
|
36
|
+
readonly specificationVersion = 'v4';
|
|
37
|
+
readonly supportedQuestionTypes = ['choice', 'score', 'boolean'] as const;
|
|
38
|
+
|
|
39
|
+
constructor(
|
|
40
|
+
readonly modelId: TypeSafeAiEvaluationModelId,
|
|
41
|
+
private readonly config: TypeSafeAiEvaluationModelConfig,
|
|
42
|
+
) {}
|
|
43
|
+
|
|
44
|
+
get provider() {
|
|
45
|
+
return this.config.provider;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static [WORKFLOW_SERIALIZE](model: EvaluationTypeSafeAiModel) {
|
|
49
|
+
return serializeModelOptions({
|
|
50
|
+
modelId: model.modelId,
|
|
51
|
+
config: model.config,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static [WORKFLOW_DESERIALIZE](options: {
|
|
56
|
+
modelId: TypeSafeAiEvaluationModelId;
|
|
57
|
+
config: TypeSafeAiEvaluationModelConfig;
|
|
58
|
+
}) {
|
|
59
|
+
return new EvaluationTypeSafeAiModel(options.modelId, options.config);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async doEvaluate({
|
|
63
|
+
state,
|
|
64
|
+
questions,
|
|
65
|
+
headers,
|
|
66
|
+
abortSignal,
|
|
67
|
+
providerOptions,
|
|
68
|
+
}: Parameters<EvaluationModelV4['doEvaluate']>[0]): Promise<
|
|
69
|
+
Awaited<ReturnType<EvaluationModelV4['doEvaluate']>>
|
|
70
|
+
> {
|
|
71
|
+
for (const [id, question] of Object.entries(questions)) {
|
|
72
|
+
if (
|
|
73
|
+
question.type === 'choice' &&
|
|
74
|
+
Object.keys(question.criteria).length > 255
|
|
75
|
+
) {
|
|
76
|
+
throw new InvalidArgumentError({
|
|
77
|
+
argument: `questions.${id}.criteria`,
|
|
78
|
+
message: 'TypeSafe Choice questions support at most 255 options.',
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if (question.type === 'score' && question.criteria.length > 10) {
|
|
82
|
+
throw new InvalidArgumentError({
|
|
83
|
+
argument: `questions.${id}.criteria`,
|
|
84
|
+
message: 'TypeSafe Score questions support at most 10 levels.',
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const warnings: SharedV4Warning[] = Object.keys(
|
|
90
|
+
providerOptions?.typesafe ?? {},
|
|
91
|
+
).map(option => ({
|
|
92
|
+
type: 'unsupported',
|
|
93
|
+
feature: `providerOptions.typesafe.${option}`,
|
|
94
|
+
}));
|
|
95
|
+
const modelHeaders =
|
|
96
|
+
this.config.headers === undefined
|
|
97
|
+
? withUserAgentSuffix(
|
|
98
|
+
{
|
|
99
|
+
Authorization: `Bearer ${loadApiKey({ apiKey: undefined, environmentVariableName: 'TYPESAFE_AI_API_KEY', description: 'TypeSafe' })}`,
|
|
100
|
+
},
|
|
101
|
+
`ai-sdk/typesafe-ai/${VERSION}`,
|
|
102
|
+
)
|
|
103
|
+
: await resolve(this.config.headers);
|
|
104
|
+
const {
|
|
105
|
+
value: response,
|
|
106
|
+
rawValue,
|
|
107
|
+
responseHeaders,
|
|
108
|
+
} = await postJsonToApi({
|
|
109
|
+
url: `${this.config.baseURL}/systemone`,
|
|
110
|
+
headers: combineHeaders(modelHeaders, headers),
|
|
111
|
+
body: {
|
|
112
|
+
model: this.modelId,
|
|
113
|
+
state,
|
|
114
|
+
questions: Object.fromEntries(
|
|
115
|
+
Object.entries(questions).map(([id, question]) => [
|
|
116
|
+
id,
|
|
117
|
+
question.type === 'boolean'
|
|
118
|
+
? { ...question, type: 'noul' }
|
|
119
|
+
: question,
|
|
120
|
+
]),
|
|
121
|
+
),
|
|
122
|
+
},
|
|
123
|
+
abortSignal,
|
|
124
|
+
fetch: this.config.fetch,
|
|
125
|
+
failedResponseHandler: typesafeFailedResponseHandler,
|
|
126
|
+
successfulResponseHandler: createJsonResponseHandler(
|
|
127
|
+
typesafeEvaluationResponseSchema,
|
|
128
|
+
),
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const confidence = Object.fromEntries(
|
|
132
|
+
Object.entries(response.answers).flatMap(([id, answer]) =>
|
|
133
|
+
answer.type !== 'noul' && answer.confidence != null
|
|
134
|
+
? [[id, answer.confidence]]
|
|
135
|
+
: [],
|
|
136
|
+
),
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
answers: Object.fromEntries(
|
|
141
|
+
Object.entries(response.answers).map(
|
|
142
|
+
([id, answer]): [string, EvaluationModelV4Answer] => {
|
|
143
|
+
switch (answer.type) {
|
|
144
|
+
case 'noul':
|
|
145
|
+
return [id, { type: 'boolean', probability: answer.noul }];
|
|
146
|
+
case 'choice':
|
|
147
|
+
return [
|
|
148
|
+
id,
|
|
149
|
+
{
|
|
150
|
+
type: 'choice',
|
|
151
|
+
choice: answer.choice,
|
|
152
|
+
probabilities: answer.probabilities,
|
|
153
|
+
},
|
|
154
|
+
];
|
|
155
|
+
case 'score':
|
|
156
|
+
return [
|
|
157
|
+
id,
|
|
158
|
+
{
|
|
159
|
+
type: 'score',
|
|
160
|
+
score: answer.score,
|
|
161
|
+
probabilities: answer.probabilities,
|
|
162
|
+
},
|
|
163
|
+
];
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
),
|
|
167
|
+
),
|
|
168
|
+
usage: {
|
|
169
|
+
inputTokens: response.usage?.input_tokens ?? undefined,
|
|
170
|
+
outputTokens: response.usage?.output_tokens ?? undefined,
|
|
171
|
+
},
|
|
172
|
+
// The API rounds displayed probabilities and scores to two decimal places.
|
|
173
|
+
rounding: { probabilityDecimals: 2, scoreDecimals: 2 },
|
|
174
|
+
warnings,
|
|
175
|
+
providerMetadata: { typesafe: { confidence } },
|
|
176
|
+
response: {
|
|
177
|
+
modelId: response.model ?? this.modelId,
|
|
178
|
+
headers: responseHeaders,
|
|
179
|
+
body: rawValue,
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NoSuchModelError,
|
|
3
|
+
type Experimental_EvaluationModelV4 as EvaluationModelV4,
|
|
4
|
+
type ProviderV4,
|
|
5
|
+
} from '@ai-sdk/provider';
|
|
6
|
+
import {
|
|
7
|
+
loadApiKey,
|
|
8
|
+
withoutTrailingSlash,
|
|
9
|
+
withUserAgentSuffix,
|
|
10
|
+
type FetchFunction,
|
|
11
|
+
} from '@ai-sdk/provider-utils';
|
|
12
|
+
import {
|
|
13
|
+
EvaluationTypeSafeAiModel,
|
|
14
|
+
type TypeSafeAiEvaluationModelId,
|
|
15
|
+
} from './typesafe-ai-evaluation-model';
|
|
16
|
+
import { VERSION } from './version';
|
|
17
|
+
|
|
18
|
+
/** TypeSafe's experimental evaluation capability, isolated from ProviderV4. */
|
|
19
|
+
export interface TypeSafeAiProvider extends ProviderV4 {
|
|
20
|
+
evaluationModel(modelId: TypeSafeAiEvaluationModelId): EvaluationModelV4;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface TypeSafeAiProviderSettings {
|
|
24
|
+
/** API key. Defaults to the TYPESAFE_AI_API_KEY environment variable. */
|
|
25
|
+
apiKey?: string;
|
|
26
|
+
/** API base URL. Defaults to https://api.typesafe.ai/v1. */
|
|
27
|
+
baseURL?: string;
|
|
28
|
+
headers?: Record<string, string>;
|
|
29
|
+
fetch?: FetchFunction;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function createTypeSafeAi(
|
|
33
|
+
options: TypeSafeAiProviderSettings = {},
|
|
34
|
+
): TypeSafeAiProvider {
|
|
35
|
+
const baseURL =
|
|
36
|
+
withoutTrailingSlash(options.baseURL) ?? 'https://api.typesafe.ai/v1';
|
|
37
|
+
const headers = () =>
|
|
38
|
+
withUserAgentSuffix(
|
|
39
|
+
{
|
|
40
|
+
Authorization: `Bearer ${loadApiKey({ apiKey: options.apiKey, environmentVariableName: 'TYPESAFE_AI_API_KEY', description: 'TypeSafe' })}`,
|
|
41
|
+
...options.headers,
|
|
42
|
+
},
|
|
43
|
+
`ai-sdk/typesafe-ai/${VERSION}`,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
specificationVersion: 'v4',
|
|
48
|
+
evaluationModel: modelId =>
|
|
49
|
+
new EvaluationTypeSafeAiModel(modelId, {
|
|
50
|
+
provider: 'typesafe.evaluation',
|
|
51
|
+
baseURL,
|
|
52
|
+
headers,
|
|
53
|
+
fetch: options.fetch,
|
|
54
|
+
}),
|
|
55
|
+
languageModel: modelId => {
|
|
56
|
+
throw new NoSuchModelError({ modelId, modelType: 'languageModel' });
|
|
57
|
+
},
|
|
58
|
+
embeddingModel: modelId => {
|
|
59
|
+
throw new NoSuchModelError({ modelId, modelType: 'embeddingModel' });
|
|
60
|
+
},
|
|
61
|
+
imageModel: modelId => {
|
|
62
|
+
throw new NoSuchModelError({ modelId, modelType: 'imageModel' });
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const typeSafeAi = createTypeSafeAi();
|
package/src/version.ts
ADDED
package/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|