@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 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
@@ -0,0 +1,3 @@
1
+ import { a as make, i as layerConfig, r as layer, t as TypeSafeClient } from "./TypeSafeClient-OS5hTVMz.mjs";
2
+ import "./TypeSafeSchema.mjs";
3
+ export { TypeSafeClient, layer, layerConfig, make };