refactor(ts): make client gateway effect native

This commit is contained in:
elpresidank 2026-06-11 08:06:31 -05:00
parent 174d636178
commit a7bdbb9257
16 changed files with 1168 additions and 1262 deletions

View file

@ -3,7 +3,6 @@ import * as BrowserHttpClient from "@effect/platform-browser/BrowserHttpClient";
import * as BrowserKeyValueStore from "@effect/platform-browser/BrowserKeyValueStore";
import type {
GraphRagOptions,
BaseApi,
BeginUploadResponse,
ChunkedUploadDocumentMetadata,
CompleteUploadResponse,
@ -18,7 +17,6 @@ import type {
import {
DispatchPayload,
GatewayWorkbenchHttpApi,
makeBaseApi,
TrustGraphRpcs,
} from "@trustgraph/client";
import type { Scope, } from "effect";
@ -204,10 +202,6 @@ export class Settings extends S.Class<Settings>("Settings")({
featureSwitches: FeatureSwitches,
}, { description: "Persisted workbench connection and display settings." }) {}
export interface WorkbenchApiFactory {
readonly create: (settings: Settings) => BaseApi;
}
export type Theme = "dark" | "light";
export type ChatMode = "graph-rag" | "document-rag" | "agent";
@ -764,7 +758,11 @@ function explainTriplesFrom(source: unknown): Triple[] | undefined {
}
function streamingMetadataFrom(source: unknown): StreamingMetadata | undefined {
const metadata: StreamingMetadata = {};
const metadata: {
in_token?: number;
out_token?: number;
model?: string;
} = {};
let hasMetadata = false;
const inToken = numberProperty(source, "in_token");
@ -804,9 +802,9 @@ function ensureNoGatewayResponseError<A>(operation: string, value: A): Effect.Ef
: Effect.fail(WorkbenchPromiseError.make({ cause: value, message: `${operation}: ${message}` }));
}
function qaBaseApi(): BaseApi | undefined {
function qaBaseApi(): import("@trustgraph/client").BaseApi | undefined {
if (typeof window === "undefined") return undefined;
return (window as Window & { __TRUSTGRAPH_WORKBENCH_QA_API__?: BaseApi }).__TRUSTGRAPH_WORKBENCH_QA_API__;
return (window as Window & { __TRUSTGRAPH_WORKBENCH_QA_API__?: import("@trustgraph/client").BaseApi }).__TRUSTGRAPH_WORKBENCH_QA_API__;
}
function makeWorkbenchGatewayApi(settings: Settings) {
@ -1016,9 +1014,9 @@ function makeWorkbenchGatewayApi(settings: Settings) {
tags,
"document-type": "source",
documentType: "source",
...(id !== undefined ? { id } : {}),
...(metadata !== undefined ? { metadata } : {}),
};
if (id !== undefined) documentMetadata.id = id;
if (metadata !== undefined) documentMetadata.metadata = metadata;
return yield* dispatch("librarian", {
operation: "add-document",
"document-metadata": documentMetadata,
@ -1184,10 +1182,8 @@ function makeWorkbenchGatewayApi(settings: Settings) {
const event: ExplainEvent = {
explainId: explainId ?? "",
explainGraph: stringProperty(resp, "explain_graph") ?? "",
...(explainTriples !== undefined ? { explainTriples } : {}),
};
if (explainTriples !== undefined) {
event.explainTriples = explainTriples;
}
onExplain?.(event);
if (
stringProperty(resp, "response") === undefined &&
@ -1305,10 +1301,8 @@ function makeWorkbenchGatewayApi(settings: Settings) {
const event: ExplainEvent = {
explainId: explainId ?? "",
explainGraph: stringProperty(resp, "explain_graph") ?? "",
...(explainTriples !== undefined ? { explainTriples } : {}),
};
if (explainTriples !== undefined) {
event.explainTriples = explainTriples;
}
onExplain?.(event);
return false;
}
@ -1621,34 +1615,14 @@ export const toggleThemeAtom = Atom.writable(
// Socket lifecycle
// ---------------------------------------------------------------------------
const liveApiFactory: WorkbenchApiFactory = {
create: (settings) =>
makeBaseApi(
settings.user,
settings.apiKey.length > 0 ? settings.apiKey : undefined,
settings.gatewayUrl.length > 0 ? settings.gatewayUrl : undefined,
),
};
export const apiFactoryAtom = Atom.make<WorkbenchApiFactory>(liveApiFactory).pipe(Atom.keepAlive);
export const apiAtom = Atom.make((get) => {
const settings = get(settingsAtom);
const api = get(apiFactoryAtom).create(settings);
get.addFinalizer(() => api.close());
return api;
}).pipe(Atom.keepAlive);
export const connectionStateAtom = Atom.make((get) => {
const api = get(apiAtom);
const fallback: ConnectionState = {
status: "connecting",
hasApiKey: get(settingsAtom).apiKey.length > 0,
const settings = get(settingsAtom);
const hasApiKey = settings.apiKey.length > 0;
const state: ConnectionState = {
status: hasApiKey ? "authenticated" : "unauthenticated",
hasApiKey,
};
const previous = Option.getOrElse(get.self<ConnectionState>(), () => fallback);
const unsubscribe = api.onConnectionStateChange((state) => get.setSelf(state));
get.addFinalizer(unsubscribe);
return previous;
return state;
}).pipe(Atom.keepAlive);
// ---------------------------------------------------------------------------

View file

@ -2,10 +2,8 @@ import type * as Atom from "effect/unstable/reactivity/Atom";
import type {
FeatureSwitches,
Settings,
WorkbenchApiFactory,
} from "@/atoms/workbench";
import {
apiFactoryAtom,
DEFAULT_SETTINGS,
flowIdAtom,
settingsAtom,
@ -45,12 +43,8 @@ export function getWorkbenchQaInitialValues(): Iterable<readonly [Atom.Atom<unkn
if (config?.enabled !== true) return undefined;
const fixture = config.fixture ?? {};
const api = makeMockBaseApi(fixture);
const apiFactory: WorkbenchApiFactory = {
create: () => api,
};
window.__TRUSTGRAPH_WORKBENCH_QA_API__ = api;
return [
[apiFactoryAtom as Atom.Atom<unknown>, apiFactory],
[settingsAtom as Atom.Atom<unknown>, qaSettings(fixture)],
[flowIdAtom as Atom.Atom<unknown>, config.flowId ?? "default"],
];