mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-07-01 17:39:39 +02:00
refactor(ts): make port effect native
This commit is contained in:
parent
2868ced2d3
commit
b6759e75df
113 changed files with 4140 additions and 4554 deletions
|
|
@ -4,21 +4,19 @@
|
|||
* Python reference: trustgraph-cli/trustgraph/cli/invoke_agent.py
|
||||
*/
|
||||
|
||||
import type { Command } from "commander";
|
||||
import { Effect } from "effect";
|
||||
import * as Argument from "effect/unstable/cli/Argument";
|
||||
import * as Command from "effect/unstable/cli/Command";
|
||||
import { cliCommandError, withSocket } from "./util.js";
|
||||
|
||||
export function registerAgentCommands(program: Command): void {
|
||||
program
|
||||
.command("agent")
|
||||
.description("Ask the TrustGraph agent a question")
|
||||
.argument("<question>", "Question to ask")
|
||||
.action((question: string, _opts, cmd) =>
|
||||
Effect.runPromise(withSocket(cmd, (socket, opts) =>
|
||||
Effect.gen(function* () {
|
||||
export const agentCommand = Command.make("agent", {
|
||||
question: Argument.string("question").pipe(Argument.withDescription("Question to ask")),
|
||||
}, ({ question }) =>
|
||||
withSocket((socket, opts) =>
|
||||
Effect.gen(function* () {
|
||||
const flow = socket.flow(opts.flow);
|
||||
|
||||
yield* Effect.callback<void, ReturnType<typeof cliCommandError>>((resume) => {
|
||||
yield* Effect.callback<void, ReturnType<typeof cliCommandError>>((resume) => {
|
||||
flow.agent(
|
||||
question,
|
||||
(chunk) => {
|
||||
|
|
@ -40,7 +38,6 @@ export function registerAgentCommands(program: Command): void {
|
|||
(err) => resume(Effect.fail(cliCommandError("agent", err))),
|
||||
);
|
||||
});
|
||||
}),
|
||||
)),
|
||||
);
|
||||
}
|
||||
}),
|
||||
),
|
||||
).pipe(Command.withDescription("Ask the TrustGraph agent a question"));
|
||||
|
|
|
|||
|
|
@ -4,38 +4,29 @@
|
|||
* Python reference: trustgraph-cli/trustgraph/cli/show_config.py etc.
|
||||
*/
|
||||
|
||||
import type { Command } from "commander";
|
||||
import { Effect } from "effect";
|
||||
import * as Argument from "effect/unstable/cli/Argument";
|
||||
import * as Command from "effect/unstable/cli/Command";
|
||||
import { cliCommandError, withSocket, writeJson } from "./util.js";
|
||||
|
||||
export function registerConfigCommands(program: Command): void {
|
||||
const config = program
|
||||
.command("config")
|
||||
.description("Configuration management");
|
||||
|
||||
config
|
||||
.command("show")
|
||||
.description("Show current configuration")
|
||||
.action((_opts, cmd) =>
|
||||
Effect.runPromise(withSocket(cmd, (socket) =>
|
||||
Effect.gen(function* () {
|
||||
const show = Command.make("show", {}, () =>
|
||||
withSocket((socket) =>
|
||||
Effect.gen(function* () {
|
||||
const cfg = socket.config();
|
||||
const resp = yield* Effect.tryPromise({
|
||||
try: () => cfg.getConfigAll(),
|
||||
catch: (error) => cliCommandError("config.show", error),
|
||||
});
|
||||
yield* writeJson(resp);
|
||||
}),
|
||||
)),
|
||||
);
|
||||
const resp = yield* Effect.tryPromise({
|
||||
try: () => cfg.getConfigAll(),
|
||||
catch: (error) => cliCommandError("config.show", error),
|
||||
});
|
||||
yield* writeJson(resp);
|
||||
}),
|
||||
),
|
||||
).pipe(Command.withDescription("Show current configuration"));
|
||||
|
||||
config
|
||||
.command("get")
|
||||
.description("Get a configuration value")
|
||||
.argument("<key>", "Config key (format: type/key)")
|
||||
.action((key: string, _opts, cmd) =>
|
||||
Effect.runPromise(withSocket(cmd, (socket) =>
|
||||
Effect.gen(function* () {
|
||||
const get = Command.make("get", {
|
||||
key: Argument.string("key").pipe(Argument.withDescription("Config key (format: type/key)")),
|
||||
}, ({ key }) =>
|
||||
withSocket((socket) =>
|
||||
Effect.gen(function* () {
|
||||
const cfg = socket.config();
|
||||
// Support "type/key" format; fall back to using the whole string as key
|
||||
const parts = key.split("/");
|
||||
|
|
@ -43,74 +34,75 @@ export function registerConfigCommands(program: Command): void {
|
|||
parts.length >= 2
|
||||
? { type: parts[0], key: parts.slice(1).join("/") }
|
||||
: { type: "config", key };
|
||||
const resp = yield* Effect.tryPromise({
|
||||
try: () => cfg.getConfig([configKey]),
|
||||
catch: (error) => cliCommandError("config.get", error),
|
||||
});
|
||||
yield* writeJson(resp);
|
||||
}),
|
||||
)),
|
||||
);
|
||||
const resp = yield* Effect.tryPromise({
|
||||
try: () => cfg.getConfig([configKey]),
|
||||
catch: (error) => cliCommandError("config.get", error),
|
||||
});
|
||||
yield* writeJson(resp);
|
||||
}),
|
||||
),
|
||||
).pipe(Command.withDescription("Get a configuration value"));
|
||||
|
||||
config
|
||||
.command("set")
|
||||
.description("Set a configuration value")
|
||||
.argument("<key>", "Config key (format: type/key)")
|
||||
.argument("<value>", "Config value (JSON)")
|
||||
.action((key: string, value: string, _opts, cmd) =>
|
||||
Effect.runPromise(withSocket(cmd, (socket) =>
|
||||
Effect.gen(function* () {
|
||||
const set = Command.make("set", {
|
||||
key: Argument.string("key").pipe(Argument.withDescription("Config key (format: type/key)")),
|
||||
value: Argument.string("value").pipe(Argument.withDescription("Config value (JSON)")),
|
||||
}, ({ key, value }) =>
|
||||
withSocket((socket) =>
|
||||
Effect.gen(function* () {
|
||||
const cfg = socket.config();
|
||||
const parts = key.split("/");
|
||||
const configEntry =
|
||||
parts.length >= 2
|
||||
? { type: parts[0], key: parts.slice(1).join("/"), value }
|
||||
: { type: "config", key, value };
|
||||
const resp = yield* Effect.tryPromise({
|
||||
try: () => cfg.putConfig([configEntry]),
|
||||
catch: (error) => cliCommandError("config.set", error),
|
||||
});
|
||||
yield* writeJson(resp);
|
||||
}),
|
||||
)),
|
||||
);
|
||||
const resp = yield* Effect.tryPromise({
|
||||
try: () => cfg.putConfig([configEntry]),
|
||||
catch: (error) => cliCommandError("config.set", error),
|
||||
});
|
||||
yield* writeJson(resp);
|
||||
}),
|
||||
),
|
||||
).pipe(Command.withDescription("Set a configuration value"));
|
||||
|
||||
config
|
||||
.command("list")
|
||||
.description("List configuration keys for a type")
|
||||
.argument("[type]", "Config type to list", "config")
|
||||
.action((type: string, _opts, cmd) =>
|
||||
Effect.runPromise(withSocket(cmd, (socket) =>
|
||||
Effect.gen(function* () {
|
||||
const list = Command.make("list", {
|
||||
type: Argument.string("type").pipe(
|
||||
Argument.withDescription("Config type to list"),
|
||||
Argument.withDefault("config"),
|
||||
),
|
||||
}, ({ type }) =>
|
||||
withSocket((socket) =>
|
||||
Effect.gen(function* () {
|
||||
const cfg = socket.config();
|
||||
const resp = yield* Effect.tryPromise({
|
||||
try: () => cfg.list(type),
|
||||
catch: (error) => cliCommandError("config.list", error),
|
||||
});
|
||||
yield* writeJson(resp);
|
||||
}),
|
||||
)),
|
||||
);
|
||||
const resp = yield* Effect.tryPromise({
|
||||
try: () => cfg.list(type),
|
||||
catch: (error) => cliCommandError("config.list", error),
|
||||
});
|
||||
yield* writeJson(resp);
|
||||
}),
|
||||
),
|
||||
).pipe(Command.withDescription("List configuration keys for a type"));
|
||||
|
||||
config
|
||||
.command("delete")
|
||||
.description("Delete a configuration entry")
|
||||
.argument("<key>", "Config key (format: type/key)")
|
||||
.action((key: string, _opts, cmd) =>
|
||||
Effect.runPromise(withSocket(cmd, (socket) =>
|
||||
Effect.gen(function* () {
|
||||
const deleteCommand = Command.make("delete", {
|
||||
key: Argument.string("key").pipe(Argument.withDescription("Config key (format: type/key)")),
|
||||
}, ({ key }) =>
|
||||
withSocket((socket) =>
|
||||
Effect.gen(function* () {
|
||||
const cfg = socket.config();
|
||||
const parts = key.split("/");
|
||||
const configKey =
|
||||
parts.length >= 2
|
||||
? { type: parts[0], key: parts.slice(1).join("/") }
|
||||
: { type: "config", key };
|
||||
const resp = yield* Effect.tryPromise({
|
||||
try: () => cfg.deleteConfig(configKey),
|
||||
catch: (error) => cliCommandError("config.delete", error),
|
||||
});
|
||||
yield* writeJson(resp);
|
||||
}),
|
||||
)),
|
||||
);
|
||||
}
|
||||
const resp = yield* Effect.tryPromise({
|
||||
try: () => cfg.deleteConfig(configKey),
|
||||
catch: (error) => cliCommandError("config.delete", error),
|
||||
});
|
||||
yield* writeJson(resp);
|
||||
}),
|
||||
),
|
||||
).pipe(Command.withDescription("Delete a configuration entry"));
|
||||
|
||||
export const configCommand = Command.make("config").pipe(
|
||||
Command.withDescription("Configuration management"),
|
||||
Command.withSubcommands([show, get, set, list, deleteCommand]),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,25 +4,25 @@
|
|||
* Generate text embeddings using the configured embedding model.
|
||||
*/
|
||||
|
||||
import type { Command } from "commander";
|
||||
import { Effect } from "effect";
|
||||
import * as Argument from "effect/unstable/cli/Argument";
|
||||
import * as Command from "effect/unstable/cli/Command";
|
||||
import { cliCommandError, withSocket, writeJson } from "./util.js";
|
||||
|
||||
export function registerEmbeddingsCommands(program: Command): void {
|
||||
program
|
||||
.command("embeddings")
|
||||
.description("Generate text embeddings")
|
||||
.argument("<text...>", "Text(s) to embed")
|
||||
.action((texts: string[], _opts, cmd) =>
|
||||
Effect.runPromise(withSocket(cmd, (socket, opts) =>
|
||||
Effect.gen(function* () {
|
||||
export const embeddingsCommand = Command.make("embeddings", {
|
||||
texts: Argument.string("text").pipe(
|
||||
Argument.withDescription("Text(s) to embed"),
|
||||
Argument.variadic({ min: 1 }),
|
||||
),
|
||||
}, ({ texts }) =>
|
||||
withSocket((socket, opts) =>
|
||||
Effect.gen(function* () {
|
||||
const flow = socket.flow(opts.flow);
|
||||
const vectors = yield* Effect.tryPromise({
|
||||
try: () => flow.embeddings(texts),
|
||||
catch: (error) => cliCommandError("embeddings", error),
|
||||
});
|
||||
yield* writeJson(vectors);
|
||||
}),
|
||||
)),
|
||||
);
|
||||
}
|
||||
const vectors = yield* Effect.tryPromise({
|
||||
try: () => flow.embeddings(Array.from(texts)),
|
||||
catch: (error) => cliCommandError("embeddings", error),
|
||||
});
|
||||
yield* writeJson(vectors);
|
||||
}),
|
||||
),
|
||||
).pipe(Command.withDescription("Generate text embeddings"));
|
||||
|
|
|
|||
|
|
@ -4,96 +4,99 @@
|
|||
* Python reference: trustgraph-cli/trustgraph/cli/start_flow.py, stop_flow.py, etc.
|
||||
*/
|
||||
|
||||
import type { Command } from "commander";
|
||||
import { Effect } from "effect";
|
||||
import * as S from "effect/Schema";
|
||||
import * as Argument from "effect/unstable/cli/Argument";
|
||||
import * as Command from "effect/unstable/cli/Command";
|
||||
import * as Flag from "effect/unstable/cli/Flag";
|
||||
import { cliCommandError, withSocket, writeJson } from "./util.js";
|
||||
|
||||
export function registerFlowCommands(program: Command): void {
|
||||
const flow = program
|
||||
.command("flow")
|
||||
.description("Flow management");
|
||||
|
||||
flow
|
||||
.command("list")
|
||||
.description("List active flows")
|
||||
.action((_opts, cmd) =>
|
||||
Effect.runPromise(withSocket(cmd, (socket) =>
|
||||
Effect.gen(function* () {
|
||||
const list = Command.make("list", {}, () =>
|
||||
withSocket((socket) =>
|
||||
Effect.gen(function* () {
|
||||
const flows = socket.flows();
|
||||
const ids = yield* Effect.tryPromise({
|
||||
try: () => flows.getFlows(),
|
||||
catch: (error) => cliCommandError("flow.list", error),
|
||||
});
|
||||
yield* writeJson(ids);
|
||||
}),
|
||||
)),
|
||||
);
|
||||
const ids = yield* Effect.tryPromise({
|
||||
try: () => flows.getFlows(),
|
||||
catch: (error) => cliCommandError("flow.list", error),
|
||||
});
|
||||
yield* writeJson(ids);
|
||||
}),
|
||||
),
|
||||
).pipe(Command.withDescription("List active flows"));
|
||||
|
||||
flow
|
||||
.command("get")
|
||||
.description("Get a flow definition")
|
||||
.argument("<id>", "Flow ID")
|
||||
.action((id: string, _opts, cmd) =>
|
||||
Effect.runPromise(withSocket(cmd, (socket) =>
|
||||
Effect.gen(function* () {
|
||||
const get = Command.make("get", {
|
||||
id: Argument.string("id").pipe(Argument.withDescription("Flow ID")),
|
||||
}, ({ id }) =>
|
||||
withSocket((socket) =>
|
||||
Effect.gen(function* () {
|
||||
const flows = socket.flows();
|
||||
const def = yield* Effect.tryPromise({
|
||||
try: () => flows.getFlow(id),
|
||||
catch: (error) => cliCommandError("flow.get", error),
|
||||
});
|
||||
yield* writeJson(def);
|
||||
}),
|
||||
)),
|
||||
);
|
||||
const def = yield* Effect.tryPromise({
|
||||
try: () => flows.getFlow(id),
|
||||
catch: (error) => cliCommandError("flow.get", error),
|
||||
});
|
||||
yield* writeJson(def);
|
||||
}),
|
||||
),
|
||||
).pipe(Command.withDescription("Get a flow definition"));
|
||||
|
||||
flow
|
||||
.command("start")
|
||||
.description("Start a flow")
|
||||
.argument("<id>", "Flow ID")
|
||||
.requiredOption("-b, --blueprint <name>", "Blueprint name")
|
||||
.option("-d, --description <text>", "Flow description", "")
|
||||
.option("-p, --parameters <json>", "Parameters as JSON")
|
||||
.action((id: string, cmdOpts, cmd) =>
|
||||
Effect.runPromise(withSocket(cmd, (socket) =>
|
||||
Effect.gen(function* () {
|
||||
const start = Command.make("start", {
|
||||
id: Argument.string("id").pipe(Argument.withDescription("Flow ID")),
|
||||
blueprint: Flag.string("blueprint").pipe(
|
||||
Flag.withAlias("b"),
|
||||
Flag.withDescription("Blueprint name"),
|
||||
),
|
||||
description: Flag.string("description").pipe(
|
||||
Flag.withAlias("d"),
|
||||
Flag.withDescription("Flow description"),
|
||||
Flag.withDefault(""),
|
||||
),
|
||||
parameters: Flag.string("parameters").pipe(
|
||||
Flag.withAlias("p"),
|
||||
Flag.withDescription("Parameters as JSON"),
|
||||
Flag.optional,
|
||||
),
|
||||
}, ({ id, blueprint, description, parameters }) =>
|
||||
withSocket((socket) =>
|
||||
Effect.gen(function* () {
|
||||
const flows = socket.flows();
|
||||
const rawParameters = cmdOpts.parameters as string | undefined;
|
||||
const params = rawParameters !== undefined && rawParameters.length > 0
|
||||
const rawParameters = parameters._tag === "Some" ? parameters.value : undefined;
|
||||
const params = rawParameters !== undefined && rawParameters.length > 0
|
||||
? yield* S.decodeUnknownEffect(S.UnknownFromJsonString)(rawParameters).pipe(
|
||||
Effect.flatMap(S.decodeUnknownEffect(S.Record(S.String, S.Unknown))),
|
||||
Effect.mapError((error) => cliCommandError("flow.start.parameters", error)),
|
||||
)
|
||||
: undefined;
|
||||
const resp = yield* Effect.tryPromise({
|
||||
try: () =>
|
||||
flows.startFlow(
|
||||
id,
|
||||
cmdOpts.blueprint as string,
|
||||
cmdOpts.description as string,
|
||||
params,
|
||||
),
|
||||
catch: (error) => cliCommandError("flow.start", error),
|
||||
});
|
||||
yield* writeJson(resp);
|
||||
}),
|
||||
)),
|
||||
);
|
||||
const resp = yield* Effect.tryPromise({
|
||||
try: () =>
|
||||
flows.startFlow(
|
||||
id,
|
||||
blueprint,
|
||||
description,
|
||||
params,
|
||||
),
|
||||
catch: (error) => cliCommandError("flow.start", error),
|
||||
});
|
||||
yield* writeJson(resp);
|
||||
}),
|
||||
),
|
||||
).pipe(Command.withDescription("Start a flow"));
|
||||
|
||||
flow
|
||||
.command("stop")
|
||||
.description("Stop a flow")
|
||||
.argument("<id>", "Flow ID")
|
||||
.action((id: string, _opts, cmd) =>
|
||||
Effect.runPromise(withSocket(cmd, (socket) =>
|
||||
Effect.gen(function* () {
|
||||
const stop = Command.make("stop", {
|
||||
id: Argument.string("id").pipe(Argument.withDescription("Flow ID")),
|
||||
}, ({ id }) =>
|
||||
withSocket((socket) =>
|
||||
Effect.gen(function* () {
|
||||
const flows = socket.flows();
|
||||
const resp = yield* Effect.tryPromise({
|
||||
try: () => flows.stopFlow(id),
|
||||
catch: (error) => cliCommandError("flow.stop", error),
|
||||
});
|
||||
yield* writeJson(resp);
|
||||
}),
|
||||
)),
|
||||
);
|
||||
}
|
||||
const resp = yield* Effect.tryPromise({
|
||||
try: () => flows.stopFlow(id),
|
||||
catch: (error) => cliCommandError("flow.stop", error),
|
||||
});
|
||||
yield* writeJson(resp);
|
||||
}),
|
||||
),
|
||||
).pipe(Command.withDescription("Stop a flow"));
|
||||
|
||||
export const flowCommand = Command.make("flow").pipe(
|
||||
Command.withDescription("Flow management"),
|
||||
Command.withSubcommands([list, get, start, stop]),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,65 +4,72 @@
|
|||
* Python reference: trustgraph-cli/trustgraph/cli/invoke_graph_rag.py
|
||||
*/
|
||||
|
||||
import type { Command } from "commander";
|
||||
import { Effect } from "effect";
|
||||
import * as O from "effect/Option";
|
||||
import * as Argument from "effect/unstable/cli/Argument";
|
||||
import * as Command from "effect/unstable/cli/Command";
|
||||
import * as Flag from "effect/unstable/cli/Flag";
|
||||
import { cliCommandError, withSocket, writeLine } from "./util.js";
|
||||
|
||||
export function registerGraphRagCommands(program: Command): void {
|
||||
program
|
||||
.command("graph-rag")
|
||||
.description("Query the knowledge graph using RAG")
|
||||
.argument("<query>", "Natural language query")
|
||||
.option("--entity-limit <n>", "Max entities", "50")
|
||||
.option("--triple-limit <n>", "Max triples per entity", "30")
|
||||
.option("--collection <name>", "Collection name")
|
||||
.action((query: string, cmdOpts, cmd) =>
|
||||
Effect.runPromise(withSocket(cmd, (socket, opts) =>
|
||||
Effect.gen(function* () {
|
||||
export const graphRagCommand = Command.make("graph-rag", {
|
||||
query: Argument.string("query").pipe(Argument.withDescription("Natural language query")),
|
||||
entityLimit: Flag.integer("entity-limit").pipe(
|
||||
Flag.withDescription("Max entities"),
|
||||
Flag.withDefault(50),
|
||||
),
|
||||
tripleLimit: Flag.integer("triple-limit").pipe(
|
||||
Flag.withDescription("Max triples per entity"),
|
||||
Flag.withDefault(30),
|
||||
),
|
||||
collection: Flag.string("collection").pipe(
|
||||
Flag.withDescription("Collection name"),
|
||||
Flag.optional,
|
||||
),
|
||||
}, ({ query, entityLimit, tripleLimit, collection }) =>
|
||||
withSocket((socket, opts) =>
|
||||
Effect.gen(function* () {
|
||||
const flow = socket.flow(opts.flow);
|
||||
const collection = cmdOpts.collection as string | undefined;
|
||||
const response = yield* Effect.tryPromise({
|
||||
try: () =>
|
||||
flow.graphRag(
|
||||
query,
|
||||
{
|
||||
entityLimit: parseInt(cmdOpts.entityLimit, 10),
|
||||
tripleLimit: parseInt(cmdOpts.tripleLimit, 10),
|
||||
},
|
||||
collection,
|
||||
),
|
||||
catch: (error) => cliCommandError("graph-rag", error),
|
||||
});
|
||||
yield* writeLine(response);
|
||||
}),
|
||||
)),
|
||||
);
|
||||
const response = yield* Effect.tryPromise({
|
||||
try: () =>
|
||||
flow.graphRag(
|
||||
query,
|
||||
{
|
||||
entityLimit,
|
||||
tripleLimit,
|
||||
},
|
||||
O.getOrUndefined(collection),
|
||||
),
|
||||
catch: (error) => cliCommandError("graph-rag", error),
|
||||
});
|
||||
yield* writeLine(response);
|
||||
}),
|
||||
),
|
||||
).pipe(Command.withDescription("Query the knowledge graph using RAG"));
|
||||
|
||||
program
|
||||
.command("document-rag")
|
||||
.description("Query documents using RAG")
|
||||
.argument("<query>", "Natural language query")
|
||||
.option("--doc-limit <n>", "Max documents", "20")
|
||||
.option("--collection <name>", "Collection name")
|
||||
.action((query: string, cmdOpts, cmd) =>
|
||||
Effect.runPromise(withSocket(cmd, (socket, opts) =>
|
||||
Effect.gen(function* () {
|
||||
export const documentRagCommand = Command.make("document-rag", {
|
||||
query: Argument.string("query").pipe(Argument.withDescription("Natural language query")),
|
||||
docLimit: Flag.integer("doc-limit").pipe(
|
||||
Flag.withDescription("Max documents"),
|
||||
Flag.withDefault(20),
|
||||
),
|
||||
collection: Flag.string("collection").pipe(
|
||||
Flag.withDescription("Collection name"),
|
||||
Flag.optional,
|
||||
),
|
||||
}, ({ query, docLimit, collection }) =>
|
||||
withSocket((socket, opts) =>
|
||||
Effect.gen(function* () {
|
||||
const flow = socket.flow(opts.flow);
|
||||
const docLimit = cmdOpts.docLimit as string | undefined;
|
||||
const collection = cmdOpts.collection as string | undefined;
|
||||
const response = yield* Effect.tryPromise({
|
||||
try: () =>
|
||||
flow.documentRag(
|
||||
query,
|
||||
docLimit !== undefined && docLimit.length > 0
|
||||
? parseInt(docLimit, 10)
|
||||
: undefined,
|
||||
collection,
|
||||
),
|
||||
catch: (error) => cliCommandError("document-rag", error),
|
||||
});
|
||||
yield* writeLine(response);
|
||||
}),
|
||||
)),
|
||||
);
|
||||
}
|
||||
const response = yield* Effect.tryPromise({
|
||||
try: () =>
|
||||
flow.documentRag(
|
||||
query,
|
||||
docLimit,
|
||||
O.getOrUndefined(collection),
|
||||
),
|
||||
catch: (error) => cliCommandError("document-rag", error),
|
||||
});
|
||||
yield* writeLine(response);
|
||||
}),
|
||||
),
|
||||
).pipe(Command.withDescription("Query documents using RAG"));
|
||||
|
|
|
|||
|
|
@ -4,8 +4,11 @@
|
|||
* Manages documents stored in the TrustGraph library.
|
||||
*/
|
||||
|
||||
import type { Command } from "commander";
|
||||
import { Effect, Match } from "effect";
|
||||
import * as O from "effect/Option";
|
||||
import * as Argument from "effect/unstable/cli/Argument";
|
||||
import * as Command from "effect/unstable/cli/Command";
|
||||
import * as Flag from "effect/unstable/cli/Flag";
|
||||
import { cliCommandError, withSocket, writeJson } from "./util.js";
|
||||
|
||||
function basenamePath(filepath: string): string {
|
||||
|
|
@ -30,98 +33,106 @@ export function guessMimeType(filepath: string): string {
|
|||
);
|
||||
}
|
||||
|
||||
export function registerLibraryCommands(program: Command): void {
|
||||
const library = program
|
||||
.command("library")
|
||||
.description("Document library management");
|
||||
|
||||
library
|
||||
.command("list")
|
||||
.description("List documents in the library")
|
||||
.action((_opts, cmd) =>
|
||||
Effect.runPromise(withSocket(cmd, (socket) =>
|
||||
Effect.gen(function* () {
|
||||
const list = Command.make("list", {}, () =>
|
||||
withSocket((socket) =>
|
||||
Effect.gen(function* () {
|
||||
const lib = socket.librarian();
|
||||
const docs = yield* Effect.tryPromise({
|
||||
try: () => lib.getDocuments(),
|
||||
catch: (error) => cliCommandError("library.list", error),
|
||||
});
|
||||
yield* writeJson(docs);
|
||||
}),
|
||||
)),
|
||||
);
|
||||
const docs = yield* Effect.tryPromise({
|
||||
try: () => lib.getDocuments(),
|
||||
catch: (error) => cliCommandError("library.list", error),
|
||||
});
|
||||
yield* writeJson(docs);
|
||||
}),
|
||||
),
|
||||
).pipe(Command.withDescription("List documents in the library"));
|
||||
|
||||
library
|
||||
.command("load")
|
||||
.description("Load a document into the library")
|
||||
.argument("<file>", "Path to the file to load")
|
||||
.option("-t, --title <title>", "Document title")
|
||||
.option("-m, --mime-type <type>", "MIME type (auto-detected if omitted)")
|
||||
.option("-c, --comments <text>", "Comments", "")
|
||||
.option("--tags <tags...>", "Document tags")
|
||||
.option("--id <id>", "Optional document ID")
|
||||
.action((file: string, cmdOpts, cmd) =>
|
||||
Effect.runPromise(withSocket(cmd, (socket) =>
|
||||
Effect.gen(function* () {
|
||||
const load = Command.make("load", {
|
||||
file: Argument.string("file").pipe(Argument.withDescription("Path to the file to load")),
|
||||
title: Flag.string("title").pipe(
|
||||
Flag.withAlias("t"),
|
||||
Flag.withDescription("Document title"),
|
||||
Flag.optional,
|
||||
),
|
||||
mimeType: Flag.string("mime-type").pipe(
|
||||
Flag.withAlias("m"),
|
||||
Flag.withDescription("MIME type (auto-detected if omitted)"),
|
||||
Flag.optional,
|
||||
),
|
||||
comments: Flag.string("comments").pipe(
|
||||
Flag.withAlias("c"),
|
||||
Flag.withDescription("Comments"),
|
||||
Flag.withDefault(""),
|
||||
),
|
||||
tags: Flag.string("tags").pipe(
|
||||
Flag.withDescription("Document tags"),
|
||||
Flag.atMost(Number.MAX_SAFE_INTEGER),
|
||||
),
|
||||
id: Flag.string("id").pipe(
|
||||
Flag.withDescription("Optional document ID"),
|
||||
Flag.optional,
|
||||
),
|
||||
}, ({ file, title, mimeType, comments, tags, id }) =>
|
||||
withSocket((socket) =>
|
||||
Effect.gen(function* () {
|
||||
const lib = socket.librarian();
|
||||
const data = new Uint8Array(yield* Effect.tryPromise({
|
||||
try: () => Bun.file(file).arrayBuffer(),
|
||||
catch: (error) => cliCommandError("library.load.read-file", error),
|
||||
}));
|
||||
const b64 = Buffer.from(data).toString("base64");
|
||||
const mimeType = (cmdOpts.mimeType as string | undefined) ?? guessMimeType(file);
|
||||
const title = (cmdOpts.title as string | undefined) ?? basenamePath(file);
|
||||
const comments = cmdOpts.comments as string;
|
||||
const tags: string[] = (cmdOpts.tags as string[] | undefined) ?? [];
|
||||
const data = new Uint8Array(yield* Effect.tryPromise({
|
||||
try: () => Bun.file(file).arrayBuffer(),
|
||||
catch: (error) => cliCommandError("library.load.read-file", error),
|
||||
}));
|
||||
const b64 = Buffer.from(data).toString("base64");
|
||||
const resolvedMimeType = O.getOrUndefined(mimeType) ?? guessMimeType(file);
|
||||
const resolvedTitle = O.getOrUndefined(title) ?? basenamePath(file);
|
||||
|
||||
const resp = yield* Effect.tryPromise({
|
||||
try: () =>
|
||||
lib.loadDocument(
|
||||
b64,
|
||||
mimeType,
|
||||
title,
|
||||
comments,
|
||||
tags,
|
||||
cmdOpts.id as string | undefined,
|
||||
),
|
||||
catch: (error) => cliCommandError("library.load", error),
|
||||
});
|
||||
yield* writeJson(resp);
|
||||
}),
|
||||
)),
|
||||
);
|
||||
const resp = yield* Effect.tryPromise({
|
||||
try: () =>
|
||||
lib.loadDocument(
|
||||
b64,
|
||||
resolvedMimeType,
|
||||
resolvedTitle,
|
||||
comments,
|
||||
Array.from(tags),
|
||||
O.getOrUndefined(id),
|
||||
),
|
||||
catch: (error) => cliCommandError("library.load", error),
|
||||
});
|
||||
yield* writeJson(resp);
|
||||
}),
|
||||
),
|
||||
).pipe(Command.withDescription("Load a document into the library"));
|
||||
|
||||
library
|
||||
.command("remove")
|
||||
.description("Remove a document from the library")
|
||||
.argument("<id>", "Document ID to remove")
|
||||
.option("--collection <name>", "Collection name")
|
||||
.action((id: string, cmdOpts, cmd) =>
|
||||
Effect.runPromise(withSocket(cmd, (socket) =>
|
||||
Effect.gen(function* () {
|
||||
const remove = Command.make("remove", {
|
||||
id: Argument.string("id").pipe(Argument.withDescription("Document ID to remove")),
|
||||
collection: Flag.string("collection").pipe(
|
||||
Flag.withDescription("Collection name"),
|
||||
Flag.optional,
|
||||
),
|
||||
}, ({ id, collection }) =>
|
||||
withSocket((socket) =>
|
||||
Effect.gen(function* () {
|
||||
const lib = socket.librarian();
|
||||
const resp = yield* Effect.tryPromise({
|
||||
try: () => lib.removeDocument(id, cmdOpts.collection as string | undefined),
|
||||
catch: (error) => cliCommandError("library.remove", error),
|
||||
});
|
||||
yield* writeJson(resp);
|
||||
}),
|
||||
)),
|
||||
);
|
||||
const resp = yield* Effect.tryPromise({
|
||||
try: () => lib.removeDocument(id, O.getOrUndefined(collection)),
|
||||
catch: (error) => cliCommandError("library.remove", error),
|
||||
});
|
||||
yield* writeJson(resp);
|
||||
}),
|
||||
),
|
||||
).pipe(Command.withDescription("Remove a document from the library"));
|
||||
|
||||
library
|
||||
.command("processing")
|
||||
.description("List documents currently being processed")
|
||||
.action((_opts, cmd) =>
|
||||
Effect.runPromise(withSocket(cmd, (socket) =>
|
||||
Effect.gen(function* () {
|
||||
const processing = Command.make("processing", {}, () =>
|
||||
withSocket((socket) =>
|
||||
Effect.gen(function* () {
|
||||
const lib = socket.librarian();
|
||||
const items = yield* Effect.tryPromise({
|
||||
try: () => lib.getProcessing(),
|
||||
catch: (error) => cliCommandError("library.processing", error),
|
||||
});
|
||||
yield* writeJson(items);
|
||||
}),
|
||||
)),
|
||||
);
|
||||
}
|
||||
const items = yield* Effect.tryPromise({
|
||||
try: () => lib.getProcessing(),
|
||||
catch: (error) => cliCommandError("library.processing", error),
|
||||
});
|
||||
yield* writeJson(items);
|
||||
}),
|
||||
),
|
||||
).pipe(Command.withDescription("List documents currently being processed"));
|
||||
|
||||
export const libraryCommand = Command.make("library").pipe(
|
||||
Command.withDescription("Document library management"),
|
||||
Command.withSubcommands([list, load, remove, processing]),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,50 +4,67 @@
|
|||
* Query the knowledge graph for subject-predicate-object triples.
|
||||
*/
|
||||
|
||||
import type { Command } from "commander";
|
||||
import type { Term } from "@trustgraph/client";
|
||||
import { Effect } from "effect";
|
||||
import * as O from "effect/Option";
|
||||
import * as Command from "effect/unstable/cli/Command";
|
||||
import * as Flag from "effect/unstable/cli/Flag";
|
||||
import { cliCommandError, withSocket, writeJson } from "./util.js";
|
||||
|
||||
export function registerTriplesCommands(program: Command): void {
|
||||
program
|
||||
.command("triples")
|
||||
.description("Query knowledge graph triples")
|
||||
.option("-s, --subject <iri>", "Subject IRI")
|
||||
.option("-p, --predicate <iri>", "Predicate IRI")
|
||||
.option("-o, --object <iri>", "Object IRI or literal")
|
||||
.option("-l, --limit <n>", "Max results", "20")
|
||||
.option("--collection <name>", "Collection name")
|
||||
.action((cmdOpts, cmd) =>
|
||||
Effect.runPromise(withSocket(cmd, (socket, opts) =>
|
||||
Effect.gen(function* () {
|
||||
export const triplesCommand = Command.make("triples", {
|
||||
subject: Flag.string("subject").pipe(
|
||||
Flag.withAlias("s"),
|
||||
Flag.withDescription("Subject IRI"),
|
||||
Flag.optional,
|
||||
),
|
||||
predicate: Flag.string("predicate").pipe(
|
||||
Flag.withAlias("p"),
|
||||
Flag.withDescription("Predicate IRI"),
|
||||
Flag.optional,
|
||||
),
|
||||
object: Flag.string("object").pipe(
|
||||
Flag.withAlias("o"),
|
||||
Flag.withDescription("Object IRI or literal"),
|
||||
Flag.optional,
|
||||
),
|
||||
limit: Flag.integer("limit").pipe(
|
||||
Flag.withAlias("l"),
|
||||
Flag.withDescription("Max results"),
|
||||
Flag.withDefault(20),
|
||||
),
|
||||
collection: Flag.string("collection").pipe(
|
||||
Flag.withDescription("Collection name"),
|
||||
Flag.optional,
|
||||
),
|
||||
}, ({ subject, predicate, object, limit, collection }) =>
|
||||
withSocket((socket, opts) =>
|
||||
Effect.gen(function* () {
|
||||
const flow = socket.flow(opts.flow);
|
||||
const subject = cmdOpts.subject as string | undefined;
|
||||
const predicate = cmdOpts.predicate as string | undefined;
|
||||
const object = cmdOpts.object as string | undefined;
|
||||
const s: Term | undefined = subject !== undefined && subject.length > 0
|
||||
? { t: "i", i: subject }
|
||||
const subjectValue = O.getOrUndefined(subject);
|
||||
const predicateValue = O.getOrUndefined(predicate);
|
||||
const objectValue = O.getOrUndefined(object);
|
||||
const s: Term | undefined = subjectValue !== undefined && subjectValue.length > 0
|
||||
? { t: "i", i: subjectValue }
|
||||
: undefined;
|
||||
const p: Term | undefined = predicate !== undefined && predicate.length > 0
|
||||
? { t: "i", i: predicate }
|
||||
const p: Term | undefined = predicateValue !== undefined && predicateValue.length > 0
|
||||
? { t: "i", i: predicateValue }
|
||||
: undefined;
|
||||
const o: Term | undefined = object !== undefined && object.length > 0
|
||||
? { t: "i", i: object }
|
||||
const o: Term | undefined = objectValue !== undefined && objectValue.length > 0
|
||||
? { t: "i", i: objectValue }
|
||||
: undefined;
|
||||
|
||||
const triples = yield* Effect.tryPromise({
|
||||
try: () =>
|
||||
flow.triplesQuery(
|
||||
s,
|
||||
p,
|
||||
o,
|
||||
parseInt(cmdOpts.limit as string, 10),
|
||||
cmdOpts.collection as string | undefined,
|
||||
),
|
||||
catch: (error) => cliCommandError("triples", error),
|
||||
});
|
||||
yield* writeJson(triples);
|
||||
}),
|
||||
)),
|
||||
);
|
||||
}
|
||||
const triples = yield* Effect.tryPromise({
|
||||
try: () =>
|
||||
flow.triplesQuery(
|
||||
s,
|
||||
p,
|
||||
o,
|
||||
limit,
|
||||
O.getOrUndefined(collection),
|
||||
),
|
||||
catch: (error) => cliCommandError("triples", error),
|
||||
});
|
||||
yield* writeJson(triples);
|
||||
}),
|
||||
),
|
||||
).pipe(Command.withDescription("Query knowledge graph triples"));
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@
|
|||
* Shared CLI utilities.
|
||||
*/
|
||||
|
||||
import type { Command } from "commander";
|
||||
import { createTrustGraphSocket, type BaseApi } from "@trustgraph/client";
|
||||
import { Duration, Effect } from "effect";
|
||||
import * as O from "effect/Option";
|
||||
import * as S from "effect/Schema";
|
||||
import * as Command from "effect/unstable/cli/Command";
|
||||
import * as Flag from "effect/unstable/cli/Flag";
|
||||
|
||||
export interface CliOpts {
|
||||
gateway: string;
|
||||
|
|
@ -14,12 +16,42 @@ export interface CliOpts {
|
|||
flow: string;
|
||||
}
|
||||
|
||||
export function getOpts(cmd: Command): CliOpts {
|
||||
// Walk up to root command to get global options
|
||||
let root = cmd;
|
||||
while (root.parent !== null) root = root.parent;
|
||||
return root.opts() as CliOpts;
|
||||
}
|
||||
export const rootCommand = Command.make("tg").pipe(
|
||||
Command.withDescription("TrustGraph CLI - interact with TrustGraph services"),
|
||||
Command.withSharedFlags({
|
||||
gateway: Flag.string("gateway").pipe(
|
||||
Flag.withAlias("g"),
|
||||
Flag.withDescription("Gateway WebSocket URL"),
|
||||
Flag.withDefault("ws://localhost:8088/api/v1/rpc"),
|
||||
),
|
||||
user: Flag.string("user").pipe(
|
||||
Flag.withAlias("u"),
|
||||
Flag.withDescription("User identifier"),
|
||||
Flag.withDefault("cli"),
|
||||
),
|
||||
token: Flag.string("token").pipe(
|
||||
Flag.withAlias("t"),
|
||||
Flag.withDescription("Authentication token"),
|
||||
Flag.optional,
|
||||
),
|
||||
flow: Flag.string("flow").pipe(
|
||||
Flag.withAlias("f"),
|
||||
Flag.withDescription("Flow ID"),
|
||||
Flag.withDefault("default"),
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
export const getOpts = Effect.gen(function* () {
|
||||
const opts = yield* rootCommand;
|
||||
const base = {
|
||||
gateway: opts.gateway,
|
||||
user: opts.user,
|
||||
flow: opts.flow,
|
||||
};
|
||||
const token = O.getOrUndefined(opts.token);
|
||||
return token === undefined ? base : { ...base, token } satisfies CliOpts;
|
||||
});
|
||||
|
||||
export class CliCommandError extends S.TaggedErrorClass<CliCommandError>()(
|
||||
"CliCommandError",
|
||||
|
|
@ -78,19 +110,16 @@ export function createSocketEffect(opts: CliOpts): Effect.Effect<BaseApi, CliCom
|
|||
);
|
||||
}
|
||||
|
||||
export function createSocket(opts: CliOpts): Promise<BaseApi> {
|
||||
return Effect.runPromise(createSocketEffect(opts));
|
||||
}
|
||||
|
||||
export const withSocket = <A, E, R>(
|
||||
cmd: Command,
|
||||
export const withSocket = Effect.fn("withSocket")(function* <A, E, R>(
|
||||
use: (socket: BaseApi, opts: CliOpts) => Effect.Effect<A, E, R>,
|
||||
) =>
|
||||
Effect.acquireUseRelease(
|
||||
createSocketEffect(getOpts(cmd)),
|
||||
(socket) => use(socket, getOpts(cmd)),
|
||||
) {
|
||||
const opts = yield* getOpts;
|
||||
return yield* Effect.acquireUseRelease(
|
||||
createSocketEffect(opts),
|
||||
(socket) => use(socket, opts),
|
||||
(socket) =>
|
||||
Effect.sync(() => {
|
||||
socket.close();
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/** @effect-diagnostics strictEffectProvide:skip-file */
|
||||
|
||||
/**
|
||||
* Unified TrustGraph CLI.
|
||||
*
|
||||
|
|
@ -9,32 +11,33 @@
|
|||
* Python reference: trustgraph-cli/trustgraph/cli/
|
||||
*/
|
||||
|
||||
import { Command } from "commander";
|
||||
import { registerAgentCommands } from "./commands/agent.js";
|
||||
import { registerGraphRagCommands } from "./commands/graph-rag.js";
|
||||
import { registerConfigCommands } from "./commands/config.js";
|
||||
import { registerFlowCommands } from "./commands/flow.js";
|
||||
import { registerLibraryCommands } from "./commands/library.js";
|
||||
import { registerTriplesCommands } from "./commands/triples.js";
|
||||
import { registerEmbeddingsCommands } from "./commands/embeddings.js";
|
||||
import { BunRuntime, BunServices } from "@effect/platform-bun";
|
||||
import { Effect } from "effect";
|
||||
import * as Command from "effect/unstable/cli/Command";
|
||||
import { agentCommand } from "./commands/agent.js";
|
||||
import { configCommand } from "./commands/config.js";
|
||||
import { embeddingsCommand } from "./commands/embeddings.js";
|
||||
import { flowCommand } from "./commands/flow.js";
|
||||
import { graphRagCommand, documentRagCommand } from "./commands/graph-rag.js";
|
||||
import { libraryCommand } from "./commands/library.js";
|
||||
import { triplesCommand } from "./commands/triples.js";
|
||||
import { rootCommand } from "./commands/util.js";
|
||||
|
||||
const program = new Command();
|
||||
export const cli = rootCommand.pipe(
|
||||
Command.withSubcommands([
|
||||
agentCommand,
|
||||
graphRagCommand,
|
||||
documentRagCommand,
|
||||
configCommand,
|
||||
flowCommand,
|
||||
libraryCommand,
|
||||
triplesCommand,
|
||||
embeddingsCommand,
|
||||
]),
|
||||
);
|
||||
|
||||
program
|
||||
.name("tg")
|
||||
.description("TrustGraph CLI — interact with TrustGraph services")
|
||||
.version("0.1.0")
|
||||
.option("-g, --gateway <url>", "Gateway WebSocket URL", "ws://localhost:8088/api/v1/rpc")
|
||||
.option("-u, --user <id>", "User identifier", "cli")
|
||||
.option("-t, --token <token>", "Authentication token")
|
||||
.option("-f, --flow <id>", "Flow ID", "default");
|
||||
export const program = Command.run(cli, { version: "0.1.0" }).pipe(
|
||||
Effect.provide(BunServices.layer),
|
||||
);
|
||||
|
||||
registerAgentCommands(program);
|
||||
registerGraphRagCommands(program);
|
||||
registerConfigCommands(program);
|
||||
registerFlowCommands(program);
|
||||
registerLibraryCommands(program);
|
||||
registerTriplesCommands(program);
|
||||
registerEmbeddingsCommands(program);
|
||||
|
||||
program.parse();
|
||||
BunRuntime.runMain(program);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue