mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-07-02 02:58:10 +02:00
chore(ts): consolidate effect native closeout
This commit is contained in:
parent
cd6c9107d7
commit
fab718dce8
21 changed files with 199 additions and 3533 deletions
|
|
@ -75,12 +75,12 @@ export const Field = S.Struct({
|
|||
});
|
||||
export type Field = typeof Field.Type;
|
||||
|
||||
export const RowSchema = S.Struct({
|
||||
export const Row = S.Struct({
|
||||
name: S.String,
|
||||
description: S.optionalKey(S.String),
|
||||
fields: S.Array(Field).pipe(S.mutable),
|
||||
});
|
||||
export type RowSchema = typeof RowSchema.Type;
|
||||
export type Row = typeof Row.Type;
|
||||
|
||||
export const LlmResult = S.Struct({
|
||||
text: S.String,
|
||||
|
|
|
|||
|
|
@ -6,8 +6,13 @@ export * from "./models/Triple.js";
|
|||
export * from "./models/messages.js";
|
||||
export * from "./models/namespaces.js";
|
||||
|
||||
// Export socket client
|
||||
export * from "./socket/trustgraph-socket.js";
|
||||
// Export retained compatibility types from the legacy socket shim.
|
||||
export type {
|
||||
ConnectionState,
|
||||
ExplainEvent,
|
||||
GraphRagOptions,
|
||||
StreamingMetadata,
|
||||
} from "./socket/trustgraph-socket.js";
|
||||
export * from "./socket/effect-rpc-client.js";
|
||||
export * from "./rpc/contract.js";
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import type {
|
|||
RpcConnectionState,
|
||||
} from "./effect-rpc-client.js";
|
||||
import { getDefaultSocketUrl, getRandomValues } from "./websocket-adapter.js";
|
||||
import { Match, Option, Schema as S } from "effect";
|
||||
import { Array as A, Match, Option, Order, Schema as S } from "effect";
|
||||
import * as Predicate from "effect/Predicate";
|
||||
|
||||
// Import all message types for different services
|
||||
|
|
@ -1240,9 +1240,7 @@ export function makeFlowsApi(api: BaseApi) {
|
|||
return this.getConfigAll().then((r) => {
|
||||
const config = r as { config?: { prompt?: Record<string, unknown> } };
|
||||
const promptNs = config.config?.prompt ?? {};
|
||||
return Object.keys(promptNs)
|
||||
.filter((k) => k !== "system")
|
||||
.sort()
|
||||
return A.sort(Object.keys(promptNs).filter((k) => k !== "system"), Order.String)
|
||||
.map((id) => ({ id, name: id }));
|
||||
});
|
||||
},
|
||||
|
|
@ -2204,9 +2202,7 @@ export function makeConfigApi(api: BaseApi) {
|
|||
return this.getConfigAll().then((r) => {
|
||||
const config = r as { config?: { prompt?: Record<string, unknown> } };
|
||||
const promptNs = config.config?.prompt ?? {};
|
||||
return Object.keys(promptNs)
|
||||
.filter((k) => k !== "system")
|
||||
.sort()
|
||||
return A.sort(Object.keys(promptNs).filter((k) => k !== "system"), Order.String)
|
||||
.map((id) => ({ id, name: id }));
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import {NodeRuntime} from "@effect/platform-node";
|
||||
import {Duration, Effect, HashMap, Match, Option, SynchronizedRef} from "effect";
|
||||
import {Array as A, Duration, Effect, HashMap, Match, Option, Order, SynchronizedRef} from "effect";
|
||||
import * as Predicate from "effect/Predicate";
|
||||
import * as S from "effect/Schema";
|
||||
import type {
|
||||
|
|
@ -122,26 +122,27 @@ const initialState = (): ConfigServiceState => ({
|
|||
const getHashMapValue = <K, V>(store: HashMap.HashMap<K, V>, key: K): V | undefined =>
|
||||
Option.getOrUndefined(HashMap.get(store, key));
|
||||
|
||||
const compareText = (left: string, right: string): number =>
|
||||
left.localeCompare(right);
|
||||
|
||||
const compareWorkspace = (left: string, right: string): number =>
|
||||
const workspaceOrder = Order.make<string>((left, right) =>
|
||||
left === right
|
||||
? 0
|
||||
: left === DEFAULT_WORKSPACE
|
||||
? -1
|
||||
: right === DEFAULT_WORKSPACE
|
||||
? 1
|
||||
: compareText(left, right);
|
||||
: Order.String(left, right)
|
||||
);
|
||||
|
||||
const orderByKey = <A>(order: Order.Order<string>): Order.Order<readonly [string, A]> =>
|
||||
Order.mapInput(order, ([key]) => key);
|
||||
|
||||
const workspaceEntries = (store: ConfigStore): ReadonlyArray<readonly [string, WorkspaceStore]> =>
|
||||
HashMap.toEntries(store).sort(([left], [right]) => compareWorkspace(left, right));
|
||||
A.sort(HashMap.toEntries(store), orderByKey<WorkspaceStore>(workspaceOrder));
|
||||
|
||||
const namespaceEntries = (store: WorkspaceStore): ReadonlyArray<readonly [string, NamespaceStore]> =>
|
||||
HashMap.toEntries(store).sort(([left], [right]) => compareText(left, right));
|
||||
A.sort(HashMap.toEntries(store), orderByKey<NamespaceStore>(Order.String));
|
||||
|
||||
const valueEntries = (store: NamespaceStore): ReadonlyArray<readonly [string, unknown]> =>
|
||||
HashMap.toEntries(store).sort(([left], [right]) => compareText(left, right));
|
||||
A.sort(HashMap.toEntries(store), orderByKey<unknown>(Order.String));
|
||||
|
||||
const toPersistedWorkspaces = (
|
||||
store: ConfigStore,
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import {
|
|||
processorLifecycleError,
|
||||
topics,
|
||||
} from "@trustgraph/base";
|
||||
import {Duration, Effect, HashMap, Match, SynchronizedRef} from "effect";
|
||||
import {Array as A, Duration, Effect, HashMap, Match, Order, SynchronizedRef} from "effect";
|
||||
import * as O from "effect/Option";
|
||||
import * as S from "effect/Schema";
|
||||
import {ensureDirectoryEffect, joinPath, readTextFileEffect, writeTextFileEffect} from "../runtime/effect-files.js";
|
||||
|
|
@ -137,8 +137,11 @@ const cloneKnowledgeCore = (core: KnowledgeCore): KnowledgeCore => ({
|
|||
})),
|
||||
});
|
||||
|
||||
const sortedEntries = <A>(store: HashMap.HashMap<string, A>): ReadonlyArray<readonly [string, A]> =>
|
||||
HashMap.toEntries(store).sort(([left], [right]) => left.localeCompare(right));
|
||||
const sortedEntries = <Value>(store: HashMap.HashMap<string, Value>): ReadonlyArray<readonly [string, Value]> =>
|
||||
A.sort(
|
||||
HashMap.toEntries(store),
|
||||
Order.make<readonly [string, Value]>(([left], [right]) => Order.String(left, right)),
|
||||
);
|
||||
|
||||
const toPersistedSnapshot = (state: KnowledgeCoreServiceState): PersistedKnowledgeSnapshot => {
|
||||
const kg: Record<string, KnowledgeCore> = {};
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ import {
|
|||
import { makeProcessorProgram } from "@trustgraph/base";
|
||||
import type { Message } from "@trustgraph/base";
|
||||
import { NodeRuntime } from "@effect/platform-node";
|
||||
import { Duration, Effect, HashMap, Match, Option, SynchronizedRef } from "effect";
|
||||
import { Array as A, Duration, Effect, HashMap, Match, Option, Order, SynchronizedRef } from "effect";
|
||||
import * as S from "effect/Schema";
|
||||
|
||||
// ---------- Internal state types ----------
|
||||
|
|
@ -236,8 +236,11 @@ const isStringRecord = (value: unknown): value is Record<string, string> =>
|
|||
const getHashMapValue = <K, V>(store: HashMap.HashMap<K, V>, key: K): V | undefined =>
|
||||
Option.getOrUndefined(HashMap.get(store, key));
|
||||
|
||||
const sortedEntries = <A>(store: HashMap.HashMap<string, A>): ReadonlyArray<readonly [string, A]> =>
|
||||
HashMap.toEntries(store).sort(([left], [right]) => left.localeCompare(right));
|
||||
const sortedEntries = <Value>(store: HashMap.HashMap<string, Value>): ReadonlyArray<readonly [string, Value]> =>
|
||||
A.sort(
|
||||
HashMap.toEntries(store),
|
||||
Order.make<readonly [string, Value]>(([left], [right]) => Order.String(left, right)),
|
||||
);
|
||||
|
||||
const sortedKeys = <A>(store: HashMap.HashMap<string, A>): Array<string> =>
|
||||
sortedEntries(store).map(([key]) => key);
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import {
|
|||
} from "@trustgraph/base";
|
||||
import type { Message } from "@trustgraph/base";
|
||||
import { NodeRuntime } from "@effect/platform-node";
|
||||
import { Clock, Config, DateTime, Duration, Effect, Match, Option, Random, SynchronizedRef } from "effect";
|
||||
import { Clock, Config, DateTime, Duration, Effect, Match, Option, Order, Random, SynchronizedRef } from "effect";
|
||||
import * as A from "effect/Array";
|
||||
import * as MutableHashMap from "effect/MutableHashMap";
|
||||
import * as S from "effect/Schema";
|
||||
|
|
@ -479,7 +479,7 @@ export function makeLibrarianService(config: LibrarianServiceConfig): LibrarianS
|
|||
if (session === undefined) {
|
||||
return yield* librarianServiceError("get-upload-status", `Upload not found: ${uploadId}`);
|
||||
}
|
||||
const receivedChunks = Array.from(MutableHashMap.keys(session.chunks)).sort((a, b) => a - b);
|
||||
const receivedChunks = A.sort(Array.from(MutableHashMap.keys(session.chunks)), Order.Number);
|
||||
const receivedSet = new Set(receivedChunks);
|
||||
const missingChunks = Array.from({ length: session.totalChunks }, (_, i) => i).filter((i) => !receivedSet.has(i));
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import type {
|
|||
TriplesQueryResponse,
|
||||
} from "@trustgraph/base";
|
||||
import { Triple, errorMessage } from "@trustgraph/base";
|
||||
import { Context, Effect, Layer, Match } from "effect";
|
||||
import { Array as A, Context, Effect, Layer, Match, Order } from "effect";
|
||||
import * as O from "effect/Option";
|
||||
import * as S from "effect/Schema";
|
||||
|
||||
|
|
@ -348,8 +348,10 @@ const scoreEdges = Effect.fn("GraphRagEngine.scoreEdges")(function* (
|
|||
|
||||
yield* Effect.log(`[GraphRag] Edge scoring LLM response (first 500 chars): ${llmResp.response.slice(0, 500)}`);
|
||||
|
||||
const scored = parseScoredEdges(llmResp.response);
|
||||
scored.sort((a, b) => b.score - a.score);
|
||||
const scored = A.sort(
|
||||
parseScoredEdges(llmResp.response),
|
||||
Order.make<typeof ScoredEdge.Type>((left, right) => Order.Number(right.score, left.score)),
|
||||
);
|
||||
const topN = scored.slice(0, config.edgeLimit);
|
||||
|
||||
const result: Triple[] = [];
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import type {
|
|||
import {
|
||||
makeTrustGraphGatewayClientScoped,
|
||||
} from "@trustgraph/client";
|
||||
import {Clock, Config, Context, Effect, Layer} from "effect";
|
||||
import {Array as A, Clock, Config, Context, Effect, Layer, Order} from "effect";
|
||||
import * as O from "effect/Option";
|
||||
import * as Predicate from "effect/Predicate";
|
||||
import {McpServer, Tool, Toolkit} from "effect/unstable/ai";
|
||||
|
|
@ -1775,9 +1775,7 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
|||
).pipe(
|
||||
Effect.map((response) => {
|
||||
const promptNs = asRecord(asRecord(response.config).prompt)
|
||||
const prompts = Object.keys(promptNs)
|
||||
.filter((key) => key !== "system")
|
||||
.sort()
|
||||
const prompts = A.sort(Object.keys(promptNs).filter((key) => key !== "system"), Order.String)
|
||||
.map((id) => ({id, name: id}))
|
||||
return GetPromptsSuccess.make({prompts})
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -19,8 +19,9 @@ import {
|
|||
GatewayWorkbenchHttpApi,
|
||||
TrustGraphRpcs,
|
||||
} from "@trustgraph/client";
|
||||
import type { WorkbenchQaApi } from "@/qa/mock-api";
|
||||
import type { Scope, } from "effect";
|
||||
import { Cause, Clock, Context, Effect, Layer, Match, Metric, Option, Random, Schema as S, Stream } from "effect";
|
||||
import { Cause, Clock, Context, Effect, Layer, Match, Metric, Option, Order, Random, Schema as S, Stream } from "effect";
|
||||
import * as A from "effect/Array";
|
||||
import * as MutableHashMap from "effect/MutableHashMap";
|
||||
import * as Predicate from "effect/Predicate";
|
||||
|
|
@ -802,9 +803,9 @@ function ensureNoGatewayResponseError<A>(operation: string, value: A): Effect.Ef
|
|||
: Effect.fail(WorkbenchPromiseError.make({ cause: value, message: `${operation}: ${message}` }));
|
||||
}
|
||||
|
||||
function qaBaseApi(): import("@trustgraph/client").BaseApi | undefined {
|
||||
function qaBaseApi(): WorkbenchQaApi | undefined {
|
||||
if (typeof window === "undefined") return undefined;
|
||||
return (window as Window & { __TRUSTGRAPH_WORKBENCH_QA_API__?: import("@trustgraph/client").BaseApi }).__TRUSTGRAPH_WORKBENCH_QA_API__;
|
||||
return (window as Window & { __TRUSTGRAPH_WORKBENCH_QA_API__?: WorkbenchQaApi }).__TRUSTGRAPH_WORKBENCH_QA_API__;
|
||||
}
|
||||
|
||||
function makeWorkbenchGatewayApi(settings: Settings) {
|
||||
|
|
@ -897,9 +898,7 @@ function makeWorkbenchGatewayApi(settings: Settings) {
|
|||
Effect.map((response) => {
|
||||
const config = asJsonRecord(response.config);
|
||||
const promptNs = asJsonRecord(config.prompt);
|
||||
return Object.keys(promptNs)
|
||||
.filter((key) => key !== "system")
|
||||
.sort()
|
||||
return A.sort(Object.keys(promptNs).filter((key) => key !== "system"), Order.String)
|
||||
.map((id) => ({ id, name: id }));
|
||||
}),
|
||||
),
|
||||
|
|
@ -1826,9 +1825,9 @@ function explainTriplesKey(input: ExplainTriplesInput): string {
|
|||
const graphs = input.events
|
||||
.map((event) => event.explainGraph ?? event.explainId ?? "")
|
||||
.filter((id) => id.length > 0)
|
||||
.sort()
|
||||
const sortedGraphs = A.sort(graphs, Order.String)
|
||||
.join(explainGraphSeparator);
|
||||
return [input.flowId, input.collection, graphs].join(atomFamilyKeySeparator);
|
||||
return [input.flowId, input.collection, sortedGraphs].join(atomFamilyKeySeparator);
|
||||
}
|
||||
|
||||
const graphTriplesAtomByKey = Atom.family((key: string) => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { lazy, Suspense } from "react";
|
||||
import { useAtom, useAtomValue } from "@effect/atom-react";
|
||||
import { Array as A, Order } from "effect";
|
||||
import { Network, ChevronRight, ChevronDown, Loader2 } from "lucide-react";
|
||||
import * as Atom from "effect/unstable/reactivity/Atom";
|
||||
import {
|
||||
|
|
@ -74,7 +75,7 @@ export function ExplainGraph({ explainEvents, collection }: ExplainGraphProps) {
|
|||
const loading = expanded && resultLoading(result, triples);
|
||||
const error = resultError(result);
|
||||
const { data: graphData, typeMap } = triplesToGraph(triples);
|
||||
const uniqueTypes = Array.from(new Set(Array.from(typeMap.values()).map(localName))).sort();
|
||||
const uniqueTypes = A.sort(Array.from(new Set(Array.from(typeMap.values()).map(localName))), Order.String);
|
||||
|
||||
return (
|
||||
<div className="mt-2 rounded-md border border-border/50">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { lazy, Suspense } from "react";
|
||||
import { useAtom, useAtomRefresh, useAtomValue } from "@effect/atom-react";
|
||||
import { Array as A, Order } from "effect";
|
||||
import {
|
||||
Rotate3d,
|
||||
Search,
|
||||
|
|
@ -180,7 +181,7 @@ export default function GraphPage() {
|
|||
const selectedNode = view.selectedNodeId !== null
|
||||
? data.nodes.find((node) => node.id === view.selectedNodeId)
|
||||
: undefined;
|
||||
const uniqueTypes = Array.from(new Set(Array.from(typeMap.values()).map(localName))).sort();
|
||||
const uniqueTypes = A.sort(Array.from(new Set(Array.from(typeMap.values()).map(localName))), Order.String);
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col">
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
flowIdAtom,
|
||||
settingsAtom,
|
||||
} from "@/atoms/workbench";
|
||||
import type { BaseApi } from "@trustgraph/client";
|
||||
import type { WorkbenchQaApi } from "@/qa/mock-api";
|
||||
import { MockWorkbenchFixture, makeMockBaseApi, qaSettingsFromFixture, } from "@/qa/mock-api";
|
||||
import { Schema as S } from "effect";
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ export class WorkbenchQaWindowConfig extends S.Class<WorkbenchQaWindowConfig>("W
|
|||
declare global {
|
||||
interface Window {
|
||||
__TRUSTGRAPH_WORKBENCH_QA__?: WorkbenchQaWindowConfig;
|
||||
__TRUSTGRAPH_WORKBENCH_QA_API__?: BaseApi;
|
||||
__TRUSTGRAPH_WORKBENCH_QA_API__?: WorkbenchQaApi;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,26 @@
|
|||
import type { BaseApi, DocumentMetadata, ProcessingMetadata, StreamingMetadata, Triple } from "@trustgraph/client";
|
||||
import { makeBaseApiWithRpc, } from "@trustgraph/client";
|
||||
import { Match, Option, Schema as S } from "effect";
|
||||
import type { DocumentMetadata, ProcessingMetadata, StreamingMetadata, Triple } from "@trustgraph/client";
|
||||
import { Array as A, Match, Option, Order, Schema as S } from "effect";
|
||||
|
||||
type ConfigValues = Record<string, Record<string, unknown>>;
|
||||
|
||||
export interface WorkbenchQaApi {
|
||||
readonly makeRequest: <RequestType extends object, ResponseType>(
|
||||
service: string,
|
||||
request: RequestType,
|
||||
timeout?: number,
|
||||
retries?: number,
|
||||
flow?: string,
|
||||
) => Promise<ResponseType>;
|
||||
readonly makeRequestMulti: <RequestType extends object, ResponseType>(
|
||||
service: string,
|
||||
request: RequestType,
|
||||
receiver: (resp: unknown) => boolean,
|
||||
timeout?: number,
|
||||
retries?: number,
|
||||
flow?: string,
|
||||
) => Promise<ResponseType>;
|
||||
}
|
||||
|
||||
const UnknownRecord = S.Record(S.String, S.Unknown);
|
||||
const ConfigValuesRecord = S.Record(S.String, UnknownRecord);
|
||||
|
||||
|
|
@ -358,7 +375,9 @@ function dispatchFlow(state: MockState, request: Record<string, unknown>): unkno
|
|||
const id = stringValue(request["flow-id"], "default");
|
||||
return { flow: encodeJson(state.flows.definitions[id] ?? { id, description: "Mock flow" }) };
|
||||
}
|
||||
if (operation === "list-blueprints") return { "blueprint-names": Object.keys(state.flows.blueprints).sort() };
|
||||
if (operation === "list-blueprints") {
|
||||
return { "blueprint-names": A.sort(Object.keys(state.flows.blueprints), Order.String) };
|
||||
}
|
||||
if (operation === "get-blueprint") {
|
||||
const name = stringValue(request["blueprint-name"], "qa-blueprint");
|
||||
return { "blueprint-definition": encodeJson(state.flows.blueprints[name] ?? {}) };
|
||||
|
|
@ -563,33 +582,37 @@ function dispatchStream<ResponseType>(
|
|||
return Promise.resolve({} as ResponseType);
|
||||
}
|
||||
|
||||
export function makeMockBaseApi(fixture: MockWorkbenchFixture = {}): BaseApi {
|
||||
export function makeMockBaseApi(fixture: MockWorkbenchFixture = {}): WorkbenchQaApi {
|
||||
const state = createState(fixture);
|
||||
const token = state.settings.apiKey.length > 0 ? state.settings.apiKey : undefined;
|
||||
return makeBaseApiWithRpc(state.settings.user, token, state.settings.gatewayUrl, {
|
||||
dispatch: (input) =>
|
||||
return {
|
||||
makeRequest: <RequestType extends object, ResponseType>(
|
||||
service: string,
|
||||
request: RequestType,
|
||||
_timeout?: number,
|
||||
_retries?: number,
|
||||
flow?: string,
|
||||
): Promise<ResponseType> =>
|
||||
Promise.resolve(
|
||||
dispatchRequest(
|
||||
state,
|
||||
input.service,
|
||||
input.request,
|
||||
input.flow,
|
||||
),
|
||||
service,
|
||||
request as Record<string, unknown>,
|
||||
flow,
|
||||
) as ResponseType,
|
||||
),
|
||||
dispatchStream: (input, receiver) =>
|
||||
dispatchStream(state, input.service, (message) => {
|
||||
makeRequestMulti: <RequestType extends object, ResponseType>(
|
||||
service: string,
|
||||
_request: RequestType,
|
||||
receiver: (resp: unknown) => boolean,
|
||||
): Promise<ResponseType> =>
|
||||
dispatchStream(state, service, (message) => {
|
||||
const chunk = message as { response?: unknown; complete?: boolean };
|
||||
return receiver({
|
||||
response: chunk.response,
|
||||
complete: chunk.complete === true,
|
||||
});
|
||||
}).then(() => undefined),
|
||||
subscribe: (listener) => {
|
||||
listener({ status: token === undefined ? "connected" : "connected" });
|
||||
return () => {};
|
||||
},
|
||||
close: () => Promise.resolve(),
|
||||
});
|
||||
}).then(() => ({}) as ResponseType),
|
||||
};
|
||||
}
|
||||
|
||||
export function qaSettingsFromFixture(fixture: MockWorkbenchFixture = {}) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue