mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-07-01 09:29:38 +02:00
refactor(ts): make client gateway effect native
This commit is contained in:
parent
174d636178
commit
a7bdbb9257
16 changed files with 1168 additions and 1262 deletions
|
|
@ -7,16 +7,12 @@
|
||||||
import { Effect } from "effect";
|
import { Effect } from "effect";
|
||||||
import * as Argument from "effect/unstable/cli/Argument";
|
import * as Argument from "effect/unstable/cli/Argument";
|
||||||
import * as Command from "effect/unstable/cli/Command";
|
import * as Command from "effect/unstable/cli/Command";
|
||||||
import { cliCommandError, withSocket, writeJson } from "./util.js";
|
import { gatewayDispatch, withGatewayClient, writeJson } from "./util.js";
|
||||||
|
|
||||||
const show = Command.make("show", {}, () =>
|
const show = Command.make("show", {}, () =>
|
||||||
withSocket((socket) =>
|
withGatewayClient((client) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const cfg = socket.config();
|
const resp = yield* gatewayDispatch(client, "config.show", "config", { operation: "config" }, { timeoutMs: 60000 });
|
||||||
const resp = yield* Effect.tryPromise({
|
|
||||||
try: () => cfg.getConfigAll(),
|
|
||||||
catch: (error) => cliCommandError("config.show", error),
|
|
||||||
});
|
|
||||||
yield* writeJson(resp);
|
yield* writeJson(resp);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
@ -25,19 +21,17 @@ const show = Command.make("show", {}, () =>
|
||||||
const get = Command.make("get", {
|
const get = Command.make("get", {
|
||||||
key: Argument.string("key").pipe(Argument.withDescription("Config key (format: type/key)")),
|
key: Argument.string("key").pipe(Argument.withDescription("Config key (format: type/key)")),
|
||||||
}, ({ key }) =>
|
}, ({ key }) =>
|
||||||
withSocket((socket) =>
|
withGatewayClient((client) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const cfg = socket.config();
|
const parts = key.split("/");
|
||||||
// Support "type/key" format; fall back to using the whole string as key
|
const configKey =
|
||||||
const parts = key.split("/");
|
parts.length >= 2
|
||||||
const configKey =
|
? { type: parts[0], key: parts.slice(1).join("/") }
|
||||||
parts.length >= 2
|
: { type: "config", key };
|
||||||
? { type: parts[0], key: parts.slice(1).join("/") }
|
const resp = yield* gatewayDispatch(client, "config.get", "config", {
|
||||||
: { type: "config", key };
|
operation: "get",
|
||||||
const resp = yield* Effect.tryPromise({
|
keys: [configKey],
|
||||||
try: () => cfg.getConfig([configKey]),
|
}, { timeoutMs: 60000 });
|
||||||
catch: (error) => cliCommandError("config.get", error),
|
|
||||||
});
|
|
||||||
yield* writeJson(resp);
|
yield* writeJson(resp);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
@ -47,18 +41,17 @@ const set = Command.make("set", {
|
||||||
key: Argument.string("key").pipe(Argument.withDescription("Config key (format: type/key)")),
|
key: Argument.string("key").pipe(Argument.withDescription("Config key (format: type/key)")),
|
||||||
value: Argument.string("value").pipe(Argument.withDescription("Config value (JSON)")),
|
value: Argument.string("value").pipe(Argument.withDescription("Config value (JSON)")),
|
||||||
}, ({ key, value }) =>
|
}, ({ key, value }) =>
|
||||||
withSocket((socket) =>
|
withGatewayClient((client) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const cfg = socket.config();
|
const parts = key.split("/");
|
||||||
const parts = key.split("/");
|
const configEntry =
|
||||||
const configEntry =
|
parts.length >= 2
|
||||||
parts.length >= 2
|
? { type: parts[0], key: parts.slice(1).join("/"), value }
|
||||||
? { type: parts[0], key: parts.slice(1).join("/"), value }
|
: { type: "config", key, value };
|
||||||
: { type: "config", key, value };
|
const resp = yield* gatewayDispatch(client, "config.set", "config", {
|
||||||
const resp = yield* Effect.tryPromise({
|
operation: "put",
|
||||||
try: () => cfg.putConfig([configEntry]),
|
values: [configEntry],
|
||||||
catch: (error) => cliCommandError("config.set", error),
|
}, { timeoutMs: 60000 });
|
||||||
});
|
|
||||||
yield* writeJson(resp);
|
yield* writeJson(resp);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
@ -70,13 +63,12 @@ const list = Command.make("list", {
|
||||||
Argument.withDefault("config"),
|
Argument.withDefault("config"),
|
||||||
),
|
),
|
||||||
}, ({ type }) =>
|
}, ({ type }) =>
|
||||||
withSocket((socket) =>
|
withGatewayClient((client) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const cfg = socket.config();
|
const resp = yield* gatewayDispatch(client, "config.list", "config", {
|
||||||
const resp = yield* Effect.tryPromise({
|
operation: "list",
|
||||||
try: () => cfg.list(type),
|
type,
|
||||||
catch: (error) => cliCommandError("config.list", error),
|
}, { timeoutMs: 60000 });
|
||||||
});
|
|
||||||
yield* writeJson(resp);
|
yield* writeJson(resp);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
@ -85,18 +77,17 @@ const list = Command.make("list", {
|
||||||
const deleteCommand = Command.make("delete", {
|
const deleteCommand = Command.make("delete", {
|
||||||
key: Argument.string("key").pipe(Argument.withDescription("Config key (format: type/key)")),
|
key: Argument.string("key").pipe(Argument.withDescription("Config key (format: type/key)")),
|
||||||
}, ({ key }) =>
|
}, ({ key }) =>
|
||||||
withSocket((socket) =>
|
withGatewayClient((client) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const cfg = socket.config();
|
const parts = key.split("/");
|
||||||
const parts = key.split("/");
|
const configKey =
|
||||||
const configKey =
|
parts.length >= 2
|
||||||
parts.length >= 2
|
? { type: parts[0], key: parts.slice(1).join("/") }
|
||||||
? { type: parts[0], key: parts.slice(1).join("/") }
|
: { type: "config", key };
|
||||||
: { type: "config", key };
|
const resp = yield* gatewayDispatch(client, "config.delete", "config", {
|
||||||
const resp = yield* Effect.tryPromise({
|
operation: "delete",
|
||||||
try: () => cfg.deleteConfig(configKey),
|
keys: [configKey],
|
||||||
catch: (error) => cliCommandError("config.delete", error),
|
}, { timeoutMs: 30000 });
|
||||||
});
|
|
||||||
yield* writeJson(resp);
|
yield* writeJson(resp);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
import { Effect } from "effect";
|
import { Effect } from "effect";
|
||||||
import * as Argument from "effect/unstable/cli/Argument";
|
import * as Argument from "effect/unstable/cli/Argument";
|
||||||
import * as Command from "effect/unstable/cli/Command";
|
import * as Command from "effect/unstable/cli/Command";
|
||||||
import { cliCommandError, withSocket, writeJson } from "./util.js";
|
import { gatewayDispatch, withGatewayClient, writeJson } from "./util.js";
|
||||||
|
|
||||||
export const embeddingsCommand = Command.make("embeddings", {
|
export const embeddingsCommand = Command.make("embeddings", {
|
||||||
texts: Argument.string("text").pipe(
|
texts: Argument.string("text").pipe(
|
||||||
|
|
@ -15,14 +15,13 @@ export const embeddingsCommand = Command.make("embeddings", {
|
||||||
Argument.variadic({ min: 1 }),
|
Argument.variadic({ min: 1 }),
|
||||||
),
|
),
|
||||||
}, ({ texts }) =>
|
}, ({ texts }) =>
|
||||||
withSocket((socket, opts) =>
|
withGatewayClient((client, opts) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const flow = socket.flow(opts.flow);
|
const response = yield* gatewayDispatch(client, "embeddings", "embeddings", {
|
||||||
const vectors = yield* Effect.tryPromise({
|
texts: Array.from(texts),
|
||||||
try: () => flow.embeddings(Array.from(texts)),
|
}, { flow: opts.flow, timeoutMs: 30000 });
|
||||||
catch: (error) => cliCommandError("embeddings", error),
|
const record = response as Record<string, unknown>;
|
||||||
});
|
yield* writeJson(record.vectors ?? []);
|
||||||
yield* writeJson(vectors);
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
).pipe(Command.withDescription("Generate text embeddings"));
|
).pipe(Command.withDescription("Generate text embeddings"));
|
||||||
|
|
|
||||||
|
|
@ -9,16 +9,14 @@ import * as S from "effect/Schema";
|
||||||
import * as Argument from "effect/unstable/cli/Argument";
|
import * as Argument from "effect/unstable/cli/Argument";
|
||||||
import * as Command from "effect/unstable/cli/Command";
|
import * as Command from "effect/unstable/cli/Command";
|
||||||
import * as Flag from "effect/unstable/cli/Flag";
|
import * as Flag from "effect/unstable/cli/Flag";
|
||||||
import { cliCommandError, withSocket, writeJson } from "./util.js";
|
import { cliCommandError, gatewayDispatch, withGatewayClient, writeJson } from "./util.js";
|
||||||
|
|
||||||
const list = Command.make("list", {}, () =>
|
const list = Command.make("list", {}, () =>
|
||||||
withSocket((socket) =>
|
withGatewayClient((client) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const flows = socket.flows();
|
const response = yield* gatewayDispatch(client, "flow.list", "flow", { operation: "list-flows" }, { timeoutMs: 60000 });
|
||||||
const ids = yield* Effect.tryPromise({
|
const record = response as Record<string, unknown>;
|
||||||
try: () => flows.getFlows(),
|
const ids = Array.isArray(record["flow-ids"]) ? record["flow-ids"] : [];
|
||||||
catch: (error) => cliCommandError("flow.list", error),
|
|
||||||
});
|
|
||||||
yield* writeJson(ids);
|
yield* writeJson(ids);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
@ -27,13 +25,16 @@ const list = Command.make("list", {}, () =>
|
||||||
const get = Command.make("get", {
|
const get = Command.make("get", {
|
||||||
id: Argument.string("id").pipe(Argument.withDescription("Flow ID")),
|
id: Argument.string("id").pipe(Argument.withDescription("Flow ID")),
|
||||||
}, ({ id }) =>
|
}, ({ id }) =>
|
||||||
withSocket((socket) =>
|
withGatewayClient((client) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const flows = socket.flows();
|
const response = yield* gatewayDispatch(client, "flow.get", "flow", {
|
||||||
const def = yield* Effect.tryPromise({
|
operation: "get-flow",
|
||||||
try: () => flows.getFlow(id),
|
"flow-id": id,
|
||||||
catch: (error) => cliCommandError("flow.get", error),
|
}, { timeoutMs: 60000 });
|
||||||
});
|
const record = response as Record<string, unknown>;
|
||||||
|
const def = typeof record.flow === "string"
|
||||||
|
? yield* S.decodeUnknownEffect(S.UnknownFromJsonString)(record.flow)
|
||||||
|
: record.flow;
|
||||||
yield* writeJson(def);
|
yield* writeJson(def);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
@ -56,26 +57,23 @@ const start = Command.make("start", {
|
||||||
Flag.optional,
|
Flag.optional,
|
||||||
),
|
),
|
||||||
}, ({ id, blueprint, description, parameters }) =>
|
}, ({ id, blueprint, description, parameters }) =>
|
||||||
withSocket((socket) =>
|
withGatewayClient((client) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const flows = socket.flows();
|
|
||||||
const rawParameters = parameters._tag === "Some" ? parameters.value : undefined;
|
const rawParameters = parameters._tag === "Some" ? parameters.value : undefined;
|
||||||
const params = rawParameters !== undefined && rawParameters.length > 0
|
const params = rawParameters !== undefined && rawParameters.length > 0
|
||||||
? yield* S.decodeUnknownEffect(S.UnknownFromJsonString)(rawParameters).pipe(
|
? yield* S.decodeUnknownEffect(S.UnknownFromJsonString)(rawParameters).pipe(
|
||||||
Effect.flatMap(S.decodeUnknownEffect(S.Record(S.String, S.Unknown))),
|
Effect.flatMap(S.decodeUnknownEffect(S.Record(S.String, S.Unknown))),
|
||||||
Effect.mapError((error) => cliCommandError("flow.start.parameters", error)),
|
Effect.mapError((error) => cliCommandError("flow.start.parameters", error)),
|
||||||
)
|
)
|
||||||
: undefined;
|
: undefined;
|
||||||
const resp = yield* Effect.tryPromise({
|
const request = {
|
||||||
try: () =>
|
operation: "start-flow",
|
||||||
flows.startFlow(
|
"flow-id": id,
|
||||||
id,
|
"blueprint-name": blueprint,
|
||||||
blueprint,
|
description,
|
||||||
description,
|
...(params !== undefined && Object.keys(params).length > 0 ? { parameters: params } : {}),
|
||||||
params,
|
};
|
||||||
),
|
const resp = yield* gatewayDispatch(client, "flow.start", "flow", request, { timeoutMs: 30000 });
|
||||||
catch: (error) => cliCommandError("flow.start", error),
|
|
||||||
});
|
|
||||||
yield* writeJson(resp);
|
yield* writeJson(resp);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
@ -84,13 +82,12 @@ const start = Command.make("start", {
|
||||||
const stop = Command.make("stop", {
|
const stop = Command.make("stop", {
|
||||||
id: Argument.string("id").pipe(Argument.withDescription("Flow ID")),
|
id: Argument.string("id").pipe(Argument.withDescription("Flow ID")),
|
||||||
}, ({ id }) =>
|
}, ({ id }) =>
|
||||||
withSocket((socket) =>
|
withGatewayClient((client) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const flows = socket.flows();
|
const resp = yield* gatewayDispatch(client, "flow.stop", "flow", {
|
||||||
const resp = yield* Effect.tryPromise({
|
operation: "stop-flow",
|
||||||
try: () => flows.stopFlow(id),
|
"flow-id": id,
|
||||||
catch: (error) => cliCommandError("flow.stop", error),
|
}, { timeoutMs: 30000 });
|
||||||
});
|
|
||||||
yield* writeJson(resp);
|
yield* writeJson(resp);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import * as O from "effect/Option";
|
||||||
import * as Argument from "effect/unstable/cli/Argument";
|
import * as Argument from "effect/unstable/cli/Argument";
|
||||||
import * as Command from "effect/unstable/cli/Command";
|
import * as Command from "effect/unstable/cli/Command";
|
||||||
import * as Flag from "effect/unstable/cli/Flag";
|
import * as Flag from "effect/unstable/cli/Flag";
|
||||||
import { cliCommandError, withSocket, writeLine } from "./util.js";
|
import { gatewayDispatch, withGatewayClient, writeLine } from "./util.js";
|
||||||
|
|
||||||
export const graphRagCommand = Command.make("graph-rag", {
|
export const graphRagCommand = Command.make("graph-rag", {
|
||||||
query: Argument.string("query").pipe(Argument.withDescription("Natural language query")),
|
query: Argument.string("query").pipe(Argument.withDescription("Natural language query")),
|
||||||
|
|
@ -26,22 +26,17 @@ export const graphRagCommand = Command.make("graph-rag", {
|
||||||
Flag.optional,
|
Flag.optional,
|
||||||
),
|
),
|
||||||
}, ({ query, entityLimit, tripleLimit, collection }) =>
|
}, ({ query, entityLimit, tripleLimit, collection }) =>
|
||||||
withSocket((socket, opts) =>
|
withGatewayClient((client, opts) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const flow = socket.flow(opts.flow);
|
const response = yield* gatewayDispatch(client, "graph-rag", "graph-rag", {
|
||||||
const response = yield* Effect.tryPromise({
|
query,
|
||||||
try: () =>
|
user: opts.user,
|
||||||
flow.graphRag(
|
collection: O.getOrUndefined(collection) ?? "default",
|
||||||
query,
|
"entity-limit": entityLimit,
|
||||||
{
|
"triple-limit": tripleLimit,
|
||||||
entityLimit,
|
}, { flow: opts.flow, timeoutMs: 60000 });
|
||||||
tripleLimit,
|
const record = response as Record<string, unknown>;
|
||||||
},
|
yield* writeLine(typeof record.response === "string" ? record.response : "");
|
||||||
O.getOrUndefined(collection),
|
|
||||||
),
|
|
||||||
catch: (error) => cliCommandError("graph-rag", error),
|
|
||||||
});
|
|
||||||
yield* writeLine(response);
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
).pipe(Command.withDescription("Query the knowledge graph using RAG"));
|
).pipe(Command.withDescription("Query the knowledge graph using RAG"));
|
||||||
|
|
@ -57,19 +52,16 @@ export const documentRagCommand = Command.make("document-rag", {
|
||||||
Flag.optional,
|
Flag.optional,
|
||||||
),
|
),
|
||||||
}, ({ query, docLimit, collection }) =>
|
}, ({ query, docLimit, collection }) =>
|
||||||
withSocket((socket, opts) =>
|
withGatewayClient((client, opts) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const flow = socket.flow(opts.flow);
|
const response = yield* gatewayDispatch(client, "document-rag", "document-rag", {
|
||||||
const response = yield* Effect.tryPromise({
|
query,
|
||||||
try: () =>
|
user: opts.user,
|
||||||
flow.documentRag(
|
collection: O.getOrUndefined(collection) ?? "default",
|
||||||
query,
|
"doc-limit": docLimit,
|
||||||
docLimit,
|
}, { flow: opts.flow, timeoutMs: 60000 });
|
||||||
O.getOrUndefined(collection),
|
const record = response as Record<string, unknown>;
|
||||||
),
|
yield* writeLine(typeof record.response === "string" ? record.response : "");
|
||||||
catch: (error) => cliCommandError("document-rag", error),
|
|
||||||
});
|
|
||||||
yield* writeLine(response);
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
).pipe(Command.withDescription("Query documents using RAG"));
|
).pipe(Command.withDescription("Query documents using RAG"));
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,12 @@
|
||||||
* Manages documents stored in the TrustGraph library.
|
* Manages documents stored in the TrustGraph library.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Effect, Match } from "effect";
|
import { Clock, Effect, Match } from "effect";
|
||||||
import * as O from "effect/Option";
|
import * as O from "effect/Option";
|
||||||
import * as Argument from "effect/unstable/cli/Argument";
|
import * as Argument from "effect/unstable/cli/Argument";
|
||||||
import * as Command from "effect/unstable/cli/Command";
|
import * as Command from "effect/unstable/cli/Command";
|
||||||
import * as Flag from "effect/unstable/cli/Flag";
|
import * as Flag from "effect/unstable/cli/Flag";
|
||||||
import { cliCommandError, withSocket, writeJson } from "./util.js";
|
import { cliCommandError, gatewayDispatch, withGatewayClient, writeJson } from "./util.js";
|
||||||
|
|
||||||
function basenamePath(filepath: string): string {
|
function basenamePath(filepath: string): string {
|
||||||
const normalized = filepath.replace(/\/+$/, "");
|
const normalized = filepath.replace(/\/+$/, "");
|
||||||
|
|
@ -34,14 +34,14 @@ export function guessMimeType(filepath: string): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
const list = Command.make("list", {}, () =>
|
const list = Command.make("list", {}, () =>
|
||||||
withSocket((socket) =>
|
withGatewayClient((client, opts) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const lib = socket.librarian();
|
const response = yield* gatewayDispatch(client, "library.list", "librarian", {
|
||||||
const docs = yield* Effect.tryPromise({
|
operation: "list-documents",
|
||||||
try: () => lib.getDocuments(),
|
user: opts.user,
|
||||||
catch: (error) => cliCommandError("library.list", error),
|
}, { timeoutMs: 60000 });
|
||||||
});
|
const record = response as Record<string, unknown>;
|
||||||
yield* writeJson(docs);
|
yield* writeJson(record["document-metadatas"] ?? record.documents ?? []);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
).pipe(Command.withDescription("List documents in the library"));
|
).pipe(Command.withDescription("List documents in the library"));
|
||||||
|
|
@ -72,9 +72,8 @@ const load = Command.make("load", {
|
||||||
Flag.optional,
|
Flag.optional,
|
||||||
),
|
),
|
||||||
}, ({ file, title, mimeType, comments, tags, id }) =>
|
}, ({ file, title, mimeType, comments, tags, id }) =>
|
||||||
withSocket((socket) =>
|
withGatewayClient((client, opts) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const lib = socket.librarian();
|
|
||||||
const data = new Uint8Array(yield* Effect.tryPromise({
|
const data = new Uint8Array(yield* Effect.tryPromise({
|
||||||
try: () => Bun.file(file).arrayBuffer(),
|
try: () => Bun.file(file).arrayBuffer(),
|
||||||
catch: (error) => cliCommandError("library.load.read-file", error),
|
catch: (error) => cliCommandError("library.load.read-file", error),
|
||||||
|
|
@ -82,19 +81,25 @@ const load = Command.make("load", {
|
||||||
const b64 = Buffer.from(data).toString("base64");
|
const b64 = Buffer.from(data).toString("base64");
|
||||||
const resolvedMimeType = O.getOrUndefined(mimeType) ?? guessMimeType(file);
|
const resolvedMimeType = O.getOrUndefined(mimeType) ?? guessMimeType(file);
|
||||||
const resolvedTitle = O.getOrUndefined(title) ?? basenamePath(file);
|
const resolvedTitle = O.getOrUndefined(title) ?? basenamePath(file);
|
||||||
|
const timestamp = yield* Clock.currentTimeMillis;
|
||||||
const resp = yield* Effect.tryPromise({
|
const documentId = O.getOrUndefined(id);
|
||||||
try: () =>
|
const documentMetadata = {
|
||||||
lib.loadDocument(
|
time: Math.floor(timestamp / 1000),
|
||||||
b64,
|
kind: resolvedMimeType,
|
||||||
resolvedMimeType,
|
title: resolvedTitle,
|
||||||
resolvedTitle,
|
comments,
|
||||||
comments,
|
user: opts.user,
|
||||||
Array.from(tags),
|
tags: Array.from(tags),
|
||||||
O.getOrUndefined(id),
|
"document-type": "source",
|
||||||
),
|
documentType: "source",
|
||||||
catch: (error) => cliCommandError("library.load", error),
|
...(documentId !== undefined ? { id: documentId } : {}),
|
||||||
});
|
};
|
||||||
|
const resp = yield* gatewayDispatch(client, "library.load", "librarian", {
|
||||||
|
operation: "add-document",
|
||||||
|
"document-metadata": documentMetadata,
|
||||||
|
documentMetadata,
|
||||||
|
content: b64,
|
||||||
|
}, { timeoutMs: 30000 });
|
||||||
yield* writeJson(resp);
|
yield* writeJson(resp);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
@ -107,27 +112,29 @@ const remove = Command.make("remove", {
|
||||||
Flag.optional,
|
Flag.optional,
|
||||||
),
|
),
|
||||||
}, ({ id, collection }) =>
|
}, ({ id, collection }) =>
|
||||||
withSocket((socket) =>
|
withGatewayClient((client, opts) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const lib = socket.librarian();
|
const resp = yield* gatewayDispatch(client, "library.remove", "librarian", {
|
||||||
const resp = yield* Effect.tryPromise({
|
operation: "remove-document",
|
||||||
try: () => lib.removeDocument(id, O.getOrUndefined(collection)),
|
"document-id": id,
|
||||||
catch: (error) => cliCommandError("library.remove", error),
|
documentId: id,
|
||||||
});
|
user: opts.user,
|
||||||
|
collection: O.getOrUndefined(collection) ?? "default",
|
||||||
|
}, { timeoutMs: 30000 });
|
||||||
yield* writeJson(resp);
|
yield* writeJson(resp);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
).pipe(Command.withDescription("Remove a document from the library"));
|
).pipe(Command.withDescription("Remove a document from the library"));
|
||||||
|
|
||||||
const processing = Command.make("processing", {}, () =>
|
const processing = Command.make("processing", {}, () =>
|
||||||
withSocket((socket) =>
|
withGatewayClient((client, opts) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const lib = socket.librarian();
|
const response = yield* gatewayDispatch(client, "library.processing", "librarian", {
|
||||||
const items = yield* Effect.tryPromise({
|
operation: "list-processing",
|
||||||
try: () => lib.getProcessing(),
|
user: opts.user,
|
||||||
catch: (error) => cliCommandError("library.processing", error),
|
}, { timeoutMs: 60000 });
|
||||||
});
|
const record = response as Record<string, unknown>;
|
||||||
yield* writeJson(items);
|
yield* writeJson(record["processing-metadatas"] ?? record.processing ?? record["processing-metadata"] ?? []);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
).pipe(Command.withDescription("List documents currently being processed"));
|
).pipe(Command.withDescription("List documents currently being processed"));
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { Effect } from "effect";
|
||||||
import * as O from "effect/Option";
|
import * as O from "effect/Option";
|
||||||
import * as Command from "effect/unstable/cli/Command";
|
import * as Command from "effect/unstable/cli/Command";
|
||||||
import * as Flag from "effect/unstable/cli/Flag";
|
import * as Flag from "effect/unstable/cli/Flag";
|
||||||
import { cliCommandError, withSocket, writeJson } from "./util.js";
|
import { gatewayDispatch, withGatewayClient, writeJson } from "./util.js";
|
||||||
|
|
||||||
export const triplesCommand = Command.make("triples", {
|
export const triplesCommand = Command.make("triples", {
|
||||||
subject: Flag.string("subject").pipe(
|
subject: Flag.string("subject").pipe(
|
||||||
|
|
@ -37,9 +37,8 @@ export const triplesCommand = Command.make("triples", {
|
||||||
Flag.optional,
|
Flag.optional,
|
||||||
),
|
),
|
||||||
}, ({ subject, predicate, object, limit, collection }) =>
|
}, ({ subject, predicate, object, limit, collection }) =>
|
||||||
withSocket((socket, opts) =>
|
withGatewayClient((client, opts) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const flow = socket.flow(opts.flow);
|
|
||||||
const subjectValue = O.getOrUndefined(subject);
|
const subjectValue = O.getOrUndefined(subject);
|
||||||
const predicateValue = O.getOrUndefined(predicate);
|
const predicateValue = O.getOrUndefined(predicate);
|
||||||
const objectValue = O.getOrUndefined(object);
|
const objectValue = O.getOrUndefined(object);
|
||||||
|
|
@ -53,18 +52,16 @@ export const triplesCommand = Command.make("triples", {
|
||||||
? { t: "i", i: objectValue }
|
? { t: "i", i: objectValue }
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const triples = yield* Effect.tryPromise({
|
const response = yield* gatewayDispatch(client, "triples", "triples", {
|
||||||
try: () =>
|
limit,
|
||||||
flow.triplesQuery(
|
user: opts.user,
|
||||||
s,
|
collection: O.getOrUndefined(collection) ?? "default",
|
||||||
p,
|
...(s !== undefined ? { s } : {}),
|
||||||
o,
|
...(p !== undefined ? { p } : {}),
|
||||||
limit,
|
...(o !== undefined ? { o } : {}),
|
||||||
O.getOrUndefined(collection),
|
}, { flow: opts.flow, timeoutMs: 30000 });
|
||||||
),
|
const record = response as Record<string, unknown>;
|
||||||
catch: (error) => cliCommandError("triples", error),
|
yield* writeJson(record.triples ?? record.response ?? []);
|
||||||
});
|
|
||||||
yield* writeJson(triples);
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
).pipe(Command.withDescription("Query knowledge graph triples"));
|
).pipe(Command.withDescription("Query knowledge graph triples"));
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
BaseApi,
|
DispatchOptions,
|
||||||
TrustGraphGatewayClient,
|
TrustGraphGatewayClient,
|
||||||
} from "@trustgraph/client";
|
} from "@trustgraph/client";
|
||||||
import {
|
import {
|
||||||
createTrustGraphSocket,
|
|
||||||
makeTrustGraphGatewayClientScoped,
|
makeTrustGraphGatewayClientScoped,
|
||||||
} from "@trustgraph/client";
|
} from "@trustgraph/client";
|
||||||
import { Duration, Effect } from "effect";
|
import { Effect } from "effect";
|
||||||
import * as O from "effect/Option";
|
import * as O from "effect/Option";
|
||||||
import * as S from "effect/Schema";
|
import * as S from "effect/Schema";
|
||||||
import * as Command from "effect/unstable/cli/Command";
|
import * as Command from "effect/unstable/cli/Command";
|
||||||
|
|
@ -85,37 +84,6 @@ export const writeJson = (value: unknown) =>
|
||||||
Effect.flatMap(writeLine),
|
Effect.flatMap(writeLine),
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a BaseApi socket client and wait for the connection to be established.
|
|
||||||
* The client auto-connects; we listen for the first "connected/authenticated"
|
|
||||||
* state before handing it back to the caller.
|
|
||||||
*/
|
|
||||||
export function createSocketEffect(opts: CliOpts): Effect.Effect<BaseApi, CliCommandError> {
|
|
||||||
const socket = createTrustGraphSocket(opts.user, opts.token, opts.gateway);
|
|
||||||
|
|
||||||
return Effect.callback<void, CliCommandError>((resume) => {
|
|
||||||
const unsub = socket.onConnectionStateChange((state) => {
|
|
||||||
if (state.status === "authenticated" || state.status === "unauthenticated") {
|
|
||||||
unsub();
|
|
||||||
resume(Effect.void);
|
|
||||||
} else if (state.status === "failed") {
|
|
||||||
unsub();
|
|
||||||
resume(Effect.fail(cliCommandError("connect", state.lastError ?? "WebSocket connection failed")));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return Effect.sync(() => {
|
|
||||||
unsub();
|
|
||||||
});
|
|
||||||
}).pipe(
|
|
||||||
Effect.timeout(Duration.seconds(15)),
|
|
||||||
Effect.catchTag("TimeoutError", () =>
|
|
||||||
Effect.fail(cliCommandError("connect", "Timed out waiting for WebSocket connection")),
|
|
||||||
),
|
|
||||||
Effect.as(socket),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function gatewayUrlWithToken(opts: CliOpts): string {
|
function gatewayUrlWithToken(opts: CliOpts): string {
|
||||||
if (opts.token === undefined || opts.token.length === 0) return opts.gateway;
|
if (opts.token === undefined || opts.token.length === 0) return opts.gateway;
|
||||||
const separator = opts.gateway.includes("?") ? "&" : "?";
|
const separator = opts.gateway.includes("?") ? "&" : "?";
|
||||||
|
|
@ -133,16 +101,37 @@ export const withGatewayClient = Effect.fn("withGatewayClient")(function* <A, E,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
export const withSocket = Effect.fn("withSocket")(function* <A, E, R>(
|
export interface GatewayDispatchOptions {
|
||||||
use: (socket: BaseApi, opts: CliOpts) => Effect.Effect<A, E, R>,
|
readonly flow?: string;
|
||||||
|
readonly timeoutMs?: number;
|
||||||
|
readonly retries?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const gatewayDispatch = Effect.fn("gatewayDispatch")(function*(
|
||||||
|
client: TrustGraphGatewayClient,
|
||||||
|
operation: string,
|
||||||
|
service: string,
|
||||||
|
request: Record<string, unknown>,
|
||||||
|
options: GatewayDispatchOptions = {},
|
||||||
) {
|
) {
|
||||||
const opts = yield* getOpts;
|
const input = options.flow === undefined
|
||||||
return yield* Effect.acquireUseRelease(
|
? {
|
||||||
createSocketEffect(opts),
|
scope: "global" as const,
|
||||||
(socket) => use(socket, opts),
|
service,
|
||||||
(socket) =>
|
request,
|
||||||
Effect.sync(() => {
|
}
|
||||||
socket.close();
|
: {
|
||||||
}),
|
scope: "flow" as const,
|
||||||
|
service,
|
||||||
|
flow: options.flow,
|
||||||
|
request,
|
||||||
|
};
|
||||||
|
const dispatchOptions: DispatchOptions = {
|
||||||
|
...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
|
||||||
|
...(options.retries !== undefined ? { retries: options.retries } : {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
return yield* client.dispatch(input, dispatchOptions).pipe(
|
||||||
|
Effect.mapError((error) => cliCommandError(operation, error)),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,43 @@
|
||||||
|
import { Schema as S } from "effect";
|
||||||
|
|
||||||
// Term type discriminators matching the wire format
|
// Term type discriminators matching the wire format
|
||||||
// i = IRI, b = BLANK node, l = LITERAL, t = TRIPLE (reified)
|
// i = IRI, b = BLANK node, l = LITERAL, t = TRIPLE (reified)
|
||||||
export type TermType = "i" | "b" | "l" | "t";
|
export type TermType = "i" | "b" | "l" | "t";
|
||||||
|
|
||||||
export interface IriTerm {
|
export class IriTerm extends S.Class<IriTerm>("IriTerm")({
|
||||||
t: "i";
|
t: S.Literal("i"),
|
||||||
i: string;
|
i: S.String,
|
||||||
}
|
}, { description: "IRI term in TrustGraph wire triples." }) {}
|
||||||
|
|
||||||
export interface BlankTerm {
|
export class BlankTerm extends S.Class<BlankTerm>("BlankTerm")({
|
||||||
t: "b";
|
t: S.Literal("b"),
|
||||||
d: string;
|
d: S.String,
|
||||||
}
|
}, { description: "Blank-node term in TrustGraph wire triples." }) {}
|
||||||
|
|
||||||
export interface LiteralTerm {
|
export class LiteralTerm extends S.Class<LiteralTerm>("LiteralTerm")({
|
||||||
t: "l";
|
t: S.Literal("l"),
|
||||||
v: string;
|
v: S.String,
|
||||||
dt?: string; // datatype
|
dt: S.optionalKey(S.String),
|
||||||
ln?: string; // language
|
ln: S.optionalKey(S.String),
|
||||||
}
|
}, { description: "Literal term in TrustGraph wire triples." }) {}
|
||||||
|
|
||||||
export interface TripleTerm {
|
export class TripleTerm extends S.Class<TripleTerm>("TripleTerm")({
|
||||||
t: "t";
|
t: S.Literal("t"),
|
||||||
tr?: Triple;
|
tr: S.optionalKey(S.suspend((): S.Codec<Triple, Triple> => Triple)),
|
||||||
}
|
}, { description: "Reified triple term in TrustGraph wire triples." }) {}
|
||||||
|
|
||||||
export type Term = IriTerm | BlankTerm | LiteralTerm | TripleTerm;
|
export const Term = S.Union([IriTerm, BlankTerm, LiteralTerm, TripleTerm]);
|
||||||
|
export type Term = typeof Term.Type;
|
||||||
|
|
||||||
export interface PartialTriple {
|
export class PartialTriple extends S.Class<PartialTriple>("PartialTriple")({
|
||||||
s?: Term;
|
s: S.optionalKey(Term),
|
||||||
p?: Term;
|
p: S.optionalKey(Term),
|
||||||
o?: Term;
|
o: S.optionalKey(Term),
|
||||||
}
|
}, { description: "Partial triple pattern for query wildcards." }) {}
|
||||||
|
|
||||||
export interface Triple {
|
export class Triple extends S.Class<Triple>("Triple")({
|
||||||
s: Term;
|
s: Term,
|
||||||
p: Term;
|
p: Term,
|
||||||
o: Term;
|
o: Term,
|
||||||
g?: string; // graph (renamed from direc to match backend)
|
g: S.optionalKey(S.String),
|
||||||
}
|
}, { description: "TrustGraph wire triple, optionally scoped to a named graph." }) {}
|
||||||
|
|
|
||||||
|
|
@ -1,521 +1,505 @@
|
||||||
import type { Term, Triple } from "./Triple.js";
|
import { Schema as S } from "effect";
|
||||||
|
import { Term, Triple } from "./Triple.js";
|
||||||
|
|
||||||
export type Request = object;
|
export type Request = object;
|
||||||
export type Response = object;
|
export type Response = object;
|
||||||
|
|
||||||
export interface ResponseError {
|
|
||||||
type?: string;
|
|
||||||
message: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type WireError = object | string;
|
export type WireError = object | string;
|
||||||
|
|
||||||
export interface RequestMessage {
|
const UnknownRecord = S.Record(S.String, S.Unknown);
|
||||||
id: string;
|
const WireErrorValue = S.Union([S.String, UnknownRecord]);
|
||||||
service: string;
|
const TypedMessageError = S.Struct({
|
||||||
request: Request;
|
message: S.String,
|
||||||
flow?: string;
|
type: S.optionalKey(S.String),
|
||||||
}
|
});
|
||||||
|
const OptionalMessageError = S.Struct({
|
||||||
|
message: S.optionalKey(S.String),
|
||||||
|
});
|
||||||
|
|
||||||
export interface ApiResponse {
|
const NumberArray = S.Array(S.Finite).pipe(S.mutable);
|
||||||
id: string;
|
const NumberMatrix = S.Array(NumberArray).pipe(S.mutable);
|
||||||
response: Response;
|
const TripleArray = S.Array(Triple).pipe(S.mutable);
|
||||||
}
|
const StringArray = S.Array(S.String).pipe(S.mutable);
|
||||||
|
|
||||||
export interface Metadata {
|
export class ResponseError extends S.Class<ResponseError>("ResponseError")({
|
||||||
id?: string;
|
type: S.optionalKey(S.String),
|
||||||
metadata?: Triple[];
|
message: S.String,
|
||||||
user?: string;
|
}, { description: "TrustGraph response error payload." }) {}
|
||||||
collection?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EntityEmbeddings {
|
export class RequestMessage extends S.Class<RequestMessage>("RequestMessage")({
|
||||||
entity?: Term;
|
id: S.String,
|
||||||
vectors?: number[][];
|
service: S.String,
|
||||||
}
|
request: UnknownRecord,
|
||||||
|
flow: S.optionalKey(S.String),
|
||||||
|
}, { description: "Envelope sent to a TrustGraph service." }) {}
|
||||||
|
|
||||||
export interface GraphEmbeddings {
|
export class ApiResponse extends S.Class<ApiResponse>("ApiResponse")({
|
||||||
metadata?: Metadata;
|
id: S.String,
|
||||||
entities?: EntityEmbeddings[];
|
response: UnknownRecord,
|
||||||
}
|
}, { description: "Envelope returned from a TrustGraph service." }) {}
|
||||||
|
|
||||||
export interface TextCompletionRequest {
|
export class Metadata extends S.Class<Metadata>("Metadata")({
|
||||||
system: string;
|
id: S.optionalKey(S.String),
|
||||||
prompt: string;
|
metadata: S.optionalKey(TripleArray),
|
||||||
streaming?: boolean;
|
user: S.optionalKey(S.String),
|
||||||
}
|
collection: S.optionalKey(S.String),
|
||||||
|
}, { description: "Shared request metadata for TrustGraph wire messages." }) {}
|
||||||
|
|
||||||
export interface TextCompletionResponse {
|
export class EntityEmbeddings extends S.Class<EntityEmbeddings>("EntityEmbeddings")({
|
||||||
response: string;
|
entity: S.optionalKey(Term),
|
||||||
// Streaming fields
|
vectors: S.optionalKey(NumberMatrix),
|
||||||
end_of_stream?: boolean;
|
}, { description: "Embedding vectors associated with a graph entity." }) {}
|
||||||
error?: {
|
|
||||||
message: string;
|
|
||||||
type?: string;
|
|
||||||
};
|
|
||||||
// Token usage (appears in final message)
|
|
||||||
in_token?: number;
|
|
||||||
out_token?: number;
|
|
||||||
model?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GraphRagRequest {
|
export class GraphEmbeddings extends S.Class<GraphEmbeddings>("GraphEmbeddings")({
|
||||||
query: string;
|
metadata: S.optionalKey(Metadata),
|
||||||
user?: string;
|
entities: S.optionalKey(S.Array(EntityEmbeddings).pipe(S.mutable)),
|
||||||
collection?: string;
|
}, { description: "Graph embedding payload grouped by entity." }) {}
|
||||||
"entity-limit"?: number; // Default: 50
|
|
||||||
"triple-limit"?: number; // Default: 30
|
|
||||||
"max-subgraph-size"?: number; // Default: 1000
|
|
||||||
"max-path-length"?: number; // Default: 2
|
|
||||||
streaming?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GraphRagResponse {
|
export class TextCompletionRequest extends S.Class<TextCompletionRequest>("TextCompletionRequest")({
|
||||||
response: string;
|
system: S.String,
|
||||||
// Streaming fields
|
prompt: S.String,
|
||||||
chunk?: string;
|
streaming: S.optionalKey(S.Boolean),
|
||||||
end_of_stream?: boolean;
|
}, { description: "Text-completion request payload." }) {}
|
||||||
endOfStream?: boolean;
|
|
||||||
error?: {
|
|
||||||
message: string;
|
|
||||||
type?: string;
|
|
||||||
};
|
|
||||||
// Token usage (appears in final message)
|
|
||||||
in_token?: number;
|
|
||||||
out_token?: number;
|
|
||||||
model?: string;
|
|
||||||
// Explainability fields
|
|
||||||
message_type?: "chunk" | "explain";
|
|
||||||
explain_id?: string;
|
|
||||||
explain_graph?: string;
|
|
||||||
explain_triples?: unknown[];
|
|
||||||
end_of_session?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DocumentRagRequest {
|
export class TextCompletionResponse extends S.Class<TextCompletionResponse>("TextCompletionResponse")({
|
||||||
query: string;
|
response: S.String,
|
||||||
user?: string;
|
end_of_stream: S.optionalKey(S.Boolean),
|
||||||
collection?: string;
|
error: S.optionalKey(TypedMessageError),
|
||||||
"doc-limit"?: number; // Default: 20
|
in_token: S.optionalKey(S.Finite),
|
||||||
streaming?: boolean;
|
out_token: S.optionalKey(S.Finite),
|
||||||
}
|
model: S.optionalKey(S.String),
|
||||||
|
}, { description: "Text-completion response payload." }) {}
|
||||||
|
|
||||||
export interface DocumentRagResponse {
|
export class GraphRagRequest extends S.Class<GraphRagRequest>("GraphRagRequest")({
|
||||||
response: string;
|
query: S.String,
|
||||||
// Streaming fields
|
user: S.optionalKey(S.String),
|
||||||
chunk?: string;
|
collection: S.optionalKey(S.String),
|
||||||
end_of_stream?: boolean;
|
"entity-limit": S.optionalKey(S.Finite),
|
||||||
endOfStream?: boolean;
|
"triple-limit": S.optionalKey(S.Finite),
|
||||||
error?: {
|
"max-subgraph-size": S.optionalKey(S.Finite),
|
||||||
message: string;
|
"max-path-length": S.optionalKey(S.Finite),
|
||||||
type?: string;
|
streaming: S.optionalKey(S.Boolean),
|
||||||
};
|
}, { description: "Graph RAG request payload." }) {}
|
||||||
// Token usage (appears in final message)
|
|
||||||
in_token?: number;
|
|
||||||
out_token?: number;
|
|
||||||
model?: string;
|
|
||||||
// Explainability fields
|
|
||||||
message_type?: "chunk" | "explain";
|
|
||||||
explain_id?: string;
|
|
||||||
explain_graph?: string;
|
|
||||||
end_of_session?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AgentRequest {
|
export class GraphRagResponse extends S.Class<GraphRagResponse>("GraphRagResponse")({
|
||||||
question: string;
|
response: S.String,
|
||||||
user?: string;
|
chunk: S.optionalKey(S.String),
|
||||||
collection?: string;
|
end_of_stream: S.optionalKey(S.Boolean),
|
||||||
streaming?: boolean;
|
endOfStream: S.optionalKey(S.Boolean),
|
||||||
}
|
error: S.optionalKey(TypedMessageError),
|
||||||
|
in_token: S.optionalKey(S.Finite),
|
||||||
|
out_token: S.optionalKey(S.Finite),
|
||||||
|
model: S.optionalKey(S.String),
|
||||||
|
message_type: S.optionalKey(S.Literals(["chunk", "explain"])),
|
||||||
|
explain_id: S.optionalKey(S.String),
|
||||||
|
explain_graph: S.optionalKey(S.String),
|
||||||
|
explain_triples: S.optionalKey(S.Array(S.Unknown).pipe(S.mutable)),
|
||||||
|
end_of_session: S.optionalKey(S.Boolean),
|
||||||
|
}, { description: "Graph RAG response payload." }) {}
|
||||||
|
|
||||||
export interface AgentResponse {
|
export class DocumentRagRequest extends S.Class<DocumentRagRequest>("DocumentRagRequest")({
|
||||||
// Streaming response format (new protocol)
|
query: S.String,
|
||||||
chunk_type?: "thought" | "action" | "observation" | "answer" | "final-answer" | "explain" | "error";
|
user: S.optionalKey(S.String),
|
||||||
content?: string;
|
collection: S.optionalKey(S.String),
|
||||||
end_of_message?: boolean;
|
"doc-limit": S.optionalKey(S.Finite),
|
||||||
end_of_dialog?: boolean;
|
streaming: S.optionalKey(S.Boolean),
|
||||||
|
}, { description: "Document RAG request payload." }) {}
|
||||||
|
|
||||||
// Legacy fields for backward compatibility with non-streaming
|
export class DocumentRagResponse extends S.Class<DocumentRagResponse>("DocumentRagResponse")({
|
||||||
thought?: string;
|
response: S.String,
|
||||||
observation?: string;
|
chunk: S.optionalKey(S.String),
|
||||||
answer?: string;
|
end_of_stream: S.optionalKey(S.Boolean),
|
||||||
error?: ResponseError;
|
endOfStream: S.optionalKey(S.Boolean),
|
||||||
|
error: S.optionalKey(TypedMessageError),
|
||||||
|
in_token: S.optionalKey(S.Finite),
|
||||||
|
out_token: S.optionalKey(S.Finite),
|
||||||
|
model: S.optionalKey(S.String),
|
||||||
|
message_type: S.optionalKey(S.Literals(["chunk", "explain"])),
|
||||||
|
explain_id: S.optionalKey(S.String),
|
||||||
|
explain_graph: S.optionalKey(S.String),
|
||||||
|
end_of_session: S.optionalKey(S.Boolean),
|
||||||
|
}, { description: "Document RAG response payload." }) {}
|
||||||
|
|
||||||
// Token usage (appears in final message)
|
export class AgentRequest extends S.Class<AgentRequest>("AgentRequest")({
|
||||||
in_token?: number;
|
question: S.String,
|
||||||
out_token?: number;
|
user: S.optionalKey(S.String),
|
||||||
model?: string;
|
collection: S.optionalKey(S.String),
|
||||||
|
streaming: S.optionalKey(S.Boolean),
|
||||||
|
}, { description: "Agent request payload." }) {}
|
||||||
|
|
||||||
// Explainability fields
|
export class AgentResponse extends S.Class<AgentResponse>("AgentResponse")({
|
||||||
message_type?: "chunk" | "explain";
|
chunk_type: S.optionalKey(S.Literals([
|
||||||
explain_id?: string;
|
"thought",
|
||||||
explain_graph?: string;
|
"action",
|
||||||
explain_triples?: unknown[];
|
"observation",
|
||||||
}
|
"answer",
|
||||||
|
"final-answer",
|
||||||
|
"explain",
|
||||||
|
"error",
|
||||||
|
])),
|
||||||
|
content: S.optionalKey(S.String),
|
||||||
|
end_of_message: S.optionalKey(S.Boolean),
|
||||||
|
end_of_dialog: S.optionalKey(S.Boolean),
|
||||||
|
thought: S.optionalKey(S.String),
|
||||||
|
observation: S.optionalKey(S.String),
|
||||||
|
answer: S.optionalKey(S.String),
|
||||||
|
error: S.optionalKey(ResponseError),
|
||||||
|
in_token: S.optionalKey(S.Finite),
|
||||||
|
out_token: S.optionalKey(S.Finite),
|
||||||
|
model: S.optionalKey(S.String),
|
||||||
|
message_type: S.optionalKey(S.Literals(["chunk", "explain"])),
|
||||||
|
explain_id: S.optionalKey(S.String),
|
||||||
|
explain_graph: S.optionalKey(S.String),
|
||||||
|
explain_triples: S.optionalKey(S.Array(S.Unknown).pipe(S.mutable)),
|
||||||
|
}, { description: "Agent response payload." }) {}
|
||||||
|
|
||||||
export interface EmbeddingsRequest {
|
export class EmbeddingsRequest extends S.Class<EmbeddingsRequest>("EmbeddingsRequest")({
|
||||||
texts: string[];
|
texts: StringArray,
|
||||||
}
|
}, { description: "Batch embeddings request payload." }) {}
|
||||||
|
|
||||||
export interface EmbeddingsResponse {
|
export class EmbeddingsResponse extends S.Class<EmbeddingsResponse>("EmbeddingsResponse")({
|
||||||
vectors: number[][]; // One vector per input text
|
vectors: NumberMatrix,
|
||||||
}
|
}, { description: "Batch embeddings response payload." }) {}
|
||||||
|
|
||||||
export interface GraphEmbeddingsQueryRequest {
|
export class GraphEmbeddingsQueryRequest extends S.Class<GraphEmbeddingsQueryRequest>("GraphEmbeddingsQueryRequest")({
|
||||||
vector: number[]; // Single query vector
|
vector: NumberArray,
|
||||||
limit: number;
|
limit: S.Finite,
|
||||||
user?: string;
|
user: S.optionalKey(S.String),
|
||||||
collection?: string;
|
collection: S.optionalKey(S.String),
|
||||||
}
|
}, { description: "Graph embeddings query request payload." }) {}
|
||||||
|
|
||||||
export interface EntityMatch {
|
export class EntityMatch extends S.Class<EntityMatch>("EntityMatch")({
|
||||||
entity: Term | null;
|
entity: S.NullOr(Term),
|
||||||
score: number;
|
score: S.Finite,
|
||||||
}
|
}, { description: "Scored graph-entity match." }) {}
|
||||||
|
|
||||||
export interface GraphEmbeddingsQueryResponse {
|
export class GraphEmbeddingsQueryResponse extends S.Class<GraphEmbeddingsQueryResponse>("GraphEmbeddingsQueryResponse")({
|
||||||
entities: EntityMatch[];
|
entities: S.Array(EntityMatch).pipe(S.mutable),
|
||||||
}
|
}, { description: "Graph embeddings query response payload." }) {}
|
||||||
|
|
||||||
export interface TriplesQueryRequest {
|
export class TriplesQueryRequest extends S.Class<TriplesQueryRequest>("TriplesQueryRequest")({
|
||||||
s?: Term;
|
s: S.optionalKey(Term),
|
||||||
p?: Term;
|
p: S.optionalKey(Term),
|
||||||
o?: Term;
|
o: S.optionalKey(Term),
|
||||||
g?: string; // Named graph URI filter (plain string, not Term)
|
g: S.optionalKey(S.String),
|
||||||
limit: number;
|
limit: S.Finite,
|
||||||
user?: string;
|
user: S.optionalKey(S.String),
|
||||||
collection?: string;
|
collection: S.optionalKey(S.String),
|
||||||
}
|
}, { description: "Triple pattern query request payload." }) {}
|
||||||
|
|
||||||
export interface TriplesQueryResponse {
|
export class TriplesQueryResponse extends S.Class<TriplesQueryResponse>("TriplesQueryResponse")({
|
||||||
triples: Triple[];
|
triples: TripleArray,
|
||||||
/** @deprecated Use `triples` — kept for backward compatibility */
|
response: S.optionalKey(TripleArray),
|
||||||
response?: Triple[];
|
}, { description: "Triple pattern query response payload." }) {}
|
||||||
}
|
|
||||||
|
|
||||||
export interface RowsQueryRequest {
|
export class RowsQueryRequest extends S.Class<RowsQueryRequest>("RowsQueryRequest")({
|
||||||
query: string;
|
query: S.String,
|
||||||
user?: string;
|
user: S.optionalKey(S.String),
|
||||||
collection?: string;
|
collection: S.optionalKey(S.String),
|
||||||
variables?: Record<string, unknown>;
|
variables: S.optionalKey(UnknownRecord),
|
||||||
operation_name?: string;
|
operation_name: S.optionalKey(S.String),
|
||||||
}
|
}, { description: "Structured rows GraphQL request payload." }) {}
|
||||||
|
|
||||||
export interface RowsQueryResponse {
|
export class RowsQueryResponse extends S.Class<RowsQueryResponse>("RowsQueryResponse")({
|
||||||
data?: Record<string, unknown>;
|
data: S.optionalKey(UnknownRecord),
|
||||||
errors?: Record<string, unknown>[];
|
errors: S.optionalKey(S.Array(UnknownRecord).pipe(S.mutable)),
|
||||||
extensions?: Record<string, unknown>;
|
extensions: S.optionalKey(UnknownRecord),
|
||||||
values?: unknown[];
|
values: S.optionalKey(S.Array(S.Unknown).pipe(S.mutable)),
|
||||||
}
|
}, { description: "Structured rows GraphQL response payload." }) {}
|
||||||
|
|
||||||
export interface NlpQueryRequest {
|
export class NlpQueryRequest extends S.Class<NlpQueryRequest>("NlpQueryRequest")({
|
||||||
question: string;
|
question: S.String,
|
||||||
max_results?: number;
|
max_results: S.optionalKey(S.Finite),
|
||||||
}
|
}, { description: "Natural-language-to-GraphQL request payload." }) {}
|
||||||
|
|
||||||
export interface NlpQueryResponse {
|
export class NlpQueryResponse extends S.Class<NlpQueryResponse>("NlpQueryResponse")({
|
||||||
graphql_query?: string;
|
graphql_query: S.optionalKey(S.String),
|
||||||
variables?: Record<string, unknown>;
|
variables: S.optionalKey(UnknownRecord),
|
||||||
detected_schemas?: Record<string, unknown>[];
|
detected_schemas: S.optionalKey(S.Array(UnknownRecord).pipe(S.mutable)),
|
||||||
confidence?: number;
|
confidence: S.optionalKey(S.Finite),
|
||||||
}
|
}, { description: "Natural-language-to-GraphQL response payload." }) {}
|
||||||
|
|
||||||
export interface StructuredQueryRequest {
|
export class StructuredQueryRequest extends S.Class<StructuredQueryRequest>("StructuredQueryRequest")({
|
||||||
question: string;
|
question: S.String,
|
||||||
user?: string;
|
user: S.optionalKey(S.String),
|
||||||
collection?: string;
|
collection: S.optionalKey(S.String),
|
||||||
}
|
}, { description: "Structured query request payload." }) {}
|
||||||
|
|
||||||
export interface StructuredQueryResponse {
|
export class StructuredQueryResponse extends S.Class<StructuredQueryResponse>("StructuredQueryResponse")({
|
||||||
data?: Record<string, unknown>;
|
data: S.optionalKey(UnknownRecord),
|
||||||
errors?: Record<string, unknown>[];
|
errors: S.optionalKey(S.Array(UnknownRecord).pipe(S.mutable)),
|
||||||
}
|
}, { description: "Structured query response payload." }) {}
|
||||||
|
|
||||||
export interface RowEmbeddingsQueryRequest {
|
export class RowEmbeddingsQueryRequest extends S.Class<RowEmbeddingsQueryRequest>("RowEmbeddingsQueryRequest")({
|
||||||
vector: number[]; // Single query vector
|
vector: NumberArray,
|
||||||
schema_name: string;
|
schema_name: S.String,
|
||||||
user?: string;
|
user: S.optionalKey(S.String),
|
||||||
collection?: string;
|
collection: S.optionalKey(S.String),
|
||||||
index_name?: string;
|
index_name: S.optionalKey(S.String),
|
||||||
limit?: number;
|
limit: S.optionalKey(S.Finite),
|
||||||
}
|
}, { description: "Row embeddings query request payload." }) {}
|
||||||
|
|
||||||
export interface RowEmbeddingsMatch {
|
export class RowEmbeddingsMatch extends S.Class<RowEmbeddingsMatch>("RowEmbeddingsMatch")({
|
||||||
index_name: string;
|
index_name: S.String,
|
||||||
index_value: string[];
|
index_value: StringArray,
|
||||||
text: string;
|
text: S.String,
|
||||||
score: number;
|
score: S.Finite,
|
||||||
}
|
}, { description: "Scored row embeddings match." }) {}
|
||||||
|
|
||||||
export interface RowEmbeddingsQueryResponse {
|
export class RowEmbeddingsQueryResponse extends S.Class<RowEmbeddingsQueryResponse>("RowEmbeddingsQueryResponse")({
|
||||||
matches?: RowEmbeddingsMatch[];
|
matches: S.optionalKey(S.Array(RowEmbeddingsMatch).pipe(S.mutable)),
|
||||||
error?: {
|
error: S.optionalKey(TypedMessageError),
|
||||||
message: string;
|
}, { description: "Row embeddings query response payload." }) {}
|
||||||
type?: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LoadDocumentRequest {
|
export class LoadDocumentRequest extends S.Class<LoadDocumentRequest>("LoadDocumentRequest")({
|
||||||
id?: string;
|
id: S.optionalKey(S.String),
|
||||||
data: string;
|
data: S.String,
|
||||||
metadata?: Triple[];
|
metadata: S.optionalKey(TripleArray),
|
||||||
}
|
}, { description: "Flow-scoped document load request payload." }) {}
|
||||||
|
|
||||||
export type LoadDocumentResponse = void;
|
export type LoadDocumentResponse = void;
|
||||||
|
|
||||||
export interface LoadTextRequest {
|
export class LoadTextRequest extends S.Class<LoadTextRequest>("LoadTextRequest")({
|
||||||
id?: string;
|
id: S.optionalKey(S.String),
|
||||||
text: string;
|
text: S.String,
|
||||||
charset?: string;
|
charset: S.optionalKey(S.String),
|
||||||
metadata?: Triple[];
|
metadata: S.optionalKey(TripleArray),
|
||||||
}
|
}, { description: "Flow-scoped text load request payload." }) {}
|
||||||
|
|
||||||
export type LoadTextResponse = void;
|
export type LoadTextResponse = void;
|
||||||
|
|
||||||
export interface DocumentMetadata {
|
export class DocumentMetadata extends S.Class<DocumentMetadata>("DocumentMetadata")({
|
||||||
id?: string;
|
id: S.optionalKey(S.String),
|
||||||
time?: number;
|
time: S.optionalKey(S.Finite),
|
||||||
kind?: string;
|
kind: S.optionalKey(S.String),
|
||||||
title?: string;
|
title: S.optionalKey(S.String),
|
||||||
comments?: string;
|
comments: S.optionalKey(S.String),
|
||||||
metadata?: Triple[];
|
metadata: S.optionalKey(TripleArray),
|
||||||
user?: string;
|
user: S.optionalKey(S.String),
|
||||||
tags?: string[];
|
tags: S.optionalKey(StringArray),
|
||||||
parentId?: string;
|
parentId: S.optionalKey(S.String),
|
||||||
documentType?: string;
|
documentType: S.optionalKey(S.String),
|
||||||
"parent-id"?: string;
|
"parent-id": S.optionalKey(S.String),
|
||||||
"document-type"?: string;
|
"document-type": S.optionalKey(S.String),
|
||||||
}
|
}, { description: "Library document metadata payload." }) {}
|
||||||
|
|
||||||
export interface ProcessingMetadata {
|
export class ProcessingMetadata extends S.Class<ProcessingMetadata>("ProcessingMetadata")({
|
||||||
id?: string;
|
id: S.optionalKey(S.String),
|
||||||
"document-id"?: string;
|
"document-id": S.optionalKey(S.String),
|
||||||
documentId?: string;
|
documentId: S.optionalKey(S.String),
|
||||||
time?: number;
|
time: S.optionalKey(S.Finite),
|
||||||
flow?: string;
|
flow: S.optionalKey(S.String),
|
||||||
user?: string;
|
user: S.optionalKey(S.String),
|
||||||
collection?: string;
|
collection: S.optionalKey(S.String),
|
||||||
tags?: string[];
|
tags: S.optionalKey(StringArray),
|
||||||
}
|
}, { description: "Library processing metadata payload." }) {}
|
||||||
|
|
||||||
export interface LibraryRequest {
|
export class LibraryRequest extends S.Class<LibraryRequest>("LibraryRequest")({
|
||||||
operation: string;
|
operation: S.String,
|
||||||
documentId?: string;
|
documentId: S.optionalKey(S.String),
|
||||||
"document-id"?: string;
|
"document-id": S.optionalKey(S.String),
|
||||||
processingId?: string;
|
processingId: S.optionalKey(S.String),
|
||||||
"processing-id"?: string;
|
"processing-id": S.optionalKey(S.String),
|
||||||
"document-metadata"?: DocumentMetadata;
|
"document-metadata": S.optionalKey(DocumentMetadata),
|
||||||
documentMetadata?: DocumentMetadata;
|
documentMetadata: S.optionalKey(DocumentMetadata),
|
||||||
"processing-metadata"?: ProcessingMetadata;
|
"processing-metadata": S.optionalKey(ProcessingMetadata),
|
||||||
content?: string;
|
content: S.optionalKey(S.String),
|
||||||
user?: string;
|
user: S.optionalKey(S.String),
|
||||||
collection?: string;
|
collection: S.optionalKey(S.String),
|
||||||
metadata?: Triple[];
|
metadata: S.optionalKey(TripleArray),
|
||||||
id?: string;
|
id: S.optionalKey(S.String),
|
||||||
flow?: string;
|
flow: S.optionalKey(S.String),
|
||||||
}
|
}, { description: "Library service request payload." }) {}
|
||||||
|
|
||||||
export interface LibraryResponse {
|
export class LibraryResponse extends S.Class<LibraryResponse>("LibraryResponse")({
|
||||||
error?: WireError;
|
error: S.optionalKey(WireErrorValue),
|
||||||
"document-metadata"?: DocumentMetadata;
|
"document-metadata": S.optionalKey(DocumentMetadata),
|
||||||
documentMetadata?: DocumentMetadata;
|
documentMetadata: S.optionalKey(DocumentMetadata),
|
||||||
content?: string;
|
content: S.optionalKey(S.String),
|
||||||
"document-metadatas"?: DocumentMetadata[];
|
"document-metadatas": S.optionalKey(S.Array(DocumentMetadata).pipe(S.mutable)),
|
||||||
documents?: DocumentMetadata[];
|
documents: S.optionalKey(S.Array(DocumentMetadata).pipe(S.mutable)),
|
||||||
"processing-metadata"?: ProcessingMetadata;
|
"processing-metadata": S.optionalKey(ProcessingMetadata),
|
||||||
"processing-metadatas"?: ProcessingMetadata[];
|
"processing-metadatas": S.optionalKey(S.Array(ProcessingMetadata).pipe(S.mutable)),
|
||||||
processing?: ProcessingMetadata[];
|
processing: S.optionalKey(S.Array(ProcessingMetadata).pipe(S.mutable)),
|
||||||
}
|
}, { description: "Library service response payload." }) {}
|
||||||
|
|
||||||
export interface KnowledgeRequest {
|
export class KnowledgeRequest extends S.Class<KnowledgeRequest>("KnowledgeRequest")({
|
||||||
operation: string;
|
operation: S.String,
|
||||||
user?: string;
|
user: S.optionalKey(S.String),
|
||||||
id?: string;
|
id: S.optionalKey(S.String),
|
||||||
flow?: string;
|
flow: S.optionalKey(S.String),
|
||||||
collection?: string;
|
collection: S.optionalKey(S.String),
|
||||||
triples?: Triple[];
|
triples: S.optionalKey(TripleArray),
|
||||||
"graph-embeddings"?: GraphEmbeddings;
|
"graph-embeddings": S.optionalKey(GraphEmbeddings),
|
||||||
graphEmbeddings?: GraphEmbeddings;
|
graphEmbeddings: S.optionalKey(GraphEmbeddings),
|
||||||
"document-embeddings"?: unknown;
|
"document-embeddings": S.optionalKey(S.Unknown),
|
||||||
documentEmbeddings?: unknown;
|
documentEmbeddings: S.optionalKey(S.Unknown),
|
||||||
}
|
}, { description: "Knowledge service request payload." }) {}
|
||||||
|
|
||||||
export interface KnowledgeResponse {
|
export class KnowledgeResponse extends S.Class<KnowledgeResponse>("KnowledgeResponse")({
|
||||||
error?: WireError;
|
error: S.optionalKey(WireErrorValue),
|
||||||
ids?: string[];
|
ids: S.optionalKey(StringArray),
|
||||||
eos?: boolean;
|
eos: S.optionalKey(S.Boolean),
|
||||||
triples?: Triple[];
|
triples: S.optionalKey(TripleArray),
|
||||||
"graph-embeddings"?: GraphEmbeddings;
|
"graph-embeddings": S.optionalKey(GraphEmbeddings),
|
||||||
graphEmbeddings?: GraphEmbeddings;
|
graphEmbeddings: S.optionalKey(GraphEmbeddings),
|
||||||
"document-embeddings"?: unknown;
|
"document-embeddings": S.optionalKey(S.Unknown),
|
||||||
documentEmbeddings?: unknown;
|
documentEmbeddings: S.optionalKey(S.Unknown),
|
||||||
}
|
}, { description: "Knowledge service response payload." }) {}
|
||||||
|
|
||||||
export interface FlowRequest {
|
export class FlowRequest extends S.Class<FlowRequest>("FlowRequest")({
|
||||||
operation: string;
|
operation: S.String,
|
||||||
"blueprint-name"?: string;
|
"blueprint-name": S.optionalKey(S.String),
|
||||||
"blueprint-definition"?: string;
|
"blueprint-definition": S.optionalKey(S.String),
|
||||||
description?: string;
|
description: S.optionalKey(S.String),
|
||||||
"flow-id"?: string;
|
"flow-id": S.optionalKey(S.String),
|
||||||
parameters?: Record<string, unknown>;
|
parameters: S.optionalKey(UnknownRecord),
|
||||||
user?: string;
|
user: S.optionalKey(S.String),
|
||||||
}
|
}, { description: "Flow service request payload." }) {}
|
||||||
|
|
||||||
export interface FlowResponse {
|
export class FlowResponse extends S.Class<FlowResponse>("FlowResponse")({
|
||||||
"blueprint-names"?: string[];
|
"blueprint-names": S.optionalKey(StringArray),
|
||||||
"flow-ids"?: string[];
|
"flow-ids": S.optionalKey(StringArray),
|
||||||
ids?: string[];
|
ids: S.optionalKey(StringArray),
|
||||||
flow?: string;
|
flow: S.optionalKey(S.String),
|
||||||
"blueprint-definition"?: string;
|
"blueprint-definition": S.optionalKey(S.String),
|
||||||
description?: string;
|
description: S.optionalKey(S.String),
|
||||||
error?:
|
error: S.optionalKey(S.Union([OptionalMessageError, WireErrorValue])),
|
||||||
| {
|
}, { description: "Flow service response payload." }) {}
|
||||||
message?: string;
|
|
||||||
}
|
|
||||||
| WireError;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PromptRequest {
|
export class PromptRequest extends S.Class<PromptRequest>("PromptRequest")({
|
||||||
id: string;
|
id: S.String,
|
||||||
terms: Record<string, unknown>;
|
terms: UnknownRecord,
|
||||||
streaming?: boolean;
|
streaming: S.optionalKey(S.Boolean),
|
||||||
}
|
}, { description: "Prompt rendering request payload." }) {}
|
||||||
|
|
||||||
export interface PromptResponse {
|
export class PromptResponse extends S.Class<PromptResponse>("PromptResponse")({
|
||||||
text: string;
|
text: S.String,
|
||||||
// Streaming fields
|
end_of_stream: S.optionalKey(S.Boolean),
|
||||||
end_of_stream?: boolean;
|
error: S.optionalKey(TypedMessageError),
|
||||||
error?: {
|
in_token: S.optionalKey(S.Finite),
|
||||||
message: string;
|
out_token: S.optionalKey(S.Finite),
|
||||||
type?: string;
|
model: S.optionalKey(S.String),
|
||||||
};
|
}, { description: "Prompt rendering response payload." }) {}
|
||||||
// Token usage (appears in final message)
|
|
||||||
in_token?: number;
|
|
||||||
out_token?: number;
|
|
||||||
model?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ConfigRequest = object;
|
export type ConfigRequest = object;
|
||||||
export type ConfigResponse = object;
|
export type ConfigResponse = object;
|
||||||
|
|
||||||
// Chunked Upload Types
|
export class ChunkedUploadDocumentMetadata extends S.Class<ChunkedUploadDocumentMetadata>("ChunkedUploadDocumentMetadata")({
|
||||||
|
id: S.String,
|
||||||
|
time: S.Finite,
|
||||||
|
kind: S.String,
|
||||||
|
title: S.String,
|
||||||
|
comments: S.optionalKey(S.String),
|
||||||
|
metadata: S.optionalKey(TripleArray),
|
||||||
|
user: S.String,
|
||||||
|
collection: S.optionalKey(S.String),
|
||||||
|
tags: S.optionalKey(StringArray),
|
||||||
|
}, { description: "Document metadata used to begin a chunked upload." }) {}
|
||||||
|
|
||||||
export interface ChunkedUploadDocumentMetadata {
|
export class BeginUploadRequest extends S.Class<BeginUploadRequest>("BeginUploadRequest")({
|
||||||
id: string;
|
operation: S.Literal("begin-upload"),
|
||||||
time: number;
|
"document-metadata": S.optionalKey(ChunkedUploadDocumentMetadata),
|
||||||
kind: string;
|
documentMetadata: S.optionalKey(ChunkedUploadDocumentMetadata),
|
||||||
title: string;
|
"total-size": S.Finite,
|
||||||
comments?: string;
|
"chunk-size": S.optionalKey(S.Finite),
|
||||||
metadata?: Triple[];
|
}, { description: "Chunked upload begin request payload." }) {}
|
||||||
user: string;
|
|
||||||
collection?: string;
|
|
||||||
tags?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BeginUploadRequest {
|
export class BeginUploadResponse extends S.Class<BeginUploadResponse>("BeginUploadResponse")({
|
||||||
operation: "begin-upload";
|
"upload-id": S.String,
|
||||||
"document-metadata"?: ChunkedUploadDocumentMetadata;
|
"chunk-size": S.Finite,
|
||||||
documentMetadata?: ChunkedUploadDocumentMetadata;
|
"total-chunks": S.Finite,
|
||||||
"total-size": number;
|
error: S.optionalKey(ResponseError),
|
||||||
"chunk-size"?: number;
|
}, { description: "Chunked upload begin response payload." }) {}
|
||||||
}
|
|
||||||
|
|
||||||
export interface BeginUploadResponse {
|
export class UploadChunkRequest extends S.Class<UploadChunkRequest>("UploadChunkRequest")({
|
||||||
"upload-id": string;
|
operation: S.Literal("upload-chunk"),
|
||||||
"chunk-size": number;
|
"upload-id": S.String,
|
||||||
"total-chunks": number;
|
"chunk-index": S.Finite,
|
||||||
error?: ResponseError;
|
content: S.String,
|
||||||
}
|
user: S.String,
|
||||||
|
}, { description: "Chunked upload chunk request payload." }) {}
|
||||||
|
|
||||||
export interface UploadChunkRequest {
|
export class UploadChunkResponse extends S.Class<UploadChunkResponse>("UploadChunkResponse")({
|
||||||
operation: "upload-chunk";
|
"upload-id": S.String,
|
||||||
"upload-id": string;
|
"chunk-index": S.Finite,
|
||||||
"chunk-index": number;
|
"chunks-received": S.Finite,
|
||||||
content: string; // base64-encoded
|
"total-chunks": S.Finite,
|
||||||
user: string;
|
"bytes-received": S.Finite,
|
||||||
}
|
"total-bytes": S.Finite,
|
||||||
|
error: S.optionalKey(ResponseError),
|
||||||
|
}, { description: "Chunked upload chunk response payload." }) {}
|
||||||
|
|
||||||
export interface UploadChunkResponse {
|
export class CompleteUploadRequest extends S.Class<CompleteUploadRequest>("CompleteUploadRequest")({
|
||||||
"upload-id": string;
|
operation: S.Literal("complete-upload"),
|
||||||
"chunk-index": number;
|
"upload-id": S.String,
|
||||||
"chunks-received": number;
|
user: S.String,
|
||||||
"total-chunks": number;
|
}, { description: "Chunked upload completion request payload." }) {}
|
||||||
"bytes-received": number;
|
|
||||||
"total-bytes": number;
|
|
||||||
error?: ResponseError;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CompleteUploadRequest {
|
export class CompleteUploadResponse extends S.Class<CompleteUploadResponse>("CompleteUploadResponse")({
|
||||||
operation: "complete-upload";
|
"document-id": S.String,
|
||||||
"upload-id": string;
|
"object-id": S.String,
|
||||||
user: string;
|
error: S.optionalKey(ResponseError),
|
||||||
}
|
}, { description: "Chunked upload completion response payload." }) {}
|
||||||
|
|
||||||
export interface CompleteUploadResponse {
|
export class GetUploadStatusRequest extends S.Class<GetUploadStatusRequest>("GetUploadStatusRequest")({
|
||||||
"document-id": string;
|
operation: S.Literal("get-upload-status"),
|
||||||
"object-id": string;
|
"upload-id": S.String,
|
||||||
error?: ResponseError;
|
user: S.String,
|
||||||
}
|
}, { description: "Chunked upload status request payload." }) {}
|
||||||
|
|
||||||
export interface GetUploadStatusRequest {
|
export class GetUploadStatusResponse extends S.Class<GetUploadStatusResponse>("GetUploadStatusResponse")({
|
||||||
operation: "get-upload-status";
|
"upload-id": S.String,
|
||||||
"upload-id": string;
|
"upload-state": S.Literals(["in-progress", "completed", "expired"]),
|
||||||
user: string;
|
"chunks-received": S.Finite,
|
||||||
}
|
"total-chunks": S.Finite,
|
||||||
|
"received-chunks": S.Array(S.Finite).pipe(S.mutable),
|
||||||
|
"missing-chunks": S.Array(S.Finite).pipe(S.mutable),
|
||||||
|
"bytes-received": S.Finite,
|
||||||
|
"total-bytes": S.Finite,
|
||||||
|
error: S.optionalKey(ResponseError),
|
||||||
|
}, { description: "Chunked upload status response payload." }) {}
|
||||||
|
|
||||||
export interface GetUploadStatusResponse {
|
export class AbortUploadRequest extends S.Class<AbortUploadRequest>("AbortUploadRequest")({
|
||||||
"upload-id": string;
|
operation: S.Literal("abort-upload"),
|
||||||
"upload-state": "in-progress" | "completed" | "expired";
|
"upload-id": S.String,
|
||||||
"chunks-received": number;
|
user: S.String,
|
||||||
"total-chunks": number;
|
}, { description: "Chunked upload abort request payload." }) {}
|
||||||
"received-chunks": number[];
|
|
||||||
"missing-chunks": number[];
|
|
||||||
"bytes-received": number;
|
|
||||||
"total-bytes": number;
|
|
||||||
error?: ResponseError;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AbortUploadRequest {
|
export class AbortUploadResponse extends S.Class<AbortUploadResponse>("AbortUploadResponse")({
|
||||||
operation: "abort-upload";
|
error: S.optionalKey(ResponseError),
|
||||||
"upload-id": string;
|
}, { description: "Chunked upload abort response payload." }) {}
|
||||||
user: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AbortUploadResponse {
|
export class ListUploadsRequest extends S.Class<ListUploadsRequest>("ListUploadsRequest")({
|
||||||
error?: ResponseError;
|
operation: S.Literal("list-uploads"),
|
||||||
}
|
user: S.String,
|
||||||
|
}, { description: "Pending uploads list request payload." }) {}
|
||||||
|
|
||||||
export interface ListUploadsRequest {
|
export class UploadSession extends S.Class<UploadSession>("UploadSession")({
|
||||||
operation: "list-uploads";
|
"upload-id": S.String,
|
||||||
user: string;
|
"document-id": S.String,
|
||||||
}
|
"document-metadata-json": S.String,
|
||||||
|
"total-size": S.Finite,
|
||||||
|
"chunk-size": S.Finite,
|
||||||
|
"total-chunks": S.Finite,
|
||||||
|
"chunks-received": S.Finite,
|
||||||
|
"created-at": S.String,
|
||||||
|
}, { description: "Pending upload session payload." }) {}
|
||||||
|
|
||||||
export interface UploadSession {
|
export class ListUploadsResponse extends S.Class<ListUploadsResponse>("ListUploadsResponse")({
|
||||||
"upload-id": string;
|
"upload-sessions": S.Array(UploadSession).pipe(S.mutable),
|
||||||
"document-id": string;
|
error: S.optionalKey(ResponseError),
|
||||||
"document-metadata-json": string;
|
}, { description: "Pending uploads list response payload." }) {}
|
||||||
"total-size": number;
|
|
||||||
"chunk-size": number;
|
|
||||||
"total-chunks": number;
|
|
||||||
"chunks-received": number;
|
|
||||||
"created-at": string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ListUploadsResponse {
|
export class StreamDocumentRequest extends S.Class<StreamDocumentRequest>("StreamDocumentRequest")({
|
||||||
"upload-sessions": UploadSession[];
|
operation: S.Literal("stream-document"),
|
||||||
error?: ResponseError;
|
"document-id": S.String,
|
||||||
}
|
"chunk-size": S.optionalKey(S.Finite),
|
||||||
|
user: S.String,
|
||||||
|
}, { description: "Document chunk stream request payload." }) {}
|
||||||
|
|
||||||
export interface StreamDocumentRequest {
|
export class StreamDocumentResponse extends S.Class<StreamDocumentResponse>("StreamDocumentResponse")({
|
||||||
operation: "stream-document";
|
content: S.String,
|
||||||
"document-id": string;
|
"chunk-index": S.Finite,
|
||||||
"chunk-size"?: number;
|
"total-chunks": S.Finite,
|
||||||
user: string;
|
error: S.optionalKey(ResponseError),
|
||||||
}
|
}, { description: "Document chunk stream response payload." }) {}
|
||||||
|
|
||||||
export interface StreamDocumentResponse {
|
|
||||||
content: string; // base64-encoded chunk
|
|
||||||
"chunk-index": number;
|
|
||||||
"total-chunks": number;
|
|
||||||
error?: ResponseError;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Cause, Context, Effect, Exit, Fiber, Layer, Ref, Scope, Stream, SubscriptionRef } from "effect";
|
import { Cause, Context, Effect, Fiber, Layer, Ref, Schema as S, Scope, Stream, SubscriptionRef } from "effect";
|
||||||
import type * as RpcGroup from "effect/unstable/rpc/RpcGroup";
|
import type * as RpcGroup from "effect/unstable/rpc/RpcGroup";
|
||||||
import * as RpcClient from "effect/unstable/rpc/RpcClient";
|
import * as RpcClient from "effect/unstable/rpc/RpcClient";
|
||||||
import type { RpcClientError } from "effect/unstable/rpc/RpcClientError";
|
import type { RpcClientError } from "effect/unstable/rpc/RpcClientError";
|
||||||
|
|
@ -19,17 +19,17 @@ class TrustGraphRpcClientService extends Context.Service<
|
||||||
|
|
||||||
export type RpcConnectionStatus = "connecting" | "connected" | "failed" | "closed";
|
export type RpcConnectionStatus = "connecting" | "connected" | "failed" | "closed";
|
||||||
|
|
||||||
export interface RpcConnectionState {
|
export class RpcConnectionState extends S.Class<RpcConnectionState>("RpcConnectionState")({
|
||||||
status: RpcConnectionStatus;
|
status: S.Literals(["connecting", "connected", "failed", "closed"]),
|
||||||
lastError?: string;
|
lastError: S.optionalKey(S.String),
|
||||||
}
|
}, { description: "Current Effect RPC gateway connection state." }) {}
|
||||||
|
|
||||||
export interface DispatchInput {
|
export class DispatchInput extends S.Class<DispatchInput>("DispatchInput")({
|
||||||
scope: "global" | "flow";
|
scope: S.Literals(["global", "flow"]),
|
||||||
service: string;
|
service: S.String,
|
||||||
flow?: string;
|
flow: S.optionalKey(S.String),
|
||||||
request: Record<string, unknown>;
|
request: S.Record(S.String, S.Unknown),
|
||||||
}
|
}, { description: "TrustGraph gateway dispatch target and request payload." }) {}
|
||||||
|
|
||||||
export interface DispatchOptions {
|
export interface DispatchOptions {
|
||||||
readonly timeoutMs?: number;
|
readonly timeoutMs?: number;
|
||||||
|
|
@ -74,20 +74,6 @@ export interface TrustGraphGatewayClientOptions {
|
||||||
const DEFAULT_REQUEST_TIMEOUT_MS = 10_000;
|
const DEFAULT_REQUEST_TIMEOUT_MS = 10_000;
|
||||||
const DEFAULT_REQUEST_ATTEMPTS = 3;
|
const DEFAULT_REQUEST_ATTEMPTS = 3;
|
||||||
|
|
||||||
export interface EffectRpcClient {
|
|
||||||
readonly subscribe: (listener: (state: RpcConnectionState) => void) => () => void;
|
|
||||||
readonly dispatch: (
|
|
||||||
input: DispatchInput,
|
|
||||||
options?: DispatchOptions,
|
|
||||||
) => Promise<unknown>;
|
|
||||||
readonly dispatchStream: (
|
|
||||||
input: DispatchInput,
|
|
||||||
receiver: (chunk: DispatchStreamChunk) => boolean,
|
|
||||||
options?: DispatchOptions,
|
|
||||||
) => Promise<DispatchStreamChunk | undefined>;
|
|
||||||
readonly close: () => Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const makeClientLayer = (
|
const makeClientLayer = (
|
||||||
options: TrustGraphGatewayClientOptions,
|
options: TrustGraphGatewayClientOptions,
|
||||||
stateRef: SubscriptionRef.SubscriptionRef<RpcConnectionState>,
|
stateRef: SubscriptionRef.SubscriptionRef<RpcConnectionState>,
|
||||||
|
|
@ -234,59 +220,12 @@ export function makeEffectRpcClient(
|
||||||
url: string,
|
url: string,
|
||||||
onConnect?: () => void,
|
onConnect?: () => void,
|
||||||
onDisconnect?: () => void,
|
onDisconnect?: () => void,
|
||||||
): EffectRpcClient {
|
): Effect.Effect<TrustGraphGatewayClient, never, Scope.Scope> {
|
||||||
const stateRef = Effect.runSync(SubscriptionRef.make<RpcConnectionState>({ status: "connecting" }));
|
return makeTrustGraphGatewayClientScoped({
|
||||||
const closedRef = Effect.runSync(Ref.make(false));
|
|
||||||
const scope = Effect.runSync(Scope.make());
|
|
||||||
const options: TrustGraphGatewayClientOptions = {
|
|
||||||
url,
|
url,
|
||||||
stateRef,
|
|
||||||
closedRef,
|
|
||||||
...(onConnect === undefined ? {} : { onConnect }),
|
...(onConnect === undefined ? {} : { onConnect }),
|
||||||
...(onDisconnect === undefined ? {} : { onDisconnect }),
|
...(onDisconnect === undefined ? {} : { onDisconnect }),
|
||||||
};
|
});
|
||||||
const clientPromise = Effect.runPromise(
|
|
||||||
makeTrustGraphGatewayClientScoped(options).pipe(Scope.provide(scope)),
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
subscribe: (listener) => {
|
|
||||||
let unsubscribe: Effect.Effect<void> | undefined;
|
|
||||||
let cancelled = false;
|
|
||||||
listener(SubscriptionRef.getUnsafe(stateRef));
|
|
||||||
void clientPromise.then((client) =>
|
|
||||||
Effect.runPromise(client.subscribe(listener)).then((release) => {
|
|
||||||
if (cancelled) {
|
|
||||||
return Effect.runPromise(release);
|
|
||||||
}
|
|
||||||
unsubscribe = release;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
cancelled = true;
|
|
||||||
if (unsubscribe !== undefined) {
|
|
||||||
Effect.runFork(unsubscribe);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
dispatch: (input, options = {}) =>
|
|
||||||
clientPromise.then((client) =>
|
|
||||||
Effect.runPromise(client.dispatch(input, options))
|
|
||||||
),
|
|
||||||
dispatchStream: (input, receiver, options = {}) =>
|
|
||||||
clientPromise.then((client) =>
|
|
||||||
Effect.runPromise(client.runDispatchStream(input, receiver, options))
|
|
||||||
),
|
|
||||||
close: () =>
|
|
||||||
clientPromise.then((client) =>
|
|
||||||
Effect.runPromise(
|
|
||||||
client.close.pipe(
|
|
||||||
Effect.andThen(Scope.close(scope, Exit.void)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function withDispatchRequestPolicy<A, E, R>(
|
export function withDispatchRequestPolicy<A, E, R>(
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,13 @@
|
||||||
// Import core types and classes for the TrustGraph API
|
// Import core types and classes for the TrustGraph API
|
||||||
import type { Term, Triple } from "../models/Triple.js";
|
import { Triple } from "../models/Triple.js";
|
||||||
|
import type { Term } from "../models/Triple.js";
|
||||||
import type {
|
import type {
|
||||||
EffectRpcClient,
|
|
||||||
DispatchInput,
|
DispatchInput,
|
||||||
DispatchOptions,
|
DispatchOptions,
|
||||||
RpcConnectionState,
|
RpcConnectionState,
|
||||||
} from "./effect-rpc-client.js";
|
} from "./effect-rpc-client.js";
|
||||||
import {
|
|
||||||
makeEffectRpcClient,
|
|
||||||
} from "./effect-rpc-client.js";
|
|
||||||
import { getDefaultSocketUrl, getRandomValues } from "./websocket-adapter.js";
|
import { getDefaultSocketUrl, getRandomValues } from "./websocket-adapter.js";
|
||||||
import { Clock, Effect, Fiber, Match, Option, Result, Schema as S, Stream, SubscriptionRef } from "effect";
|
import { Match, Option, Schema as S } from "effect";
|
||||||
import * as Predicate from "effect/Predicate";
|
import * as Predicate from "effect/Predicate";
|
||||||
|
|
||||||
// Import all message types for different services
|
// Import all message types for different services
|
||||||
|
|
@ -89,19 +86,32 @@ export interface GraphRagOptions {
|
||||||
pathLength?: number;
|
pathLength?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metadata included in final streaming message
|
export interface LegacyRpcClient {
|
||||||
export interface StreamingMetadata {
|
readonly subscribe: (listener: (state: RpcConnectionState) => void) => () => void;
|
||||||
in_token?: number;
|
readonly dispatch: (
|
||||||
out_token?: number;
|
input: DispatchInput,
|
||||||
model?: string;
|
options?: DispatchOptions,
|
||||||
|
) => Promise<unknown>;
|
||||||
|
readonly dispatchStream: (
|
||||||
|
input: DispatchInput,
|
||||||
|
receiver: (chunk: { readonly response: unknown; readonly complete: boolean }) => boolean,
|
||||||
|
options?: DispatchOptions,
|
||||||
|
) => Promise<unknown>;
|
||||||
|
readonly close: () => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explainability event data
|
// Metadata included in final streaming message
|
||||||
export interface ExplainEvent {
|
export class StreamingMetadata extends S.Class<StreamingMetadata>("StreamingMetadata")({
|
||||||
explainId: string;
|
in_token: S.optionalKey(S.Finite),
|
||||||
explainGraph: string; // Named graph where explain data is stored (e.g., urn:graph:retrieval)
|
out_token: S.optionalKey(S.Finite),
|
||||||
explainTriples?: Triple[]; // Inline subgraph triples (when available)
|
model: S.optionalKey(S.String),
|
||||||
}
|
}, { description: "Token and model metadata attached to a final streaming chunk." }) {}
|
||||||
|
|
||||||
|
export class ExplainEvent extends S.Class<ExplainEvent>("ExplainEvent")({
|
||||||
|
explainId: S.String,
|
||||||
|
explainGraph: S.String,
|
||||||
|
explainTriples: S.optionalKey(S.Array(Triple).pipe(S.mutable)),
|
||||||
|
}, { description: "Explainability graph reference or inline triples for a stream." }) {}
|
||||||
|
|
||||||
// Configuration constants
|
// Configuration constants
|
||||||
const SOCKET_URL = getDefaultSocketUrl(); // WebSocket endpoint path (isomorphic)
|
const SOCKET_URL = getDefaultSocketUrl(); // WebSocket endpoint path (isomorphic)
|
||||||
|
|
@ -155,7 +165,11 @@ function dispatchOptions(
|
||||||
}
|
}
|
||||||
|
|
||||||
function streamingMetadataFrom(source: unknown): StreamingMetadata | undefined {
|
function streamingMetadataFrom(source: unknown): StreamingMetadata | undefined {
|
||||||
const metadata: StreamingMetadata = {};
|
const metadata: {
|
||||||
|
in_token?: number;
|
||||||
|
out_token?: number;
|
||||||
|
model?: string;
|
||||||
|
} = {};
|
||||||
let hasMetadata = false;
|
let hasMetadata = false;
|
||||||
|
|
||||||
const inToken = numberProperty(source, "in_token");
|
const inToken = numberProperty(source, "in_token");
|
||||||
|
|
@ -185,12 +199,12 @@ function throwIfResponseError(error: ResponseError | undefined): void {
|
||||||
|
|
||||||
const decodeJsonUnknown = S.decodeUnknownOption(S.UnknownFromJsonString);
|
const decodeJsonUnknown = S.decodeUnknownOption(S.UnknownFromJsonString);
|
||||||
|
|
||||||
export interface ConfigValueEntry {
|
export class ConfigValueEntry extends S.Class<ConfigValueEntry>("ConfigValueEntry")({
|
||||||
workspace?: string;
|
workspace: S.optionalKey(S.String),
|
||||||
type?: string;
|
type: S.optionalKey(S.String),
|
||||||
key: string;
|
key: S.String,
|
||||||
value: unknown;
|
value: S.Unknown,
|
||||||
}
|
}, { description: "Config key/value entry returned from the TrustGraph config service." }) {}
|
||||||
|
|
||||||
function asConfigValues(response: unknown): ConfigValueEntry[] {
|
function asConfigValues(response: unknown): ConfigValueEntry[] {
|
||||||
if (response === null || typeof response !== "object") return [];
|
if (response === null || typeof response !== "object") return [];
|
||||||
|
|
@ -201,9 +215,12 @@ function asConfigValues(response: unknown): ConfigValueEntry[] {
|
||||||
const item = value as Record<string, unknown>;
|
const item = value as Record<string, unknown>;
|
||||||
const key = item.key;
|
const key = item.key;
|
||||||
if (typeof key !== "string") return [];
|
if (typeof key !== "string") return [];
|
||||||
const entry: ConfigValueEntry = { key, value: item.value };
|
const entry: ConfigValueEntry = {
|
||||||
if (typeof item.workspace === "string") entry.workspace = item.workspace;
|
key,
|
||||||
if (typeof item.type === "string") entry.type = item.type;
|
value: item.value,
|
||||||
|
...(typeof item.workspace === "string" ? { workspace: item.workspace } : {}),
|
||||||
|
...(typeof item.type === "string" ? { type: item.type } : {}),
|
||||||
|
};
|
||||||
return [entry];
|
return [entry];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -222,15 +239,11 @@ function parseResponseJson(value: string | undefined, operation: string): unknow
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentEpochSeconds = (): number =>
|
const currentEpochSeconds = (): number =>
|
||||||
Math.floor(Effect.runSync(Clock.currentTimeMillis) / 1000);
|
Math.floor((globalThis.performance.timeOrigin + globalThis.performance.now()) / 1000);
|
||||||
|
|
||||||
const logClientInfo = (message: string): void => {
|
const logClientInfo = (_message: string): void => {};
|
||||||
Effect.runFork(Effect.log(message));
|
|
||||||
};
|
|
||||||
|
|
||||||
const logClientError = (message: string, error: unknown): void => {
|
const logClientError = (_message: string, _error: unknown): void => {};
|
||||||
Effect.runFork(Effect.logError(message, { error: toErrorMessage(error, message) }));
|
|
||||||
};
|
|
||||||
|
|
||||||
const runLegacyStreamingRequest = (
|
const runLegacyStreamingRequest = (
|
||||||
operation: string,
|
operation: string,
|
||||||
|
|
@ -238,18 +251,9 @@ const runLegacyStreamingRequest = (
|
||||||
request: () => Promise<unknown>,
|
request: () => Promise<unknown>,
|
||||||
onError: (message: string) => void,
|
onError: (message: string) => void,
|
||||||
): Promise<unknown | void> =>
|
): Promise<unknown | void> =>
|
||||||
Effect.runPromise(
|
request().catch((error) => {
|
||||||
Effect.tryPromise({
|
onError(`${label} request failed: ${toErrorMessage(error, `${operation} failed`)}`);
|
||||||
try: request,
|
});
|
||||||
catch: (error) => socketError(operation, toErrorMessage(error, "Unknown error")),
|
|
||||||
}).pipe(
|
|
||||||
Effect.catch((error) =>
|
|
||||||
Effect.sync(() => {
|
|
||||||
onError(`${label} request failed: ${error.message}`);
|
|
||||||
})
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
const StreamingEnvelopeSchema = S.Struct({
|
const StreamingEnvelopeSchema = S.Struct({
|
||||||
response: S.optionalKey(S.Unknown),
|
response: S.optionalKey(S.Unknown),
|
||||||
|
|
@ -453,34 +457,35 @@ function makeid(length: number) {
|
||||||
* functionality
|
* functionality
|
||||||
*/
|
*/
|
||||||
// Connection state interface for UI consumption
|
// Connection state interface for UI consumption
|
||||||
export interface ConnectionState {
|
export class ConnectionState extends S.Class<ConnectionState>("ConnectionState")({
|
||||||
status:
|
status: S.Literals([
|
||||||
| "connecting"
|
"connecting",
|
||||||
| "connected"
|
"connected",
|
||||||
| "reconnecting"
|
"reconnecting",
|
||||||
| "failed"
|
"failed",
|
||||||
| "authenticated"
|
"authenticated",
|
||||||
| "unauthenticated";
|
"unauthenticated",
|
||||||
hasApiKey: boolean;
|
]),
|
||||||
reconnectAttempt?: number;
|
hasApiKey: S.Boolean,
|
||||||
maxAttempts?: number;
|
reconnectAttempt: S.optionalKey(S.Finite),
|
||||||
nextRetryIn?: number;
|
maxAttempts: S.optionalKey(S.Finite),
|
||||||
lastError?: string;
|
nextRetryIn: S.optionalKey(S.Finite),
|
||||||
}
|
lastError: S.optionalKey(S.String),
|
||||||
|
}, { description: "Workbench-facing TrustGraph gateway connection state." }) {}
|
||||||
|
|
||||||
export function makeBaseApi(
|
export function makeBaseApi(
|
||||||
user: string,
|
user: string,
|
||||||
token?: string,
|
token: string | undefined,
|
||||||
socketUrl?: string,
|
socketUrl: string | undefined,
|
||||||
rpcFactory: (url: string) => EffectRpcClient = makeEffectRpcClient,
|
rpcFactory: (url: string) => LegacyRpcClient,
|
||||||
) {
|
) {
|
||||||
let rpc: EffectRpcClient;
|
let rpc: LegacyRpcClient;
|
||||||
const connectionStateRef = Effect.runSync(
|
let unsubscribeRpc: (() => void) | undefined;
|
||||||
SubscriptionRef.make<ConnectionState>({
|
const connectionStateListeners = new Set<(state: ConnectionState) => void>();
|
||||||
status: "connecting",
|
let connectionState: ConnectionState = {
|
||||||
hasApiKey: isNonEmptyString(token),
|
status: "connecting",
|
||||||
}),
|
hasApiKey: isNonEmptyString(token),
|
||||||
);
|
};
|
||||||
let lastError: string | undefined ;
|
let lastError: string | undefined ;
|
||||||
let rpcState: RpcConnectionState = { status: "connecting" };
|
let rpcState: RpcConnectionState = { status: "connecting" };
|
||||||
|
|
||||||
|
|
@ -495,27 +500,10 @@ export function makeBaseApi(
|
||||||
* Subscribe to connection state changes for UI updates
|
* Subscribe to connection state changes for UI updates
|
||||||
*/
|
*/
|
||||||
onConnectionStateChange(listener: (state: ConnectionState) => void) {
|
onConnectionStateChange(listener: (state: ConnectionState) => void) {
|
||||||
let latest = SubscriptionRef.getUnsafe(connectionStateRef);
|
connectionStateListeners.add(listener);
|
||||||
listener(latest);
|
notifyConnectionStateListener(listener, connectionState);
|
||||||
let replaySeen = false;
|
|
||||||
const fiber = Effect.runFork(
|
|
||||||
SubscriptionRef.changes(connectionStateRef).pipe(
|
|
||||||
Stream.runForEach((state) =>
|
|
||||||
Effect.sync(() => {
|
|
||||||
if (!replaySeen) {
|
|
||||||
replaySeen = true;
|
|
||||||
if (state === latest) return;
|
|
||||||
}
|
|
||||||
latest = state;
|
|
||||||
notifyConnectionStateListener(listener, state);
|
|
||||||
})
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Return unsubscribe function
|
|
||||||
return () => {
|
return () => {
|
||||||
Effect.runFork(Fiber.interrupt(fiber));
|
connectionStateListeners.delete(listener);
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -523,13 +511,9 @@ export function makeBaseApi(
|
||||||
* Closes the WebSocket connection and cleans up
|
* Closes the WebSocket connection and cleans up
|
||||||
*/
|
*/
|
||||||
close() {
|
close() {
|
||||||
Effect.runFork(
|
unsubscribeRpc?.();
|
||||||
Effect.tryPromise({
|
void rpc.close().catch((error) =>
|
||||||
try: () => rpc.close(),
|
logClientError("[socket close error]", socketError("socket-close", toErrorMessage(error, "Socket close failed")))
|
||||||
catch: (error) => socketError("socket-close", toErrorMessage(error, "Socket close failed")),
|
|
||||||
}).pipe(
|
|
||||||
Effect.catch((error) => Effect.sync(() => logClientError("[socket close error]", error))),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -643,10 +627,8 @@ export function makeBaseApi(
|
||||||
const state: ConnectionState = {
|
const state: ConnectionState = {
|
||||||
status,
|
status,
|
||||||
hasApiKey,
|
hasApiKey,
|
||||||
|
...(lastError !== undefined ? { lastError } : {}),
|
||||||
};
|
};
|
||||||
if (lastError !== undefined) {
|
|
||||||
state.lastError = lastError;
|
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
};
|
};
|
||||||
|
|
@ -655,21 +637,24 @@ export function makeBaseApi(
|
||||||
listener: (state: ConnectionState) => void,
|
listener: (state: ConnectionState) => void,
|
||||||
state: ConnectionState,
|
state: ConnectionState,
|
||||||
): void => {
|
): void => {
|
||||||
const result = Result.try({
|
try {
|
||||||
try: () => listener(state),
|
listener(state);
|
||||||
catch: (error) =>
|
} catch (error) {
|
||||||
|
logClientError(
|
||||||
|
"Error in connection state listener",
|
||||||
socketError(
|
socketError(
|
||||||
"connection-state-listener",
|
"connection-state-listener",
|
||||||
toErrorMessage(error, "Error in connection state listener"),
|
toErrorMessage(error, "Error in connection state listener"),
|
||||||
),
|
),
|
||||||
});
|
);
|
||||||
if (Result.isFailure(result)) {
|
|
||||||
logClientError("Error in connection state listener", result.failure);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const publishConnectionState = () => {
|
const publishConnectionState = () => {
|
||||||
Effect.runSync(SubscriptionRef.set(connectionStateRef, getConnectionState()));
|
connectionState = getConnectionState();
|
||||||
|
for (const listener of connectionStateListeners) {
|
||||||
|
notifyConnectionStateListener(listener, connectionState);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const connectionStatusFromRpc = (hasApiKey: boolean): ConnectionState["status"] =>
|
const connectionStatusFromRpc = (hasApiKey: boolean): ConnectionState["status"] =>
|
||||||
|
|
@ -712,7 +697,7 @@ export function makeBaseApi(
|
||||||
};
|
};
|
||||||
|
|
||||||
rpc = rpcFactory(socketUrlWithToken());
|
rpc = rpcFactory(socketUrlWithToken());
|
||||||
rpc.subscribe((state) => {
|
unsubscribeRpc = rpc.subscribe((state) => {
|
||||||
rpcState = state;
|
rpcState = state;
|
||||||
lastError = state.lastError;
|
lastError = state.lastError;
|
||||||
publishConnectionState();
|
publishConnectionState();
|
||||||
|
|
@ -726,13 +711,12 @@ export function makeBaseApi(
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BaseApi = ReturnType<typeof makeBaseApi>;
|
export type BaseApi = ReturnType<typeof makeBaseApi>;
|
||||||
export const BaseApi = makeBaseApi;
|
|
||||||
|
|
||||||
export function makeBaseApiWithRpc(
|
export function makeBaseApiWithRpc(
|
||||||
user: string,
|
user: string,
|
||||||
token: string | undefined,
|
token: string | undefined,
|
||||||
socketUrl: string | undefined,
|
socketUrl: string | undefined,
|
||||||
rpc: EffectRpcClient,
|
rpc: LegacyRpcClient,
|
||||||
): BaseApi {
|
): BaseApi {
|
||||||
return makeBaseApi(user, token, socketUrl, () => rpc);
|
return makeBaseApi(user, token, socketUrl, () => rpc);
|
||||||
}
|
}
|
||||||
|
|
@ -833,13 +817,9 @@ export function makeLibrarianApi(api: BaseApi) {
|
||||||
tags,
|
tags,
|
||||||
"document-type": "source",
|
"document-type": "source",
|
||||||
documentType: "source",
|
documentType: "source",
|
||||||
|
...(id !== undefined ? { id } : {}),
|
||||||
|
...(metadata !== undefined ? { metadata } : {}),
|
||||||
};
|
};
|
||||||
if (id !== undefined) {
|
|
||||||
documentMetadata.id = id;
|
|
||||||
}
|
|
||||||
if (metadata !== undefined) {
|
|
||||||
documentMetadata.metadata = metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.api.makeRequest<LibraryRequest, LibraryResponse>(
|
return this.api.makeRequest<LibraryRequest, LibraryResponse>(
|
||||||
"librarian",
|
"librarian",
|
||||||
|
|
@ -929,10 +909,8 @@ export function makeLibrarianApi(api: BaseApi) {
|
||||||
"document-metadata": metadata,
|
"document-metadata": metadata,
|
||||||
documentMetadata: metadata,
|
documentMetadata: metadata,
|
||||||
"total-size": totalSize,
|
"total-size": totalSize,
|
||||||
|
...(chunkSize !== undefined ? { "chunk-size": chunkSize } : {}),
|
||||||
};
|
};
|
||||||
if (chunkSize !== undefined) {
|
|
||||||
request["chunk-size"] = chunkSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.api
|
return this.api
|
||||||
.makeRequest<BeginUploadRequest, BeginUploadResponse>(
|
.makeRequest<BeginUploadRequest, BeginUploadResponse>(
|
||||||
|
|
@ -1124,10 +1102,8 @@ export function makeLibrarianApi(api: BaseApi) {
|
||||||
operation: "stream-document",
|
operation: "stream-document",
|
||||||
"document-id": documentId,
|
"document-id": documentId,
|
||||||
user: this.api.user,
|
user: this.api.user,
|
||||||
|
...(chunkSize !== undefined ? { "chunk-size": chunkSize } : {}),
|
||||||
};
|
};
|
||||||
if (chunkSize !== undefined) {
|
|
||||||
request["chunk-size"] = chunkSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.api.makeRequestMulti<StreamDocumentRequest, StreamDocumentResponse>(
|
this.api.makeRequestMulti<StreamDocumentRequest, StreamDocumentResponse>(
|
||||||
"librarian",
|
"librarian",
|
||||||
|
|
@ -1369,13 +1345,9 @@ export function makeFlowsApi(api: BaseApi) {
|
||||||
"flow-id": id,
|
"flow-id": id,
|
||||||
"blueprint-name": blueprint_name,
|
"blueprint-name": blueprint_name,
|
||||||
description: description,
|
description: description,
|
||||||
|
...(parameters !== undefined && Object.keys(parameters).length > 0 ? { parameters } : {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Only include parameters if provided and not empty
|
|
||||||
if (parameters !== undefined && Object.keys(parameters).length > 0) {
|
|
||||||
request.parameters = parameters;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.api
|
return this.api
|
||||||
.makeRequest<FlowRequest, FlowResponse>("flow", request, 30000)
|
.makeRequest<FlowRequest, FlowResponse>("flow", request, 30000)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
|
|
@ -1447,19 +1419,11 @@ export function makeFlowApi(api: BaseApi, flowId: string) {
|
||||||
query: text,
|
query: text,
|
||||||
user: this.api.user,
|
user: this.api.user,
|
||||||
collection: withDefault(collection, "default"),
|
collection: withDefault(collection, "default"),
|
||||||
|
...(options?.entityLimit !== undefined ? { "entity-limit": options.entityLimit } : {}),
|
||||||
|
...(options?.tripleLimit !== undefined ? { "triple-limit": options.tripleLimit } : {}),
|
||||||
|
...(options?.maxSubgraphSize !== undefined ? { "max-subgraph-size": options.maxSubgraphSize } : {}),
|
||||||
|
...(options?.pathLength !== undefined ? { "max-path-length": options.pathLength } : {}),
|
||||||
};
|
};
|
||||||
if (options?.entityLimit !== undefined) {
|
|
||||||
request["entity-limit"] = options.entityLimit;
|
|
||||||
}
|
|
||||||
if (options?.tripleLimit !== undefined) {
|
|
||||||
request["triple-limit"] = options.tripleLimit;
|
|
||||||
}
|
|
||||||
if (options?.maxSubgraphSize !== undefined) {
|
|
||||||
request["max-subgraph-size"] = options.maxSubgraphSize;
|
|
||||||
}
|
|
||||||
if (options?.pathLength !== undefined) {
|
|
||||||
request["max-path-length"] = options.pathLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.api
|
return this.api
|
||||||
.makeRequest<GraphRagRequest, GraphRagResponse>(
|
.makeRequest<GraphRagRequest, GraphRagResponse>(
|
||||||
|
|
@ -1539,10 +1503,8 @@ export function makeFlowApi(api: BaseApi, flowId: string) {
|
||||||
const event: ExplainEvent = {
|
const event: ExplainEvent = {
|
||||||
explainId: explainId ?? "",
|
explainId: explainId ?? "",
|
||||||
explainGraph: stringProperty(resp, "explain_graph") ?? "",
|
explainGraph: stringProperty(resp, "explain_graph") ?? "",
|
||||||
|
...(explainTriples !== undefined ? { explainTriples } : {}),
|
||||||
};
|
};
|
||||||
if (explainTriples !== undefined) {
|
|
||||||
event.explainTriples = explainTriples;
|
|
||||||
}
|
|
||||||
onExplain?.(event);
|
onExplain?.(event);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -1635,10 +1597,8 @@ export function makeFlowApi(api: BaseApi, flowId: string) {
|
||||||
const event: ExplainEvent = {
|
const event: ExplainEvent = {
|
||||||
explainId: explainId ?? "",
|
explainId: explainId ?? "",
|
||||||
explainGraph: stringProperty(resp, "explain_graph") ?? "",
|
explainGraph: stringProperty(resp, "explain_graph") ?? "",
|
||||||
|
...(explainTriples !== undefined ? { explainTriples } : {}),
|
||||||
};
|
};
|
||||||
if (explainTriples !== undefined) {
|
|
||||||
event.explainTriples = explainTriples;
|
|
||||||
}
|
|
||||||
onExplain?.(event);
|
onExplain?.(event);
|
||||||
// If this message also carries answer text, fall through to chunk handling.
|
// If this message also carries answer text, fall through to chunk handling.
|
||||||
// If it's a standalone explain event (no answer text), stop here.
|
// If it's a standalone explain event (no answer text), stop here.
|
||||||
|
|
@ -1668,19 +1628,11 @@ export function makeFlowApi(api: BaseApi, flowId: string) {
|
||||||
user: this.api.user,
|
user: this.api.user,
|
||||||
collection: withDefault(collection, "default"),
|
collection: withDefault(collection, "default"),
|
||||||
streaming: true,
|
streaming: true,
|
||||||
|
...(options?.entityLimit !== undefined ? { "entity-limit": options.entityLimit } : {}),
|
||||||
|
...(options?.tripleLimit !== undefined ? { "triple-limit": options.tripleLimit } : {}),
|
||||||
|
...(options?.maxSubgraphSize !== undefined ? { "max-subgraph-size": options.maxSubgraphSize } : {}),
|
||||||
|
...(options?.pathLength !== undefined ? { "max-path-length": options.pathLength } : {}),
|
||||||
};
|
};
|
||||||
if (options?.entityLimit !== undefined) {
|
|
||||||
request["entity-limit"] = options.entityLimit;
|
|
||||||
}
|
|
||||||
if (options?.tripleLimit !== undefined) {
|
|
||||||
request["triple-limit"] = options.tripleLimit;
|
|
||||||
}
|
|
||||||
if (options?.maxSubgraphSize !== undefined) {
|
|
||||||
request["max-subgraph-size"] = options.maxSubgraphSize;
|
|
||||||
}
|
|
||||||
if (options?.pathLength !== undefined) {
|
|
||||||
request["max-path-length"] = options.pathLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
void runLegacyStreamingRequest(
|
void runLegacyStreamingRequest(
|
||||||
"graph-rag-stream",
|
"graph-rag-stream",
|
||||||
|
|
@ -1765,10 +1717,8 @@ export function makeFlowApi(api: BaseApi, flowId: string) {
|
||||||
user: this.api.user,
|
user: this.api.user,
|
||||||
collection: withDefault(collection, "default"),
|
collection: withDefault(collection, "default"),
|
||||||
streaming: true,
|
streaming: true,
|
||||||
|
...(docLimit !== undefined ? { "doc-limit": docLimit } : {}),
|
||||||
};
|
};
|
||||||
if (docLimit !== undefined) {
|
|
||||||
request["doc-limit"] = docLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
void runLegacyStreamingRequest(
|
void runLegacyStreamingRequest(
|
||||||
"document-rag-stream",
|
"document-rag-stream",
|
||||||
|
|
@ -1968,19 +1918,11 @@ export function makeFlowApi(api: BaseApi, flowId: string) {
|
||||||
limit: limit ?? 20,
|
limit: limit ?? 20,
|
||||||
user: this.api.user,
|
user: this.api.user,
|
||||||
collection: withDefault(collection, "default"),
|
collection: withDefault(collection, "default"),
|
||||||
|
...(s !== undefined ? { s } : {}),
|
||||||
|
...(p !== undefined ? { p } : {}),
|
||||||
|
...(o !== undefined ? { o } : {}),
|
||||||
|
...(graph !== undefined ? { g: graph } : {}),
|
||||||
};
|
};
|
||||||
if (s !== undefined) {
|
|
||||||
request.s = s;
|
|
||||||
}
|
|
||||||
if (p !== undefined) {
|
|
||||||
request.p = p;
|
|
||||||
}
|
|
||||||
if (o !== undefined) {
|
|
||||||
request.o = o;
|
|
||||||
}
|
|
||||||
if (graph !== undefined) {
|
|
||||||
request.g = graph;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.api
|
return this.api
|
||||||
.makeRequest<TriplesQueryRequest, TriplesQueryResponse>(
|
.makeRequest<TriplesQueryRequest, TriplesQueryResponse>(
|
||||||
|
|
@ -2005,13 +1947,9 @@ export function makeFlowApi(api: BaseApi, flowId: string) {
|
||||||
) {
|
) {
|
||||||
const request: LoadDocumentRequest = {
|
const request: LoadDocumentRequest = {
|
||||||
data: document,
|
data: document,
|
||||||
|
...(id !== undefined ? { id } : {}),
|
||||||
|
...(metadata !== undefined ? { metadata } : {}),
|
||||||
};
|
};
|
||||||
if (id !== undefined) {
|
|
||||||
request.id = id;
|
|
||||||
}
|
|
||||||
if (metadata !== undefined) {
|
|
||||||
request.metadata = metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.api.makeRequest<LoadDocumentRequest, LoadDocumentResponse>(
|
return this.api.makeRequest<LoadDocumentRequest, LoadDocumentResponse>(
|
||||||
"document-load",
|
"document-load",
|
||||||
|
|
@ -2035,16 +1973,10 @@ export function makeFlowApi(api: BaseApi, flowId: string) {
|
||||||
) {
|
) {
|
||||||
const request: LoadTextRequest = {
|
const request: LoadTextRequest = {
|
||||||
text,
|
text,
|
||||||
|
...(id !== undefined ? { id } : {}),
|
||||||
|
...(metadata !== undefined ? { metadata } : {}),
|
||||||
|
...(charset !== undefined ? { charset } : {}),
|
||||||
};
|
};
|
||||||
if (id !== undefined) {
|
|
||||||
request.id = id;
|
|
||||||
}
|
|
||||||
if (metadata !== undefined) {
|
|
||||||
request.metadata = metadata;
|
|
||||||
}
|
|
||||||
if (charset !== undefined) {
|
|
||||||
request.charset = charset;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.api.makeRequest<LoadTextRequest, LoadTextResponse>(
|
return this.api.makeRequest<LoadTextRequest, LoadTextResponse>(
|
||||||
"text-load",
|
"text-load",
|
||||||
|
|
@ -2070,13 +2002,9 @@ export function makeFlowApi(api: BaseApi, flowId: string) {
|
||||||
query,
|
query,
|
||||||
user: this.api.user,
|
user: this.api.user,
|
||||||
collection: withDefault(collection, "default"),
|
collection: withDefault(collection, "default"),
|
||||||
|
...(variables !== undefined ? { variables } : {}),
|
||||||
|
...(operationName !== undefined ? { operation_name: operationName } : {}),
|
||||||
};
|
};
|
||||||
if (variables !== undefined) {
|
|
||||||
request.variables = variables;
|
|
||||||
}
|
|
||||||
if (operationName !== undefined) {
|
|
||||||
request.operation_name = operationName;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.api
|
return this.api
|
||||||
.makeRequest<RowsQueryRequest, RowsQueryResponse>(
|
.makeRequest<RowsQueryRequest, RowsQueryResponse>(
|
||||||
|
|
@ -2167,12 +2095,9 @@ export function makeFlowApi(api: BaseApi, flowId: string) {
|
||||||
user: this.api.user,
|
user: this.api.user,
|
||||||
collection: withDefault(collection, "default"),
|
collection: withDefault(collection, "default"),
|
||||||
limit: limit ?? 10,
|
limit: limit ?? 10,
|
||||||
|
...(indexName !== undefined ? { index_name: indexName } : {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (indexName !== undefined) {
|
|
||||||
request.index_name = indexName;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.api
|
return this.api
|
||||||
.makeRequest<RowEmbeddingsQueryRequest, RowEmbeddingsQueryResponse>(
|
.makeRequest<RowEmbeddingsQueryRequest, RowEmbeddingsQueryResponse>(
|
||||||
"row-embeddings",
|
"row-embeddings",
|
||||||
|
|
@ -2664,16 +2589,3 @@ export function makeCollectionManagementApi(api: BaseApi) {
|
||||||
|
|
||||||
export type CollectionManagementApi = ReturnType<typeof makeCollectionManagementApi>;
|
export type CollectionManagementApi = ReturnType<typeof makeCollectionManagementApi>;
|
||||||
export const CollectionManagementApi = makeCollectionManagementApi;
|
export const CollectionManagementApi = makeCollectionManagementApi;
|
||||||
|
|
||||||
/**
|
|
||||||
* Factory function to create a new TrustGraph WebSocket connection
|
|
||||||
* This is the main entry point for using the TrustGraph API
|
|
||||||
* @param user - User identifier for API requests
|
|
||||||
* @param token - Optional authentication token for secure connections
|
|
||||||
* @param socketUrl - Optional WebSocket URL (defaults to /api/v1/rpc for browser, provide full URL for Node.js)
|
|
||||||
*/
|
|
||||||
export const createTrustGraphSocket = (
|
|
||||||
user: string,
|
|
||||||
token?: string,
|
|
||||||
socketUrl?: string,
|
|
||||||
): BaseApi => makeBaseApi(user, token, socketUrl);
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { describe, expect, it } from "@effect/vitest";
|
import { describe, expect, it } from "@effect/vitest";
|
||||||
import type { BaseApi, TrustGraphGatewayClient } from "@trustgraph/client";
|
import type { DispatchInput, TrustGraphGatewayClient } from "@trustgraph/client";
|
||||||
import { DispatchStreamChunk, } from "@trustgraph/client";
|
import { DispatchError, DispatchStreamChunk, } from "@trustgraph/client";
|
||||||
import { Effect, Layer, Stream } from "effect";
|
import { Effect, Layer, Stream } from "effect";
|
||||||
import * as S from "effect/Schema";
|
import * as S from "effect/Schema";
|
||||||
import { McpServer } from "effect/unstable/ai";
|
import { McpServer } from "effect/unstable/ai";
|
||||||
|
|
@ -15,7 +15,6 @@ import {
|
||||||
TrustGraphMcpToolkit,
|
TrustGraphMcpToolkit,
|
||||||
TrustGraphMcpToolkitLive,
|
TrustGraphMcpToolkitLive,
|
||||||
TrustGraphGateway,
|
TrustGraphGateway,
|
||||||
TrustGraphSocket,
|
|
||||||
} from "../server-effect.js";
|
} from "../server-effect.js";
|
||||||
|
|
||||||
const expectedToolNames = [
|
const expectedToolNames = [
|
||||||
|
|
@ -61,7 +60,7 @@ interface NativeTestClientOptions {
|
||||||
const decodeJsonText = S.decodeUnknownSync(S.UnknownFromJsonString);
|
const decodeJsonText = S.decodeUnknownSync(S.UnknownFromJsonString);
|
||||||
|
|
||||||
const makeFakeSocket = (
|
const makeFakeSocket = (
|
||||||
options: {
|
_options: {
|
||||||
readonly textCompletion?: (() => Promise<string>) | undefined;
|
readonly textCompletion?: (() => Promise<string>) | undefined;
|
||||||
readonly graphRag?: (() => Promise<string>) | undefined;
|
readonly graphRag?: (() => Promise<string>) | undefined;
|
||||||
} = {},
|
} = {},
|
||||||
|
|
@ -71,66 +70,62 @@ const makeFakeSocket = (
|
||||||
graphRag: [],
|
graphRag: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
const socket = {
|
return { calls };
|
||||||
close: () => {},
|
|
||||||
flow: (flowId: string) => {
|
|
||||||
calls.flowIds.push(flowId);
|
|
||||||
return {
|
|
||||||
textCompletion: () => options.textCompletion === undefined
|
|
||||||
? Promise.resolve("gateway text completion")
|
|
||||||
: options.textCompletion(),
|
|
||||||
graphRag: (query: string, ragOptions: unknown, collection?: string) => {
|
|
||||||
calls.graphRag.push({ query, options: ragOptions, collection });
|
|
||||||
return options.graphRag === undefined
|
|
||||||
? Promise.resolve("graph rag answer")
|
|
||||||
: options.graphRag();
|
|
||||||
},
|
|
||||||
documentRag: () => Promise.resolve("document rag answer"),
|
|
||||||
agent: (
|
|
||||||
_question: string,
|
|
||||||
_onThought: () => void,
|
|
||||||
_onObservation: () => void,
|
|
||||||
onAnswer: (chunk: string, complete: boolean) => void,
|
|
||||||
) => onAnswer("agent answer", true),
|
|
||||||
embeddings: () => Promise.resolve([[0.25, 0.75]]),
|
|
||||||
triplesQuery: () => Promise.resolve([]),
|
|
||||||
graphEmbeddingsQuery: () => Promise.resolve([]),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
config: () => ({
|
|
||||||
getConfigAll: () => Promise.resolve({}),
|
|
||||||
getConfig: () => Promise.resolve({}),
|
|
||||||
putConfig: () => Promise.resolve({ ok: true }),
|
|
||||||
deleteConfig: () => Promise.resolve({ ok: true }),
|
|
||||||
getPrompts: () => Promise.resolve([]),
|
|
||||||
getPrompt: () => Promise.resolve({}),
|
|
||||||
}),
|
|
||||||
flows: () => ({
|
|
||||||
getFlows: () => Promise.resolve(["default"]),
|
|
||||||
getFlow: () => Promise.resolve({}),
|
|
||||||
startFlow: () => Promise.resolve({ ok: true }),
|
|
||||||
stopFlow: () => Promise.resolve({ ok: true }),
|
|
||||||
}),
|
|
||||||
librarian: () => ({
|
|
||||||
getDocuments: () => Promise.resolve([]),
|
|
||||||
loadDocument: () => Promise.resolve({ ok: true }),
|
|
||||||
removeDocument: () => Promise.resolve({ ok: true }),
|
|
||||||
}),
|
|
||||||
knowledge: () => ({
|
|
||||||
getKnowledgeCores: () => Promise.resolve([]),
|
|
||||||
deleteKgCore: () => Promise.resolve({ ok: true }),
|
|
||||||
loadKgCore: () => Promise.resolve({ ok: true }),
|
|
||||||
}),
|
|
||||||
} as unknown as BaseApi;
|
|
||||||
|
|
||||||
return { socket, calls };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const makeFakeGateway = (): TrustGraphGatewayClient => ({
|
const makeFakeGateway = (
|
||||||
|
calls: FakeSocketCalls,
|
||||||
|
options: NativeTestClientOptions = {},
|
||||||
|
): TrustGraphGatewayClient => ({
|
||||||
state: Effect.succeed({ status: "connected" }),
|
state: Effect.succeed({ status: "connected" }),
|
||||||
changes: Stream.empty,
|
changes: Stream.empty,
|
||||||
subscribe: () => Effect.succeed(Effect.void),
|
subscribe: () => Effect.succeed(Effect.void),
|
||||||
dispatch: () => Effect.succeed({}),
|
dispatch: Effect.fn("FakeTrustGraphGateway.dispatch")(function*(input: DispatchInput) {
|
||||||
|
if (input.flow !== undefined) calls.flowIds.push(input.flow);
|
||||||
|
if (input.service === "text-completion") {
|
||||||
|
const response = options.textCompletion === undefined
|
||||||
|
? "gateway text completion"
|
||||||
|
: yield* Effect.tryPromise({
|
||||||
|
try: options.textCompletion,
|
||||||
|
catch: (cause) => DispatchError.make({
|
||||||
|
message: cause instanceof Error ? cause.message : String(cause),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
return { response };
|
||||||
|
}
|
||||||
|
if (input.service === "graph-rag") {
|
||||||
|
calls.graphRag.push({
|
||||||
|
query: String(input.request.query ?? ""),
|
||||||
|
options: {
|
||||||
|
entityLimit: input.request["entity-limit"],
|
||||||
|
tripleLimit: input.request["triple-limit"],
|
||||||
|
},
|
||||||
|
collection: typeof input.request.collection === "string" ? input.request.collection : undefined,
|
||||||
|
});
|
||||||
|
const response = options.graphRag === undefined
|
||||||
|
? "graph rag answer"
|
||||||
|
: yield* Effect.tryPromise({
|
||||||
|
try: options.graphRag,
|
||||||
|
catch: (cause) => DispatchError.make({
|
||||||
|
message: cause instanceof Error ? cause.message : String(cause),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
return { response };
|
||||||
|
}
|
||||||
|
if (input.service === "document-rag") return { response: "document rag answer" };
|
||||||
|
if (input.service === "embeddings") return { vectors: [[0.25, 0.75]] };
|
||||||
|
if (input.service === "triples") return { triples: [] };
|
||||||
|
if (input.service === "graph-embeddings") return { entities: [] };
|
||||||
|
if (input.service === "config") return {};
|
||||||
|
if (input.service === "flow" && input.request.operation === "list-flows") return { "flow-ids": ["default"] };
|
||||||
|
if (input.service === "flow" && input.request.operation === "get-flow") return { flow: "{}" };
|
||||||
|
if (input.service === "flow") return { ok: true };
|
||||||
|
if (input.service === "librarian" && input.request.operation === "list-documents") return { "document-metadatas": [] };
|
||||||
|
if (input.service === "librarian") return { ok: true };
|
||||||
|
if (input.service === "knowledge" && input.request.operation === "list-kg-cores") return { ids: [] };
|
||||||
|
if (input.service === "knowledge") return { ok: true };
|
||||||
|
return {};
|
||||||
|
}),
|
||||||
dispatchStream: () => Stream.empty,
|
dispatchStream: () => Stream.empty,
|
||||||
runDispatchStream: (_input, receiver) =>
|
runDispatchStream: (_input, receiver) =>
|
||||||
Effect.sync(() => {
|
Effect.sync(() => {
|
||||||
|
|
@ -168,15 +163,14 @@ const makeNativeTestClient = (
|
||||||
const makeNativeTestClientEffect = Effect.fn("makeNativeTestClient")(function*(
|
const makeNativeTestClientEffect = Effect.fn("makeNativeTestClient")(function*(
|
||||||
options: NativeTestClientOptions,
|
options: NativeTestClientOptions,
|
||||||
) {
|
) {
|
||||||
const { socket, calls } = makeFakeSocket({
|
const { calls } = makeFakeSocket({
|
||||||
textCompletion: options.textCompletion,
|
textCompletion: options.textCompletion,
|
||||||
graphRag: options.graphRag,
|
graphRag: options.graphRag,
|
||||||
});
|
});
|
||||||
const gateway = makeFakeGateway();
|
const gateway = makeFakeGateway(calls, options);
|
||||||
const serverLayer = McpServer.toolkit(TrustGraphMcpToolkit).pipe(
|
const serverLayer = McpServer.toolkit(TrustGraphMcpToolkit).pipe(
|
||||||
Layer.provide(TrustGraphMcpToolkitLive),
|
Layer.provide(TrustGraphMcpToolkitLive),
|
||||||
Layer.provide(Layer.succeed(TrustGraphGateway, TrustGraphGateway.of(gateway))),
|
Layer.provide(Layer.succeed(TrustGraphGateway, TrustGraphGateway.of(gateway))),
|
||||||
Layer.provide(Layer.succeed(TrustGraphSocket, TrustGraphSocket.of(socket))),
|
|
||||||
Layer.provide(Layer.succeed(TrustGraphMcpConfig, testConfig)),
|
Layer.provide(Layer.succeed(TrustGraphMcpConfig, testConfig)),
|
||||||
Layer.provide(McpServer.layerHttp({
|
Layer.provide(McpServer.layerHttp({
|
||||||
name: "trustgraph",
|
name: "trustgraph",
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
import {BunHttpServer, BunRuntime} from "@effect/platform-bun";
|
import {BunHttpServer, BunRuntime} from "@effect/platform-bun";
|
||||||
import {NodeRuntime, NodeStdio} from "@effect/platform-node";
|
import {NodeRuntime, NodeStdio} from "@effect/platform-node";
|
||||||
import type {
|
import type {
|
||||||
BaseApi,
|
EntityMatch as ClientEntityMatch,
|
||||||
Term as ClientTerm,
|
Term as ClientTerm,
|
||||||
|
Triple as ClientTriple,
|
||||||
TrustGraphGatewayClient,
|
TrustGraphGatewayClient,
|
||||||
} from "@trustgraph/client";
|
} from "@trustgraph/client";
|
||||||
import {
|
import {
|
||||||
createTrustGraphSocket,
|
|
||||||
makeTrustGraphGatewayClientScoped,
|
makeTrustGraphGatewayClientScoped,
|
||||||
} from "@trustgraph/client";
|
} from "@trustgraph/client";
|
||||||
import {Config, Context, Effect, Layer} from "effect";
|
import {Clock, Config, Context, Effect, Layer} from "effect";
|
||||||
import * as O from "effect/Option";
|
import * as O from "effect/Option";
|
||||||
import * as Predicate from "effect/Predicate";
|
import * as Predicate from "effect/Predicate";
|
||||||
import {McpServer, Tool, Toolkit} from "effect/unstable/ai";
|
import {McpServer, Tool, Toolkit} from "effect/unstable/ai";
|
||||||
|
|
@ -1279,22 +1279,6 @@ export class TrustGraphMcpConfig extends Context.Service<TrustGraphMcpConfig, Tr
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TrustGraphSocket extends Context.Service<TrustGraphSocket, BaseApi>()(
|
|
||||||
"@trustgraph/mcp/server-effect/TrustGraphSocket",
|
|
||||||
) {
|
|
||||||
static readonly layer = Layer.effect(
|
|
||||||
TrustGraphSocket,
|
|
||||||
Effect.gen(function*() {
|
|
||||||
const config = yield* TrustGraphMcpConfig
|
|
||||||
const socket = yield* Effect.acquireRelease(
|
|
||||||
Effect.sync(() => createTrustGraphSocket(config.user, config.token, config.gatewayUrl)),
|
|
||||||
(socket) => Effect.sync(() => socket.close()),
|
|
||||||
)
|
|
||||||
return TrustGraphSocket.of(socket)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export class TrustGraphGateway extends Context.Service<TrustGraphGateway, TrustGraphGatewayClient>()(
|
export class TrustGraphGateway extends Context.Service<TrustGraphGateway, TrustGraphGatewayClient>()(
|
||||||
"@trustgraph/mcp/server-effect/TrustGraphGateway",
|
"@trustgraph/mcp/server-effect/TrustGraphGateway",
|
||||||
) {
|
) {
|
||||||
|
|
@ -1360,6 +1344,36 @@ const decodeJsonArrayOrFail = <E>(
|
||||||
const asIriTerm = (value: string | undefined): ClientTerm | undefined =>
|
const asIriTerm = (value: string | undefined): ClientTerm | undefined =>
|
||||||
value !== undefined && value.length > 0 ? {t: "i", i: value} : undefined
|
value !== undefined && value.length > 0 ? {t: "i", i: value} : undefined
|
||||||
|
|
||||||
|
const dispatchGlobal = <E>(
|
||||||
|
gateway: TrustGraphGatewayClient,
|
||||||
|
service: string,
|
||||||
|
request: Record<string, unknown>,
|
||||||
|
makeError: (cause: unknown) => E,
|
||||||
|
options: {readonly timeoutMs?: number; readonly retries?: number} = {},
|
||||||
|
) =>
|
||||||
|
gateway.dispatch({scope: "global", service, request}, options).pipe(
|
||||||
|
Effect.map(asRecord),
|
||||||
|
Effect.mapError(makeError),
|
||||||
|
)
|
||||||
|
|
||||||
|
const dispatchFlow = <E>(
|
||||||
|
gateway: TrustGraphGatewayClient,
|
||||||
|
config: TrustGraphMcpConfigShape,
|
||||||
|
service: string,
|
||||||
|
request: Record<string, unknown>,
|
||||||
|
makeError: (cause: unknown) => E,
|
||||||
|
options: {readonly timeoutMs?: number; readonly retries?: number} = {},
|
||||||
|
) =>
|
||||||
|
gateway.dispatch({
|
||||||
|
scope: "flow",
|
||||||
|
flow: config.flowId,
|
||||||
|
service,
|
||||||
|
request,
|
||||||
|
}, options).pipe(
|
||||||
|
Effect.map(asRecord),
|
||||||
|
Effect.mapError(makeError),
|
||||||
|
)
|
||||||
|
|
||||||
const runAgentTool = Effect.fn("TrustGraphMcpToolkit.agent")(function*(
|
const runAgentTool = Effect.fn("TrustGraphMcpToolkit.agent")(function*(
|
||||||
gateway: TrustGraphGatewayClient,
|
gateway: TrustGraphGatewayClient,
|
||||||
config: TrustGraphMcpConfigShape,
|
config: TrustGraphMcpConfigShape,
|
||||||
|
|
@ -1411,90 +1425,134 @@ const runAgentTool = Effect.fn("TrustGraphMcpToolkit.agent")(function*(
|
||||||
export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
||||||
Effect.gen(function*() {
|
Effect.gen(function*() {
|
||||||
const config = yield* TrustGraphMcpConfig
|
const config = yield* TrustGraphMcpConfig
|
||||||
const socket = yield* TrustGraphSocket
|
|
||||||
const gateway = yield* TrustGraphGateway
|
const gateway = yield* TrustGraphGateway
|
||||||
|
|
||||||
return TrustGraphMcpToolkit.of({
|
return TrustGraphMcpToolkit.of({
|
||||||
text_completion: ({system, prompt}) =>
|
text_completion: ({system, prompt}) =>
|
||||||
Effect.tryPromise({
|
dispatchFlow(
|
||||||
try: () => socket.flow(config.flowId).textCompletion(system, prompt),
|
gateway,
|
||||||
catch: (cause) => TextCompletionError.make({message: toErrorMessage(cause)}),
|
config,
|
||||||
}).pipe(
|
"text-completion",
|
||||||
Effect.map((text) => TextCompletionSuccess.make({text})),
|
{system, prompt},
|
||||||
|
(cause) => TextCompletionError.make({message: toErrorMessage(cause)}),
|
||||||
|
{timeoutMs: 30_000},
|
||||||
|
).pipe(
|
||||||
|
Effect.map((response) => TextCompletionSuccess.make({text: stringProperty(response, "response") ?? ""})),
|
||||||
),
|
),
|
||||||
|
|
||||||
graph_rag: ({query, entity_limit, triple_limit, collection}) =>
|
graph_rag: ({query, entity_limit, triple_limit, collection}) =>
|
||||||
Effect.tryPromise({
|
dispatchFlow(
|
||||||
try: () =>
|
gateway,
|
||||||
socket.flow(config.flowId).graphRag(
|
config,
|
||||||
query,
|
"graph-rag",
|
||||||
{
|
{
|
||||||
...(entity_limit !== undefined ? {entityLimit: entity_limit} : {}),
|
query,
|
||||||
...(triple_limit !== undefined ? {tripleLimit: triple_limit} : {}),
|
user: config.user,
|
||||||
},
|
collection: collection ?? "default",
|
||||||
collection,
|
...(entity_limit !== undefined ? {"entity-limit": entity_limit} : {}),
|
||||||
),
|
...(triple_limit !== undefined ? {"triple-limit": triple_limit} : {}),
|
||||||
catch: (cause) => GraphRagError.make({message: toErrorMessage(cause)}),
|
},
|
||||||
}).pipe(
|
(cause) => GraphRagError.make({message: toErrorMessage(cause)}),
|
||||||
Effect.map((text) => GraphRagSuccess.make({text})),
|
{timeoutMs: 60_000},
|
||||||
|
).pipe(
|
||||||
|
Effect.map((response) => GraphRagSuccess.make({text: stringProperty(response, "response") ?? ""})),
|
||||||
),
|
),
|
||||||
|
|
||||||
document_rag: ({query, doc_limit, collection}) =>
|
document_rag: ({query, doc_limit, collection}) =>
|
||||||
Effect.tryPromise({
|
dispatchFlow(
|
||||||
try: () => socket.flow(config.flowId).documentRag(query, doc_limit, collection),
|
gateway,
|
||||||
catch: (cause) => DocumentRagError.make({message: toErrorMessage(cause)}),
|
config,
|
||||||
}).pipe(
|
"document-rag",
|
||||||
Effect.map((text) => DocumentRagSuccess.make({text})),
|
{
|
||||||
|
query,
|
||||||
|
user: config.user,
|
||||||
|
collection: collection ?? "default",
|
||||||
|
...(doc_limit !== undefined ? {"doc-limit": doc_limit} : {}),
|
||||||
|
},
|
||||||
|
(cause) => DocumentRagError.make({message: toErrorMessage(cause)}),
|
||||||
|
{timeoutMs: 60_000},
|
||||||
|
).pipe(
|
||||||
|
Effect.map((response) => DocumentRagSuccess.make({text: stringProperty(response, "response") ?? ""})),
|
||||||
),
|
),
|
||||||
|
|
||||||
agent: ({question}) => runAgentTool(gateway, config, question),
|
agent: ({question}) => runAgentTool(gateway, config, question),
|
||||||
|
|
||||||
embeddings: ({text}) =>
|
embeddings: ({text}) =>
|
||||||
Effect.tryPromise({
|
dispatchFlow(
|
||||||
try: () => socket.flow(config.flowId).embeddings([...text]),
|
gateway,
|
||||||
catch: (cause) => EmbeddingsError.make({message: toErrorMessage(cause)}),
|
config,
|
||||||
}).pipe(
|
"embeddings",
|
||||||
Effect.map((vectors) => EmbeddingsSuccess.make({vectors})),
|
{texts: [...text]},
|
||||||
|
(cause) => EmbeddingsError.make({message: toErrorMessage(cause)}),
|
||||||
|
{timeoutMs: 30_000},
|
||||||
|
).pipe(
|
||||||
|
Effect.map((response) => EmbeddingsSuccess.make({
|
||||||
|
vectors: Array.isArray(response.vectors) ? response.vectors as number[][] : [],
|
||||||
|
})),
|
||||||
),
|
),
|
||||||
|
|
||||||
triples_query: ({s, p, o, limit, collection}) =>
|
triples_query: ({s, p, o, limit, collection}) =>
|
||||||
Effect.tryPromise({
|
dispatchFlow(
|
||||||
try: () =>
|
gateway,
|
||||||
socket.flow(config.flowId).triplesQuery(
|
config,
|
||||||
asIriTerm(s),
|
"triples",
|
||||||
asIriTerm(p),
|
{
|
||||||
asIriTerm(o),
|
limit: limit ?? 20,
|
||||||
limit,
|
user: config.user,
|
||||||
collection,
|
collection: collection ?? "default",
|
||||||
),
|
...(asIriTerm(s) !== undefined ? {s: asIriTerm(s)} : {}),
|
||||||
catch: (cause) => TriplesQueryError.make({message: toErrorMessage(cause)}),
|
...(asIriTerm(p) !== undefined ? {p: asIriTerm(p)} : {}),
|
||||||
}).pipe(
|
...(asIriTerm(o) !== undefined ? {o: asIriTerm(o)} : {}),
|
||||||
Effect.map((triples) => TriplesQuerySuccess.make({triples})),
|
},
|
||||||
|
(cause) => TriplesQueryError.make({message: toErrorMessage(cause)}),
|
||||||
|
{timeoutMs: 30_000},
|
||||||
|
).pipe(
|
||||||
|
Effect.map((response) => TriplesQuerySuccess.make({
|
||||||
|
triples: Array.isArray(response.triples ?? response.response)
|
||||||
|
? (response.triples ?? response.response) as ClientTriple[]
|
||||||
|
: [],
|
||||||
|
})),
|
||||||
),
|
),
|
||||||
|
|
||||||
graph_embeddings_query: ({query, limit, collection}) =>
|
graph_embeddings_query: ({query, limit, collection}) =>
|
||||||
Effect.tryPromise({
|
dispatchFlow(
|
||||||
try: () => socket.flow(config.flowId).embeddings([query]),
|
gateway,
|
||||||
catch: (cause) => GraphEmbeddingsQueryError.make({message: toErrorMessage(cause)}),
|
config,
|
||||||
}).pipe(
|
"embeddings",
|
||||||
Effect.flatMap((vectors) =>
|
{texts: [query]},
|
||||||
Effect.tryPromise({
|
(cause) => GraphEmbeddingsQueryError.make({message: toErrorMessage(cause)}),
|
||||||
try: () => socket.flow(config.flowId).graphEmbeddingsQuery(
|
{timeoutMs: 30_000},
|
||||||
vectors[0] ?? [],
|
).pipe(
|
||||||
limit ?? 10,
|
Effect.flatMap((embeddingResponse) => {
|
||||||
collection,
|
const vectors = Array.isArray(embeddingResponse.vectors) ? embeddingResponse.vectors : []
|
||||||
),
|
const firstVector = Array.isArray(vectors[0]) ? vectors[0] as number[] : []
|
||||||
catch: (cause) => GraphEmbeddingsQueryError.make({message: toErrorMessage(cause)}),
|
return dispatchFlow(
|
||||||
})
|
gateway,
|
||||||
),
|
config,
|
||||||
Effect.map((entities) => GraphEmbeddingsQuerySuccess.make({entities})),
|
"graph-embeddings",
|
||||||
|
{
|
||||||
|
vector: firstVector,
|
||||||
|
limit: limit ?? 10,
|
||||||
|
user: config.user,
|
||||||
|
collection: collection ?? "default",
|
||||||
|
},
|
||||||
|
(cause) => GraphEmbeddingsQueryError.make({message: toErrorMessage(cause)}),
|
||||||
|
{timeoutMs: 30_000},
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
Effect.map((response) => GraphEmbeddingsQuerySuccess.make({
|
||||||
|
entities: Array.isArray(response.entities) ? response.entities as ClientEntityMatch[] : [],
|
||||||
|
})),
|
||||||
),
|
),
|
||||||
|
|
||||||
get_config_all: () =>
|
get_config_all: () =>
|
||||||
Effect.tryPromise({
|
dispatchGlobal(
|
||||||
try: () => socket.config().getConfigAll(),
|
gateway,
|
||||||
catch: (cause) => GetConfigAllError.make({message: toErrorMessage(cause)}),
|
"config",
|
||||||
}).pipe(
|
{operation: "config"},
|
||||||
|
(cause) => GetConfigAllError.make({message: toErrorMessage(cause)}),
|
||||||
|
{timeoutMs: 60_000},
|
||||||
|
).pipe(
|
||||||
Effect.flatMap((value) =>
|
Effect.flatMap((value) =>
|
||||||
decodeJsonOrFail(
|
decodeJsonOrFail(
|
||||||
value,
|
value,
|
||||||
|
|
@ -1506,10 +1564,13 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
||||||
),
|
),
|
||||||
|
|
||||||
get_config: ({keys}) =>
|
get_config: ({keys}) =>
|
||||||
Effect.tryPromise({
|
dispatchGlobal(
|
||||||
try: () => socket.config().getConfig(keys.map(({type, key}) => ({type, key}))),
|
gateway,
|
||||||
catch: (cause) => GetConfigError.make({message: toErrorMessage(cause)}),
|
"config",
|
||||||
}).pipe(
|
{operation: "get", keys: keys.map(({type, key}) => ({type, key}))},
|
||||||
|
(cause) => GetConfigError.make({message: toErrorMessage(cause)}),
|
||||||
|
{timeoutMs: 60_000},
|
||||||
|
).pipe(
|
||||||
Effect.flatMap((value) =>
|
Effect.flatMap((value) =>
|
||||||
decodeJsonOrFail(
|
decodeJsonOrFail(
|
||||||
value,
|
value,
|
||||||
|
|
@ -1521,10 +1582,13 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
||||||
),
|
),
|
||||||
|
|
||||||
put_config: ({values}) =>
|
put_config: ({values}) =>
|
||||||
Effect.tryPromise({
|
dispatchGlobal(
|
||||||
try: () => socket.config().putConfig(values.map(({type, key, value}) => ({type, key, value}))),
|
gateway,
|
||||||
catch: (cause) => PutConfigError.make({message: toErrorMessage(cause)}),
|
"config",
|
||||||
}).pipe(
|
{operation: "put", values: values.map(({type, key, value}) => ({type, key, value}))},
|
||||||
|
(cause) => PutConfigError.make({message: toErrorMessage(cause)}),
|
||||||
|
{timeoutMs: 60_000},
|
||||||
|
).pipe(
|
||||||
Effect.flatMap((value) =>
|
Effect.flatMap((value) =>
|
||||||
decodeJsonOrFail(
|
decodeJsonOrFail(
|
||||||
value,
|
value,
|
||||||
|
|
@ -1536,10 +1600,13 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
||||||
),
|
),
|
||||||
|
|
||||||
delete_config: ({type, key}) =>
|
delete_config: ({type, key}) =>
|
||||||
Effect.tryPromise({
|
dispatchGlobal(
|
||||||
try: () => socket.config().deleteConfig({type, key}),
|
gateway,
|
||||||
catch: (cause) => DeleteConfigError.make({message: toErrorMessage(cause)}),
|
"config",
|
||||||
}).pipe(
|
{operation: "delete", keys: [{type, key}]},
|
||||||
|
(cause) => DeleteConfigError.make({message: toErrorMessage(cause)}),
|
||||||
|
{timeoutMs: 30_000},
|
||||||
|
).pipe(
|
||||||
Effect.flatMap((value) =>
|
Effect.flatMap((value) =>
|
||||||
decodeJsonOrFail(
|
decodeJsonOrFail(
|
||||||
value,
|
value,
|
||||||
|
|
@ -1551,21 +1618,29 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
||||||
),
|
),
|
||||||
|
|
||||||
get_flows: () =>
|
get_flows: () =>
|
||||||
Effect.tryPromise({
|
dispatchGlobal(
|
||||||
try: () => socket.flows().getFlows(),
|
gateway,
|
||||||
catch: (cause) => GetFlowsError.make({message: toErrorMessage(cause)}),
|
"flow",
|
||||||
}).pipe(
|
{operation: "list-flows"},
|
||||||
Effect.map((flow_ids) => GetFlowsSuccess.make({flow_ids})),
|
(cause) => GetFlowsError.make({message: toErrorMessage(cause)}),
|
||||||
|
{timeoutMs: 60_000},
|
||||||
|
).pipe(
|
||||||
|
Effect.map((response) => GetFlowsSuccess.make({
|
||||||
|
flow_ids: Array.isArray(response["flow-ids"]) ? response["flow-ids"] as string[] : [],
|
||||||
|
})),
|
||||||
),
|
),
|
||||||
|
|
||||||
get_flow: ({flow_id}) =>
|
get_flow: ({flow_id}) =>
|
||||||
Effect.tryPromise({
|
dispatchGlobal(
|
||||||
try: () => socket.flows().getFlow(flow_id),
|
gateway,
|
||||||
catch: (cause) => GetFlowError.make({message: toErrorMessage(cause)}),
|
"flow",
|
||||||
}).pipe(
|
{operation: "get-flow", "flow-id": flow_id},
|
||||||
Effect.flatMap((value) =>
|
(cause) => GetFlowError.make({message: toErrorMessage(cause)}),
|
||||||
|
{timeoutMs: 60_000},
|
||||||
|
).pipe(
|
||||||
|
Effect.flatMap((response) =>
|
||||||
decodeJsonOrFail(
|
decodeJsonOrFail(
|
||||||
value,
|
response.flow,
|
||||||
(cause) => GetFlowError.make({message: toErrorMessage(cause)}),
|
(cause) => GetFlowError.make({message: toErrorMessage(cause)}),
|
||||||
).pipe(
|
).pipe(
|
||||||
Effect.map((flow) => GetFlowSuccess.make({flow})),
|
Effect.map((flow) => GetFlowSuccess.make({flow})),
|
||||||
|
|
@ -1574,16 +1649,19 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
||||||
),
|
),
|
||||||
|
|
||||||
start_flow: ({flow_id, blueprint_name, description, parameters}) =>
|
start_flow: ({flow_id, blueprint_name, description, parameters}) =>
|
||||||
Effect.tryPromise({
|
dispatchGlobal(
|
||||||
try: () =>
|
gateway,
|
||||||
socket.flows().startFlow(
|
"flow",
|
||||||
flow_id,
|
{
|
||||||
blueprint_name,
|
operation: "start-flow",
|
||||||
description,
|
"flow-id": flow_id,
|
||||||
parameters === undefined ? undefined : {...parameters},
|
"blueprint-name": blueprint_name,
|
||||||
),
|
description,
|
||||||
catch: (cause) => StartFlowError.make({message: toErrorMessage(cause)}),
|
...(parameters === undefined ? {} : {parameters: {...parameters}}),
|
||||||
}).pipe(
|
},
|
||||||
|
(cause) => StartFlowError.make({message: toErrorMessage(cause)}),
|
||||||
|
{timeoutMs: 30_000},
|
||||||
|
).pipe(
|
||||||
Effect.flatMap((value) =>
|
Effect.flatMap((value) =>
|
||||||
decodeJsonOrFail(
|
decodeJsonOrFail(
|
||||||
value,
|
value,
|
||||||
|
|
@ -1595,10 +1673,13 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
||||||
),
|
),
|
||||||
|
|
||||||
stop_flow: ({flow_id}) =>
|
stop_flow: ({flow_id}) =>
|
||||||
Effect.tryPromise({
|
dispatchGlobal(
|
||||||
try: () => socket.flows().stopFlow(flow_id),
|
gateway,
|
||||||
catch: (cause) => StopFlowError.make({message: toErrorMessage(cause)}),
|
"flow",
|
||||||
}).pipe(
|
{operation: "stop-flow", "flow-id": flow_id},
|
||||||
|
(cause) => StopFlowError.make({message: toErrorMessage(cause)}),
|
||||||
|
{timeoutMs: 30_000},
|
||||||
|
).pipe(
|
||||||
Effect.flatMap((value) =>
|
Effect.flatMap((value) =>
|
||||||
decodeJsonOrFail(
|
decodeJsonOrFail(
|
||||||
value,
|
value,
|
||||||
|
|
@ -1610,13 +1691,16 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
||||||
),
|
),
|
||||||
|
|
||||||
get_documents: () =>
|
get_documents: () =>
|
||||||
Effect.tryPromise({
|
dispatchGlobal(
|
||||||
try: () => socket.librarian().getDocuments(),
|
gateway,
|
||||||
catch: (cause) => GetDocumentsError.make({message: toErrorMessage(cause)}),
|
"librarian",
|
||||||
}).pipe(
|
{operation: "list-documents", user: config.user},
|
||||||
|
(cause) => GetDocumentsError.make({message: toErrorMessage(cause)}),
|
||||||
|
{timeoutMs: 60_000},
|
||||||
|
).pipe(
|
||||||
Effect.flatMap((value) =>
|
Effect.flatMap((value) =>
|
||||||
decodeJsonArrayOrFail(
|
decodeJsonArrayOrFail(
|
||||||
value,
|
value["document-metadatas"] ?? value.documents ?? [],
|
||||||
(cause) => GetDocumentsError.make({message: toErrorMessage(cause)}),
|
(cause) => GetDocumentsError.make({message: toErrorMessage(cause)}),
|
||||||
).pipe(
|
).pipe(
|
||||||
Effect.map((documents) => GetDocumentsSuccess.make({documents})),
|
Effect.map((documents) => GetDocumentsSuccess.make({documents})),
|
||||||
|
|
@ -1624,34 +1708,53 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
load_document: ({document, mime_type, title, comments, tags, id}) =>
|
load_document: Effect.fn("TrustGraphMcpToolkit.load_document")(function*({document, mime_type, title, comments, tags, id}) {
|
||||||
Effect.tryPromise({
|
const timestamp = yield* Clock.currentTimeMillis
|
||||||
try: () =>
|
const metadata = {
|
||||||
socket.librarian().loadDocument(
|
time: Math.floor(timestamp / 1000),
|
||||||
document,
|
kind: mime_type,
|
||||||
mime_type,
|
title,
|
||||||
title,
|
comments: comments ?? "",
|
||||||
comments ?? "",
|
user: config.user,
|
||||||
tags === undefined ? [] : [...tags],
|
tags: tags === undefined ? [] : [...tags],
|
||||||
id,
|
"document-type": "source",
|
||||||
),
|
documentType: "source",
|
||||||
catch: (cause) => LoadDocumentError.make({message: toErrorMessage(cause)}),
|
...(id === undefined ? {} : {id}),
|
||||||
}).pipe(
|
}
|
||||||
Effect.flatMap((value) =>
|
const value = yield* dispatchGlobal(
|
||||||
decodeJsonOrFail(
|
gateway,
|
||||||
value,
|
"librarian",
|
||||||
(cause) => LoadDocumentError.make({message: toErrorMessage(cause)}),
|
{
|
||||||
).pipe(
|
operation: "add-document",
|
||||||
Effect.map((response) => LoadDocumentSuccess.make({response})),
|
"document-metadata": metadata,
|
||||||
)
|
documentMetadata: metadata,
|
||||||
),
|
content: document,
|
||||||
),
|
},
|
||||||
|
(cause) => LoadDocumentError.make({message: toErrorMessage(cause)}),
|
||||||
|
{timeoutMs: 30_000},
|
||||||
|
)
|
||||||
|
return yield* decodeJsonOrFail(
|
||||||
|
value,
|
||||||
|
(cause) => LoadDocumentError.make({message: toErrorMessage(cause)}),
|
||||||
|
).pipe(
|
||||||
|
Effect.map((response) => LoadDocumentSuccess.make({response})),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
remove_document: ({id, collection}) =>
|
remove_document: ({id, collection}) =>
|
||||||
Effect.tryPromise({
|
dispatchGlobal(
|
||||||
try: () => socket.librarian().removeDocument(id, collection),
|
gateway,
|
||||||
catch: (cause) => RemoveDocumentError.make({message: toErrorMessage(cause)}),
|
"librarian",
|
||||||
}).pipe(
|
{
|
||||||
|
operation: "remove-document",
|
||||||
|
"document-id": id,
|
||||||
|
documentId: id,
|
||||||
|
user: config.user,
|
||||||
|
collection: collection ?? "default",
|
||||||
|
},
|
||||||
|
(cause) => RemoveDocumentError.make({message: toErrorMessage(cause)}),
|
||||||
|
{timeoutMs: 30_000},
|
||||||
|
).pipe(
|
||||||
Effect.flatMap((value) =>
|
Effect.flatMap((value) =>
|
||||||
decodeJsonOrFail(
|
decodeJsonOrFail(
|
||||||
value,
|
value,
|
||||||
|
|
@ -1663,21 +1766,34 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
||||||
),
|
),
|
||||||
|
|
||||||
get_prompts: () =>
|
get_prompts: () =>
|
||||||
Effect.tryPromise({
|
dispatchGlobal(
|
||||||
try: () => socket.config().getPrompts(),
|
gateway,
|
||||||
catch: (cause) => GetPromptsError.make({message: toErrorMessage(cause)}),
|
"config",
|
||||||
}).pipe(
|
{operation: "config"},
|
||||||
Effect.map((prompts) => GetPromptsSuccess.make({prompts})),
|
(cause) => GetPromptsError.make({message: toErrorMessage(cause)}),
|
||||||
|
{timeoutMs: 60_000},
|
||||||
|
).pipe(
|
||||||
|
Effect.map((response) => {
|
||||||
|
const promptNs = asRecord(asRecord(response.config).prompt)
|
||||||
|
const prompts = Object.keys(promptNs)
|
||||||
|
.filter((key) => key !== "system")
|
||||||
|
.sort()
|
||||||
|
.map((id) => ({id, name: id}))
|
||||||
|
return GetPromptsSuccess.make({prompts})
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
|
|
||||||
get_prompt: ({id}) =>
|
get_prompt: ({id}) =>
|
||||||
Effect.tryPromise({
|
dispatchGlobal(
|
||||||
try: () => socket.config().getPrompt(id),
|
gateway,
|
||||||
catch: (cause) => GetPromptError.make({message: toErrorMessage(cause)}),
|
"config",
|
||||||
}).pipe(
|
{operation: "config"},
|
||||||
Effect.flatMap((value) =>
|
(cause) => GetPromptError.make({message: toErrorMessage(cause)}),
|
||||||
|
{timeoutMs: 60_000},
|
||||||
|
).pipe(
|
||||||
|
Effect.flatMap((response) =>
|
||||||
decodeJsonOrFail(
|
decodeJsonOrFail(
|
||||||
value,
|
asRecord(asRecord(response.config).prompt)[id] ?? null,
|
||||||
(cause) => GetPromptError.make({message: toErrorMessage(cause)}),
|
(cause) => GetPromptError.make({message: toErrorMessage(cause)}),
|
||||||
).pipe(
|
).pipe(
|
||||||
Effect.map((prompt) => GetPromptSuccess.make({prompt})),
|
Effect.map((prompt) => GetPromptSuccess.make({prompt})),
|
||||||
|
|
@ -1686,18 +1802,31 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
||||||
),
|
),
|
||||||
|
|
||||||
get_knowledge_cores: () =>
|
get_knowledge_cores: () =>
|
||||||
Effect.tryPromise({
|
dispatchGlobal(
|
||||||
try: () => socket.knowledge().getKnowledgeCores(),
|
gateway,
|
||||||
catch: (cause) => GetKnowledgeCoresError.make({message: toErrorMessage(cause)}),
|
"knowledge",
|
||||||
}).pipe(
|
{operation: "list-kg-cores", user: config.user},
|
||||||
Effect.map((ids) => GetKnowledgeCoresSuccess.make({ids})),
|
(cause) => GetKnowledgeCoresError.make({message: toErrorMessage(cause)}),
|
||||||
|
{timeoutMs: 60_000},
|
||||||
|
).pipe(
|
||||||
|
Effect.map((response) => GetKnowledgeCoresSuccess.make({
|
||||||
|
ids: Array.isArray(response.ids) ? response.ids as string[] : [],
|
||||||
|
})),
|
||||||
),
|
),
|
||||||
|
|
||||||
delete_kg_core: ({id, collection}) =>
|
delete_kg_core: ({id, collection}) =>
|
||||||
Effect.tryPromise({
|
dispatchGlobal(
|
||||||
try: () => socket.knowledge().deleteKgCore(id, collection),
|
gateway,
|
||||||
catch: (cause) => DeleteKgCoreError.make({message: toErrorMessage(cause)}),
|
"knowledge",
|
||||||
}).pipe(
|
{
|
||||||
|
operation: "delete-kg-core",
|
||||||
|
id,
|
||||||
|
user: config.user,
|
||||||
|
collection: collection ?? "default",
|
||||||
|
},
|
||||||
|
(cause) => DeleteKgCoreError.make({message: toErrorMessage(cause)}),
|
||||||
|
{timeoutMs: 30_000},
|
||||||
|
).pipe(
|
||||||
Effect.flatMap((value) =>
|
Effect.flatMap((value) =>
|
||||||
decodeJsonOrFail(
|
decodeJsonOrFail(
|
||||||
value,
|
value,
|
||||||
|
|
@ -1709,10 +1838,19 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
||||||
),
|
),
|
||||||
|
|
||||||
load_kg_core: ({id, flow, collection}) =>
|
load_kg_core: ({id, flow, collection}) =>
|
||||||
Effect.tryPromise({
|
dispatchGlobal(
|
||||||
try: () => socket.knowledge().loadKgCore(id, flow, collection),
|
gateway,
|
||||||
catch: (cause) => LoadKgCoreError.make({message: toErrorMessage(cause)}),
|
"knowledge",
|
||||||
}).pipe(
|
{
|
||||||
|
operation: "load-kg-core",
|
||||||
|
id,
|
||||||
|
flow,
|
||||||
|
user: config.user,
|
||||||
|
collection: collection ?? "default",
|
||||||
|
},
|
||||||
|
(cause) => LoadKgCoreError.make({message: toErrorMessage(cause)}),
|
||||||
|
{timeoutMs: 30_000},
|
||||||
|
).pipe(
|
||||||
Effect.flatMap((value) =>
|
Effect.flatMap((value) =>
|
||||||
decodeJsonOrFail(
|
decodeJsonOrFail(
|
||||||
value,
|
value,
|
||||||
|
|
@ -1773,7 +1911,6 @@ const makeTrustGraphMcpHttpLayerFromConfig = (
|
||||||
path: config.mcpPath,
|
path: config.mcpPath,
|
||||||
})),
|
})),
|
||||||
Layer.provide(TrustGraphGateway.layer),
|
Layer.provide(TrustGraphGateway.layer),
|
||||||
Layer.provide(TrustGraphSocket.layer),
|
|
||||||
Layer.provide(Layer.succeed(TrustGraphMcpConfig, TrustGraphMcpConfig.of(config))),
|
Layer.provide(Layer.succeed(TrustGraphMcpConfig, TrustGraphMcpConfig.of(config))),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -1793,7 +1930,6 @@ const makeTrustGraphMcpStdioLayerFromConfig = (
|
||||||
})),
|
})),
|
||||||
Layer.provide(NodeStdio.layer),
|
Layer.provide(NodeStdio.layer),
|
||||||
Layer.provide(TrustGraphGateway.layer),
|
Layer.provide(TrustGraphGateway.layer),
|
||||||
Layer.provide(TrustGraphSocket.layer),
|
|
||||||
Layer.provide(Layer.succeed(TrustGraphMcpConfig, TrustGraphMcpConfig.of(config))),
|
Layer.provide(Layer.succeed(TrustGraphMcpConfig, TrustGraphMcpConfig.of(config))),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import * as BrowserHttpClient from "@effect/platform-browser/BrowserHttpClient";
|
||||||
import * as BrowserKeyValueStore from "@effect/platform-browser/BrowserKeyValueStore";
|
import * as BrowserKeyValueStore from "@effect/platform-browser/BrowserKeyValueStore";
|
||||||
import type {
|
import type {
|
||||||
GraphRagOptions,
|
GraphRagOptions,
|
||||||
BaseApi,
|
|
||||||
BeginUploadResponse,
|
BeginUploadResponse,
|
||||||
ChunkedUploadDocumentMetadata,
|
ChunkedUploadDocumentMetadata,
|
||||||
CompleteUploadResponse,
|
CompleteUploadResponse,
|
||||||
|
|
@ -18,7 +17,6 @@ import type {
|
||||||
import {
|
import {
|
||||||
DispatchPayload,
|
DispatchPayload,
|
||||||
GatewayWorkbenchHttpApi,
|
GatewayWorkbenchHttpApi,
|
||||||
makeBaseApi,
|
|
||||||
TrustGraphRpcs,
|
TrustGraphRpcs,
|
||||||
} from "@trustgraph/client";
|
} from "@trustgraph/client";
|
||||||
import type { Scope, } from "effect";
|
import type { Scope, } from "effect";
|
||||||
|
|
@ -204,10 +202,6 @@ export class Settings extends S.Class<Settings>("Settings")({
|
||||||
featureSwitches: FeatureSwitches,
|
featureSwitches: FeatureSwitches,
|
||||||
}, { description: "Persisted workbench connection and display settings." }) {}
|
}, { description: "Persisted workbench connection and display settings." }) {}
|
||||||
|
|
||||||
export interface WorkbenchApiFactory {
|
|
||||||
readonly create: (settings: Settings) => BaseApi;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Theme = "dark" | "light";
|
export type Theme = "dark" | "light";
|
||||||
|
|
||||||
export type ChatMode = "graph-rag" | "document-rag" | "agent";
|
export type ChatMode = "graph-rag" | "document-rag" | "agent";
|
||||||
|
|
@ -764,7 +758,11 @@ function explainTriplesFrom(source: unknown): Triple[] | undefined {
|
||||||
}
|
}
|
||||||
|
|
||||||
function streamingMetadataFrom(source: unknown): StreamingMetadata | undefined {
|
function streamingMetadataFrom(source: unknown): StreamingMetadata | undefined {
|
||||||
const metadata: StreamingMetadata = {};
|
const metadata: {
|
||||||
|
in_token?: number;
|
||||||
|
out_token?: number;
|
||||||
|
model?: string;
|
||||||
|
} = {};
|
||||||
let hasMetadata = false;
|
let hasMetadata = false;
|
||||||
|
|
||||||
const inToken = numberProperty(source, "in_token");
|
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}` }));
|
: 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;
|
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) {
|
function makeWorkbenchGatewayApi(settings: Settings) {
|
||||||
|
|
@ -1016,9 +1014,9 @@ function makeWorkbenchGatewayApi(settings: Settings) {
|
||||||
tags,
|
tags,
|
||||||
"document-type": "source",
|
"document-type": "source",
|
||||||
documentType: "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", {
|
return yield* dispatch("librarian", {
|
||||||
operation: "add-document",
|
operation: "add-document",
|
||||||
"document-metadata": documentMetadata,
|
"document-metadata": documentMetadata,
|
||||||
|
|
@ -1184,10 +1182,8 @@ function makeWorkbenchGatewayApi(settings: Settings) {
|
||||||
const event: ExplainEvent = {
|
const event: ExplainEvent = {
|
||||||
explainId: explainId ?? "",
|
explainId: explainId ?? "",
|
||||||
explainGraph: stringProperty(resp, "explain_graph") ?? "",
|
explainGraph: stringProperty(resp, "explain_graph") ?? "",
|
||||||
|
...(explainTriples !== undefined ? { explainTriples } : {}),
|
||||||
};
|
};
|
||||||
if (explainTriples !== undefined) {
|
|
||||||
event.explainTriples = explainTriples;
|
|
||||||
}
|
|
||||||
onExplain?.(event);
|
onExplain?.(event);
|
||||||
if (
|
if (
|
||||||
stringProperty(resp, "response") === undefined &&
|
stringProperty(resp, "response") === undefined &&
|
||||||
|
|
@ -1305,10 +1301,8 @@ function makeWorkbenchGatewayApi(settings: Settings) {
|
||||||
const event: ExplainEvent = {
|
const event: ExplainEvent = {
|
||||||
explainId: explainId ?? "",
|
explainId: explainId ?? "",
|
||||||
explainGraph: stringProperty(resp, "explain_graph") ?? "",
|
explainGraph: stringProperty(resp, "explain_graph") ?? "",
|
||||||
|
...(explainTriples !== undefined ? { explainTriples } : {}),
|
||||||
};
|
};
|
||||||
if (explainTriples !== undefined) {
|
|
||||||
event.explainTriples = explainTriples;
|
|
||||||
}
|
|
||||||
onExplain?.(event);
|
onExplain?.(event);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -1621,34 +1615,14 @@ export const toggleThemeAtom = Atom.writable(
|
||||||
// Socket lifecycle
|
// 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) => {
|
export const connectionStateAtom = Atom.make((get) => {
|
||||||
const api = get(apiAtom);
|
const settings = get(settingsAtom);
|
||||||
const fallback: ConnectionState = {
|
const hasApiKey = settings.apiKey.length > 0;
|
||||||
status: "connecting",
|
const state: ConnectionState = {
|
||||||
hasApiKey: get(settingsAtom).apiKey.length > 0,
|
status: hasApiKey ? "authenticated" : "unauthenticated",
|
||||||
|
hasApiKey,
|
||||||
};
|
};
|
||||||
const previous = Option.getOrElse(get.self<ConnectionState>(), () => fallback);
|
return state;
|
||||||
const unsubscribe = api.onConnectionStateChange((state) => get.setSelf(state));
|
|
||||||
get.addFinalizer(unsubscribe);
|
|
||||||
return previous;
|
|
||||||
}).pipe(Atom.keepAlive);
|
}).pipe(Atom.keepAlive);
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,8 @@ import type * as Atom from "effect/unstable/reactivity/Atom";
|
||||||
import type {
|
import type {
|
||||||
FeatureSwitches,
|
FeatureSwitches,
|
||||||
Settings,
|
Settings,
|
||||||
WorkbenchApiFactory,
|
|
||||||
} from "@/atoms/workbench";
|
} from "@/atoms/workbench";
|
||||||
import {
|
import {
|
||||||
apiFactoryAtom,
|
|
||||||
DEFAULT_SETTINGS,
|
DEFAULT_SETTINGS,
|
||||||
flowIdAtom,
|
flowIdAtom,
|
||||||
settingsAtom,
|
settingsAtom,
|
||||||
|
|
@ -45,12 +43,8 @@ export function getWorkbenchQaInitialValues(): Iterable<readonly [Atom.Atom<unkn
|
||||||
if (config?.enabled !== true) return undefined;
|
if (config?.enabled !== true) return undefined;
|
||||||
const fixture = config.fixture ?? {};
|
const fixture = config.fixture ?? {};
|
||||||
const api = makeMockBaseApi(fixture);
|
const api = makeMockBaseApi(fixture);
|
||||||
const apiFactory: WorkbenchApiFactory = {
|
|
||||||
create: () => api,
|
|
||||||
};
|
|
||||||
window.__TRUSTGRAPH_WORKBENCH_QA_API__ = api;
|
window.__TRUSTGRAPH_WORKBENCH_QA_API__ = api;
|
||||||
return [
|
return [
|
||||||
[apiFactoryAtom as Atom.Atom<unknown>, apiFactory],
|
|
||||||
[settingsAtom as Atom.Atom<unknown>, qaSettings(fixture)],
|
[settingsAtom as Atom.Atom<unknown>, qaSettings(fixture)],
|
||||||
[flowIdAtom as Atom.Atom<unknown>, config.flowId ?? "default"],
|
[flowIdAtom as Atom.Atom<unknown>, config.flowId ?? "default"],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
{"exemptions":[],"baseline":[{"rule":"no-effect-run","path":"packages/client/src/socket/effect-rpc-client.ts","count":10},{"rule":"no-effect-run","path":"packages/client/src/socket/trustgraph-socket.ts","count":9},{"rule":"no-error-throw","path":"packages/workbench/src/main.tsx","count":1},{"rule":"no-error-throw","path":"scripts/seed-config.ts","count":1},{"rule":"no-error-throw","path":"scripts/seed-demo.ts","count":4},{"rule":"no-error-throw","path":"scripts/seed-flows.ts","count":2},{"rule":"no-error-throw","path":"scripts/test-pipeline.ts","count":2},{"rule":"no-native-fetch","path":"scripts/seed-config.ts","count":1},{"rule":"no-native-fetch","path":"scripts/seed-demo.ts","count":11},{"rule":"no-native-fetch","path":"scripts/seed-flows.ts","count":1},{"rule":"no-native-fetch","path":"scripts/test-pipeline.ts","count":5},{"rule":"no-native-json","path":"scripts/seed-config.ts","count":6},{"rule":"no-native-json","path":"scripts/seed-demo.ts","count":6},{"rule":"no-native-json","path":"scripts/seed-flows.ts","count":3},{"rule":"no-native-json","path":"scripts/test-pipeline.ts","count":6},{"rule":"no-native-sort","path":"packages/client/src/socket/trustgraph-socket.ts","count":2},{"rule":"no-native-sort","path":"packages/flow/src/config/service.ts","count":3},{"rule":"no-native-sort","path":"packages/flow/src/cores/service.ts","count":1},{"rule":"no-native-sort","path":"packages/flow/src/flow-manager/service.ts","count":1},{"rule":"no-native-sort","path":"packages/flow/src/librarian/service.ts","count":1},{"rule":"no-native-sort","path":"packages/flow/src/retrieval/graph-rag.ts","count":1},{"rule":"no-native-sort","path":"packages/workbench/src/atoms/workbench.ts","count":2},{"rule":"no-native-sort","path":"packages/workbench/src/components/chat/explain-graph.tsx","count":1},{"rule":"no-native-sort","path":"packages/workbench/src/pages/graph.tsx","count":1},{"rule":"no-native-sort","path":"packages/workbench/src/qa/mock-api.ts","count":1},{"rule":"no-native-sort","path":"scripts/inventory-native-classes.ts","count":1},{"rule":"no-native-sort","path":"scripts/seed-demo.ts","count":1},{"rule":"no-native-sort","path":"scripts/seed-flows.ts","count":1},{"rule":"no-native-timers","path":"scripts/test-pipeline.ts","count":2},{"rule":"no-node-fs-path","path":"scripts/create-test-pdf.ts","count":1},{"rule":"no-node-fs-path","path":"scripts/inventory-native-classes.ts","count":2},{"rule":"no-process-env","path":"scripts/seed-config.ts","count":2},{"rule":"no-process-env","path":"scripts/seed-demo.ts","count":5},{"rule":"no-process-env","path":"scripts/seed-flows.ts","count":1},{"rule":"no-process-env","path":"scripts/test-pipeline.ts","count":11},{"rule":"no-schema-suffix","path":"packages/base/src/schema/primitives.ts","count":1},{"rule":"schema-first-data","path":"packages/client/src/models/Triple.ts","count":6},{"rule":"schema-first-data","path":"packages/client/src/models/messages.ts","count":58},{"rule":"schema-first-data","path":"packages/client/src/socket/effect-rpc-client.ts","count":2},{"rule":"schema-first-data","path":"packages/client/src/socket/trustgraph-socket.ts","count":4}]}
|
{"exemptions":[],"baseline":[{"rule":"no-error-throw","path":"packages/workbench/src/main.tsx","count":1},{"rule":"no-error-throw","path":"scripts/seed-config.ts","count":1},{"rule":"no-error-throw","path":"scripts/seed-demo.ts","count":4},{"rule":"no-error-throw","path":"scripts/seed-flows.ts","count":2},{"rule":"no-error-throw","path":"scripts/test-pipeline.ts","count":2},{"rule":"no-native-fetch","path":"scripts/seed-config.ts","count":1},{"rule":"no-native-fetch","path":"scripts/seed-demo.ts","count":11},{"rule":"no-native-fetch","path":"scripts/seed-flows.ts","count":1},{"rule":"no-native-fetch","path":"scripts/test-pipeline.ts","count":5},{"rule":"no-native-json","path":"scripts/seed-config.ts","count":6},{"rule":"no-native-json","path":"scripts/seed-demo.ts","count":6},{"rule":"no-native-json","path":"scripts/seed-flows.ts","count":3},{"rule":"no-native-json","path":"scripts/test-pipeline.ts","count":6},{"rule":"no-native-sort","path":"packages/client/src/socket/trustgraph-socket.ts","count":2},{"rule":"no-native-sort","path":"packages/flow/src/config/service.ts","count":3},{"rule":"no-native-sort","path":"packages/flow/src/cores/service.ts","count":1},{"rule":"no-native-sort","path":"packages/flow/src/flow-manager/service.ts","count":1},{"rule":"no-native-sort","path":"packages/flow/src/librarian/service.ts","count":1},{"rule":"no-native-sort","path":"packages/flow/src/retrieval/graph-rag.ts","count":1},{"rule":"no-native-sort","path":"packages/mcp/src/server-effect.ts","count":1},{"rule":"no-native-sort","path":"packages/workbench/src/atoms/workbench.ts","count":2},{"rule":"no-native-sort","path":"packages/workbench/src/components/chat/explain-graph.tsx","count":1},{"rule":"no-native-sort","path":"packages/workbench/src/pages/graph.tsx","count":1},{"rule":"no-native-sort","path":"packages/workbench/src/qa/mock-api.ts","count":1},{"rule":"no-native-sort","path":"scripts/inventory-native-classes.ts","count":1},{"rule":"no-native-sort","path":"scripts/seed-demo.ts","count":1},{"rule":"no-native-sort","path":"scripts/seed-flows.ts","count":1},{"rule":"no-native-timers","path":"scripts/test-pipeline.ts","count":2},{"rule":"no-node-fs-path","path":"scripts/create-test-pdf.ts","count":1},{"rule":"no-node-fs-path","path":"scripts/inventory-native-classes.ts","count":2},{"rule":"no-process-env","path":"scripts/seed-config.ts","count":2},{"rule":"no-process-env","path":"scripts/seed-demo.ts","count":5},{"rule":"no-process-env","path":"scripts/seed-flows.ts","count":1},{"rule":"no-process-env","path":"scripts/test-pipeline.ts","count":11},{"rule":"no-schema-suffix","path":"packages/base/src/schema/primitives.ts","count":1}]}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue