138 lines · 5.3 KB · TypeScript
RawDownload
import {  ProviderApprovalDecision,  ProviderSessionId,  ProviderUserInputAnswers,  RuntimeRequestId,  ThreadId,} from "@t3tools/contracts";import * as Context from "effect/Context";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as Schema from "effect/Schema"; import { ProjectionStoreV2 } from "./ProjectionStore.ts";import { ProviderSessionManagerV2 } from "./ProviderSessionManager.ts"; export class RuntimeRequestResponseExecutionError extends Schema.TaggedErrorClass<RuntimeRequestResponseExecutionError>()(  "RuntimeRequestResponseExecutionError",  {    reason: Schema.Literals([      "request-missing",      "request-not-ready",      "request-not-resumable",      "provider-session-not-active",      "unexpected-failure",    ]),    threadId: ThreadId,    providerSessionId: ProviderSessionId,    requestId: RuntimeRequestId,    cause: Schema.optional(Schema.Defect()),  },) {  override get message(): string {    switch (this.reason) {      case "request-missing":        return `Runtime request ${this.requestId} no longer exists on thread ${this.threadId}.`;      case "request-not-ready":        return `Runtime request ${this.requestId} on thread ${this.threadId} is not ready for response execution.`;      case "request-not-resumable":        return `Runtime request ${this.requestId} on thread ${this.threadId} is not resumable on provider session ${this.providerSessionId}.`;      case "provider-session-not-active":        return `Provider session ${this.providerSessionId} is not active for runtime request ${this.requestId} on thread ${this.threadId}.`;      case "unexpected-failure":        return `Failed to respond to runtime request ${this.requestId} on thread ${this.threadId} via provider session ${this.providerSessionId}.`;    }  }} const isRuntimeRequestResponseExecutionError = Schema.is(RuntimeRequestResponseExecutionError); export interface RuntimeRequestServiceV2Shape {  readonly respond: (input: {    readonly threadId: ThreadId;    readonly providerSessionId: ProviderSessionId;    readonly requestId: RuntimeRequestId;    readonly decision?: ProviderApprovalDecision;    readonly answers?: ProviderUserInputAnswers;  }) => Effect.Effect<void, RuntimeRequestResponseExecutionError>;} export class RuntimeRequestServiceV2 extends Context.Service<  RuntimeRequestServiceV2,  RuntimeRequestServiceV2Shape>()("t3/orchestration-v2/RuntimeRequestService/RuntimeRequestServiceV2") {} export const layer: Layer.Layer<  RuntimeRequestServiceV2,  never,  ProjectionStoreV2 | ProviderSessionManagerV2> = Layer.effect(  RuntimeRequestServiceV2,  Effect.gen(function* () {    const projections = yield* ProjectionStoreV2;    const sessions = yield* ProviderSessionManagerV2;     return RuntimeRequestServiceV2.of({      respond: (input) =>        Effect.gen(function* () {          const request = yield* projections.getRuntimeRequest(input.threadId, input.requestId);          if (request === undefined) {            return yield* new RuntimeRequestResponseExecutionError({              reason: "request-missing",              threadId: input.threadId,              providerSessionId: input.providerSessionId,              requestId: input.requestId,            });          }          // Dispatch validates the request while it is pending, then persists the          // resolved projection before this effect is executed.          if (request.status !== "resolved") {            return yield* new RuntimeRequestResponseExecutionError({              reason: "request-not-ready",              threadId: input.threadId,              providerSessionId: input.providerSessionId,              requestId: input.requestId,            });          }          if (            request.responseCapability.type !== "live" ||            request.responseCapability.providerSessionId !== input.providerSessionId          ) {            return yield* new RuntimeRequestResponseExecutionError({              reason: "request-not-resumable",              threadId: input.threadId,              providerSessionId: input.providerSessionId,              requestId: input.requestId,            });          }          const session = yield* sessions.get(input.providerSessionId);          if (Option.isNone(session)) {            return yield* new RuntimeRequestResponseExecutionError({              reason: "provider-session-not-active",              threadId: input.threadId,              providerSessionId: input.providerSessionId,              requestId: input.requestId,            });          }          yield* session.value.respondToRuntimeRequest({            requestId: input.requestId,            ...(input.decision === undefined ? {} : { decision: input.decision }),            ...(input.answers === undefined ? {} : { answers: input.answers }),          });        }).pipe(          Effect.mapError((cause) =>            isRuntimeRequestResponseExecutionError(cause)              ? cause              : new RuntimeRequestResponseExecutionError({                  reason: "unexpected-failure",                  threadId: input.threadId,                  providerSessionId: input.providerSessionId,                  requestId: input.requestId,                  cause,                }),          ),        ),    });  }),);