mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-07-02 02:58:10 +02:00
Remove native classes from TS runtime
This commit is contained in:
parent
952daf325d
commit
dca2786828
79 changed files with 7622 additions and 6703 deletions
|
|
@ -14,13 +14,14 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|||
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
||||
|
||||
import {
|
||||
FlowProcessor,
|
||||
ConsumerSpec,
|
||||
ProducerSpec,
|
||||
makeFlowProcessor,
|
||||
makeConsumerSpec,
|
||||
makeProducerSpec,
|
||||
makeFlowProcessorProgram,
|
||||
errorMessage,
|
||||
type ProcessorConfig,
|
||||
type FlowContext,
|
||||
type FlowProcessorRuntime,
|
||||
type ToolRequest,
|
||||
type ToolResponse,
|
||||
type EffectConfigHandler,
|
||||
|
|
@ -281,41 +282,35 @@ const onMcpToolRequest = Effect.fn("McpToolService.onRequest")(function* (
|
|||
});
|
||||
|
||||
export const makeMcpToolSpecs = (): ReadonlyArray<Spec<McpToolRuntime>> => [
|
||||
new ConsumerSpec<ToolRequest, McpToolHandlerError, McpToolRuntime>(
|
||||
makeConsumerSpec<ToolRequest, McpToolHandlerError, McpToolRuntime>(
|
||||
"mcp-tool-request",
|
||||
onMcpToolRequest,
|
||||
),
|
||||
new ProducerSpec<ToolResponse>("mcp-tool-response"),
|
||||
makeProducerSpec<ToolResponse>("mcp-tool-response"),
|
||||
];
|
||||
|
||||
export const makeMcpToolConfigHandlers = (): ReadonlyArray<
|
||||
EffectConfigHandler<never, McpToolRuntime>
|
||||
> => [onMcpConfig];
|
||||
|
||||
export class McpToolService extends FlowProcessor<McpToolRuntime> {
|
||||
private readonly runtime = Effect.runSync(makeMcpToolRuntime);
|
||||
export type McpToolService = FlowProcessorRuntime<McpToolRuntime>;
|
||||
|
||||
constructor(config: ProcessorConfig) {
|
||||
super(config);
|
||||
|
||||
for (const spec of makeMcpToolSpecs()) {
|
||||
this.registerSpecification(spec);
|
||||
}
|
||||
|
||||
this.registerConfigHandler((config, version) =>
|
||||
Effect.runPromise(onMcpConfig(config, version).pipe(
|
||||
Effect.provideService(McpToolRuntime, this.runtime),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
override startEffect() {
|
||||
return super.startEffect().pipe(
|
||||
Effect.provideService(McpToolRuntime, this.runtime),
|
||||
);
|
||||
}
|
||||
export function makeMcpToolService(config: ProcessorConfig): McpToolService {
|
||||
const runtime = Effect.runSync(makeMcpToolRuntime);
|
||||
const service = makeFlowProcessor(config, {
|
||||
specifications: makeMcpToolSpecs(),
|
||||
provide: (effect) => effect.pipe(Effect.provideService(McpToolRuntime, runtime)),
|
||||
});
|
||||
service.registerConfigHandler((pushedConfig, version) =>
|
||||
Effect.runPromise(onMcpConfig(pushedConfig, version).pipe(
|
||||
Effect.provideService(McpToolRuntime, runtime),
|
||||
)),
|
||||
);
|
||||
return service;
|
||||
}
|
||||
|
||||
export const McpToolService = makeMcpToolService;
|
||||
|
||||
export const program = makeFlowProcessorProgram<ProcessorConfig, never, McpToolRuntime>({
|
||||
id: "mcp-tool",
|
||||
specs: () => makeMcpToolSpecs(),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// ReAct agent -- barrel exports
|
||||
|
||||
export { AgentService } from "./service.js";
|
||||
export { StreamingReActParser } from "./parser.js";
|
||||
export { makeStreamingReActParser, type StreamingReActParser } from "./parser.js";
|
||||
export { buildReActPrompt } from "./prompt.js";
|
||||
export {
|
||||
createKnowledgeQueryTool,
|
||||
|
|
|
|||
|
|
@ -22,57 +22,75 @@ const MARKERS = [
|
|||
// Longest marker prefix for partial-match detection
|
||||
const MAX_MARKER_LEN = Math.max(...MARKERS.map((m) => m.prefix.length));
|
||||
|
||||
export class StreamingReActParser {
|
||||
private state: ReActState = "initial";
|
||||
private buffer = "";
|
||||
private onThought: (text: string) => void;
|
||||
private onAction: (name: string) => void;
|
||||
private onActionInput: (input: string) => void;
|
||||
private onFinalAnswer: (text: string) => void;
|
||||
export interface StreamingReActParser {
|
||||
readonly feed: (text: string) => void;
|
||||
readonly flush: () => void;
|
||||
}
|
||||
|
||||
constructor(
|
||||
onThought: (text: string) => void,
|
||||
onAction: (name: string) => void,
|
||||
onActionInput: (input: string) => void,
|
||||
onFinalAnswer: (text: string) => void,
|
||||
) {
|
||||
this.onThought = onThought;
|
||||
this.onAction = onAction;
|
||||
this.onActionInput = onActionInput;
|
||||
this.onFinalAnswer = onFinalAnswer;
|
||||
}
|
||||
export function makeStreamingReActParser(
|
||||
onThought: (text: string) => void,
|
||||
onAction: (name: string) => void,
|
||||
onActionInput: (input: string) => void,
|
||||
onFinalAnswer: (text: string) => void,
|
||||
): StreamingReActParser {
|
||||
let state: ReActState = "initial";
|
||||
let buffer = "";
|
||||
|
||||
/**
|
||||
* Feed a chunk of LLM output text into the parser.
|
||||
* Accumulates in a buffer and processes complete lines.
|
||||
*/
|
||||
feed(text: string): void {
|
||||
this.buffer += text;
|
||||
this.processBuffer(false);
|
||||
}
|
||||
const emitContent = (content: string): void => {
|
||||
if (content.length === 0) return;
|
||||
|
||||
/**
|
||||
* Flush any remaining buffered content at the end of output.
|
||||
*/
|
||||
flush(): void {
|
||||
this.processBuffer(true);
|
||||
// Emit any remaining buffer content in the current state
|
||||
if (this.buffer.trim().length > 0) {
|
||||
this.emitContent(this.buffer);
|
||||
this.buffer = "";
|
||||
switch (state) {
|
||||
case "thought":
|
||||
onThought(content);
|
||||
break;
|
||||
case "action":
|
||||
onAction(content);
|
||||
break;
|
||||
case "action_input":
|
||||
onActionInput(content);
|
||||
break;
|
||||
case "final_answer":
|
||||
onFinalAnswer(content);
|
||||
break;
|
||||
case "initial":
|
||||
// Content before any marker -- treat as thought
|
||||
state = "thought";
|
||||
onThought(content);
|
||||
break;
|
||||
case "complete":
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private processBuffer(isFinal: boolean): void {
|
||||
const processLine = (line: string): void => {
|
||||
const trimmed = line.trimStart();
|
||||
|
||||
// Check if this line starts a new section
|
||||
for (const marker of MARKERS) {
|
||||
if (trimmed.startsWith(marker.prefix)) {
|
||||
const content = trimmed.slice(marker.prefix.length).trim();
|
||||
state = marker.state;
|
||||
emitContent(content);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, this is continuation content for the current state
|
||||
if (trimmed.length > 0) {
|
||||
emitContent(trimmed);
|
||||
}
|
||||
};
|
||||
|
||||
const processBuffer = (isFinal: boolean): void => {
|
||||
// Process complete lines (terminated by newline)
|
||||
while (true) {
|
||||
const newlineIdx = this.buffer.indexOf("\n");
|
||||
const newlineIdx = buffer.indexOf("\n");
|
||||
if (newlineIdx === -1) {
|
||||
// No complete line yet.
|
||||
// If not final, check for partial marker match at the end and wait.
|
||||
if (!isFinal) {
|
||||
// If the remaining buffer could be the start of a marker, wait for more input.
|
||||
const trimmed = this.buffer.trimStart();
|
||||
const trimmed = buffer.trimStart();
|
||||
if (trimmed.length > 0 && trimmed.length < MAX_MARKER_LEN) {
|
||||
const couldBeMarker = MARKERS.some((m) =>
|
||||
m.prefix.startsWith(trimmed),
|
||||
|
|
@ -86,54 +104,29 @@ export class StreamingReActParser {
|
|||
break;
|
||||
}
|
||||
|
||||
const line = this.buffer.slice(0, newlineIdx);
|
||||
this.buffer = this.buffer.slice(newlineIdx + 1);
|
||||
this.processLine(line);
|
||||
const line = buffer.slice(0, newlineIdx);
|
||||
buffer = buffer.slice(newlineIdx + 1);
|
||||
processLine(line);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private processLine(line: string): void {
|
||||
const trimmed = line.trimStart();
|
||||
/**
|
||||
* Feed a chunk of LLM output text into the parser.
|
||||
* Accumulates in a buffer and processes complete lines.
|
||||
*/
|
||||
const feed = (text: string): void => {
|
||||
buffer += text;
|
||||
processBuffer(false);
|
||||
};
|
||||
|
||||
// Check if this line starts a new section
|
||||
for (const marker of MARKERS) {
|
||||
if (trimmed.startsWith(marker.prefix)) {
|
||||
const content = trimmed.slice(marker.prefix.length).trim();
|
||||
this.state = marker.state;
|
||||
this.emitContent(content);
|
||||
return;
|
||||
}
|
||||
const flush = (): void => {
|
||||
processBuffer(true);
|
||||
// Emit any remaining buffer content in the current state
|
||||
if (buffer.trim().length > 0) {
|
||||
emitContent(buffer);
|
||||
buffer = "";
|
||||
}
|
||||
};
|
||||
|
||||
// Otherwise, this is continuation content for the current state
|
||||
if (trimmed.length > 0) {
|
||||
this.emitContent(trimmed);
|
||||
}
|
||||
}
|
||||
|
||||
private emitContent(content: string): void {
|
||||
if (content.length === 0) return;
|
||||
|
||||
switch (this.state) {
|
||||
case "thought":
|
||||
this.onThought(content);
|
||||
break;
|
||||
case "action":
|
||||
this.onAction(content);
|
||||
break;
|
||||
case "action_input":
|
||||
this.onActionInput(content);
|
||||
break;
|
||||
case "final_answer":
|
||||
this.onFinalAnswer(content);
|
||||
break;
|
||||
case "initial":
|
||||
// Content before any marker -- treat as thought
|
||||
this.state = "thought";
|
||||
this.onThought(content);
|
||||
break;
|
||||
case "complete":
|
||||
break;
|
||||
}
|
||||
}
|
||||
return { feed, flush };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,14 +17,15 @@
|
|||
*/
|
||||
|
||||
import {
|
||||
FlowProcessor,
|
||||
ConsumerSpec,
|
||||
ProducerSpec,
|
||||
RequestResponseSpec,
|
||||
makeFlowProcessor,
|
||||
makeConsumerSpec,
|
||||
makeProducerSpec,
|
||||
makeRequestResponseSpec,
|
||||
makeFlowProcessorProgram,
|
||||
errorMessage,
|
||||
type ProcessorConfig,
|
||||
type FlowContext,
|
||||
type FlowProcessorRuntime,
|
||||
type AgentRequest,
|
||||
type AgentResponse,
|
||||
type TextCompletionRequest,
|
||||
|
|
@ -488,32 +489,32 @@ const onAgentRequest = Effect.fn("AgentService.onRequest")(function* (
|
|||
});
|
||||
|
||||
export const makeAgentSpecs = (): ReadonlyArray<Spec<AgentRuntime>> => [
|
||||
new ConsumerSpec<AgentRequest, AgentHandlerError, AgentRuntime>(
|
||||
makeConsumerSpec<AgentRequest, AgentHandlerError, AgentRuntime>(
|
||||
"agent-request",
|
||||
onAgentRequest,
|
||||
),
|
||||
new ProducerSpec<AgentResponse>("agent-response"),
|
||||
new RequestResponseSpec<TextCompletionRequest, TextCompletionResponse>(
|
||||
makeProducerSpec<AgentResponse>("agent-response"),
|
||||
makeRequestResponseSpec<TextCompletionRequest, TextCompletionResponse>(
|
||||
"llm",
|
||||
"text-completion-request",
|
||||
"text-completion-response",
|
||||
),
|
||||
new RequestResponseSpec<GraphRagRequest, GraphRagResponse>(
|
||||
makeRequestResponseSpec<GraphRagRequest, GraphRagResponse>(
|
||||
"graph-rag",
|
||||
"graph-rag-request",
|
||||
"graph-rag-response",
|
||||
),
|
||||
new RequestResponseSpec<DocumentRagRequest, DocumentRagResponse>(
|
||||
makeRequestResponseSpec<DocumentRagRequest, DocumentRagResponse>(
|
||||
"doc-rag",
|
||||
"document-rag-request",
|
||||
"document-rag-response",
|
||||
),
|
||||
new RequestResponseSpec<TriplesQueryRequest, TriplesQueryResponse>(
|
||||
makeRequestResponseSpec<TriplesQueryRequest, TriplesQueryResponse>(
|
||||
"triples",
|
||||
"triples-request",
|
||||
"triples-response",
|
||||
),
|
||||
new RequestResponseSpec<ToolRequest, ToolResponse>(
|
||||
makeRequestResponseSpec<ToolRequest, ToolResponse>(
|
||||
"mcp-tool",
|
||||
"mcp-tool-request",
|
||||
"mcp-tool-response",
|
||||
|
|
@ -524,32 +525,25 @@ export const makeAgentConfigHandlers = (): ReadonlyArray<
|
|||
EffectConfigHandler<never, AgentRuntime>
|
||||
> => [onToolsConfig];
|
||||
|
||||
export class AgentService extends FlowProcessor<AgentRuntime> {
|
||||
private readonly runtime = Effect.runSync(makeAgentRuntime);
|
||||
export type AgentService = FlowProcessorRuntime<AgentRuntime>;
|
||||
|
||||
constructor(config: ProcessorConfig) {
|
||||
super(config);
|
||||
|
||||
for (const spec of makeAgentSpecs()) {
|
||||
this.registerSpecification(spec);
|
||||
}
|
||||
|
||||
this.registerConfigHandler((config, version) =>
|
||||
Effect.runPromise(onToolsConfig(config, version).pipe(
|
||||
Effect.provideService(AgentRuntime, this.runtime),
|
||||
)),
|
||||
);
|
||||
|
||||
console.log("[AgentService] Service initialized");
|
||||
}
|
||||
|
||||
override startEffect() {
|
||||
return super.startEffect().pipe(
|
||||
Effect.provideService(AgentRuntime, this.runtime),
|
||||
);
|
||||
}
|
||||
export function makeAgentService(config: ProcessorConfig): AgentService {
|
||||
const runtime = Effect.runSync(makeAgentRuntime);
|
||||
const service = makeFlowProcessor(config, {
|
||||
specifications: makeAgentSpecs(),
|
||||
provide: (effect) => effect.pipe(Effect.provideService(AgentRuntime, runtime)),
|
||||
});
|
||||
service.registerConfigHandler((pushedConfig, version) =>
|
||||
Effect.runPromise(onToolsConfig(pushedConfig, version).pipe(
|
||||
Effect.provideService(AgentRuntime, runtime),
|
||||
)),
|
||||
);
|
||||
console.log("[AgentService] Service initialized");
|
||||
return service;
|
||||
}
|
||||
|
||||
export const AgentService = makeAgentService;
|
||||
|
||||
/**
|
||||
* Simple line-based parser for ReAct LLM output.
|
||||
*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue