import type { OrchestrationV2ThreadProjection, ScopedThreadRef } from "@t3tools/contracts";import * as Option from "effect/Option";import { AsyncResult, Atom } from "effect/unstable/reactivity"; import type { EnvironmentThread } from "./models.ts";import { EMPTY_ENVIRONMENT_THREAD_STATE, type EnvironmentThreadState } from "./threadState.ts";import { parseThreadKey, threadKey } from "./entities.ts"; const EMPTY_VISIBLE_TURN_ITEMS: OrchestrationV2ThreadProjection["visibleTurnItems"] = Object.freeze( [],); export function createEnvironmentThreadDetailAtoms<E>( threadStateAtom: ( environmentId: ScopedThreadRef["environmentId"], threadId: ScopedThreadRef["threadId"], ) => Atom.Atom<AsyncResult.AsyncResult<EnvironmentThreadState, E>>,) { const threadStateValueAtomFamily = Atom.family((key: string) => { const ref = parseThreadKey(key); return Atom.make((get) => Option.getOrElse( AsyncResult.value(get(threadStateAtom(ref.environmentId, ref.threadId))), () => EMPTY_ENVIRONMENT_THREAD_STATE, ), ).pipe(Atom.setIdleTTL(0), Atom.withLabel(`environment-thread-state-value:${key}`)); }); const threadAtomFamily = Atom.family((key: string) => { const ref = parseThreadKey(key); let previousProjection: OrchestrationV2ThreadProjection | null = null; let previousValue: EnvironmentThread | null = null; return Atom.make((get) => { const projection = Option.getOrNull(get(threadStateValueAtomFamily(key)).data); if (projection === previousProjection) return previousValue; previousProjection = projection; previousValue = projection === null ? null : { environmentId: ref.environmentId, projection }; return previousValue; }).pipe(Atom.setIdleTTL(0), Atom.withLabel(`environment-thread:${key}`)); }); const visibleTurnItemsAtomFamily = Atom.family((key: string) => Atom.make( (get): OrchestrationV2ThreadProjection["visibleTurnItems"] => Option.getOrNull(get(threadStateValueAtomFamily(key)).data)?.visibleTurnItems ?? EMPTY_VISIBLE_TURN_ITEMS, ).pipe(Atom.setIdleTTL(0), Atom.withLabel(`environment-thread-visible-turn-items:${key}`)), ); const statusAtomFamily = Atom.family((key: string) => Atom.make((get) => get(threadStateValueAtomFamily(key)).status).pipe( Atom.setIdleTTL(0), Atom.withLabel(`environment-thread-status:${key}`), ), ); const errorAtomFamily = Atom.family((key: string) => Atom.make((get) => Option.getOrNull(get(threadStateValueAtomFamily(key)).error)).pipe( Atom.setIdleTTL(0), Atom.withLabel(`environment-thread-error:${key}`), ), ); const historyAtomFamily = Atom.family((key: string) => Atom.make((get) => get(threadStateValueAtomFamily(key)).history).pipe( Atom.setIdleTTL(0), Atom.withLabel(`environment-thread-history:${key}`), ), ); return { stateAtom: (ref: ScopedThreadRef) => threadStateValueAtomFamily(threadKey(ref)), threadAtom: (ref: ScopedThreadRef) => threadAtomFamily(threadKey(ref)), visibleTurnItemsAtom: (ref: ScopedThreadRef) => visibleTurnItemsAtomFamily(threadKey(ref)), statusAtom: (ref: ScopedThreadRef) => statusAtomFamily(threadKey(ref)), errorAtom: (ref: ScopedThreadRef) => errorAtomFamily(threadKey(ref)), historyAtom: (ref: ScopedThreadRef) => historyAtomFamily(threadKey(ref)), };}
import type { OrchestrationV2ThreadProjection, ScopedThreadRef } from "@t3tools/contracts";
import * as Option from "effect/Option";
import { AsyncResult, Atom } from "effect/unstable/reactivity";
import type { EnvironmentThread } from "./models.ts";
import { EMPTY_ENVIRONMENT_THREAD_STATE, type EnvironmentThreadState } from "./threadState.ts";
import { parseThreadKey, threadKey } from "./entities.ts";
const EMPTY_VISIBLE_TURN_ITEMS: OrchestrationV2ThreadProjection["visibleTurnItems"] = Object.freeze(
[],
);
export function createEnvironmentThreadDetailAtoms<E>(
threadStateAtom: (
environmentId: ScopedThreadRef["environmentId"],
threadId: ScopedThreadRef["threadId"],
) => Atom.Atom<AsyncResult.AsyncResult<EnvironmentThreadState, E>>,
) {
const threadStateValueAtomFamily = Atom.family((key: string) => {
const ref = parseThreadKey(key);
return Atom.make((get) =>
Option.getOrElse(
AsyncResult.value(get(threadStateAtom(ref.environmentId, ref.threadId))),
() => EMPTY_ENVIRONMENT_THREAD_STATE,
),
).pipe(Atom.setIdleTTL(0), Atom.withLabel(`environment-thread-state-value:${key}`));
});
const threadAtomFamily = Atom.family((key: string) => {
const ref = parseThreadKey(key);
let previousProjection: OrchestrationV2ThreadProjection | null = null;
let previousValue: EnvironmentThread | null = null;
return Atom.make((get) => {
const projection = Option.getOrNull(get(threadStateValueAtomFamily(key)).data);
if (projection === previousProjection) return previousValue;
previousProjection = projection;
previousValue = projection === null ? null : { environmentId: ref.environmentId, projection };
return previousValue;
}).pipe(Atom.setIdleTTL(0), Atom.withLabel(`environment-thread:${key}`));
});
const visibleTurnItemsAtomFamily = Atom.family((key: string) =>
Atom.make(
(get): OrchestrationV2ThreadProjection["visibleTurnItems"] =>
Option.getOrNull(get(threadStateValueAtomFamily(key)).data)?.visibleTurnItems ??
EMPTY_VISIBLE_TURN_ITEMS,
).pipe(Atom.setIdleTTL(0), Atom.withLabel(`environment-thread-visible-turn-items:${key}`)),
);
const statusAtomFamily = Atom.family((key: string) =>
Atom.make((get) => get(threadStateValueAtomFamily(key)).status).pipe(
Atom.setIdleTTL(0),
Atom.withLabel(`environment-thread-status:${key}`),
),
);
const errorAtomFamily = Atom.family((key: string) =>
Atom.make((get) => Option.getOrNull(get(threadStateValueAtomFamily(key)).error)).pipe(
Atom.setIdleTTL(0),
Atom.withLabel(`environment-thread-error:${key}`),
),
);
const historyAtomFamily = Atom.family((key: string) =>
Atom.make((get) => get(threadStateValueAtomFamily(key)).history).pipe(
Atom.setIdleTTL(0),
Atom.withLabel(`environment-thread-history:${key}`),
),
);
return {
stateAtom: (ref: ScopedThreadRef) => threadStateValueAtomFamily(threadKey(ref)),
threadAtom: (ref: ScopedThreadRef) => threadAtomFamily(threadKey(ref)),
visibleTurnItemsAtom: (ref: ScopedThreadRef) => visibleTurnItemsAtomFamily(threadKey(ref)),
statusAtom: (ref: ScopedThreadRef) => statusAtomFamily(threadKey(ref)),
errorAtom: (ref: ScopedThreadRef) => errorAtomFamily(threadKey(ref)),
historyAtom: (ref: ScopedThreadRef) => historyAtomFamily(threadKey(ref)),
};
}