@effect-agent/ai-typesafe 0.1.0-beta.104 → 0.1.0-beta.107

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 CHANGED
@@ -1,8 +1,8 @@
1
1
  # @effect-agent/ai-typesafe
2
2
 
3
3
  Use Jev to evaluate typed questions with Effect. `TypeSafeDecisionModel` supplies the
4
- provider for [`@effect-agent/ai-decision`](../ai-decision); `TypeSafeClient` owns HTTP
5
- configuration and also exposes Jev's API directly.
4
+ provider for [`@effect-agent/ai-decision`](../ai-decision); `TypeSafeClient` requires
5
+ configuration and an HttpClient, and also exposes Jev's API directly.
6
6
 
7
7
  ```text
8
8
  DecisionModel → TypeSafeDecisionModel → TypeSafeClient → Jev
@@ -16,16 +16,20 @@ import { Layer } from "effect";
16
16
  import { FetchHttpClient } from "effect/unstable/http";
17
17
 
18
18
  const DecisionLive = TypeSafeDecisionModel.model("jev-latest").pipe(
19
- Layer.provide(TypeSafeClient.layerConfig().pipe(Layer.provide(FetchHttpClient.layer))),
19
+ Layer.provide(TypeSafeClient.layer),
20
+ Layer.provide(TypeSafeClient.Config.layer),
21
+ Layer.provide(FetchHttpClient.layer),
20
22
  );
21
23
  ```
22
24
 
23
- Set `TYPESAFE_API_KEY`, then provide `DecisionLive` to the Effect that evaluates your
24
- decision set. The adapter maps shared `probability` questions to Jev's `noul` and retains
25
+ `TypeSafeClient.Config.layer` reads `TYPESAFE_API_KEY` and optional `TYPESAFE_API_URL`.
26
+ Provide your own `TypeSafeClient.Config` layer to use application-owned configuration.
27
+ Provide `DecisionLive` to the Effect that evaluates your decision set.
28
+ The adapter maps shared `probability` questions to Jev's `noul` and retains
25
29
  choice and score distributions. Application code chooses how to use the answers.
26
30
 
27
- There are no default retries or deadlines. Add those policies with Effect or the client's
28
- `transformClient` option. See the [configuration and error reference](https://effect-agent.com/reference/decision-models#typesafe-client)
31
+ There are no default retries or deadlines. Add those policies with Effect or to the supplied
32
+ HttpClient. See the [configuration and error reference](https://effect-agent.com/reference/decision-models#typesafe-client)
29
33
  for details, including Jev's [rounded probabilities](https://effect-agent.com/reference/decision-models#probability-validation).
30
34
 
31
35
  ## Examples
@@ -1,9 +1,9 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
2
  import { ChoiceAnswer, EvaluateRequest, EvaluateResponse, NoulAnswer, Probability, ScoreAnswer } from "./TypeSafeSchema.mjs";
3
- import * as Config from "effect/Config";
3
+ import * as EffectConfig from "effect/Config";
4
4
  import * as Context from "effect/Context";
5
5
  import * as Effect from "effect/Effect";
6
- import { flow, identity } from "effect/Function";
6
+ import { flow } from "effect/Function";
7
7
  import * as Layer from "effect/Layer";
8
8
  import * as Redacted from "effect/Redacted";
9
9
  import * as Schema from "effect/Schema";
@@ -137,27 +137,47 @@ function responseFor(questions) {
137
137
  * @since 0.1.0
138
138
  */
139
139
  var TypeSafeClient_exports = /* @__PURE__ */ __exportAll({
140
+ Config: () => Config,
140
141
  TypeSafeClient: () => TypeSafeClient,
141
142
  layer: () => layer,
142
- layerConfig: () => layerConfig,
143
143
  make: () => make
144
144
  });
145
145
  /** @category services
146
146
  * @since 0.1.0
147
147
  */
148
148
  var TypeSafeClient = class extends Context.Service()("@effect-agent/ai-typesafe/TypeSafeClient") {};
149
+ const defaultApiUrl = "https://api.typesafe.ai/v1";
150
+ /**
151
+ * Configuration captured when the client is acquired. Supply this service from
152
+ * application configuration or use `Config.layer` to load environment values.
153
+ *
154
+ * @category services
155
+ * @since 0.1.0
156
+ */
157
+ var Config = class Config extends Context.Service()("@effect-agent/ai-typesafe/TypeSafeClient/Config") {
158
+ /**
159
+ * Load `TYPESAFE_API_KEY` and optional `TYPESAFE_API_URL` through Effect Config.
160
+ * Missing credentials fail with ConfigError before any HTTP request.
161
+ */
162
+ static layer = Layer.effect(Config, EffectConfig.all({
163
+ apiKey: EffectConfig.Redacted("TYPESAFE_API_KEY"),
164
+ apiUrl: EffectConfig.String("TYPESAFE_API_URL").pipe(EffectConfig.withDefault(defaultApiUrl))
165
+ }));
166
+ };
149
167
  const encodeRequest = Schema.encodeEffect(Schema.fromJsonString(EvaluateRequest));
150
168
  /**
151
- * Construct the client using a supplied, platform-neutral HttpClient.
152
- * Acquiring the service does not send a request.
169
+ * Capture Config and a platform-neutral HttpClient at construction.
170
+ * Acquiring the service does not send a request. Apply HTTP policies to the
171
+ * supplied HttpClient; its request middleware receives authenticated, absolute URLs.
153
172
  *
154
173
  * @category constructors
155
174
  * @since 0.1.0
156
175
  */
157
- const make = Effect.fnUntraced(function* (options) {
158
- const apiKey = Redacted.value(options.apiKey);
176
+ const make = Effect.gen(function* () {
177
+ const config = yield* Config;
178
+ const apiKey = Redacted.value(config.apiKey);
159
179
  const redact = (text) => apiKey.length === 0 ? text : text.replaceAll(apiKey, "<redacted>");
160
- 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);
180
+ const client = (yield* HttpClient.HttpClient).pipe(HttpClient.mapRequestInput(flow(HttpClientRequest.prependUrl(config.apiUrl ?? defaultApiUrl), HttpClientRequest.bearerToken(config.apiKey), HttpClientRequest.acceptJson)), HttpClient.filterStatusOk);
161
181
  const evaluate = Effect.fnUntraced(function* (options) {
162
182
  const body = yield* encodeRequest(options).pipe(Effect.mapError((error) => make$1(new AiError.InvalidRequestError({ description: redact(error.message) }))));
163
183
  const schema = responseFor(options.questions);
@@ -174,22 +194,8 @@ const make = Effect.fnUntraced(function* (options) {
174
194
  /** @category layers
175
195
  * @since 0.1.0
176
196
  */
177
- const layer = (options) => Layer.effect(TypeSafeClient, make(options));
178
- /**
179
- * Configure the client with Effect Config. The API key defaults to
180
- * `Config.Redacted("TYPESAFE_API_KEY")`.
181
- *
182
- * @category layers
183
- * @since 0.1.0
184
- */
185
- const layerConfig = (options) => Layer.effect(TypeSafeClient, Effect.gen(function* () {
186
- return yield* make({
187
- apiKey: yield* options?.apiKey ?? Config.Redacted("TYPESAFE_API_KEY"),
188
- apiUrl: options?.apiUrl === void 0 ? void 0 : yield* options.apiUrl,
189
- transformClient: options?.transformClient
190
- });
191
- }));
197
+ const layer = Layer.effect(TypeSafeClient, make);
192
198
  //#endregion
193
- export { make as a, layerConfig as i, TypeSafeClient_exports as n, choiceProbabilitySum as o, layer as r, TypeSafeClient as t };
199
+ export { make as a, layer as i, TypeSafeClient as n, choiceProbabilitySum as o, TypeSafeClient_exports as r, Config as t };
194
200
 
195
- //# sourceMappingURL=TypeSafeClient-SjzPX2Gl.mjs.map
201
+ //# sourceMappingURL=TypeSafeClient-DTNc_B47.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TypeSafeClient-DTNc_B47.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 probabilitySum = Schema.makeFilter(\n (probabilities: Readonly<Record<string, number>>) =>\n Math.abs(Object.values(probabilities).reduce((sum, value) => sum + value, 0) - 1) <= tolerance,\n { expected: \"probabilities summing to 1 (within 1e-6)\" },\n);\n\n// Jev Choice responses have been observed with two-decimal probabilities totaling 0.99.\n// Limit compatibility to one percentage point, even for very large option catalogues.\n// Score sums/weighting retain their strict checks; no Score rounding contract is assumed.\nexport const choiceProbabilitySum = Schema.makeFilter(\n (probabilities: Readonly<Record<string, number>>) => {\n const values = Object.values(probabilities);\n const error = Math.abs(values.reduce((sum, value) => sum + value, 0) - 1);\n\n return (\n error <= tolerance ||\n (error <= Math.min(0.01, values.length * 0.005) + tolerance &&\n values.every((value) => Math.abs(value * 100 - Math.round(value * 100)) < 1e-8))\n );\n },\n { expected: \"probabilities summing to 1 within bounded two-decimal Choice rounding\" },\n);\n\nconst distribution = (keys: ReadonlyArray<string>, sumCheck = probabilitySum) =>\n Schema.Record(Schema.Literals(keys), TypeSafeSchema.Probability).check(sumCheck);\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, choiceProbabilitySum),\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 EffectConfig from \"effect/Config\";\nimport * as Context from \"effect/Context\";\nimport * as Effect from \"effect/Effect\";\nimport { flow } 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\nconst defaultApiUrl = \"https://api.typesafe.ai/v1\";\n\n/**\n * Configuration captured when the client is acquired. Supply this service from\n * application configuration or use `Config.layer` to load environment values.\n *\n * @category services\n * @since 0.1.0\n */\nexport class Config extends Context.Service<\n Config,\n {\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 }\n>()(\"@effect-agent/ai-typesafe/TypeSafeClient/Config\") {\n /**\n * Load `TYPESAFE_API_KEY` and optional `TYPESAFE_API_URL` through Effect Config.\n * Missing credentials fail with ConfigError before any HTTP request.\n */\n static readonly layer: Layer.Layer<Config, EffectConfig.ConfigError> = Layer.effect(\n Config,\n EffectConfig.all({\n apiKey: EffectConfig.Redacted(\"TYPESAFE_API_KEY\"),\n apiUrl: EffectConfig.String(\"TYPESAFE_API_URL\").pipe(EffectConfig.withDefault(defaultApiUrl)),\n }),\n );\n}\n\nconst encodeRequest = Schema.encodeEffect(Schema.fromJsonString(TypeSafeSchema.EvaluateRequest));\n\n/**\n * Capture Config and a platform-neutral HttpClient at construction.\n * Acquiring the service does not send a request. Apply HTTP policies to the\n * supplied HttpClient; its request middleware receives authenticated, absolute URLs.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const make: Effect.Effect<Service, never, Config | HttpClient.HttpClient> = Effect.gen(\n function* () {\n const config = yield* Config;\n const apiKey = Redacted.value(config.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.mapRequestInput(\n flow(\n HttpClientRequest.prependUrl(config.apiUrl ?? defaultApiUrl),\n HttpClientRequest.bearerToken(config.apiKey),\n HttpClientRequest.acceptJson,\n ),\n ),\n HttpClient.filterStatusOk,\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) => [\n ...names,\n \"authorization\",\n ]),\n );\n });\n\n return TypeSafeClient.of({ client, evaluate });\n },\n);\n\n/** @category layers\n * @since 0.1.0\n */\nexport const layer: Layer.Layer<TypeSafeClient, never, Config | HttpClient.HttpClient> =\n Layer.effect(TypeSafeClient, make);\n"],"mappings":";;;;;;;;;;;;;;;;;AASA,MAAaA,UAAQ,WACnB,QAAQ,KAAK;CAAE,QAAQ;CAAkB,QAAQ;CAAY;AAAO,CAAC;AAEvE,MAAa,kBACX,OACA,WACoB;CACpB,MAAM,SAAS,QAAQ,mBAAmB,gBAAgB,KAAK;CAE/D,OAAOA,OACL,IAAI,QAAQ,mBAAmB;EAAE,GAAG;EAAQ,aAAa,OAAO,OAAO,WAAW;CAAE,CAAC,CACvF;AACF;AAEA,MAAM,iBACJ,SACA,YAC4C;CAC5C,GAAG;CACH,KAAK,OAAO,QAAQ,GAAG;CACvB,WAAW,QAAQ,UAAU,KAAK,CAAC,KAAK,WAAW,CAAC,KAAK,OAAO,KAAK,CAAC,CAAC;CACvE,MAAM,QAAQ,SAAS,KAAA,IAAY,KAAA,IAAY,OAAO,QAAQ,IAAI;CAClE,SAAS,OAAO,IAAI,QAAQ,UAAU,UACpC,SAAS,WAAW,KAAK,IAAI,eAAe,OAAO,KAAK,CAC1D;AACF;AAEA,MAAa,qBAAqB,OAAO,WAAW,WAClD,OACA,QAC0C;CAC1C,MAAM,SAAS,MAAM;CAErB,QAAQ,OAAO,MAAf;EACE,KAAK;EACL,KAAK;EACL,KAAK,mBAAmB;GACtB,MAAM,UAAU,QAAQ,aAAa,iBAAiB,MAAM;GAE5D,OAAO,OAAOA,OACZ,IAAI,QAAQ,aAAa;IACvB,GAAG;IACH,SAAS,cAAc,QAAQ,SAAS,MAAM;IAC9C,aAAa,QAAQ,gBAAgB,KAAA,IAAY,KAAA,IAAY,OAAO,QAAQ,WAAW;GACzF,CAAC,CACH;EACF;EACA,KAAK;EACL,KAAK,kBACH,OAAO,OAAOA,OACZ,IAAI,QAAQ,mBAAmB,EAC7B,aAAa,OAAO,OAAO,eAAe,6CAA6C,EACzF,CAAC,CACH;EACF,KAAK,mBAAmB;GACtB,MAAM,EAAE,SAAS,aAAa;GAC9B,MAAM,gBAAgB,OAAO,QAAQ;GAErC,MAAM,WAAW,UACf,OAAO,IAAI,QAAQ,OAAO,OAAO,aAAa,IAAI,UAChD,SAAS,WAAW,KAAK,IAAI,eAAe,OAAO,KAAK,CAC1D;GAEF,MAAM,OAAO,OAAO,OAAO,OAAO,SAAS,IAAI;GAC/C,MAAM,OAAO,OAAO,OAAO,IAAI,IAAI,OAAO,KAAK,KAAK,IAAI,KAAA;GAExD,MAAM,OAAwC;IAC5C,SAAS;KACP,QAAQ,QAAQ;KAChB,KAAK,OAAO,QAAQ,GAAG;KACvB,WAAW,MAAM,KAAK,QAAQ,YAAY,CAAC,KAAK,WAAW,CAAC,KAAK,OAAO,KAAK,CAAC,CAAC;KAC/E,MAAM,OAAO,eAAe,OAAO,IAAI,QAAQ,MAAM,MAAM,CAAC;KAC5D,SAAS,QAAQ,QAAQ,OAAO;IAClC;IACA,UAAU;KAAE,QAAQ,SAAS;KAAQ,SAAS,QAAQ,SAAS,OAAO;IAAE;IACxE;GACF;GAEA,MAAM,cAAc,QAAQ,sBAAsB;IAChD,QAAQ,SAAS;IACjB,QAAQ,QAAQ;IAChB,KAAK,KAAK,QAAQ;IAClB,SAAS,KAAA;IACT;GACF,CAAC;GAED,OAAO,OAAOA,OACZ,SAAS,WAAW,MAChB,IAAI,QAAQ,oBAAoB;IAAE;IAAa;GAAK,CAAC,IACrD,QAAQ,qBAAqB;IAAE,QAAQ,SAAS;IAAQ;IAAa;GAAK,CAAC,CACjF;EACF;CACF;AACF,CAAC;;;ACjGD,MAAM,YAAY;AAElB,MAAM,iBAAiB,OAAO,YAC3B,kBACC,KAAK,IAAI,OAAO,OAAO,aAAa,CAAC,CAAC,QAAQ,KAAK,UAAU,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,WACvF,EAAE,UAAU,2CAA2C,CACzD;AAKA,MAAa,uBAAuB,OAAO,YACxC,kBAAoD;CACnD,MAAM,SAAS,OAAO,OAAO,aAAa;CAC1C,MAAM,QAAQ,KAAK,IAAI,OAAO,QAAQ,KAAK,UAAU,MAAM,OAAO,CAAC,IAAI,CAAC;CAExE,OACE,SAAS,aACR,SAAS,KAAK,IAAI,KAAM,OAAO,SAAS,IAAK,IAAI,aAChD,OAAO,OAAO,UAAU,KAAK,IAAI,QAAQ,MAAM,KAAK,MAAM,QAAQ,GAAG,CAAC,IAAI,IAAI;AAEpF,GACA,EAAE,UAAU,wEAAwE,CACtF;AAEA,MAAM,gBAAgB,MAA6B,WAAW,mBAC5D,OAAO,OAAO,OAAO,SAAS,IAAI,GAAGC,WAA0B,CAAC,CAAC,MAAM,QAAQ;AAEjF,MAAM,aAAa,aAAsC;CACvD,QAAQ,SAAS,MAAjB;EACE,KAAK,UAAU;GACb,MAAM,OAAO,OAAO,KAAK,SAAS,QAAQ;GAE1C,OAAO,OAAO,OAAO;IACnB,GAAA,aAA+B;IAC/B,QAAQ,OAAO,SAAS,IAAI;IAC5B,eAAe,aAAa,MAAM,oBAAoB;GACxD,CAAC,CAAC,CAAC,MACD,OAAO,YACJ,EAAE,QAAQ,oBACT,OAAO,OAAO,aAAa,CAAC,CAAC,OAAO,UAAU,SAAS,cAAc,OAAO,GAC9E,EAAE,UAAU,+BAA+B,CAC7C,CACF;EACF;EACA,KAAK,SAAS;GACZ,MAAM,WAAW,SAAS,SAAS,SAAS;GAE5C,MAAM,SAAS,SAAS,SAAS,KAC9B,aAAa,UAAU,CAAC,OAAO,KAAK,GAAG,WAAW,CACrD;GAEA,OAAO,OAAO,OAAO;IACnB,GAAA,YAA8B;IAC9B,OAAO,OAAO,OAAO,MAAM,OAAO,UAAU;KAAE,SAAS;KAAG,SAAS;IAAS,CAAC,CAAC;IAC9E,QAAQ,OAAO,OACb,OAAO,YACL,OAAO,KAAK,CAAC,KAAK,iBAAiB,CAAC,KAAK,OAAO,QAAQ,WAAW,CAAC,CAAC,CACvE,CACF;IACA,eAAe,aAAa,OAAO,KAAK,CAAC,SAAS,GAAG,CAAC;GACxD,CAAC,CAAC,CAAC,MACD,OAAO,YACJ,EAAE,OAAO,oBACR,KAAK,IACH,QACE,OAAO,QAAQ,aAAa,CAAC,CAAC,QAC3B,KAAK,CAAC,OAAO,iBAAiB,MAAM,OAAO,KAAK,IAAI,aACrD,CACF,CACJ,KACA,YAAY,KAAK,IAAI,GAAG,QAAQ,GAClC,EAAE,UAAU,yDAAyD,CACvE,CACF;EACF;EACA,KAAK,QACH,OAAOC;CACX;AACF;AAQA,SAAgB,YAAY,WAAiD;CAC3E,OAAO,OAAO,OAAO;EACnB,GAAA,iBAAmC;EACnC,SAAS,OAAO,OACd,OAAO,YACL,OAAO,QAAQ,SAAS,CAAC,CAAC,KAAK,CAAC,IAAI,cAAc,CAAC,IAAI,UAAU,QAAQ,CAAC,CAAC,CAC7E,CACF;CACF,CAAC;AACH;;;;;;;;;;;;;;;;;AC5DA,IAAa,iBAAb,cAAoC,QAAQ,QAAiC,CAAC,CAC5E,0CACF,CAAC,CAAC,CAAC;AAEH,MAAM,gBAAgB;;;;;;;;AAStB,IAAa,SAAb,MAAa,eAAe,QAAQ,QAOlC,CAAC,CAAC,iDAAiD,CAAC,CAAC;;;;;CAKrD,OAAgB,QAAuD,MAAM,OAC3E,QACA,aAAa,IAAI;EACf,QAAQ,aAAa,SAAS,kBAAkB;EAChD,QAAQ,aAAa,OAAO,kBAAkB,CAAC,CAAC,KAAK,aAAa,YAAY,aAAa,CAAC;CAC9F,CAAC,CACH;AACF;AAEA,MAAM,gBAAgB,OAAO,aAAa,OAAO,eAAeC,eAA8B,CAAC;;;;;;;;;AAU/F,MAAa,OAAsE,OAAO,IACxF,aAAa;CACX,MAAM,SAAS,OAAO;CACtB,MAAM,SAAS,SAAS,MAAM,OAAO,MAAM;CAE3C,MAAM,UAAU,SACd,OAAO,WAAW,IAAI,OAAO,KAAK,WAAW,QAAQ,YAAY;CAEnE,MAAM,UAAU,OAAO,WAAW,WAAA,CAAY,KAC5C,WAAW,gBACT,KACE,kBAAkB,WAAW,OAAO,UAAU,aAAa,GAC3D,kBAAkB,YAAY,OAAO,MAAM,GAC3C,kBAAkB,UACpB,CACF,GACA,WAAW,cACb;CAEA,MAAM,WAAW,OAAO,WAAW,WACjC,SACuE;EACvE,MAAM,OAAO,OAAO,cAAc,OAAO,CAAC,CAAC,KACzC,OAAO,UAAU,UACfC,OAAY,IAAI,QAAQ,oBAAoB,EAAE,aAAa,OAAO,MAAM,OAAO,EAAE,CAAC,CAAC,CACrF,CACF;EAEA,MAAM,SAAS,YAAY,QAAQ,SAAS;EAE5C,OAAO,OAAO,OACX,QACC,kBAAkB,KAAK,YAAY,CAAC,CAAC,KACnC,kBAAkB,SAAS,MAAM,kBAAkB,CACrD,CACF,CAAC,CACA,KACC,OAAO,QAAQ,mBAAmB,eAAe,QAAQ,EAAE,kBAAkB,QAAQ,CAAC,CAAC,GACvF,OAAO,UAAU;GACf,kBAAkB,UAAUC,mBAA0B,OAAO,MAAM;GACnE,cAAc,UAAU,OAAO,KAAKC,eAAsB,OAAO,MAAM,CAAC;EAC1E,CAAC,GACD,OAAO,cAAc,QAAQ,uBAAuB,UAAU,CAC5D,GAAG,OACH,eACF,CAAC,CACH;CACJ,CAAC;CAED,OAAO,eAAe,GAAG;EAAE;EAAQ;CAAS,CAAC;AAC/C,CACF;;;;AAKA,MAAa,QACX,MAAM,OAAO,gBAAgB,IAAI"}
@@ -1,5 +1,5 @@
1
1
  import { c as EvaluateResponse, g as TypeSafeSchema_d_exports, p as Questions, s as EvaluateRequest } from "./TypeSafeSchema-Dn4_Tptf.mjs";
2
- import * as Config from "effect/Config";
2
+ import * as EffectConfig from "effect/Config";
3
3
  import * as Context from "effect/Context";
4
4
  import * as Effect from "effect/Effect";
5
5
  import * as Layer from "effect/Layer";
@@ -7,7 +7,7 @@ import * as Redacted from "effect/Redacted";
7
7
  import * as AiError from "effect/unstable/ai/AiError";
8
8
  import * as HttpClient from "effect/unstable/http/HttpClient";
9
9
  declare namespace TypeSafeClient_d_exports {
10
- export { Options, Service, TypeSafeClient, layer, layerConfig, make };
10
+ export { Config, Service, TypeSafeClient, layer, make };
11
11
  }
12
12
  /** @category services
13
13
  * @since 0.1.0
@@ -26,40 +26,38 @@ declare const TypeSafeClient_base: Context.ServiceClass<TypeSafeClient, "@effect
26
26
  * @since 0.1.0
27
27
  */
28
28
  export declare class TypeSafeClient extends TypeSafeClient_base {}
29
- /** @category options
30
- * @since 0.1.0
31
- */
32
- export interface Options {
29
+ declare const Config_base: Context.ServiceClass<Config, "@effect-agent/ai-typesafe/TypeSafeClient/Config", {
33
30
  readonly apiKey: Redacted.Redacted<string>;
34
31
  /** Base URL, including the API version. Defaults to https://api.typesafe.ai/v1. */
35
32
  readonly apiUrl?: string | undefined;
36
- /** Applied after authentication and status filtering, for explicit HTTP policies. */
37
- readonly transformClient?: ((client: HttpClient.HttpClient) => HttpClient.HttpClient) | undefined;
33
+ }>;
34
+ /**
35
+ * Configuration captured when the client is acquired. Supply this service from
36
+ * application configuration or use `Config.layer` to load environment values.
37
+ *
38
+ * @category services
39
+ * @since 0.1.0
40
+ */
41
+ export declare class Config extends Config_base {
42
+ /**
43
+ * Load `TYPESAFE_API_KEY` and optional `TYPESAFE_API_URL` through Effect Config.
44
+ * Missing credentials fail with ConfigError before any HTTP request.
45
+ */
46
+ static readonly layer: Layer.Layer<Config, EffectConfig.ConfigError>;
38
47
  }
39
48
  /**
40
- * Construct the client using a supplied, platform-neutral HttpClient.
41
- * Acquiring the service does not send a request.
49
+ * Capture Config and a platform-neutral HttpClient at construction.
50
+ * Acquiring the service does not send a request. Apply HTTP policies to the
51
+ * supplied HttpClient; its request middleware receives authenticated, absolute URLs.
42
52
  *
43
53
  * @category constructors
44
54
  * @since 0.1.0
45
55
  */
46
- export declare const make: (options: Options) => Effect.Effect<Service, never, HttpClient.HttpClient>;
56
+ export declare const make: Effect.Effect<Service, never, Config | HttpClient.HttpClient>;
47
57
  /** @category layers
48
58
  * @since 0.1.0
49
59
  */
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>;
60
+ export declare const layer: Layer.Layer<TypeSafeClient, never, Config | HttpClient.HttpClient>;
63
61
  //#endregion
64
62
  export { TypeSafeClient_d_exports as t };
65
63
  //# sourceMappingURL=TypeSafeClient.d.mts.map
@@ -1,3 +1,3 @@
1
- import { a as make, i as layerConfig, r as layer, t as TypeSafeClient } from "./TypeSafeClient-SjzPX2Gl.mjs";
1
+ import { a as make, i as layer, n as TypeSafeClient, t as Config } from "./TypeSafeClient-DTNc_B47.mjs";
2
2
  import "./TypeSafeSchema.mjs";
3
- export { TypeSafeClient, layer, layerConfig, make };
3
+ export { Config, TypeSafeClient, layer, make };
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
- import { o as choiceProbabilitySum, t as TypeSafeClient } from "./TypeSafeClient-SjzPX2Gl.mjs";
2
+ import { n as TypeSafeClient, o as choiceProbabilitySum } from "./TypeSafeClient-DTNc_B47.mjs";
3
3
  import { Probability } from "./TypeSafeSchema.mjs";
4
4
  import { DecisionModel } from "@effect-agent/ai-decision";
5
5
  import { Effect, Layer, Schema } from "effect";
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { n as TypeSafeClient_exports } from "./TypeSafeClient-SjzPX2Gl.mjs";
1
+ import { r as TypeSafeClient_exports } from "./TypeSafeClient-DTNc_B47.mjs";
2
2
  import { t as TypeSafeSchema_exports } from "./TypeSafeSchema.mjs";
3
3
  import { t as TypeSafeDecisionModel_exports } from "./TypeSafeDecisionModel.mjs";
4
4
  export { TypeSafeClient_exports as TypeSafeClient, TypeSafeDecisionModel_exports as TypeSafeDecisionModel, TypeSafeSchema_exports as TypeSafeSchema };
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@effect-agent/ai-typesafe","version":"0.1.0-beta.104","dependencies":{"@effect-agent/ai-decision":"0.1.0-beta.104"},"devDependencies":{"@effect/vitest":"4.0.0-rc.115","effect":"4.0.0-rc.115","typescript":"7.0.2","vite-plus":"0.3.2"},"peerDependencies":{"effect":"^4.0.0-rc.115"},"exports":{".":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"./type-safe-client":{"types":"./dist/TypeSafeClient.d.mts","default":"./dist/TypeSafeClient.mjs"},"./type-safe-schema":{"types":"./dist/TypeSafeSchema.d.mts","default":"./dist/TypeSafeSchema.mjs"},"./type-safe-decision-model":{"types":"./dist/TypeSafeDecisionModel.d.mts","default":"./dist/TypeSafeDecisionModel.mjs"}},"description":"TypeSafe AI evaluation with Effect HttpClient and request-derived answer types.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/danieljvdm/effect-agent.git","directory":"packages/ai-typesafe"},"files":["dist","src"],"type":"module","sideEffects":[],"publishConfig":{"access":"public"},"scripts":{"build":"vp pack","check":"tsc --noEmit -p tsconfig.json","test":"vp test"}}
1
+ {"name":"@effect-agent/ai-typesafe","version":"0.1.0-beta.107","dependencies":{"@effect-agent/ai-decision":"0.1.0-beta.107"},"devDependencies":{"@effect/vitest":"4.0.0-rc.115","effect":"4.0.0-rc.115","typescript":"7.0.2","vite-plus":"0.3.2"},"peerDependencies":{"effect":"^4.0.0-rc.115"},"exports":{".":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"./type-safe-client":{"types":"./dist/TypeSafeClient.d.mts","default":"./dist/TypeSafeClient.mjs"},"./type-safe-schema":{"types":"./dist/TypeSafeSchema.d.mts","default":"./dist/TypeSafeSchema.mjs"},"./type-safe-decision-model":{"types":"./dist/TypeSafeDecisionModel.d.mts","default":"./dist/TypeSafeDecisionModel.mjs"}},"description":"TypeSafe AI evaluation with Effect HttpClient and request-derived answer types.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/danieljvdm/effect-agent.git","directory":"packages/ai-typesafe"},"files":["dist","src"],"type":"module","sideEffects":[],"publishConfig":{"access":"public"},"scripts":{"build":"vp pack","check":"tsc --noEmit -p tsconfig.json","test":"vp test"}}
@@ -3,10 +3,10 @@
3
3
  *
4
4
  * @since 0.1.0
5
5
  */
6
- import * as Config from "effect/Config";
6
+ import * as EffectConfig from "effect/Config";
7
7
  import * as Context from "effect/Context";
8
8
  import * as Effect from "effect/Effect";
9
- import { flow, identity } from "effect/Function";
9
+ import { flow } from "effect/Function";
10
10
  import * as Layer from "effect/Layer";
11
11
  import * as Redacted from "effect/Redacted";
12
12
  import * as Schema from "effect/Schema";
@@ -43,103 +43,101 @@ export class TypeSafeClient extends Context.Service<TypeSafeClient, Service>()(
43
43
  "@effect-agent/ai-typesafe/TypeSafeClient",
44
44
  ) {}
45
45
 
46
- /** @category options
46
+ const defaultApiUrl = "https://api.typesafe.ai/v1";
47
+
48
+ /**
49
+ * Configuration captured when the client is acquired. Supply this service from
50
+ * application configuration or use `Config.layer` to load environment values.
51
+ *
52
+ * @category services
47
53
  * @since 0.1.0
48
54
  */
49
- export interface Options {
50
- readonly apiKey: Redacted.Redacted<string>;
51
- /** Base URL, including the API version. Defaults to https://api.typesafe.ai/v1. */
52
- readonly apiUrl?: string | undefined;
53
- /** Applied after authentication and status filtering, for explicit HTTP policies. */
54
- readonly transformClient?: ((client: HttpClient.HttpClient) => HttpClient.HttpClient) | undefined;
55
+ export class Config extends Context.Service<
56
+ Config,
57
+ {
58
+ readonly apiKey: Redacted.Redacted<string>;
59
+ /** Base URL, including the API version. Defaults to https://api.typesafe.ai/v1. */
60
+ readonly apiUrl?: string | undefined;
61
+ }
62
+ >()("@effect-agent/ai-typesafe/TypeSafeClient/Config") {
63
+ /**
64
+ * Load `TYPESAFE_API_KEY` and optional `TYPESAFE_API_URL` through Effect Config.
65
+ * Missing credentials fail with ConfigError before any HTTP request.
66
+ */
67
+ static readonly layer: Layer.Layer<Config, EffectConfig.ConfigError> = Layer.effect(
68
+ Config,
69
+ EffectConfig.all({
70
+ apiKey: EffectConfig.Redacted("TYPESAFE_API_KEY"),
71
+ apiUrl: EffectConfig.String("TYPESAFE_API_URL").pipe(EffectConfig.withDefault(defaultApiUrl)),
72
+ }),
73
+ );
55
74
  }
56
75
 
57
76
  const encodeRequest = Schema.encodeEffect(Schema.fromJsonString(TypeSafeSchema.EvaluateRequest));
58
77
 
59
78
  /**
60
- * Construct the client using a supplied, platform-neutral HttpClient.
61
- * Acquiring the service does not send a request.
79
+ * Capture Config and a platform-neutral HttpClient at construction.
80
+ * Acquiring the service does not send a request. Apply HTTP policies to the
81
+ * supplied HttpClient; its request middleware receives authenticated, absolute URLs.
62
82
  *
63
83
  * @category constructors
64
84
  * @since 0.1.0
65
85
  */
66
- export const make = Effect.fnUntraced(function* (
67
- options: Options,
68
- ): Effect.fn.Return<Service, never, HttpClient.HttpClient> {
69
- const apiKey = Redacted.value(options.apiKey);
86
+ export const make: Effect.Effect<Service, never, Config | HttpClient.HttpClient> = Effect.gen(
87
+ function* () {
88
+ const config = yield* Config;
89
+ const apiKey = Redacted.value(config.apiKey);
70
90
 
71
- const redact = (text: string) =>
72
- apiKey.length === 0 ? text : text.replaceAll(apiKey, "<redacted>");
91
+ const redact = (text: string) =>
92
+ apiKey.length === 0 ? text : text.replaceAll(apiKey, "<redacted>");
73
93
 
74
- const client = (yield* HttpClient.HttpClient).pipe(
75
- HttpClient.mapRequest(
76
- flow(
77
- HttpClientRequest.prependUrl(options.apiUrl ?? "https://api.typesafe.ai/v1"),
78
- HttpClientRequest.bearerToken(options.apiKey),
79
- HttpClientRequest.acceptJson,
80
- ),
81
- ),
82
- HttpClient.filterStatusOk,
83
- options.transformClient ?? identity,
84
- );
85
-
86
- const evaluate = Effect.fnUntraced(function* <const Q extends TypeSafeSchema.Questions>(
87
- options: TypeSafeSchema.EvaluateRequest<Q>,
88
- ): Effect.fn.Return<TypeSafeSchema.EvaluateResponse<Q>, AiError.AiError> {
89
- const body = yield* encodeRequest(options).pipe(
90
- Effect.mapError((error) =>
91
- Errors.make(new AiError.InvalidRequestError({ description: redact(error.message) })),
94
+ const client = (yield* HttpClient.HttpClient).pipe(
95
+ HttpClient.mapRequestInput(
96
+ flow(
97
+ HttpClientRequest.prependUrl(config.apiUrl ?? defaultApiUrl),
98
+ HttpClientRequest.bearerToken(config.apiKey),
99
+ HttpClientRequest.acceptJson,
100
+ ),
92
101
  ),
102
+ HttpClient.filterStatusOk,
93
103
  );
94
104
 
95
- const schema = responseFor(options.questions);
96
-
97
- return yield* client
98
- .execute(
99
- HttpClientRequest.post("/systemone").pipe(
100
- HttpClientRequest.bodyText(body, "application/json"),
105
+ const evaluate = Effect.fnUntraced(function* <const Q extends TypeSafeSchema.Questions>(
106
+ options: TypeSafeSchema.EvaluateRequest<Q>,
107
+ ): Effect.fn.Return<TypeSafeSchema.EvaluateResponse<Q>, AiError.AiError> {
108
+ const body = yield* encodeRequest(options).pipe(
109
+ Effect.mapError((error) =>
110
+ Errors.make(new AiError.InvalidRequestError({ description: redact(error.message) })),
101
111
  ),
102
- )
103
- .pipe(
104
- Effect.flatMap(HttpClientResponse.schemaBodyJson(schema, { onExcessProperty: "error" })),
105
- Effect.catchTags({
106
- HttpClientError: (error) => Errors.mapHttpClientError(error, redact),
107
- SchemaError: (error) => Effect.fail(Errors.mapSchemaError(error, redact)),
108
- }),
109
- Effect.updateService(Headers.CurrentRedactedNames, (names) => [...names, "authorization"]),
110
112
  );
111
- });
112
113
 
113
- return TypeSafeClient.of({ client, evaluate });
114
- });
114
+ const schema = responseFor(options.questions);
115
115
 
116
- /** @category layers
117
- * @since 0.1.0
118
- */
119
- export const layer = (
120
- options: Options,
121
- ): Layer.Layer<TypeSafeClient, never, HttpClient.HttpClient> =>
122
- Layer.effect(TypeSafeClient, make(options));
116
+ return yield* client
117
+ .execute(
118
+ HttpClientRequest.post("/systemone").pipe(
119
+ HttpClientRequest.bodyText(body, "application/json"),
120
+ ),
121
+ )
122
+ .pipe(
123
+ Effect.flatMap(HttpClientResponse.schemaBodyJson(schema, { onExcessProperty: "error" })),
124
+ Effect.catchTags({
125
+ HttpClientError: (error) => Errors.mapHttpClientError(error, redact),
126
+ SchemaError: (error) => Effect.fail(Errors.mapSchemaError(error, redact)),
127
+ }),
128
+ Effect.updateService(Headers.CurrentRedactedNames, (names) => [
129
+ ...names,
130
+ "authorization",
131
+ ]),
132
+ );
133
+ });
123
134
 
124
- /**
125
- * Configure the client with Effect Config. The API key defaults to
126
- * `Config.Redacted("TYPESAFE_API_KEY")`.
127
- *
128
- * @category layers
135
+ return TypeSafeClient.of({ client, evaluate });
136
+ },
137
+ );
138
+
139
+ /** @category layers
129
140
  * @since 0.1.0
130
141
  */
131
- export const layerConfig = (options?: {
132
- readonly apiKey?: Config.Config<Redacted.Redacted<string>> | undefined;
133
- readonly apiUrl?: Config.Config<string> | undefined;
134
- readonly transformClient?: Options["transformClient"];
135
- }): Layer.Layer<TypeSafeClient, Config.ConfigError, HttpClient.HttpClient> =>
136
- Layer.effect(
137
- TypeSafeClient,
138
- Effect.gen(function* () {
139
- return yield* make({
140
- apiKey: yield* options?.apiKey ?? Config.Redacted("TYPESAFE_API_KEY"),
141
- apiUrl: options?.apiUrl === undefined ? undefined : yield* options.apiUrl,
142
- transformClient: options?.transformClient,
143
- });
144
- }),
145
- );
142
+ export const layer: Layer.Layer<TypeSafeClient, never, Config | HttpClient.HttpClient> =
143
+ Layer.effect(TypeSafeClient, make);
@@ -1 +0,0 @@
1
- {"version":3,"file":"TypeSafeClient-SjzPX2Gl.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 probabilitySum = Schema.makeFilter(\n (probabilities: Readonly<Record<string, number>>) =>\n Math.abs(Object.values(probabilities).reduce((sum, value) => sum + value, 0) - 1) <= tolerance,\n { expected: \"probabilities summing to 1 (within 1e-6)\" },\n);\n\n// Jev Choice responses have been observed with two-decimal probabilities totaling 0.99.\n// Limit compatibility to one percentage point, even for very large option catalogues.\n// Score sums/weighting retain their strict checks; no Score rounding contract is assumed.\nexport const choiceProbabilitySum = Schema.makeFilter(\n (probabilities: Readonly<Record<string, number>>) => {\n const values = Object.values(probabilities);\n const error = Math.abs(values.reduce((sum, value) => sum + value, 0) - 1);\n\n return (\n error <= tolerance ||\n (error <= Math.min(0.01, values.length * 0.005) + tolerance &&\n values.every((value) => Math.abs(value * 100 - Math.round(value * 100)) < 1e-8))\n );\n },\n { expected: \"probabilities summing to 1 within bounded two-decimal Choice rounding\" },\n);\n\nconst distribution = (keys: ReadonlyArray<string>, sumCheck = probabilitySum) =>\n Schema.Record(Schema.Literals(keys), TypeSafeSchema.Probability).check(sumCheck);\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, choiceProbabilitySum),\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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"}