Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 46 additions & 32 deletions packages/discern/src/DiscernError.schema.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,49 @@
import { Schema } from 'effect'
import { EvalRecord } from './EvalReport.schema.js'

export class UncertainMatchError extends Schema.TaggedError<UncertainMatchError>()('UncertainMatchError', {
caseId: Schema.String,
reason: Schema.String,
}) {
export class UncertainMatchError
extends Schema.TaggedError<UncertainMatchError, { readonly brand: unique symbol }>()('UncertainMatchError', {
caseId: Schema.String,
reason: Schema.String,
})
{
override get message(): string {
return `Semantic case "${this.caseId}" was uncertain: ${this.reason}`
}
}

export class ExhaustiveMatchError extends Schema.TaggedError<ExhaustiveMatchError>()('ExhaustiveMatchError', {}) {
export class ExhaustiveMatchError
extends Schema.TaggedError<ExhaustiveMatchError, { readonly brand: unique symbol }>()('ExhaustiveMatchError', {})
{
override get message(): string {
return 'A supposedly exhaustive semantic classification had no matching case'
}
}

export class DecisionIdCollisionError extends Schema.TaggedError<DecisionIdCollisionError>()(
'DecisionIdCollisionError',
{
decisionId: Schema.String,
},
) {
export class DecisionIdCollisionError
extends Schema.TaggedError<DecisionIdCollisionError, { readonly brand: unique symbol }>()(
'DecisionIdCollisionError',
{
decisionId: Schema.String,
},
)
{
override get message(): string {
return `Decision id collision for "${this.decisionId}": definitions differ`
}
}

export class InvalidThresholdError extends Schema.TaggedError<InvalidThresholdError>()(
'InvalidThresholdError',
{
threshold: Schema.String,
value: Schema.Finite,
limit: Schema.Finite,
message: Schema.String,
},
) {}
export class InvalidThresholdError
extends Schema.TaggedError<InvalidThresholdError, { readonly brand: unique symbol }>()(
'InvalidThresholdError',
{
threshold: Schema.String,
value: Schema.Finite,
limit: Schema.Finite,
message: Schema.String,
},
)
{}

/**
* A span names its ends in the wrong order. `low` and `high` carry the ends as
Expand Down Expand Up @@ -66,10 +74,12 @@ export class CalibrationValuesRefused extends Schema.TaggedError<CalibrationValu
* belonged to, and the rejection itself as `cause` (C2, mirrored from
* `await-readiness.cell.ts`).
*/
export class PolicyCommandRejected extends Schema.TaggedError<PolicyCommandRejected>()('PolicyCommandRejected', {
caseIds: Schema.Array(Schema.String),
cause: Schema.optional(Schema.Unknown),
}) {
export class PolicyCommandRejected
extends Schema.TaggedError<PolicyCommandRejected, { readonly brand: unique symbol }>()('PolicyCommandRejected', {
caseIds: Schema.Array(Schema.String),
cause: Schema.optional(Schema.Unknown),
})
{
override get message(): string {
return `The policy run rejected its command for the cases [${this.caseIds.join(', ')}]`
}
Expand All @@ -80,10 +90,12 @@ export class PolicyCommandRejected extends Schema.TaggedError<PolicyCommandRejec
* Carries the measurement record the read had built, plus the rejection as
* `cause` (C2, mirrored from `await-readiness.cell.ts`).
*/
export class MeasureCommandRejected extends Schema.TaggedError<MeasureCommandRejected>()('MeasureCommandRejected', {
record: EvalRecord,
cause: Schema.optional(Schema.Unknown),
}) {
export class MeasureCommandRejected
extends Schema.TaggedError<MeasureCommandRejected, { readonly brand: unique symbol }>()('MeasureCommandRejected', {
record: EvalRecord,
cause: Schema.optional(Schema.Unknown),
})
{
override get message(): string {
return `The eval run rejected its command for the example scored ${this.record['status']}`
}
Expand All @@ -94,10 +106,12 @@ export class MeasureCommandRejected extends Schema.TaggedError<MeasureCommandRej
* record cannot be written. The refusal names what the example expected and
* carries the encoding issue as `cause`.
*/
export class MeasureInputRefused extends Schema.TaggedError<MeasureInputRefused>()('MeasureInputRefused', {
expected: Schema.Boolean,
cause: Schema.optional(Schema.Unknown),
}) {
export class MeasureInputRefused
extends Schema.TaggedError<MeasureInputRefused, { readonly brand: unique symbol }>()('MeasureInputRefused', {
expected: Schema.Boolean,
cause: Schema.optional(Schema.Unknown),
})
{
override get message(): string {
return `The labelled example the measurement expected to ${
this.expected ? 'match' : 'miss'
Expand Down
30 changes: 17 additions & 13 deletions packages/discern/src/Observation.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,24 +37,28 @@ export const Observations = Schema.Struct({
})
export type Observations = typeof Observations.Type

export class UnsupportedObservationFormatError extends Schema.TaggedError<UnsupportedObservationFormatError>()(
'UnsupportedObservationFormatError',
{
version: Schema.Finite,
detail: Schema.String,
},
) {
export class UnsupportedObservationFormatError
extends Schema.TaggedError<UnsupportedObservationFormatError, { readonly brand: unique symbol }>()(
'UnsupportedObservationFormatError',
{
version: Schema.Finite,
detail: Schema.String,
},
)
{
override get message(): string {
return `Observation snapshot version ${this.version} is unsupported: ${this.detail}`
}
}

export class MalformedObservationSnapshotError extends Schema.TaggedError<MalformedObservationSnapshotError>()(
'MalformedObservationSnapshotError',
{
detail: Schema.String,
},
) {
export class MalformedObservationSnapshotError
extends Schema.TaggedError<MalformedObservationSnapshotError, { readonly brand: unique symbol }>()(
'MalformedObservationSnapshotError',
{
detail: Schema.String,
},
)
{
override get message(): string {
return `The observation snapshot is malformed: ${this.detail}`
}
Expand Down
42 changes: 24 additions & 18 deletions packages/discern/src/ProcedureError.schema.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import { Schema } from 'effect'
import { RouteCandidate } from './Route.schema.js'

export class NoEligibleProcedureError extends Schema.TaggedError<NoEligibleProcedureError>()(
'NoEligibleProcedureError',
{
reason: Schema.String,
},
) {
export class NoEligibleProcedureError
extends Schema.TaggedError<NoEligibleProcedureError, { readonly brand: unique symbol }>()(
'NoEligibleProcedureError',
{
reason: Schema.String,
},
)
{
override get message(): string {
return this.reason
}
}

export class RoutingUncertainError extends Schema.TaggedError<RoutingUncertainError>()('RoutingUncertainError', {
reason: Schema.String,
ranked: Schema.Array(RouteCandidate),
}) {
export class RoutingUncertainError
extends Schema.TaggedError<RoutingUncertainError, { readonly brand: unique symbol }>()('RoutingUncertainError', {
reason: Schema.String,
ranked: Schema.Array(RouteCandidate),
})
{
override get message(): string {
return `Could not route the request confidently: ${this.reason}`
}
Expand All @@ -28,14 +32,16 @@ export class RoutingUncertainError extends Schema.TaggedError<RoutingUncertainEr
* to — and keeps the rejection as `cause`, the same context-carrying shape
* every other infrastructure refusal in the package takes.
*/
export class ProcedureCommandRejectedError extends Schema.TaggedError<ProcedureCommandRejectedError>()(
'ProcedureCommandRejectedError',
{
membership: Schema.Array(Schema.String),
issue: Schema.String,
cause: Schema.optional(Schema.Unknown),
},
) {
export class ProcedureCommandRejectedError
extends Schema.TaggedError<ProcedureCommandRejectedError, { readonly brand: unique symbol }>()(
'ProcedureCommandRejectedError',
{
membership: Schema.Array(Schema.String),
issue: Schema.String,
cause: Schema.optional(Schema.Unknown),
},
)
{
override get message(): string {
return `Routing rejected the command for a registry of ${this.membership.join(', ')}: ${this.issue}`
}
Expand Down
42 changes: 24 additions & 18 deletions packages/discern/src/admit-budget-charge.workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,38 @@ import { BudgetLimit } from './Budget.schema.js'
const BudgetChargeTypeId: unique symbol = Symbol.for('@systemfsoftware/discern/BudgetCharge')
type BudgetChargeTypeId = typeof BudgetChargeTypeId

export class ChargeAdmitted extends Schema.TaggedClass<ChargeAdmitted>()('ChargeAdmitted', {
decisions: Schema.Finite,
}) {
export class ChargeAdmitted
extends Schema.TaggedClass<ChargeAdmitted, { readonly brand: unique symbol }>()('ChargeAdmitted', {
decisions: Schema.Finite,
})
{
readonly [BudgetChargeTypeId] = BudgetChargeTypeId
}

export class BudgetExhausted extends Schema.TaggedError<BudgetExhausted>()('BudgetExhausted', {
reason: Schema.String,
spentDecisions: Schema.Finite,
spentCalls: Schema.Finite,
requestedDecisions: Schema.Finite,
maxDecisions: BudgetLimit,
maxCalls: BudgetLimit,
}) {
export class BudgetExhausted
extends Schema.TaggedError<BudgetExhausted, { readonly brand: unique symbol }>()('BudgetExhausted', {
reason: Schema.String,
spentDecisions: Schema.Finite,
spentCalls: Schema.Finite,
requestedDecisions: Schema.Finite,
maxDecisions: BudgetLimit,
maxCalls: BudgetLimit,
})
{
override get message(): string {
return `Budget exhausted after ${this.spentDecisions} decisions and ${this.spentCalls} calls: ${this.reason}`
}
}

export class AdmitBudgetCharge extends Schema.TaggedClass<AdmitBudgetCharge>()('AdmitBudgetCharge', {
spentDecisions: Schema.Finite,
spentCalls: Schema.Finite,
requestedDecisions: Schema.Finite,
maxDecisions: BudgetLimit,
maxCalls: BudgetLimit,
}) {
export class AdmitBudgetCharge
extends Schema.TaggedClass<AdmitBudgetCharge, { readonly brand: unique symbol }>()('AdmitBudgetCharge', {
spentDecisions: Schema.Finite,
spentCalls: Schema.Finite,
requestedDecisions: Schema.Finite,
maxDecisions: BudgetLimit,
maxCalls: BudgetLimit,
})
{
static readonly [Workflow.InstrumentationBrand] = {
requestedDecisions: 'app.discern.requested_decisions',
} as const
Expand Down
30 changes: 18 additions & 12 deletions packages/discern/src/admit-procedure-depth.workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import * as Result from 'effect/Result'
const ProcedureDepthTypeId: unique symbol = Symbol.for('@systemfsoftware/discern/ProcedureDepth')
type ProcedureDepthTypeId = typeof ProcedureDepthTypeId

export class DepthAdmitted extends Schema.TaggedClass<DepthAdmitted>()('DepthAdmitted', {
depth: Schema.Finite,
limit: Schema.Finite,
}) {
export class DepthAdmitted
extends Schema.TaggedClass<DepthAdmitted, { readonly brand: unique symbol }>()('DepthAdmitted', {
depth: Schema.Finite,
limit: Schema.Finite,
})
{
readonly [ProcedureDepthTypeId] = ProcedureDepthTypeId
}

Expand All @@ -19,19 +21,23 @@ export class DepthAdmitted extends Schema.TaggedClass<DepthAdmitted>()('DepthAdm
* bounded by the code that makes them — so the depth limit counts uncertain
* routing invocations alone.
*/
export class DepthExceededError extends Schema.TaggedError<DepthExceededError>()('DepthExceededError', {
depth: Schema.Finite,
limit: Schema.Finite,
}) {
export class DepthExceededError
extends Schema.TaggedError<DepthExceededError, { readonly brand: unique symbol }>()('DepthExceededError', {
depth: Schema.Finite,
limit: Schema.Finite,
})
{
override get message(): string {
return `Procedure routing reached depth ${this.depth}, at the limit of ${this.limit}`
}
}

export class AdmitProcedureDepth extends Schema.TaggedClass<AdmitProcedureDepth>()('AdmitProcedureDepth', {
depth: Schema.Finite,
limit: Schema.Finite,
}) {
export class AdmitProcedureDepth
extends Schema.TaggedClass<AdmitProcedureDepth, { readonly brand: unique symbol }>()('AdmitProcedureDepth', {
depth: Schema.Finite,
limit: Schema.Finite,
})
{
static readonly [Workflow.InstrumentationBrand] = {} as const
}

Expand Down
9 changes: 4 additions & 5 deletions packages/discern/src/decision-model.blueprint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,11 +235,10 @@ export const recording = (into: ObservationStore): Interceptor => (inner) =>
Effect.flatMap(
CurrentRegion.useSync((path) => path),
(regionPath) =>
Effect.flatMap(inner.decide(options), (response) =>
Effect.as(
recordAnswers(into, options.decisions, options.state, response.answers, regionPath),
response,
)),
Effect.tap(
inner.decide(options),
(response) => recordAnswers(into, options.decisions, options.state, response.answers, regionPath),
),
)
)

Expand Down
2 changes: 1 addition & 1 deletion packages/discern/src/routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export interface InvokeRequest<Input> {
* by position, never looked up again.
*/
export type InvokeRead<Input, Ids extends string = string, Options = InvokeOptions<Input>> =
& (typeof SelectRoute)['Type']
& Omit<(typeof SelectRoute)['Type'], 'brand'>
& {
readonly ranking: ReadonlyArray<RouteCandidate<Ids>>
}
Expand Down
Loading
Loading