mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-07-01 09:29:38 +02:00
feat(ts): complete schema-first phase 2
This commit is contained in:
parent
0746d7ffd5
commit
be2370ee7b
24 changed files with 465 additions and 433 deletions
|
|
@ -18,7 +18,7 @@ import {
|
||||||
} from "./async-processor.js";
|
} from "./async-processor.js";
|
||||||
import type { Spec } from "../spec/types.js";
|
import type { Spec } from "../spec/types.js";
|
||||||
import type { BackendConsumer, PubSubBackend } from "../backend/types.js";
|
import type { BackendConsumer, PubSubBackend } from "../backend/types.js";
|
||||||
import type { FlowDefinition } from "./flow.js";
|
import { FlowDefinition } from "./flow.js";
|
||||||
import { Flow, } from "./flow.js";
|
import { Flow, } from "./flow.js";
|
||||||
import { topics } from "../schema/topics.js";
|
import { topics } from "../schema/topics.js";
|
||||||
import type {
|
import type {
|
||||||
|
|
@ -128,14 +128,9 @@ const ConfigPushSchema = S.Struct({
|
||||||
config: S.Record(S.String, S.Unknown),
|
config: S.Record(S.String, S.Unknown),
|
||||||
});
|
});
|
||||||
|
|
||||||
const FlowDefinitionSchema = S.Struct({
|
const FlowDefinitions = S.Record(S.String, FlowDefinition);
|
||||||
topics: S.optionalKey(S.Record(S.String, S.String)),
|
|
||||||
parameters: S.optionalKey(S.Record(S.String, S.Unknown)),
|
|
||||||
});
|
|
||||||
|
|
||||||
const FlowDefinitionsSchema = S.Record(S.String, FlowDefinitionSchema);
|
const decodeFlowDefinitions = S.decodeUnknownOption(FlowDefinitions);
|
||||||
|
|
||||||
const decodeFlowDefinitions = S.decodeUnknownOption(FlowDefinitionsSchema);
|
|
||||||
|
|
||||||
export function runFlowProcessorDefinitionScoped<
|
export function runFlowProcessorDefinitionScoped<
|
||||||
FlowRequirements = never,
|
FlowRequirements = never,
|
||||||
|
|
|
||||||
|
|
@ -43,12 +43,14 @@ import type { ProducerSpec } from "../spec/producer-spec.js";
|
||||||
import type { RequestResponseSpec } from "../spec/request-response-spec.js";
|
import type { RequestResponseSpec } from "../spec/request-response-spec.js";
|
||||||
import type { Spec, SpecRuntimeRequirements } from "../spec/types.js";
|
import type { Spec, SpecRuntimeRequirements } from "../spec/types.js";
|
||||||
|
|
||||||
export interface FlowDefinition {
|
export class FlowDefinition extends S.Class<FlowDefinition>("FlowDefinition")({
|
||||||
/** Topic overrides keyed by spec name */
|
/** Topic overrides keyed by spec name */
|
||||||
topics?: Record<string, string>;
|
topics: S.optionalKey(S.Record(S.String, S.String)),
|
||||||
/** Parameter values keyed by spec name */
|
/** Parameter values keyed by spec name */
|
||||||
parameters?: Record<string, unknown>;
|
parameters: S.optionalKey(S.Record(S.String, S.Unknown)),
|
||||||
}
|
}, {
|
||||||
|
description: "Per-flow configuration: topic overrides and parameter values keyed by spec name.",
|
||||||
|
}) {}
|
||||||
|
|
||||||
export interface FlowProducer<T> {
|
export interface FlowProducer<T> {
|
||||||
readonly send: (id: string, message: T) => Effect.Effect<void, MessagingDeliveryError>;
|
readonly send: (id: string, message: T) => Effect.Effect<void, MessagingDeliveryError>;
|
||||||
|
|
|
||||||
|
|
@ -3,22 +3,25 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Config, Duration, Effect } from "effect";
|
import { Config, Duration, Effect } from "effect";
|
||||||
|
import * as S from "effect/Schema";
|
||||||
|
|
||||||
export interface MessagingRuntimeConfig {
|
export class MessagingRuntimeConfig extends S.Class<MessagingRuntimeConfig>("MessagingRuntimeConfig")({
|
||||||
readonly consumerReceiveTimeout: Duration.Duration;
|
consumerReceiveTimeout: S.Duration,
|
||||||
readonly consumerErrorBackoff: Duration.Duration;
|
consumerErrorBackoff: S.Duration,
|
||||||
readonly rateLimitRetry: Duration.Duration;
|
rateLimitRetry: S.Duration,
|
||||||
readonly rateLimitTimeout: Duration.Duration;
|
rateLimitTimeout: S.Duration,
|
||||||
readonly requestTimeout: Duration.Duration;
|
requestTimeout: S.Duration,
|
||||||
}
|
}, {
|
||||||
|
description: "Messaging runtime timing windows for consumer receive, backoff, rate-limit retry, and request timeout.",
|
||||||
|
}) {}
|
||||||
|
|
||||||
export const defaultMessagingRuntimeConfig: MessagingRuntimeConfig = {
|
export const defaultMessagingRuntimeConfig: MessagingRuntimeConfig = MessagingRuntimeConfig.make({
|
||||||
consumerReceiveTimeout: Duration.millis(2_000),
|
consumerReceiveTimeout: Duration.millis(2_000),
|
||||||
consumerErrorBackoff: Duration.millis(1_000),
|
consumerErrorBackoff: Duration.millis(1_000),
|
||||||
rateLimitRetry: Duration.millis(10_000),
|
rateLimitRetry: Duration.millis(10_000),
|
||||||
rateLimitTimeout: Duration.millis(7_200_000),
|
rateLimitTimeout: Duration.millis(7_200_000),
|
||||||
requestTimeout: Duration.millis(300_000),
|
requestTimeout: Duration.millis(300_000),
|
||||||
};
|
});
|
||||||
|
|
||||||
const durationConfig = (name: string, defaultValue: Duration.Duration) =>
|
const durationConfig = (name: string, defaultValue: Duration.Duration) =>
|
||||||
Config.duration(name).pipe(
|
Config.duration(name).pipe(
|
||||||
|
|
@ -48,11 +51,11 @@ export const loadMessagingRuntimeConfig = Effect.fn("loadMessagingRuntimeConfig"
|
||||||
defaultMessagingRuntimeConfig.requestTimeout,
|
defaultMessagingRuntimeConfig.requestTimeout,
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return MessagingRuntimeConfig.make({
|
||||||
consumerReceiveTimeout,
|
consumerReceiveTimeout,
|
||||||
consumerErrorBackoff,
|
consumerErrorBackoff,
|
||||||
rateLimitRetry,
|
rateLimitRetry,
|
||||||
rateLimitTimeout,
|
rateLimitTimeout,
|
||||||
requestTimeout,
|
requestTimeout,
|
||||||
} satisfies MessagingRuntimeConfig;
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,12 @@ import * as S from "effect/Schema";
|
||||||
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";
|
||||||
|
|
||||||
export interface CliOpts {
|
export class CliOpts extends S.Class<CliOpts>("CliOpts")({
|
||||||
gateway: string;
|
gateway: S.String,
|
||||||
user: string;
|
user: S.String,
|
||||||
token?: string;
|
token: S.optionalKey(S.String),
|
||||||
flow: string;
|
flow: S.String,
|
||||||
}
|
}, { description: "Resolved TrustGraph CLI connection options." }) {}
|
||||||
|
|
||||||
export const rootCommand = Command.make("tg").pipe(
|
export const rootCommand = Command.make("tg").pipe(
|
||||||
Command.withDescription("TrustGraph CLI - interact with TrustGraph services"),
|
Command.withDescription("TrustGraph CLI - interact with TrustGraph services"),
|
||||||
|
|
|
||||||
|
|
@ -430,7 +430,7 @@ const onAgentRequest = Effect.fn("AgentService.onRequest")(function* (
|
||||||
chunk_type: "explain",
|
chunk_type: "explain",
|
||||||
content: "",
|
content: "",
|
||||||
explain_id: explain.explainId,
|
explain_id: explain.explainId,
|
||||||
explain_triples: explain.triples,
|
explain_triples: [...explain.triples],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,8 @@ import type {
|
||||||
TriplesQueryResponse,
|
TriplesQueryResponse,
|
||||||
ToolRequest,
|
ToolRequest,
|
||||||
ToolResponse,
|
ToolResponse,
|
||||||
Term,
|
|
||||||
Triple,
|
|
||||||
} from "@trustgraph/base";
|
} from "@trustgraph/base";
|
||||||
import {Term as TermSchema} from "@trustgraph/base";
|
import { Term, Triple } from "@trustgraph/base";
|
||||||
import { Effect, Match } from "effect";
|
import { Effect, Match } 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";
|
||||||
|
|
@ -27,7 +25,7 @@ import type { AgentTool, ToolArg } from "./types.js";
|
||||||
import { agentToolError, } from "./types.js";
|
import { agentToolError, } from "./types.js";
|
||||||
|
|
||||||
const decodeJsonUnknown = S.decodeUnknownOption(S.UnknownFromJsonString);
|
const decodeJsonUnknown = S.decodeUnknownOption(S.UnknownFromJsonString);
|
||||||
const decodeTerm = S.decodeUnknownOption(TermSchema);
|
const decodeTerm = S.decodeUnknownOption(Term);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a Term to a human-readable string.
|
* Format a Term to a human-readable string.
|
||||||
|
|
@ -64,10 +62,10 @@ function parseQuestion(input: string): string {
|
||||||
/**
|
/**
|
||||||
* Explain data extracted from a graph-rag response.
|
* Explain data extracted from a graph-rag response.
|
||||||
*/
|
*/
|
||||||
export interface ExplainData {
|
export class ExplainData extends S.Class<ExplainData>("ExplainData")({
|
||||||
explainId: string;
|
explainId: S.String,
|
||||||
triples: Triple[];
|
triples: S.Array(Triple),
|
||||||
}
|
}, { description: "Explain payload extracted from a graph-rag response: id plus supporting triples." }) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query the knowledge graph for information about entities and their relationships.
|
* Query the knowledge graph for information about entities and their relationships.
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,11 @@ export const agentToolError = (operation: string, cause: unknown): AgentToolErro
|
||||||
message: errorMessage(cause),
|
message: errorMessage(cause),
|
||||||
});
|
});
|
||||||
|
|
||||||
export interface ToolArg {
|
export class ToolArg extends S.Class<ToolArg>("ToolArg")({
|
||||||
name: string;
|
name: S.String,
|
||||||
type: string;
|
type: S.String,
|
||||||
description: string;
|
description: S.String,
|
||||||
}
|
}, { description: "A named, typed argument accepted by an agent tool." }) {}
|
||||||
|
|
||||||
export interface AgentTool {
|
export interface AgentTool {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -43,10 +43,10 @@ export type ReActState =
|
||||||
| "final_answer"
|
| "final_answer"
|
||||||
| "complete";
|
| "complete";
|
||||||
|
|
||||||
export interface ParsedEvent {
|
export class ParsedEvent extends S.Class<ParsedEvent>("ParsedEvent")({
|
||||||
type: "thought" | "action" | "action_input" | "final_answer";
|
type: S.Literals(["thought", "action", "action_input", "final_answer"]),
|
||||||
content: string;
|
content: S.String,
|
||||||
}
|
}, { description: "A parsed ReAct stream event with its section content." }) {}
|
||||||
|
|
||||||
export type OnThought = (text: string, isFinal: boolean) => Effect.Effect<void, AgentToolError>;
|
export type OnThought = (text: string, isFinal: boolean) => Effect.Effect<void, AgentToolError>;
|
||||||
export type OnObservation = (text: string, isFinal: boolean) => Effect.Effect<void, AgentToolError>;
|
export type OnObservation = (text: string, isFinal: boolean) => Effect.Effect<void, AgentToolError>;
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,15 @@
|
||||||
|
|
||||||
import * as MutableHashMap from "effect/MutableHashMap";
|
import * as MutableHashMap from "effect/MutableHashMap";
|
||||||
import * as O from "effect/Option";
|
import * as O from "effect/Option";
|
||||||
|
import * as S from "effect/Schema";
|
||||||
|
|
||||||
export interface CollectionEntry {
|
export class CollectionEntry extends S.Class<CollectionEntry>("CollectionEntry")({
|
||||||
user: string;
|
user: S.String,
|
||||||
collection: string;
|
collection: S.String,
|
||||||
name: string;
|
name: S.String,
|
||||||
description: string;
|
description: S.String,
|
||||||
tags: string[];
|
tags: S.Array(S.String),
|
||||||
}
|
}, { description: "A librarian collection registration with its metadata tags." }) {}
|
||||||
|
|
||||||
export interface CollectionManager {
|
export interface CollectionManager {
|
||||||
readonly listCollections: (user: string) => CollectionEntry[];
|
readonly listCollections: (user: string) => CollectionEntry[];
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,10 @@ export type TextCompletionRuntimeError =
|
||||||
| TextCompletionProviderError
|
| TextCompletionProviderError
|
||||||
| TooManyRequestsError;
|
| TooManyRequestsError;
|
||||||
|
|
||||||
export interface LanguageModelProviderRequest {
|
export class LanguageModelProviderRequest extends S.Class<LanguageModelProviderRequest>("LanguageModelProviderRequest")({
|
||||||
readonly model: string;
|
model: S.String,
|
||||||
readonly temperature: number;
|
temperature: S.Finite,
|
||||||
}
|
}, { description: "Resolved model id and temperature for a language-model call." }) {}
|
||||||
|
|
||||||
export interface LanguageModelProviderOptions<Requirements> {
|
export interface LanguageModelProviderOptions<Requirements> {
|
||||||
readonly provider: string;
|
readonly provider: string;
|
||||||
|
|
|
||||||
|
|
@ -47,10 +47,10 @@ import * as MutableHashMap from "effect/MutableHashMap";
|
||||||
import * as O from "effect/Option";
|
import * as O from "effect/Option";
|
||||||
import * as S from "effect/Schema";
|
import * as S from "effect/Schema";
|
||||||
|
|
||||||
export interface PromptTemplate {
|
export class PromptTemplate extends S.Class<PromptTemplate>("PromptTemplate")({
|
||||||
system: string;
|
system: S.String,
|
||||||
prompt: string;
|
prompt: S.String,
|
||||||
}
|
}, { description: "A prompt template: system preamble plus prompt body." }) {}
|
||||||
|
|
||||||
export interface PromptTemplateConfig extends ProcessorConfig {
|
export interface PromptTemplateConfig extends ProcessorConfig {
|
||||||
configKey?: string;
|
configKey?: string;
|
||||||
|
|
|
||||||
|
|
@ -4,22 +4,22 @@ import { errorMessage } from "@trustgraph/base";
|
||||||
import { Effect } from "effect";
|
import { Effect } from "effect";
|
||||||
import * as S from "effect/Schema";
|
import * as S from "effect/Schema";
|
||||||
|
|
||||||
export interface QdrantCollectionStatus {
|
export class QdrantCollectionStatus extends S.Class<QdrantCollectionStatus>("QdrantCollectionStatus")({
|
||||||
readonly exists: boolean;
|
exists: S.Boolean,
|
||||||
}
|
}, { description: "Qdrant collection existence probe result." }) {}
|
||||||
|
|
||||||
export interface QdrantCollectionDescription {
|
export class QdrantCollectionDescription extends S.Class<QdrantCollectionDescription>("QdrantCollectionDescription")({
|
||||||
readonly name: string;
|
name: S.String,
|
||||||
}
|
}, { description: "A named Qdrant collection." }) {}
|
||||||
|
|
||||||
export interface QdrantCollections {
|
export class QdrantCollections extends S.Class<QdrantCollections>("QdrantCollections")({
|
||||||
readonly collections: ReadonlyArray<QdrantCollectionDescription>;
|
collections: S.Array(QdrantCollectionDescription),
|
||||||
}
|
}, { description: "Qdrant collection listing." }) {}
|
||||||
|
|
||||||
export interface QdrantScoredPoint {
|
export class QdrantScoredPoint extends S.Class<QdrantScoredPoint>("QdrantScoredPoint")({
|
||||||
readonly score: number;
|
score: S.Finite,
|
||||||
readonly payload?: unknown;
|
payload: S.optionalKey(S.Unknown),
|
||||||
}
|
}, { description: "A scored Qdrant search hit with optional payload." }) {}
|
||||||
|
|
||||||
export class QdrantClientError extends S.TaggedErrorClass<QdrantClientError>()("QdrantClientError", {
|
export class QdrantClientError extends S.TaggedErrorClass<QdrantClientError>()("QdrantClientError", {
|
||||||
message: S.String,
|
message: S.String,
|
||||||
|
|
|
||||||
|
|
@ -20,18 +20,18 @@ export interface QdrantDocQueryConfig {
|
||||||
clientFactory?: QdrantClientFactory;
|
clientFactory?: QdrantClientFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChunkMatch {
|
export class ChunkMatch extends S.Class<ChunkMatch>("ChunkMatch")({
|
||||||
chunkId: string;
|
chunkId: S.String,
|
||||||
score: number;
|
score: S.Finite,
|
||||||
content?: string;
|
content: S.optionalKey(S.String),
|
||||||
}
|
}, { description: "A scored document-chunk match from embeddings query." }) {}
|
||||||
|
|
||||||
export interface DocEmbeddingsQueryRequest {
|
export class DocEmbeddingsQueryRequest extends S.Class<DocEmbeddingsQueryRequest>("DocEmbeddingsQueryRequest")({
|
||||||
vector: number[];
|
vector: S.Array(S.Finite),
|
||||||
user: string;
|
user: S.String,
|
||||||
collection: string;
|
collection: S.String,
|
||||||
limit: number;
|
limit: S.Finite,
|
||||||
}
|
}, { description: "Document embeddings similarity query request." }) {}
|
||||||
|
|
||||||
export class QdrantDocEmbeddingsQueryError extends S.TaggedErrorClass<QdrantDocEmbeddingsQueryError>()(
|
export class QdrantDocEmbeddingsQueryError extends S.TaggedErrorClass<QdrantDocEmbeddingsQueryError>()(
|
||||||
"QdrantDocEmbeddingsQueryError",
|
"QdrantDocEmbeddingsQueryError",
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,7 @@
|
||||||
* Python reference: trustgraph-flow/trustgraph/query/graph_embeddings/qdrant/service.py
|
* Python reference: trustgraph-flow/trustgraph/query/graph_embeddings/qdrant/service.py
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Term } from "@trustgraph/base";
|
import { Term, errorMessage } from "@trustgraph/base";
|
||||||
import { errorMessage, } from "@trustgraph/base";
|
|
||||||
import { Config, Context, Effect, Layer } from "effect";
|
import { Config, Context, Effect, Layer } 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";
|
||||||
|
|
@ -24,17 +23,17 @@ export interface QdrantGraphQueryConfig {
|
||||||
clientFactory?: QdrantClientFactory;
|
clientFactory?: QdrantClientFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EntityMatch {
|
export class EntityMatch extends S.Class<EntityMatch>("EntityMatch")({
|
||||||
entity: Term;
|
entity: Term,
|
||||||
score: number;
|
score: S.Finite,
|
||||||
}
|
}, { description: "A scored graph-entity match from embeddings query." }) {}
|
||||||
|
|
||||||
export interface GraphEmbeddingsQueryRequest {
|
export class GraphEmbeddingsQueryRequest extends S.Class<GraphEmbeddingsQueryRequest>("GraphEmbeddingsQueryRequest")({
|
||||||
vector: number[];
|
vector: S.Array(S.Finite),
|
||||||
user: string;
|
user: S.String,
|
||||||
collection: string;
|
collection: S.String,
|
||||||
limit: number;
|
limit: S.Finite,
|
||||||
}
|
}, { description: "Graph embeddings similarity query request." }) {}
|
||||||
|
|
||||||
export class QdrantGraphEmbeddingsQueryError extends S.TaggedErrorClass<QdrantGraphEmbeddingsQueryError>()(
|
export class QdrantGraphEmbeddingsQueryError extends S.TaggedErrorClass<QdrantGraphEmbeddingsQueryError>()(
|
||||||
"QdrantGraphEmbeddingsQueryError",
|
"QdrantGraphEmbeddingsQueryError",
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,7 @@ const onGraphRagRequest = Effect.fn("GraphRagService.onRequest")(function* (
|
||||||
endOfStream: true,
|
endOfStream: true,
|
||||||
message_type: "explain",
|
message_type: "explain",
|
||||||
explain_id: `explain-${requestId}`,
|
explain_id: `explain-${requestId}`,
|
||||||
explain_triples: result.subgraph,
|
explain_triples: [...result.subgraph],
|
||||||
};
|
};
|
||||||
|
|
||||||
yield* producer.send(requestId, response);
|
yield* producer.send(requestId, response);
|
||||||
|
|
|
||||||
|
|
@ -16,23 +16,22 @@ import type {
|
||||||
Term,
|
Term,
|
||||||
TextCompletionRequest,
|
TextCompletionRequest,
|
||||||
TextCompletionResponse,
|
TextCompletionResponse,
|
||||||
Triple,
|
|
||||||
TriplesQueryRequest,
|
TriplesQueryRequest,
|
||||||
TriplesQueryResponse,
|
TriplesQueryResponse,
|
||||||
} from "@trustgraph/base";
|
} from "@trustgraph/base";
|
||||||
import { errorMessage } from "@trustgraph/base";
|
import { Triple, errorMessage } from "@trustgraph/base";
|
||||||
import { Context, Effect, Layer, Match } from "effect";
|
import { Context, Effect, Layer, Match } 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";
|
||||||
|
|
||||||
export interface GraphRagConfig {
|
export class GraphRagConfig extends S.Class<GraphRagConfig>("GraphRagConfig")({
|
||||||
entityLimit?: number;
|
entityLimit: S.optionalKey(S.Finite),
|
||||||
tripleLimit?: number;
|
tripleLimit: S.optionalKey(S.Finite),
|
||||||
maxSubgraphSize?: number;
|
maxSubgraphSize: S.optionalKey(S.Finite),
|
||||||
maxPathLength?: number;
|
maxPathLength: S.optionalKey(S.Finite),
|
||||||
edgeScoreLimit?: number;
|
edgeScoreLimit: S.optionalKey(S.Finite),
|
||||||
edgeLimit?: number;
|
edgeLimit: S.optionalKey(S.Finite),
|
||||||
}
|
}, { description: "Graph RAG retrieval tuning limits." }) {}
|
||||||
|
|
||||||
export interface GraphRagClients {
|
export interface GraphRagClients {
|
||||||
llm: EffectRequestResponse<TextCompletionRequest, TextCompletionResponse>;
|
llm: EffectRequestResponse<TextCompletionRequest, TextCompletionResponse>;
|
||||||
|
|
@ -53,10 +52,10 @@ export interface GraphRagQueryOptions {
|
||||||
readonly chunkCallback?: ChunkCallback;
|
readonly chunkCallback?: ChunkCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GraphRagResult {
|
export class GraphRagResult extends S.Class<GraphRagResult>("GraphRagResult")({
|
||||||
answer: string;
|
answer: S.String,
|
||||||
subgraph: Triple[];
|
subgraph: S.Array(Triple),
|
||||||
}
|
}, { description: "Graph RAG answer with the supporting subgraph." }) {}
|
||||||
|
|
||||||
interface NormalizedGraphRagConfig {
|
interface NormalizedGraphRagConfig {
|
||||||
entityLimit: number;
|
entityLimit: number;
|
||||||
|
|
|
||||||
|
|
@ -22,17 +22,17 @@ export interface QdrantDocEmbeddingsConfig {
|
||||||
clientFactory?: QdrantClientFactory;
|
clientFactory?: QdrantClientFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DocEmbeddingChunk {
|
export class DocEmbeddingChunk extends S.Class<DocEmbeddingChunk>("DocEmbeddingChunk")({
|
||||||
chunkId: string;
|
chunkId: S.String,
|
||||||
vector: number[];
|
vector: S.Array(S.Finite),
|
||||||
content?: string;
|
content: S.optionalKey(S.String),
|
||||||
}
|
}, { description: "A document chunk paired with its embedding vector." }) {}
|
||||||
|
|
||||||
export interface DocEmbeddingsMessage {
|
export class DocEmbeddingsMessage extends S.Class<DocEmbeddingsMessage>("DocEmbeddingsMessage")({
|
||||||
user: string;
|
user: S.String,
|
||||||
collection: string;
|
collection: S.String,
|
||||||
chunks: DocEmbeddingChunk[];
|
chunks: S.Array(DocEmbeddingChunk),
|
||||||
}
|
}, { description: "Document embeddings store message: chunks to upsert for a user collection." }) {}
|
||||||
|
|
||||||
export class QdrantDocEmbeddingsStoreError extends S.TaggedErrorClass<QdrantDocEmbeddingsStoreError>()(
|
export class QdrantDocEmbeddingsStoreError extends S.TaggedErrorClass<QdrantDocEmbeddingsStoreError>()(
|
||||||
"QdrantDocEmbeddingsStoreError",
|
"QdrantDocEmbeddingsStoreError",
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,7 @@
|
||||||
* Python reference: trustgraph-flow/trustgraph/storage/graph_embeddings/qdrant/write.py
|
* Python reference: trustgraph-flow/trustgraph/storage/graph_embeddings/qdrant/write.py
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Term } from "@trustgraph/base";
|
import { Term, errorMessage } from "@trustgraph/base";
|
||||||
import { errorMessage, } from "@trustgraph/base";
|
|
||||||
import { Config, Context, Effect, Layer, Match, Random } from "effect";
|
import { Config, Context, Effect, Layer, Match, Random } from "effect";
|
||||||
import * as MutableHashSet from "effect/MutableHashSet";
|
import * as MutableHashSet from "effect/MutableHashSet";
|
||||||
import * as O from "effect/Option";
|
import * as O from "effect/Option";
|
||||||
|
|
@ -23,17 +22,17 @@ export interface QdrantGraphEmbeddingsConfig {
|
||||||
clientFactory?: QdrantClientFactory;
|
clientFactory?: QdrantClientFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GraphEmbeddingEntity {
|
export class GraphEmbeddingEntity extends S.Class<GraphEmbeddingEntity>("GraphEmbeddingEntity")({
|
||||||
entity: Term;
|
entity: Term,
|
||||||
vector: number[];
|
vector: S.Array(S.Finite),
|
||||||
chunkId?: string;
|
chunkId: S.optionalKey(S.String),
|
||||||
}
|
}, { description: "A graph entity paired with its embedding vector." }) {}
|
||||||
|
|
||||||
export interface GraphEmbeddingsMessage {
|
export class GraphEmbeddingsMessage extends S.Class<GraphEmbeddingsMessage>("GraphEmbeddingsMessage")({
|
||||||
user: string;
|
user: S.String,
|
||||||
collection: string;
|
collection: S.String,
|
||||||
entities: GraphEmbeddingEntity[];
|
entities: S.Array(GraphEmbeddingEntity),
|
||||||
}
|
}, { description: "Graph embeddings store message: entities to upsert for a user collection." }) {}
|
||||||
|
|
||||||
export class QdrantGraphEmbeddingsStoreError extends S.TaggedErrorClass<QdrantGraphEmbeddingsStoreError>()(
|
export class QdrantGraphEmbeddingsStoreError extends S.TaggedErrorClass<QdrantGraphEmbeddingsStoreError>()(
|
||||||
"QdrantGraphEmbeddingsStoreError",
|
"QdrantGraphEmbeddingsStoreError",
|
||||||
|
|
|
||||||
|
|
@ -1217,16 +1217,18 @@ export interface TrustGraphMcpOptions {
|
||||||
readonly port?: number | undefined
|
readonly port?: number | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TrustGraphMcpConfigShape {
|
const McpPathInput = S.Union([S.Literal("*"), S.TemplateLiteral(["/", S.String])])
|
||||||
readonly gatewayUrl: string
|
|
||||||
readonly user: string
|
export class TrustGraphMcpConfigShape extends S.Class<TrustGraphMcpConfigShape>("TrustGraphMcpConfigShape")({
|
||||||
readonly token: string | undefined
|
gatewayUrl: S.String,
|
||||||
readonly flowId: string
|
user: S.String,
|
||||||
readonly name: string
|
token: S.UndefinedOr(S.String),
|
||||||
readonly version: string
|
flowId: S.String,
|
||||||
readonly mcpPath: HttpRouter.PathInput
|
name: S.String,
|
||||||
readonly port: number
|
version: S.String,
|
||||||
}
|
mcpPath: McpPathInput,
|
||||||
|
port: S.Finite,
|
||||||
|
}, { description: "Resolved TrustGraph MCP server configuration." }) {}
|
||||||
|
|
||||||
const readNonEmpty = (value: string | undefined): string | undefined =>
|
const readNonEmpty = (value: string | undefined): string | undefined =>
|
||||||
value !== undefined && value.length > 0 ? value : undefined
|
value !== undefined && value.length > 0 ? value : undefined
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,44 @@ type WorkbenchError = WorkbenchPromiseError;
|
||||||
|
|
||||||
const isWorkbenchPromiseError = S.is(WorkbenchPromiseError);
|
const isWorkbenchPromiseError = S.is(WorkbenchPromiseError);
|
||||||
|
|
||||||
|
const ClientTriple: S.Codec<Triple, Triple> = S.suspend(() =>
|
||||||
|
S.Struct({
|
||||||
|
s: ClientTerm,
|
||||||
|
p: ClientTerm,
|
||||||
|
o: ClientTerm,
|
||||||
|
g: S.optionalKey(S.String),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const ClientTerm: S.Codec<Term, Term> = S.suspend(() =>
|
||||||
|
S.Union([
|
||||||
|
S.Struct({
|
||||||
|
t: S.Literal("i"),
|
||||||
|
i: S.String,
|
||||||
|
}),
|
||||||
|
S.Struct({
|
||||||
|
t: S.Literal("b"),
|
||||||
|
d: S.String,
|
||||||
|
}),
|
||||||
|
S.Struct({
|
||||||
|
t: S.Literal("l"),
|
||||||
|
v: S.String,
|
||||||
|
dt: S.optionalKey(S.String),
|
||||||
|
ln: S.optionalKey(S.String),
|
||||||
|
}),
|
||||||
|
S.Struct({
|
||||||
|
t: S.Literal("t"),
|
||||||
|
tr: S.optionalKey(ClientTriple),
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
const ClientExplainEvent: S.Codec<ExplainEvent, ExplainEvent> = S.Struct({
|
||||||
|
explainId: S.String,
|
||||||
|
explainGraph: S.String,
|
||||||
|
explainTriples: S.optionalKey(S.Array(ClientTriple).pipe(S.mutable)),
|
||||||
|
});
|
||||||
|
|
||||||
function errorMessage(error: unknown): string {
|
function errorMessage(error: unknown): string {
|
||||||
if (isWorkbenchPromiseError(error)) return error.message;
|
if (isWorkbenchPromiseError(error)) return error.message;
|
||||||
if (Predicate.isObject(error) && Predicate.hasProperty(error, "message")) {
|
if (Predicate.isObject(error) && Predicate.hasProperty(error, "message")) {
|
||||||
|
|
@ -146,25 +184,25 @@ const mutationCounter = Metric.counter("trustgraph_workbench_mutation_total", {
|
||||||
// Shared types
|
// Shared types
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export interface FeatureSwitches {
|
export class FeatureSwitches extends S.Class<FeatureSwitches>("FeatureSwitches")({
|
||||||
flowClasses: boolean;
|
flowClasses: S.Boolean,
|
||||||
submissions: boolean;
|
submissions: S.Boolean,
|
||||||
tokenCost: boolean;
|
tokenCost: S.Boolean,
|
||||||
schemas: boolean;
|
schemas: S.Boolean,
|
||||||
structuredQuery: boolean;
|
structuredQuery: S.Boolean,
|
||||||
ontologyEditor: boolean;
|
ontologyEditor: S.Boolean,
|
||||||
agentTools: boolean;
|
agentTools: S.Boolean,
|
||||||
mcpTools: boolean;
|
mcpTools: S.Boolean,
|
||||||
llmModels: boolean;
|
llmModels: S.Boolean,
|
||||||
}
|
}, { description: "Workbench feature visibility switches." }) {}
|
||||||
|
|
||||||
export interface Settings {
|
export class Settings extends S.Class<Settings>("Settings")({
|
||||||
user: string;
|
user: S.String,
|
||||||
apiKey: string;
|
apiKey: S.String,
|
||||||
collection: string;
|
collection: S.String,
|
||||||
gatewayUrl: string;
|
gatewayUrl: S.String,
|
||||||
featureSwitches: FeatureSwitches;
|
featureSwitches: FeatureSwitches,
|
||||||
}
|
}, { description: "Persisted workbench connection and display settings." }) {}
|
||||||
|
|
||||||
export interface WorkbenchApiFactory {
|
export interface WorkbenchApiFactory {
|
||||||
readonly create: (settings: Settings) => BaseApi;
|
readonly create: (settings: Settings) => BaseApi;
|
||||||
|
|
@ -176,31 +214,31 @@ export type ChatMode = "graph-rag" | "document-rag" | "agent";
|
||||||
export type MessageRole = "user" | "assistant" | "system";
|
export type MessageRole = "user" | "assistant" | "system";
|
||||||
export type AgentPhase = "think" | "observe" | "answer";
|
export type AgentPhase = "think" | "observe" | "answer";
|
||||||
|
|
||||||
export interface ChatMessage {
|
export class ChatMessage extends S.Class<ChatMessage>("ChatMessage")({
|
||||||
id: string;
|
id: S.String,
|
||||||
role: MessageRole;
|
role: S.Literals(["user", "assistant", "system"]),
|
||||||
content: string;
|
content: S.String,
|
||||||
timestamp: number;
|
timestamp: S.Finite,
|
||||||
isStreaming?: boolean;
|
isStreaming: S.optionalKey(S.Boolean),
|
||||||
metadata?: {
|
metadata: S.optionalKey(S.Struct({
|
||||||
model?: string;
|
model: S.optionalKey(S.String),
|
||||||
inTokens?: number;
|
inTokens: S.optionalKey(S.Finite),
|
||||||
outTokens?: number;
|
outTokens: S.optionalKey(S.Finite),
|
||||||
};
|
})),
|
||||||
agentPhases?: {
|
agentPhases: S.optionalKey(S.Struct({
|
||||||
think: string;
|
think: S.String,
|
||||||
observe: string;
|
observe: S.String,
|
||||||
answer: string;
|
answer: S.String,
|
||||||
};
|
})),
|
||||||
activePhase?: AgentPhase;
|
activePhase: S.optionalKey(S.Literals(["think", "observe", "answer"])),
|
||||||
explainEvents?: ExplainEvent[];
|
explainEvents: S.optionalKey(S.Array(ClientExplainEvent).pipe(S.mutable)),
|
||||||
}
|
}, { description: "A rendered chat transcript message." }) {}
|
||||||
|
|
||||||
export interface ConversationState {
|
export class ConversationState extends S.Class<ConversationState>("ConversationState")({
|
||||||
messages: ChatMessage[];
|
messages: S.Array(ChatMessage).pipe(S.mutable),
|
||||||
input: string;
|
input: S.String,
|
||||||
chatMode: ChatMode;
|
chatMode: S.Literals(["graph-rag", "document-rag", "agent"]),
|
||||||
}
|
}, { description: "Persisted workbench chat state." }) {}
|
||||||
|
|
||||||
export interface FlowSummary {
|
export interface FlowSummary {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -216,60 +254,60 @@ export interface ProcessingMetadata {
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UploadProgress {
|
export class UploadProgress extends S.Class<UploadProgress>("UploadProgress")({
|
||||||
phase: "preparing" | "uploading" | "finalizing";
|
phase: S.Literals(["preparing", "uploading", "finalizing"]),
|
||||||
chunksTotal: number;
|
chunksTotal: S.Finite,
|
||||||
chunksUploaded: number;
|
chunksUploaded: S.Finite,
|
||||||
bytesTotal: number;
|
bytesTotal: S.Finite,
|
||||||
bytesUploaded: number;
|
bytesUploaded: S.Finite,
|
||||||
}
|
}, { description: "Current chunked document upload progress." }) {}
|
||||||
|
|
||||||
export interface UploadForm {
|
export class UploadForm extends S.Class<UploadForm>("UploadForm")({
|
||||||
file: File | null;
|
file: S.NullOr(S.File),
|
||||||
title: string;
|
title: S.String,
|
||||||
tags: string;
|
tags: S.String,
|
||||||
comments: string;
|
comments: S.String,
|
||||||
uploading: boolean;
|
uploading: S.Boolean,
|
||||||
dragOver: boolean;
|
dragOver: S.Boolean,
|
||||||
progress: UploadProgress | null;
|
progress: S.NullOr(UploadProgress),
|
||||||
}
|
}, { description: "Workbench document upload form state." }) {}
|
||||||
|
|
||||||
export interface McpServerConfig {
|
export class McpServerConfig extends S.Class<McpServerConfig>("McpServerConfig")({
|
||||||
url: string;
|
url: S.String,
|
||||||
"remote-name"?: string;
|
"remote-name": S.optionalKey(S.String),
|
||||||
"auth-token"?: string;
|
"auth-token": S.optionalKey(S.String),
|
||||||
}
|
}, { description: "Workbench MCP server config entry payload." }) {}
|
||||||
|
|
||||||
export interface McpServerEntry {
|
export class McpServerEntry extends S.Class<McpServerEntry>("McpServerEntry")({
|
||||||
key: string;
|
key: S.String,
|
||||||
config: McpServerConfig;
|
config: McpServerConfig,
|
||||||
}
|
}, { description: "Workbench MCP server config entry." }) {}
|
||||||
|
|
||||||
export interface ToolArgument {
|
export class ToolArgument extends S.Class<ToolArgument>("ToolArgument")({
|
||||||
name: string;
|
name: S.String,
|
||||||
type: string;
|
type: S.String,
|
||||||
description: string;
|
description: S.String,
|
||||||
}
|
}, { description: "Workbench MCP tool argument descriptor." }) {}
|
||||||
|
|
||||||
export interface ToolConfig {
|
export class ToolConfig extends S.Class<ToolConfig>("ToolConfig")({
|
||||||
type: string;
|
type: S.String,
|
||||||
name: string;
|
name: S.String,
|
||||||
description: string;
|
description: S.String,
|
||||||
"mcp-tool"?: string;
|
"mcp-tool": S.optionalKey(S.String),
|
||||||
group?: string[];
|
group: S.optionalKey(S.Array(S.String).pipe(S.mutable)),
|
||||||
arguments?: ToolArgument[];
|
arguments: S.optionalKey(S.Array(ToolArgument).pipe(S.mutable)),
|
||||||
}
|
}, { description: "Workbench tool config entry payload." }) {}
|
||||||
|
|
||||||
export interface ToolEntry {
|
export class ToolEntry extends S.Class<ToolEntry>("ToolEntry")({
|
||||||
key: string;
|
key: S.String,
|
||||||
config: ToolConfig;
|
config: ToolConfig,
|
||||||
}
|
}, { description: "Workbench tool config entry." }) {}
|
||||||
|
|
||||||
export interface TokenCost {
|
export class TokenCost extends S.Class<TokenCost>("TokenCost")({
|
||||||
model: string;
|
model: S.String,
|
||||||
input_price: number;
|
input_price: S.Finite,
|
||||||
output_price: number;
|
output_price: S.Finite,
|
||||||
}
|
}, { description: "Model token pricing row." }) {}
|
||||||
|
|
||||||
export interface CollectionSummary {
|
export interface CollectionSummary {
|
||||||
id?: string;
|
id?: string;
|
||||||
|
|
@ -280,61 +318,61 @@ export interface CollectionSummary {
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Notification {
|
export class Notification extends S.Class<Notification>("Notification")({
|
||||||
id: string;
|
id: S.String,
|
||||||
type: "success" | "error" | "warning" | "info";
|
type: S.Literals(["success", "error", "warning", "info"]),
|
||||||
title: string;
|
title: S.String,
|
||||||
description?: string;
|
description: S.optionalKey(S.String),
|
||||||
}
|
}, { description: "Transient workbench notification toast." }) {}
|
||||||
|
|
||||||
export interface McpServerForm {
|
export class McpServerForm extends S.Class<McpServerForm>("McpServerForm")({
|
||||||
key: string;
|
key: S.String,
|
||||||
url: string;
|
url: S.String,
|
||||||
remoteName: string;
|
remoteName: S.String,
|
||||||
authToken: string;
|
authToken: S.String,
|
||||||
showToken: boolean;
|
showToken: S.Boolean,
|
||||||
saving: boolean;
|
saving: S.Boolean,
|
||||||
keyError: string;
|
keyError: S.String,
|
||||||
}
|
}, { description: "Editable MCP server dialog state." }) {}
|
||||||
|
|
||||||
export interface McpToolForm {
|
export class McpToolForm extends S.Class<McpToolForm>("McpToolForm")({
|
||||||
key: string;
|
key: S.String,
|
||||||
name: string;
|
name: S.String,
|
||||||
description: string;
|
description: S.String,
|
||||||
mcpTool: string;
|
mcpTool: S.String,
|
||||||
group: string;
|
group: S.String,
|
||||||
args: ToolArgument[];
|
args: S.Array(ToolArgument).pipe(S.mutable),
|
||||||
saving: boolean;
|
saving: S.Boolean,
|
||||||
keyError: string;
|
keyError: S.String,
|
||||||
}
|
}, { description: "Editable MCP tool dialog state." }) {}
|
||||||
|
|
||||||
export interface StartFlowForm {
|
export class StartFlowForm extends S.Class<StartFlowForm>("StartFlowForm")({
|
||||||
id: string;
|
id: S.String,
|
||||||
blueprint: string;
|
blueprint: S.String,
|
||||||
description: string;
|
description: S.String,
|
||||||
paramsJson: string;
|
paramsJson: S.String,
|
||||||
submitting: boolean;
|
submitting: S.Boolean,
|
||||||
paramsError: string | null;
|
paramsError: S.NullOr(S.String),
|
||||||
submitted: boolean;
|
submitted: S.Boolean,
|
||||||
definitionExpanded: boolean;
|
definitionExpanded: S.Boolean,
|
||||||
}
|
}, { description: "Start-flow dialog form state." }) {}
|
||||||
|
|
||||||
export interface CollectionForm {
|
export class CollectionForm extends S.Class<CollectionForm>("CollectionForm")({
|
||||||
id: string;
|
id: S.String,
|
||||||
name: string;
|
name: S.String,
|
||||||
description: string;
|
description: S.String,
|
||||||
tags: string;
|
tags: S.String,
|
||||||
submitting: boolean;
|
submitting: S.Boolean,
|
||||||
}
|
}, { description: "Collection creation form state." }) {}
|
||||||
|
|
||||||
export interface GraphViewState {
|
export class GraphViewState extends S.Class<GraphViewState>("GraphViewState")({
|
||||||
searchTerm: string;
|
searchTerm: S.String,
|
||||||
selectedNodeId: string | null;
|
selectedNodeId: S.NullOr(S.String),
|
||||||
selectedNodeLabel: string | null;
|
selectedNodeLabel: S.NullOr(S.String),
|
||||||
showLabels: boolean;
|
showLabels: S.Boolean,
|
||||||
showTypes: boolean;
|
showTypes: S.Boolean,
|
||||||
nodeLimit: number;
|
nodeLimit: S.Finite,
|
||||||
}
|
}, { description: "Workbench graph display controls." }) {}
|
||||||
|
|
||||||
const DEFAULT_FEATURE_SWITCHES: FeatureSwitches = {
|
const DEFAULT_FEATURE_SWITCHES: FeatureSwitches = {
|
||||||
flowClasses: false,
|
flowClasses: false,
|
||||||
|
|
@ -356,50 +394,6 @@ export const DEFAULT_SETTINGS: Settings = {
|
||||||
featureSwitches: DEFAULT_FEATURE_SWITCHES,
|
featureSwitches: DEFAULT_FEATURE_SWITCHES,
|
||||||
};
|
};
|
||||||
|
|
||||||
const SettingsSchema = S.Struct({
|
|
||||||
user: S.String,
|
|
||||||
apiKey: S.String,
|
|
||||||
collection: S.String,
|
|
||||||
gatewayUrl: S.String,
|
|
||||||
featureSwitches: S.Struct({
|
|
||||||
flowClasses: S.Boolean,
|
|
||||||
submissions: S.Boolean,
|
|
||||||
tokenCost: S.Boolean,
|
|
||||||
schemas: S.Boolean,
|
|
||||||
structuredQuery: S.Boolean,
|
|
||||||
ontologyEditor: S.Boolean,
|
|
||||||
agentTools: S.Boolean,
|
|
||||||
mcpTools: S.Boolean,
|
|
||||||
llmModels: S.Boolean,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const ChatMessageSchema = S.Struct({
|
|
||||||
id: S.String,
|
|
||||||
role: S.Union([S.Literal("user"), S.Literal("assistant"), S.Literal("system")]),
|
|
||||||
content: S.String,
|
|
||||||
timestamp: S.Finite,
|
|
||||||
isStreaming: S.optionalKey(S.Boolean),
|
|
||||||
metadata: S.optionalKey(S.Struct({
|
|
||||||
model: S.optionalKey(S.String),
|
|
||||||
inTokens: S.optionalKey(S.Finite),
|
|
||||||
outTokens: S.optionalKey(S.Finite),
|
|
||||||
})),
|
|
||||||
agentPhases: S.optionalKey(S.Struct({
|
|
||||||
think: S.String,
|
|
||||||
observe: S.String,
|
|
||||||
answer: S.String,
|
|
||||||
})),
|
|
||||||
activePhase: S.optionalKey(S.Union([S.Literal("think"), S.Literal("observe"), S.Literal("answer")])),
|
|
||||||
explainEvents: S.optionalKey(S.Array(S.Unknown)),
|
|
||||||
});
|
|
||||||
|
|
||||||
const ConversationSchema = S.Struct({
|
|
||||||
messages: S.Array(ChatMessageSchema),
|
|
||||||
input: S.String,
|
|
||||||
chatMode: S.Union([S.Literal("graph-rag"), S.Literal("document-rag"), S.Literal("agent")]),
|
|
||||||
});
|
|
||||||
|
|
||||||
const ThemeSchema = S.Union([S.Literal("dark"), S.Literal("light")]);
|
const ThemeSchema = S.Union([S.Literal("dark"), S.Literal("light")]);
|
||||||
const FlowIdSchema = S.String;
|
const FlowIdSchema = S.String;
|
||||||
|
|
||||||
|
|
@ -523,11 +517,14 @@ const randomId = Effect.fn("trustgraph.workbench.randomId")(function*(prefix: st
|
||||||
|
|
||||||
function metadataFrom(metadata: StreamingMetadata | undefined): ChatMessage["metadata"] | undefined {
|
function metadataFrom(metadata: StreamingMetadata | undefined): ChatMessage["metadata"] | undefined {
|
||||||
if (metadata === undefined) return undefined;
|
if (metadata === undefined) return undefined;
|
||||||
const result: NonNullable<ChatMessage["metadata"]> = {};
|
if (metadata.model === undefined && metadata.in_token === undefined && metadata.out_token === undefined) {
|
||||||
if (metadata.model !== undefined) result.model = metadata.model;
|
return undefined;
|
||||||
if (metadata.in_token !== undefined) result.inTokens = metadata.in_token;
|
}
|
||||||
if (metadata.out_token !== undefined) result.outTokens = metadata.out_token;
|
return {
|
||||||
return Object.keys(result).length > 0 ? result : undefined;
|
...(metadata.model !== undefined ? { model: metadata.model } : {}),
|
||||||
|
...(metadata.in_token !== undefined ? { inTokens: metadata.in_token } : {}),
|
||||||
|
...(metadata.out_token !== undefined ? { outTokens: metadata.out_token } : {}),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function withoutActivePhase(message: ChatMessage): ChatMessage {
|
function withoutActivePhase(message: ChatMessage): ChatMessage {
|
||||||
|
|
@ -609,40 +606,8 @@ const StreamingEnvelopeSchema = S.Struct({
|
||||||
});
|
});
|
||||||
type StreamingEnvelope = typeof StreamingEnvelopeSchema.Type;
|
type StreamingEnvelope = typeof StreamingEnvelopeSchema.Type;
|
||||||
|
|
||||||
const ClientTripleSchema: S.Codec<Triple, Triple> = S.suspend(() =>
|
|
||||||
S.Struct({
|
|
||||||
s: ClientTermSchema,
|
|
||||||
p: ClientTermSchema,
|
|
||||||
o: ClientTermSchema,
|
|
||||||
g: S.optionalKey(S.String),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const ClientTermSchema: S.Codec<Term, Term> = S.suspend(() =>
|
|
||||||
S.Union([
|
|
||||||
S.Struct({
|
|
||||||
t: S.Literal("i"),
|
|
||||||
i: S.String,
|
|
||||||
}),
|
|
||||||
S.Struct({
|
|
||||||
t: S.Literal("b"),
|
|
||||||
d: S.String,
|
|
||||||
}),
|
|
||||||
S.Struct({
|
|
||||||
t: S.Literal("l"),
|
|
||||||
v: S.String,
|
|
||||||
dt: S.optionalKey(S.String),
|
|
||||||
ln: S.optionalKey(S.String),
|
|
||||||
}),
|
|
||||||
S.Struct({
|
|
||||||
t: S.Literal("t"),
|
|
||||||
tr: S.optionalKey(ClientTripleSchema),
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
);
|
|
||||||
|
|
||||||
const decodeStreamingEnvelope = S.decodeUnknownOption(StreamingEnvelopeSchema);
|
const decodeStreamingEnvelope = S.decodeUnknownOption(StreamingEnvelopeSchema);
|
||||||
const decodeClientTriples = S.decodeUnknownOption(S.Array(ClientTripleSchema).pipe(S.mutable));
|
const decodeClientTriples = S.decodeUnknownOption(S.Array(ClientTriple).pipe(S.mutable));
|
||||||
|
|
||||||
function gatewayHttpBaseUrl(settings: Settings): string {
|
function gatewayHttpBaseUrl(settings: Settings): string {
|
||||||
const raw = settings.gatewayUrl.trim();
|
const raw = settings.gatewayUrl.trim();
|
||||||
|
|
@ -1532,7 +1497,7 @@ function withActivity<A, R>(
|
||||||
export const settingsAtom = Atom.kvs({
|
export const settingsAtom = Atom.kvs({
|
||||||
runtime: workbenchRuntime,
|
runtime: workbenchRuntime,
|
||||||
key: "trustgraph-workbench-settings-v1",
|
key: "trustgraph-workbench-settings-v1",
|
||||||
schema: S.toCodecJson(SettingsSchema),
|
schema: S.toCodecJson(Settings),
|
||||||
defaultValue: legacySettings,
|
defaultValue: legacySettings,
|
||||||
}).pipe(Atom.keepAlive) as Atom.Writable<Settings, Settings>;
|
}).pipe(Atom.keepAlive) as Atom.Writable<Settings, Settings>;
|
||||||
|
|
||||||
|
|
@ -1553,7 +1518,7 @@ export const flowIdAtom = Atom.kvs({
|
||||||
export const conversationAtom = Atom.kvs({
|
export const conversationAtom = Atom.kvs({
|
||||||
runtime: workbenchRuntime,
|
runtime: workbenchRuntime,
|
||||||
key: "trustgraph-workbench-conversation-v1",
|
key: "trustgraph-workbench-conversation-v1",
|
||||||
schema: S.toCodecJson(ConversationSchema),
|
schema: S.toCodecJson(ConversationState),
|
||||||
defaultValue: legacyConversation,
|
defaultValue: legacyConversation,
|
||||||
}).pipe(Atom.keepAlive) as unknown as Atom.Writable<ConversationState, ConversationState>;
|
}).pipe(Atom.keepAlive) as unknown as Atom.Writable<ConversationState, ConversationState>;
|
||||||
|
|
||||||
|
|
@ -1855,17 +1820,17 @@ export const collectionsAtom = queryAtom(
|
||||||
{ reactivityKeys: ["collections"] },
|
{ reactivityKeys: ["collections"] },
|
||||||
).pipe(Atom.setIdleTTL("2 minutes"));
|
).pipe(Atom.setIdleTTL("2 minutes"));
|
||||||
|
|
||||||
export interface GraphTriplesInput {
|
export class GraphTriplesInput extends S.Class<GraphTriplesInput>("GraphTriplesInput")({
|
||||||
readonly flowId: string;
|
flowId: S.String,
|
||||||
readonly collection: string;
|
collection: S.String,
|
||||||
readonly limit: number;
|
limit: S.Finite,
|
||||||
}
|
}, { description: "Workbench graph triples query atom input." }) {}
|
||||||
|
|
||||||
export interface ExplainTriplesInput {
|
export class ExplainTriplesInput extends S.Class<ExplainTriplesInput>("ExplainTriplesInput")({
|
||||||
readonly events: ExplainEvent[];
|
events: S.Array(ClientExplainEvent).pipe(S.mutable),
|
||||||
readonly flowId: string;
|
flowId: S.String,
|
||||||
readonly collection: string;
|
collection: S.String,
|
||||||
}
|
}, { description: "Workbench explain triples query atom input." }) {}
|
||||||
|
|
||||||
const atomFamilyKeySeparator = "\u001f";
|
const atomFamilyKeySeparator = "\u001f";
|
||||||
const explainGraphSeparator = "\u001e";
|
const explainGraphSeparator = "\u001e";
|
||||||
|
|
@ -2139,13 +2104,13 @@ export const deleteMcpToolAtom = commandAtom<string, void>("deleteMcpTool", Effe
|
||||||
|
|
||||||
const chunkedUploadThreshold = 1_000_000;
|
const chunkedUploadThreshold = 1_000_000;
|
||||||
|
|
||||||
export interface UploadDocumentInput {
|
export class UploadDocumentInput extends S.Class<UploadDocumentInput>("UploadDocumentInput")({
|
||||||
readonly base64: string;
|
base64: S.String,
|
||||||
readonly mimeType: string;
|
mimeType: S.String,
|
||||||
readonly title: string;
|
title: S.String,
|
||||||
readonly comments: string;
|
comments: S.String,
|
||||||
readonly tags: string[];
|
tags: S.Array(S.String).pipe(S.mutable),
|
||||||
}
|
}, { description: "Workbench document upload command payload." }) {}
|
||||||
|
|
||||||
const uploadDocumentEffect = Effect.fn("trustgraph.workbench.uploadDocument.effect")(function*(
|
const uploadDocumentEffect = Effect.fn("trustgraph.workbench.uploadDocument.effect")(function*(
|
||||||
input: UploadDocumentInput,
|
input: UploadDocumentInput,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import type { Triple, Term } from "@trustgraph/client";
|
import type { Triple, Term } from "@trustgraph/client";
|
||||||
import { Match } from "effect";
|
import { Match } from "effect";
|
||||||
|
import * as S from "effect/Schema";
|
||||||
import type { ForceGraphProps, NodeObject, LinkObject } from "react-force-graph-2d";
|
import type { ForceGraphProps, NodeObject, LinkObject } from "react-force-graph-2d";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
@ -27,10 +28,23 @@ export interface GraphLink extends LinkObject {
|
||||||
label: string;
|
label: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GraphData {
|
const GraphNodeValue: S.Codec<GraphNode, GraphNode> = S.Struct({
|
||||||
nodes: GraphNode[];
|
id: S.String,
|
||||||
links: GraphLink[];
|
label: S.String,
|
||||||
}
|
color: S.optionalKey(S.String),
|
||||||
|
degree: S.Finite,
|
||||||
|
});
|
||||||
|
|
||||||
|
const GraphLinkValue: S.Codec<GraphLink, GraphLink> = S.Struct({
|
||||||
|
source: S.String,
|
||||||
|
target: S.String,
|
||||||
|
label: S.String,
|
||||||
|
});
|
||||||
|
|
||||||
|
export class GraphData extends S.Class<GraphData>("GraphData")({
|
||||||
|
nodes: S.Array(GraphNodeValue).pipe(S.mutable),
|
||||||
|
links: S.Array(GraphLinkValue).pipe(S.mutable),
|
||||||
|
}, { description: "Renderable graph nodes and links derived from triples." }) {}
|
||||||
|
|
||||||
export const DEFAULT_GRAPH_NODE_COLOR = "#82b582";
|
export const DEFAULT_GRAPH_NODE_COLOR = "#82b582";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,14 @@ import {
|
||||||
settingsAtom,
|
settingsAtom,
|
||||||
} from "@/atoms/workbench";
|
} from "@/atoms/workbench";
|
||||||
import type { BaseApi } from "@trustgraph/client";
|
import type { BaseApi } from "@trustgraph/client";
|
||||||
import type { MockWorkbenchFixture } from "@/qa/mock-api";
|
import { MockWorkbenchFixture, makeMockBaseApi, qaSettingsFromFixture, } from "@/qa/mock-api";
|
||||||
import { makeMockBaseApi, qaSettingsFromFixture, } from "@/qa/mock-api";
|
import { Schema as S } from "effect";
|
||||||
|
|
||||||
export interface WorkbenchQaWindowConfig {
|
export class WorkbenchQaWindowConfig extends S.Class<WorkbenchQaWindowConfig>("WorkbenchQaWindowConfig")({
|
||||||
readonly enabled?: boolean;
|
enabled: S.optionalKey(S.Boolean),
|
||||||
readonly fixture?: MockWorkbenchFixture;
|
fixture: S.optionalKey(MockWorkbenchFixture),
|
||||||
readonly flowId?: string;
|
flowId: S.optionalKey(S.String),
|
||||||
}
|
}, { description: "Browser-provided workbench QA boot configuration." }) {}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
|
|
|
||||||
|
|
@ -4,42 +4,97 @@ import { Clock, Effect, Match, Option, Schema as S } from "effect";
|
||||||
|
|
||||||
type ConfigValues = Record<string, Record<string, unknown>>;
|
type ConfigValues = Record<string, Record<string, unknown>>;
|
||||||
|
|
||||||
export interface MockWorkbenchFixture {
|
const UnknownRecord = S.Record(S.String, S.Unknown);
|
||||||
readonly settings?: {
|
const ConfigValuesRecord = S.Record(S.String, UnknownRecord);
|
||||||
readonly user?: string;
|
|
||||||
readonly apiKey?: string;
|
const ClientTerm: S.Codec<Triple["s"], Triple["s"]> = S.suspend(() =>
|
||||||
readonly gatewayUrl?: string;
|
S.Union([
|
||||||
readonly collection?: string;
|
S.Struct({ t: S.Literal("i"), i: S.String }),
|
||||||
readonly featureSwitches?: Record<string, boolean>;
|
S.Struct({ t: S.Literal("b"), d: S.String }),
|
||||||
};
|
S.Struct({
|
||||||
readonly flows?: {
|
t: S.Literal("l"),
|
||||||
readonly activeIds?: string[];
|
v: S.String,
|
||||||
readonly definitions?: Record<string, Record<string, unknown>>;
|
dt: S.optionalKey(S.String),
|
||||||
readonly blueprints?: Record<string, Record<string, unknown>>;
|
ln: S.optionalKey(S.String),
|
||||||
};
|
}),
|
||||||
readonly config?: {
|
S.Struct({
|
||||||
readonly prompt?: Record<string, unknown>;
|
t: S.Literal("t"),
|
||||||
readonly valuesByType?: ConfigValues;
|
tr: S.optionalKey(ClientTriple),
|
||||||
};
|
}),
|
||||||
readonly library?: {
|
])
|
||||||
readonly documents?: DocumentMetadata[];
|
);
|
||||||
readonly processing?: ProcessingMetadata[];
|
|
||||||
readonly metadataById?: Record<string, DocumentMetadata>;
|
const ClientTriple: S.Codec<Triple, Triple> = S.suspend(() =>
|
||||||
};
|
S.Struct({
|
||||||
readonly knowledge?: {
|
s: ClientTerm,
|
||||||
readonly kgCores?: string[];
|
p: ClientTerm,
|
||||||
readonly deCores?: string[];
|
o: ClientTerm,
|
||||||
readonly loadedKgCores?: string[];
|
g: S.optionalKey(S.String),
|
||||||
};
|
})
|
||||||
readonly collections?: Array<Record<string, unknown>>;
|
);
|
||||||
readonly graph?: {
|
|
||||||
readonly triplesByFlowCollection?: Record<string, Triple[]>;
|
const DocumentMetadataValue: S.Codec<DocumentMetadata, DocumentMetadata> = S.Struct({
|
||||||
readonly explainTriplesByGraph?: Record<string, Triple[]>;
|
id: S.optionalKey(S.String),
|
||||||
};
|
time: S.optionalKey(S.Finite),
|
||||||
readonly chat?: {
|
kind: S.optionalKey(S.String),
|
||||||
readonly delayFrames?: number;
|
title: S.optionalKey(S.String),
|
||||||
};
|
comments: S.optionalKey(S.String),
|
||||||
}
|
metadata: S.optionalKey(S.Array(ClientTriple).pipe(S.mutable)),
|
||||||
|
user: S.optionalKey(S.String),
|
||||||
|
tags: S.optionalKey(S.Array(S.String).pipe(S.mutable)),
|
||||||
|
parentId: S.optionalKey(S.String),
|
||||||
|
documentType: S.optionalKey(S.String),
|
||||||
|
"parent-id": S.optionalKey(S.String),
|
||||||
|
"document-type": S.optionalKey(S.String),
|
||||||
|
});
|
||||||
|
|
||||||
|
const ProcessingMetadataValue: S.Codec<ProcessingMetadata, ProcessingMetadata> = S.Struct({
|
||||||
|
id: S.optionalKey(S.String),
|
||||||
|
"document-id": S.optionalKey(S.String),
|
||||||
|
documentId: S.optionalKey(S.String),
|
||||||
|
time: S.optionalKey(S.Finite),
|
||||||
|
flow: S.optionalKey(S.String),
|
||||||
|
user: S.optionalKey(S.String),
|
||||||
|
collection: S.optionalKey(S.String),
|
||||||
|
tags: S.optionalKey(S.Array(S.String).pipe(S.mutable)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export class MockWorkbenchFixture extends S.Class<MockWorkbenchFixture>("MockWorkbenchFixture")({
|
||||||
|
settings: S.optionalKey(S.Struct({
|
||||||
|
user: S.optionalKey(S.String),
|
||||||
|
apiKey: S.optionalKey(S.String),
|
||||||
|
gatewayUrl: S.optionalKey(S.String),
|
||||||
|
collection: S.optionalKey(S.String),
|
||||||
|
featureSwitches: S.optionalKey(S.Record(S.String, S.Boolean)),
|
||||||
|
})),
|
||||||
|
flows: S.optionalKey(S.Struct({
|
||||||
|
activeIds: S.optionalKey(S.Array(S.String).pipe(S.mutable)),
|
||||||
|
definitions: S.optionalKey(S.Record(S.String, UnknownRecord)),
|
||||||
|
blueprints: S.optionalKey(S.Record(S.String, UnknownRecord)),
|
||||||
|
})),
|
||||||
|
config: S.optionalKey(S.Struct({
|
||||||
|
prompt: S.optionalKey(UnknownRecord),
|
||||||
|
valuesByType: S.optionalKey(ConfigValuesRecord),
|
||||||
|
})),
|
||||||
|
library: S.optionalKey(S.Struct({
|
||||||
|
documents: S.optionalKey(S.Array(DocumentMetadataValue).pipe(S.mutable)),
|
||||||
|
processing: S.optionalKey(S.Array(ProcessingMetadataValue).pipe(S.mutable)),
|
||||||
|
metadataById: S.optionalKey(S.Record(S.String, DocumentMetadataValue)),
|
||||||
|
})),
|
||||||
|
knowledge: S.optionalKey(S.Struct({
|
||||||
|
kgCores: S.optionalKey(S.Array(S.String).pipe(S.mutable)),
|
||||||
|
deCores: S.optionalKey(S.Array(S.String).pipe(S.mutable)),
|
||||||
|
loadedKgCores: S.optionalKey(S.Array(S.String).pipe(S.mutable)),
|
||||||
|
})),
|
||||||
|
collections: S.optionalKey(S.Array(UnknownRecord).pipe(S.mutable)),
|
||||||
|
graph: S.optionalKey(S.Struct({
|
||||||
|
triplesByFlowCollection: S.optionalKey(S.Record(S.String, S.Array(ClientTriple).pipe(S.mutable))),
|
||||||
|
explainTriplesByGraph: S.optionalKey(S.Record(S.String, S.Array(ClientTriple).pipe(S.mutable))),
|
||||||
|
})),
|
||||||
|
chat: S.optionalKey(S.Struct({
|
||||||
|
delayFrames: S.optionalKey(S.Finite),
|
||||||
|
})),
|
||||||
|
}, { description: "Seed fixture for deterministic workbench QA runs." }) {}
|
||||||
|
|
||||||
interface UploadSession {
|
interface UploadSession {
|
||||||
readonly metadata: DocumentMetadata;
|
readonly metadata: DocumentMetadata;
|
||||||
|
|
|
||||||
Binary file not shown.
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue