@effect-agent/ai-typesafe 0.1.0-beta.100
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/README.md +126 -0
- package/dist/TypeSafeClient-OS5hTVMz.mjs +189 -0
- package/dist/TypeSafeClient-OS5hTVMz.mjs.map +1 -0
- package/dist/TypeSafeClient.d.mts +65 -0
- package/dist/TypeSafeClient.mjs +3 -0
- package/dist/TypeSafeSchema.d.mts +305 -0
- package/dist/TypeSafeSchema.mjs +168 -0
- package/dist/TypeSafeSchema.mjs.map +1 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +3 -0
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/package.json +1 -0
- package/src/TypeSafeClient.ts +145 -0
- package/src/TypeSafeSchema.ts +275 -0
- package/src/index.ts +2 -0
- package/src/internal/errors.ts +103 -0
- package/src/internal/schema.ts +86 -0
package/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# @effect-agent/ai-typesafe
|
|
2
|
+
|
|
3
|
+
Evaluate TypeSafe AI choice, score, and noul questions with Effect. The package uses
|
|
4
|
+
Effect's platform-neutral `HttpClient`, `Schema`, `Config`, and `AiError` and has
|
|
5
|
+
only an Effect runtime peer dependency (`^4.0.0-rc.115`).
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { TypeSafeClient } from "@effect-agent/ai-typesafe";
|
|
9
|
+
import { Config, Effect, Layer } from "effect";
|
|
10
|
+
import { FetchHttpClient } from "effect/unstable/http";
|
|
11
|
+
|
|
12
|
+
const evaluation = Effect.gen(function* () {
|
|
13
|
+
const client = yield* TypeSafeClient.TypeSafeClient;
|
|
14
|
+
const result = yield* client.evaluate({
|
|
15
|
+
model: "jev-latest",
|
|
16
|
+
state: { message: "I was charged twice. Please refund the duplicate." },
|
|
17
|
+
questions: {
|
|
18
|
+
department: {
|
|
19
|
+
type: "choice",
|
|
20
|
+
instructions: "Which team should handle this ticket?",
|
|
21
|
+
criteria: { billing: "Payments and refunds", technical: "Bugs and outages" },
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return result.answers.department.choice; // "billing" | "technical"
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const ClientLive = TypeSafeClient.layerConfig({
|
|
30
|
+
apiKey: Config.Redacted("TYPESAFE_API_KEY"),
|
|
31
|
+
}).pipe(Layer.provide(FetchHttpClient.layer));
|
|
32
|
+
|
|
33
|
+
const program = evaluation.pipe(Effect.provide(ClientLive));
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Run `program` with your application's Effect runtime. The compiling
|
|
37
|
+
[mixed-question example](examples/evaluate.ts) also shows bounded retries and an
|
|
38
|
+
overall timeout. Acquiring the client does not send requests.
|
|
39
|
+
|
|
40
|
+
## Questions and answers
|
|
41
|
+
|
|
42
|
+
`evaluate` sends `POST https://api.typesafe.ai/v1/systemone` with bearer authentication
|
|
43
|
+
and the required `{ model, state, questions }` body. State and instructions accept
|
|
44
|
+
strings, JSON objects, and JSON arrays. Nested values must be JSON-compatible.
|
|
45
|
+
|
|
46
|
+
| Question | Criteria | Answer |
|
|
47
|
+
| -------- | ------------------------------------------------------------------------ | ----------------------------------------------------------- |
|
|
48
|
+
| `choice` | Nonempty map of option names to a string or `null` | `choice`, `probabilities`, `confidence` |
|
|
49
|
+
| `score` | At least two ordered string descriptions | Fractional `score`, `legend`, `probabilities`, `confidence` |
|
|
50
|
+
| `noul` | Optional object with optional string descriptions for `true` and `false` | `noul` in [0, 1] |
|
|
51
|
+
|
|
52
|
+
Every answer includes its `type`. `model` and `usage.input_tokens` /
|
|
53
|
+
`usage.output_tokens` are retained. A returned model may resolve the requested alias
|
|
54
|
+
to a concrete version. Required fields are never filled in from examples or defaults.
|
|
55
|
+
The supported wire shapes follow the [API reference](https://docs.typesafe.ai/api),
|
|
56
|
+
including string-valued score legends.
|
|
57
|
+
|
|
58
|
+
Use `satisfies TypeSafeSchema.Questions` to retain question keys and choice literals
|
|
59
|
+
when defining questions separately. Runtime-built maps retain broader types:
|
|
60
|
+
unknown answer IDs and probability keys may be absent, and mixed answers must be
|
|
61
|
+
narrowed by `type`. Optional criteria remain optional in the probability map.
|
|
62
|
+
Open string, numeric, and template-pattern indexes allow absent entries;
|
|
63
|
+
explicitly required keys keep their required types.
|
|
64
|
+
|
|
65
|
+
The client builds a response schema from each request. It checks the exact answer
|
|
66
|
+
IDs and kinds, permitted choices, complete probability keys, and matching score
|
|
67
|
+
legends. Probabilities and confidence must be finite and in [0, 1]. A choice must
|
|
68
|
+
have maximal probability; ties are valid. Scores range from zero to the last level
|
|
69
|
+
and must match their probability-weighted value. Distribution sums allow `1e-6`
|
|
70
|
+
rounding error, and score comparisons allow `1e-6` times the highest level index.
|
|
71
|
+
Values are preserved rather than normalized. Unexpected response fields are rejected.
|
|
72
|
+
|
|
73
|
+
Confidence summarizes a distribution; it is not a correctness guarantee. Noul has
|
|
74
|
+
no separate confidence. Choose application thresholds using evaluated examples for
|
|
75
|
+
your domain. See TypeSafe's [confidence guide](https://docs.typesafe.ai/confidence).
|
|
76
|
+
|
|
77
|
+
## Errors, cancellation, and HTTP policies
|
|
78
|
+
|
|
79
|
+
Evaluation failures use native `AiError` reasons:
|
|
80
|
+
|
|
81
|
+
| Failure | Reason |
|
|
82
|
+
| ------------------------------------------------------------ | ------------------------------------ |
|
|
83
|
+
| Invalid local request or HTTP 422 | `InvalidRequestError` |
|
|
84
|
+
| HTTP 401 | `AuthenticationError` (`InvalidKey`) |
|
|
85
|
+
| HTTP 429 | `RateLimitError` |
|
|
86
|
+
| HTTP 529 | `InternalProviderError` |
|
|
87
|
+
| Transport failure | `NetworkError` |
|
|
88
|
+
| Malformed JSON or a response that disagrees with the request | `InvalidOutputError` |
|
|
89
|
+
|
|
90
|
+
`layerConfig` can also fail with `ConfigError`. Defects and interruption remain
|
|
91
|
+
defects and interruption. Cancellation reaches the supplied HttpClient, including
|
|
92
|
+
while reading the response body.
|
|
93
|
+
|
|
94
|
+
HTTP status failures retain the status, request details, response headers, and raw
|
|
95
|
+
provider error text without assuming an error-body protocol. Sensitive headers and
|
|
96
|
+
the configured API key are redacted from diagnostics. Provider error text may contain
|
|
97
|
+
submitted content. The integration adds no body logging; HTTP tracing is controlled
|
|
98
|
+
by Effect HttpClient and the application.
|
|
99
|
+
|
|
100
|
+
There are no default retries or deadlines. Compose `Effect.retry` with a bounded
|
|
101
|
+
schedule and `Effect.timeout`, or supply `transformClient` to `make`, `layer`, or
|
|
102
|
+
`layerConfig`. The transformer receives the client after authentication and status
|
|
103
|
+
filtering. `apiUrl` overrides the versioned base URL for a proxy or substitute.
|
|
104
|
+
|
|
105
|
+
## Native Effect AI tools
|
|
106
|
+
|
|
107
|
+
The compiling [tool example](examples/tool.ts) declares a native `Tool` with
|
|
108
|
+
`failure: AiError.AiError` and `dependencies: [TypeSafeClient.TypeSafeClient]`, then
|
|
109
|
+
implements it through `Toolkit.toLayer`. The declared default failure mode keeps
|
|
110
|
+
failures in the Effect error channel. Supply TypeSafeClient when executing the tool
|
|
111
|
+
or providing it to an agent's language model.
|
|
112
|
+
|
|
113
|
+
The package exposes evaluations directly. Language generation, chat, streaming,
|
|
114
|
+
and tool planning are outside its API.
|
|
115
|
+
|
|
116
|
+
## Modules and extraction
|
|
117
|
+
|
|
118
|
+
Root exports are the `TypeSafeClient` and `TypeSafeSchema` namespaces. Direct imports
|
|
119
|
+
use `@effect-agent/ai-typesafe/type-safe-client` and
|
|
120
|
+
`@effect-agent/ai-typesafe/type-safe-schema`.
|
|
121
|
+
|
|
122
|
+
This package follows Effect provider module conventions while incubating in the
|
|
123
|
+
Effect Agent release group. Its source imports only `effect/*`; it has no engine,
|
|
124
|
+
platform, persistence, or vendor SDK dependency. Upstream extraction would change
|
|
125
|
+
package metadata, service identity, import paths, and release documentation while
|
|
126
|
+
retaining the HTTP contracts and tests.
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
+
import { ChoiceAnswer, EvaluateRequest, EvaluateResponse, NoulAnswer, Probability, ScoreAnswer } from "./TypeSafeSchema.mjs";
|
|
3
|
+
import * as Config from "effect/Config";
|
|
4
|
+
import * as Context from "effect/Context";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
import { flow, identity } from "effect/Function";
|
|
7
|
+
import * as Layer from "effect/Layer";
|
|
8
|
+
import * as Redacted from "effect/Redacted";
|
|
9
|
+
import * as Schema from "effect/Schema";
|
|
10
|
+
import * as AiError from "effect/unstable/ai/AiError";
|
|
11
|
+
import * as Headers from "effect/unstable/http/Headers";
|
|
12
|
+
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
13
|
+
import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest";
|
|
14
|
+
import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse";
|
|
15
|
+
import * as Option from "effect/Option";
|
|
16
|
+
import * as Record from "effect/Record";
|
|
17
|
+
//#region src/internal/errors.ts
|
|
18
|
+
const make$1 = (reason) => AiError.make({
|
|
19
|
+
module: "TypeSafeClient",
|
|
20
|
+
method: "evaluate",
|
|
21
|
+
reason
|
|
22
|
+
});
|
|
23
|
+
const mapSchemaError = (error, redact) => {
|
|
24
|
+
const reason = AiError.InvalidOutputError.fromSchemaError(error);
|
|
25
|
+
return make$1(new AiError.InvalidOutputError({
|
|
26
|
+
...reason,
|
|
27
|
+
description: redact(reason.description)
|
|
28
|
+
}));
|
|
29
|
+
};
|
|
30
|
+
const redactRequest = (request, redact) => ({
|
|
31
|
+
...request,
|
|
32
|
+
url: redact(request.url),
|
|
33
|
+
urlParams: request.urlParams.map(([key, value]) => [key, redact(value)]),
|
|
34
|
+
hash: request.hash === void 0 ? void 0 : redact(request.hash),
|
|
35
|
+
headers: Record.map(request.headers, (value) => Redacted.isRedacted(value) ? "<redacted>" : redact(value))
|
|
36
|
+
});
|
|
37
|
+
const mapHttpClientError = Effect.fnUntraced(function* (error, redact) {
|
|
38
|
+
const reason = error.reason;
|
|
39
|
+
switch (reason._tag) {
|
|
40
|
+
case "TransportError":
|
|
41
|
+
case "EncodeError":
|
|
42
|
+
case "InvalidUrlError": {
|
|
43
|
+
const network = AiError.NetworkError.fromRequestError(reason);
|
|
44
|
+
return yield* make$1(new AiError.NetworkError({
|
|
45
|
+
...network,
|
|
46
|
+
request: redactRequest(network.request, redact),
|
|
47
|
+
description: network.description === void 0 ? void 0 : redact(network.description)
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
case "DecodeError":
|
|
51
|
+
case "EmptyBodyError": return yield* make$1(new AiError.InvalidOutputError({ description: redact(reason.description ?? "Could not decode the TypeSafe response body") }));
|
|
52
|
+
case "StatusCodeError": {
|
|
53
|
+
const { request, response } = reason;
|
|
54
|
+
const redactedNames = yield* Headers.CurrentRedactedNames;
|
|
55
|
+
const headers = (value) => Record.map(Headers.redact(value, redactedNames), (value) => Redacted.isRedacted(value) ? "<redacted>" : redact(value));
|
|
56
|
+
const text = yield* Effect.option(response.text);
|
|
57
|
+
const body = Option.isSome(text) ? redact(text.value) : void 0;
|
|
58
|
+
const http = {
|
|
59
|
+
request: {
|
|
60
|
+
method: request.method,
|
|
61
|
+
url: redact(request.url),
|
|
62
|
+
urlParams: Array.from(request.urlParams, ([key, value]) => [key, redact(value)]),
|
|
63
|
+
hash: Option.getOrUndefined(Option.map(request.hash, redact)),
|
|
64
|
+
headers: headers(request.headers)
|
|
65
|
+
},
|
|
66
|
+
response: {
|
|
67
|
+
status: response.status,
|
|
68
|
+
headers: headers(response.headers)
|
|
69
|
+
},
|
|
70
|
+
body
|
|
71
|
+
};
|
|
72
|
+
const description = AiError.buildErrorDescription({
|
|
73
|
+
status: response.status,
|
|
74
|
+
method: request.method,
|
|
75
|
+
url: http.request.url,
|
|
76
|
+
message: void 0,
|
|
77
|
+
body
|
|
78
|
+
});
|
|
79
|
+
return yield* make$1(response.status === 422 ? new AiError.InvalidRequestError({
|
|
80
|
+
description,
|
|
81
|
+
http
|
|
82
|
+
}) : AiError.reasonFromHttpStatus({
|
|
83
|
+
status: response.status,
|
|
84
|
+
description,
|
|
85
|
+
http
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/internal/schema.ts
|
|
92
|
+
const tolerance = 1e-6;
|
|
93
|
+
const distribution = (keys) => Schema.Record(Schema.Literals(keys), Probability).check(Schema.makeFilter((probabilities) => Math.abs(Object.values(probabilities).reduce((sum, value) => sum + value, 0) - 1) <= tolerance, { expected: "probabilities summing to 1 (within 1e-6)" }));
|
|
94
|
+
const answerFor = (question) => {
|
|
95
|
+
switch (question.type) {
|
|
96
|
+
case "choice": {
|
|
97
|
+
const keys = Object.keys(question.criteria);
|
|
98
|
+
return Schema.Struct({
|
|
99
|
+
...ChoiceAnswer.fields,
|
|
100
|
+
choice: Schema.Literals(keys),
|
|
101
|
+
probabilities: distribution(keys)
|
|
102
|
+
}).check(Schema.makeFilter(({ choice, probabilities }) => Object.values(probabilities).every((value) => value <= probabilities[choice]), { expected: "a highest-probability choice" }));
|
|
103
|
+
}
|
|
104
|
+
case "score": {
|
|
105
|
+
const maxLevel = question.criteria.length - 1;
|
|
106
|
+
const levels = question.criteria.map((description, index) => [String(index), description]);
|
|
107
|
+
return Schema.Struct({
|
|
108
|
+
...ScoreAnswer.fields,
|
|
109
|
+
score: Schema.Finite.check(Schema.isBetween({
|
|
110
|
+
minimum: 0,
|
|
111
|
+
maximum: maxLevel
|
|
112
|
+
})),
|
|
113
|
+
legend: Schema.Struct(Object.fromEntries(levels.map(([key, description]) => [key, Schema.Literal(description)]))),
|
|
114
|
+
probabilities: distribution(levels.map(([key]) => key))
|
|
115
|
+
}).check(Schema.makeFilter(({ score, probabilities }) => Math.abs(score - Object.entries(probabilities).reduce((sum, [level, probability]) => sum + Number(level) * probability, 0)) <= tolerance * Math.max(1, maxLevel), { expected: "the probability-weighted score (within 1e-6 per level)" }));
|
|
116
|
+
}
|
|
117
|
+
case "noul": return NoulAnswer;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
function responseFor(questions) {
|
|
121
|
+
return Schema.Struct({
|
|
122
|
+
...EvaluateResponse.fields,
|
|
123
|
+
answers: Schema.Struct(Object.fromEntries(Object.entries(questions).map(([id, question]) => [id, answerFor(question)])))
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region src/TypeSafeClient.ts
|
|
128
|
+
/**
|
|
129
|
+
* An Effect HttpClient integration for TypeSafe's System One evaluations.
|
|
130
|
+
*
|
|
131
|
+
* @since 0.1.0
|
|
132
|
+
*/
|
|
133
|
+
var TypeSafeClient_exports = /* @__PURE__ */ __exportAll({
|
|
134
|
+
TypeSafeClient: () => TypeSafeClient,
|
|
135
|
+
layer: () => layer,
|
|
136
|
+
layerConfig: () => layerConfig,
|
|
137
|
+
make: () => make
|
|
138
|
+
});
|
|
139
|
+
/** @category services
|
|
140
|
+
* @since 0.1.0
|
|
141
|
+
*/
|
|
142
|
+
var TypeSafeClient = class extends Context.Service()("@effect-agent/ai-typesafe/TypeSafeClient") {};
|
|
143
|
+
const encodeRequest = Schema.encodeEffect(Schema.fromJsonString(EvaluateRequest));
|
|
144
|
+
/**
|
|
145
|
+
* Construct the client using a supplied, platform-neutral HttpClient.
|
|
146
|
+
* Acquiring the service does not send a request.
|
|
147
|
+
*
|
|
148
|
+
* @category constructors
|
|
149
|
+
* @since 0.1.0
|
|
150
|
+
*/
|
|
151
|
+
const make = Effect.fnUntraced(function* (options) {
|
|
152
|
+
const apiKey = Redacted.value(options.apiKey);
|
|
153
|
+
const redact = (text) => apiKey.length === 0 ? text : text.replaceAll(apiKey, "<redacted>");
|
|
154
|
+
const client = (yield* HttpClient.HttpClient).pipe(HttpClient.mapRequest(flow(HttpClientRequest.prependUrl(options.apiUrl ?? "https://api.typesafe.ai/v1"), HttpClientRequest.bearerToken(options.apiKey), HttpClientRequest.acceptJson)), HttpClient.filterStatusOk, options.transformClient ?? identity);
|
|
155
|
+
const evaluate = Effect.fnUntraced(function* (options) {
|
|
156
|
+
const body = yield* encodeRequest(options).pipe(Effect.mapError((error) => make$1(new AiError.InvalidRequestError({ description: redact(error.message) }))));
|
|
157
|
+
const schema = responseFor(options.questions);
|
|
158
|
+
return yield* client.execute(HttpClientRequest.post("/systemone").pipe(HttpClientRequest.bodyText(body, "application/json"))).pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(schema, { onExcessProperty: "error" })), Effect.catchTags({
|
|
159
|
+
HttpClientError: (error) => mapHttpClientError(error, redact),
|
|
160
|
+
SchemaError: (error) => Effect.fail(mapSchemaError(error, redact))
|
|
161
|
+
}), Effect.updateService(Headers.CurrentRedactedNames, (names) => [...names, "authorization"]));
|
|
162
|
+
});
|
|
163
|
+
return TypeSafeClient.of({
|
|
164
|
+
client,
|
|
165
|
+
evaluate
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
/** @category layers
|
|
169
|
+
* @since 0.1.0
|
|
170
|
+
*/
|
|
171
|
+
const layer = (options) => Layer.effect(TypeSafeClient, make(options));
|
|
172
|
+
/**
|
|
173
|
+
* Configure the client with Effect Config. The API key defaults to
|
|
174
|
+
* `Config.Redacted("TYPESAFE_API_KEY")`.
|
|
175
|
+
*
|
|
176
|
+
* @category layers
|
|
177
|
+
* @since 0.1.0
|
|
178
|
+
*/
|
|
179
|
+
const layerConfig = (options) => Layer.effect(TypeSafeClient, Effect.gen(function* () {
|
|
180
|
+
return yield* make({
|
|
181
|
+
apiKey: yield* options?.apiKey ?? Config.Redacted("TYPESAFE_API_KEY"),
|
|
182
|
+
apiUrl: options?.apiUrl === void 0 ? void 0 : yield* options.apiUrl,
|
|
183
|
+
transformClient: options?.transformClient
|
|
184
|
+
});
|
|
185
|
+
}));
|
|
186
|
+
//#endregion
|
|
187
|
+
export { make as a, layerConfig as i, TypeSafeClient_exports as n, layer as r, TypeSafeClient as t };
|
|
188
|
+
|
|
189
|
+
//# sourceMappingURL=TypeSafeClient-OS5hTVMz.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TypeSafeClient-OS5hTVMz.mjs","names":["make","TypeSafeSchema.Probability","TypeSafeSchema.NoulAnswer","TypeSafeSchema.EvaluateRequest","Errors.make","Errors.mapHttpClientError","Errors.mapSchemaError"],"sources":["../src/internal/errors.ts","../src/internal/schema.ts","../src/TypeSafeClient.ts"],"sourcesContent":["import * as Effect from \"effect/Effect\";\nimport * as Option from \"effect/Option\";\nimport * as Record from \"effect/Record\";\nimport * as Redacted from \"effect/Redacted\";\nimport type * as Schema from \"effect/Schema\";\nimport * as AiError from \"effect/unstable/ai/AiError\";\nimport * as Headers from \"effect/unstable/http/Headers\";\nimport type * as HttpClientError from \"effect/unstable/http/HttpClientError\";\n\nexport const make = (reason: AiError.AiErrorReason): AiError.AiError =>\n AiError.make({ module: \"TypeSafeClient\", method: \"evaluate\", reason });\n\nexport const mapSchemaError = (\n error: Schema.SchemaError,\n redact: (text: string) => string,\n): AiError.AiError => {\n const reason = AiError.InvalidOutputError.fromSchemaError(error);\n\n return make(\n new AiError.InvalidOutputError({ ...reason, description: redact(reason.description) }),\n );\n};\n\nconst redactRequest = (\n request: typeof AiError.HttpRequestDetails.Type,\n redact: (text: string) => string,\n): typeof AiError.HttpRequestDetails.Type => ({\n ...request,\n url: redact(request.url),\n urlParams: request.urlParams.map(([key, value]) => [key, redact(value)]),\n hash: request.hash === undefined ? undefined : redact(request.hash),\n headers: Record.map(request.headers, (value) =>\n Redacted.isRedacted(value) ? \"<redacted>\" : redact(value),\n ),\n});\n\nexport const mapHttpClientError = Effect.fnUntraced(function* (\n error: HttpClientError.HttpClientError,\n redact: (text: string) => string,\n): Effect.fn.Return<never, AiError.AiError> {\n const reason = error.reason;\n\n switch (reason._tag) {\n case \"TransportError\":\n case \"EncodeError\":\n case \"InvalidUrlError\": {\n const network = AiError.NetworkError.fromRequestError(reason);\n\n return yield* make(\n new AiError.NetworkError({\n ...network,\n request: redactRequest(network.request, redact),\n description: network.description === undefined ? undefined : redact(network.description),\n }),\n );\n }\n case \"DecodeError\":\n case \"EmptyBodyError\":\n return yield* make(\n new AiError.InvalidOutputError({\n description: redact(reason.description ?? \"Could not decode the TypeSafe response body\"),\n }),\n );\n case \"StatusCodeError\": {\n const { request, response } = reason;\n const redactedNames = yield* Headers.CurrentRedactedNames;\n\n const headers = (value: Headers.Headers) =>\n Record.map(Headers.redact(value, redactedNames), (value) =>\n Redacted.isRedacted(value) ? \"<redacted>\" : redact(value),\n );\n\n const text = yield* Effect.option(response.text);\n const body = Option.isSome(text) ? redact(text.value) : undefined;\n\n const http: typeof AiError.HttpContext.Type = {\n request: {\n method: request.method,\n url: redact(request.url),\n urlParams: Array.from(request.urlParams, ([key, value]) => [key, redact(value)]),\n hash: Option.getOrUndefined(Option.map(request.hash, redact)),\n headers: headers(request.headers),\n },\n response: { status: response.status, headers: headers(response.headers) },\n body,\n };\n\n const description = AiError.buildErrorDescription({\n status: response.status,\n method: request.method,\n url: http.request.url,\n message: undefined,\n body,\n });\n\n return yield* make(\n response.status === 422\n ? new AiError.InvalidRequestError({ description, http })\n : AiError.reasonFromHttpStatus({ status: response.status, description, http }),\n );\n }\n }\n});\n","import * as Schema from \"effect/Schema\";\n\nimport * as TypeSafeSchema from \"../TypeSafeSchema.ts\";\n\n// Permit floating-point serialization error without changing provider values.\nconst tolerance = 1e-6;\n\nconst distribution = (keys: ReadonlyArray<string>) =>\n Schema.Record(Schema.Literals(keys), TypeSafeSchema.Probability).check(\n Schema.makeFilter(\n (probabilities) =>\n Math.abs(Object.values(probabilities).reduce((sum, value) => sum + value, 0) - 1) <=\n tolerance,\n { expected: \"probabilities summing to 1 (within 1e-6)\" },\n ),\n );\n\nconst answerFor = (question: TypeSafeSchema.Question) => {\n switch (question.type) {\n case \"choice\": {\n const keys = Object.keys(question.criteria);\n\n return Schema.Struct({\n ...TypeSafeSchema.ChoiceAnswer.fields,\n choice: Schema.Literals(keys),\n probabilities: distribution(keys),\n }).check(\n Schema.makeFilter(\n ({ choice, probabilities }) =>\n Object.values(probabilities).every((value) => value <= probabilities[choice]),\n { expected: \"a highest-probability choice\" },\n ),\n );\n }\n case \"score\": {\n const maxLevel = question.criteria.length - 1;\n\n const levels = question.criteria.map(\n (description, index) => [String(index), description] as const,\n );\n\n return Schema.Struct({\n ...TypeSafeSchema.ScoreAnswer.fields,\n score: Schema.Finite.check(Schema.isBetween({ minimum: 0, maximum: maxLevel })),\n legend: Schema.Struct(\n Object.fromEntries(\n levels.map(([key, description]) => [key, Schema.Literal(description)]),\n ),\n ),\n probabilities: distribution(levels.map(([key]) => key)),\n }).check(\n Schema.makeFilter(\n ({ score, probabilities }) =>\n Math.abs(\n score -\n Object.entries(probabilities).reduce(\n (sum, [level, probability]) => sum + Number(level) * probability,\n 0,\n ),\n ) <=\n tolerance * Math.max(1, maxLevel),\n { expected: \"the probability-weighted score (within 1e-6 per level)\" },\n ),\n );\n }\n case \"noul\":\n return TypeSafeSchema.NoulAnswer;\n }\n};\n\n// The overload describes the dependent type enforced by the literal keys and\n// per-question schemas below. No JSON value is asserted to have that type.\nexport function responseFor<const Q extends TypeSafeSchema.Questions>(\n questions: Q,\n): Schema.Codec<TypeSafeSchema.EvaluateResponse<Q>>;\n\nexport function responseFor(questions: TypeSafeSchema.Questions): Schema.Top {\n return Schema.Struct({\n ...TypeSafeSchema.EvaluateResponse.fields,\n answers: Schema.Struct(\n Object.fromEntries(\n Object.entries(questions).map(([id, question]) => [id, answerFor(question)]),\n ),\n ),\n });\n}\n","/**\n * An Effect HttpClient integration for TypeSafe's System One evaluations.\n *\n * @since 0.1.0\n */\nimport * as Config from \"effect/Config\";\nimport * as Context from \"effect/Context\";\nimport * as Effect from \"effect/Effect\";\nimport { flow, identity } from \"effect/Function\";\nimport * as Layer from \"effect/Layer\";\nimport * as Redacted from \"effect/Redacted\";\nimport * as Schema from \"effect/Schema\";\nimport * as AiError from \"effect/unstable/ai/AiError\";\nimport * as Headers from \"effect/unstable/http/Headers\";\nimport * as HttpClient from \"effect/unstable/http/HttpClient\";\nimport * as HttpClientRequest from \"effect/unstable/http/HttpClientRequest\";\nimport * as HttpClientResponse from \"effect/unstable/http/HttpClientResponse\";\n\nimport * as Errors from \"./internal/errors.ts\";\nimport { responseFor } from \"./internal/schema.ts\";\nimport * as TypeSafeSchema from \"./TypeSafeSchema.ts\";\n\n/** @category services\n * @since 0.1.0\n */\nexport interface Service {\n readonly client: HttpClient.HttpClient;\n\n /**\n * Evaluate mixed questions against one state. Answers are validated against\n * the submitted IDs, types, and criteria before receiving their inferred type.\n * There are no automatic retries or timeouts; compose those with Effect.\n */\n readonly evaluate: <const Q extends TypeSafeSchema.Questions>(\n options: TypeSafeSchema.EvaluateRequest<Q>,\n ) => Effect.Effect<TypeSafeSchema.EvaluateResponse<Q>, AiError.AiError>;\n}\n\n/** @category services\n * @since 0.1.0\n */\nexport class TypeSafeClient extends Context.Service<TypeSafeClient, Service>()(\n \"@effect-agent/ai-typesafe/TypeSafeClient\",\n) {}\n\n/** @category options\n * @since 0.1.0\n */\nexport interface Options {\n readonly apiKey: Redacted.Redacted<string>;\n /** Base URL, including the API version. Defaults to https://api.typesafe.ai/v1. */\n readonly apiUrl?: string | undefined;\n /** Applied after authentication and status filtering, for explicit HTTP policies. */\n readonly transformClient?: ((client: HttpClient.HttpClient) => HttpClient.HttpClient) | undefined;\n}\n\nconst encodeRequest = Schema.encodeEffect(Schema.fromJsonString(TypeSafeSchema.EvaluateRequest));\n\n/**\n * Construct the client using a supplied, platform-neutral HttpClient.\n * Acquiring the service does not send a request.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const make = Effect.fnUntraced(function* (\n options: Options,\n): Effect.fn.Return<Service, never, HttpClient.HttpClient> {\n const apiKey = Redacted.value(options.apiKey);\n\n const redact = (text: string) =>\n apiKey.length === 0 ? text : text.replaceAll(apiKey, \"<redacted>\");\n\n const client = (yield* HttpClient.HttpClient).pipe(\n HttpClient.mapRequest(\n flow(\n HttpClientRequest.prependUrl(options.apiUrl ?? \"https://api.typesafe.ai/v1\"),\n HttpClientRequest.bearerToken(options.apiKey),\n HttpClientRequest.acceptJson,\n ),\n ),\n HttpClient.filterStatusOk,\n options.transformClient ?? identity,\n );\n\n const evaluate = Effect.fnUntraced(function* <const Q extends TypeSafeSchema.Questions>(\n options: TypeSafeSchema.EvaluateRequest<Q>,\n ): Effect.fn.Return<TypeSafeSchema.EvaluateResponse<Q>, AiError.AiError> {\n const body = yield* encodeRequest(options).pipe(\n Effect.mapError((error) =>\n Errors.make(new AiError.InvalidRequestError({ description: redact(error.message) })),\n ),\n );\n\n const schema = responseFor(options.questions);\n\n return yield* client\n .execute(\n HttpClientRequest.post(\"/systemone\").pipe(\n HttpClientRequest.bodyText(body, \"application/json\"),\n ),\n )\n .pipe(\n Effect.flatMap(HttpClientResponse.schemaBodyJson(schema, { onExcessProperty: \"error\" })),\n Effect.catchTags({\n HttpClientError: (error) => Errors.mapHttpClientError(error, redact),\n SchemaError: (error) => Effect.fail(Errors.mapSchemaError(error, redact)),\n }),\n Effect.updateService(Headers.CurrentRedactedNames, (names) => [...names, \"authorization\"]),\n );\n });\n\n return TypeSafeClient.of({ client, evaluate });\n});\n\n/** @category layers\n * @since 0.1.0\n */\nexport const layer = (\n options: Options,\n): Layer.Layer<TypeSafeClient, never, HttpClient.HttpClient> =>\n Layer.effect(TypeSafeClient, make(options));\n\n/**\n * Configure the client with Effect Config. The API key defaults to\n * `Config.Redacted(\"TYPESAFE_API_KEY\")`.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layerConfig = (options?: {\n readonly apiKey?: Config.Config<Redacted.Redacted<string>> | undefined;\n readonly apiUrl?: Config.Config<string> | undefined;\n readonly transformClient?: Options[\"transformClient\"];\n}): Layer.Layer<TypeSafeClient, Config.ConfigError, HttpClient.HttpClient> =>\n Layer.effect(\n TypeSafeClient,\n Effect.gen(function* () {\n return yield* make({\n apiKey: yield* options?.apiKey ?? Config.Redacted(\"TYPESAFE_API_KEY\"),\n apiUrl: options?.apiUrl === undefined ? undefined : yield* options.apiUrl,\n transformClient: options?.transformClient,\n });\n }),\n );\n"],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { EvaluateRequest, EvaluateResponse, Questions, t as TypeSafeSchema_d_exports } from "./TypeSafeSchema.mjs";
|
|
2
|
+
import * as Config from "effect/Config";
|
|
3
|
+
import * as Context from "effect/Context";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as Layer from "effect/Layer";
|
|
6
|
+
import * as Redacted from "effect/Redacted";
|
|
7
|
+
import * as AiError from "effect/unstable/ai/AiError";
|
|
8
|
+
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
9
|
+
declare namespace TypeSafeClient_d_exports {
|
|
10
|
+
export { Options, Service, TypeSafeClient, layer, layerConfig, make };
|
|
11
|
+
}
|
|
12
|
+
/** @category services
|
|
13
|
+
* @since 0.1.0
|
|
14
|
+
*/
|
|
15
|
+
export interface Service {
|
|
16
|
+
readonly client: HttpClient.HttpClient;
|
|
17
|
+
/**
|
|
18
|
+
* Evaluate mixed questions against one state. Answers are validated against
|
|
19
|
+
* the submitted IDs, types, and criteria before receiving their inferred type.
|
|
20
|
+
* There are no automatic retries or timeouts; compose those with Effect.
|
|
21
|
+
*/
|
|
22
|
+
readonly evaluate: <const Q extends Questions>(options: EvaluateRequest<Q>) => Effect.Effect<EvaluateResponse<Q>, AiError.AiError>;
|
|
23
|
+
}
|
|
24
|
+
declare const TypeSafeClient_base: Context.ServiceClass<TypeSafeClient, "@effect-agent/ai-typesafe/TypeSafeClient", Service>;
|
|
25
|
+
/** @category services
|
|
26
|
+
* @since 0.1.0
|
|
27
|
+
*/
|
|
28
|
+
export declare class TypeSafeClient extends TypeSafeClient_base {}
|
|
29
|
+
/** @category options
|
|
30
|
+
* @since 0.1.0
|
|
31
|
+
*/
|
|
32
|
+
export interface Options {
|
|
33
|
+
readonly apiKey: Redacted.Redacted<string>;
|
|
34
|
+
/** Base URL, including the API version. Defaults to https://api.typesafe.ai/v1. */
|
|
35
|
+
readonly apiUrl?: string | undefined;
|
|
36
|
+
/** Applied after authentication and status filtering, for explicit HTTP policies. */
|
|
37
|
+
readonly transformClient?: ((client: HttpClient.HttpClient) => HttpClient.HttpClient) | undefined;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Construct the client using a supplied, platform-neutral HttpClient.
|
|
41
|
+
* Acquiring the service does not send a request.
|
|
42
|
+
*
|
|
43
|
+
* @category constructors
|
|
44
|
+
* @since 0.1.0
|
|
45
|
+
*/
|
|
46
|
+
export declare const make: (options: Options) => Effect.Effect<Service, never, HttpClient.HttpClient>;
|
|
47
|
+
/** @category layers
|
|
48
|
+
* @since 0.1.0
|
|
49
|
+
*/
|
|
50
|
+
export declare const layer: (options: Options) => Layer.Layer<TypeSafeClient, never, HttpClient.HttpClient>;
|
|
51
|
+
/**
|
|
52
|
+
* Configure the client with Effect Config. The API key defaults to
|
|
53
|
+
* `Config.Redacted("TYPESAFE_API_KEY")`.
|
|
54
|
+
*
|
|
55
|
+
* @category layers
|
|
56
|
+
* @since 0.1.0
|
|
57
|
+
*/
|
|
58
|
+
export declare const layerConfig: (options?: {
|
|
59
|
+
readonly apiKey?: Config.Config<Redacted.Redacted<string>> | undefined;
|
|
60
|
+
readonly apiUrl?: Config.Config<string> | undefined;
|
|
61
|
+
readonly transformClient?: Options["transformClient"];
|
|
62
|
+
}) => Layer.Layer<TypeSafeClient, Config.ConfigError, HttpClient.HttpClient>;
|
|
63
|
+
//#endregion
|
|
64
|
+
export { TypeSafeClient_d_exports as t };
|
|
65
|
+
//# sourceMappingURL=TypeSafeClient.d.mts.map
|